@joystick.js/cli-canary 0.0.0-canary.1648 → 0.0.0-canary.1649

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.
Files changed (39) hide show
  1. package/dist/cli.js +1 -1
  2. package/dist/commands/index.js +1 -1
  3. package/dist/commands/push_v2/cdn_mirrors.js +1 -0
  4. package/dist/commands/push_v2/check_if_settings_valid.js +1 -0
  5. package/dist/commands/push_v2/create_version.js +1 -0
  6. package/dist/commands/push_v2/definition.js +1 -0
  7. package/dist/commands/push_v2/get_app_domain.js +1 -0
  8. package/dist/commands/push_v2/get_deployment.js +1 -0
  9. package/dist/commands/push_v2/get_push_domain.js +1 -0
  10. package/dist/commands/push_v2/get_session_token.js +1 -0
  11. package/dist/commands/push_v2/get_settings_file.js +1 -0
  12. package/dist/commands/push_v2/handle_initial_deployment.js +1 -0
  13. package/dist/commands/push_v2/handle_version_deployment.js +1 -0
  14. package/dist/commands/push_v2/index.js +8 -0
  15. package/dist/commands/push_v2/prompts/confirm_deployment.js +17 -0
  16. package/dist/commands/push_v2/prompts/session_token.js +5 -0
  17. package/dist/commands/push_v2/upload_build_to_cdn.js +1 -0
  18. package/dist/commands/push_v2/validate_deployment.js +1 -0
  19. package/dist/commands/push_v2/validate_push_config.js +1 -0
  20. package/package.json +2 -2
  21. package/src/cli.js +10 -0
  22. package/src/commands/index.js +2 -0
  23. package/src/commands/push_v2/cdn_mirrors.js +7 -0
  24. package/src/commands/push_v2/check_if_settings_valid.js +10 -0
  25. package/src/commands/push_v2/create_version.js +20 -0
  26. package/src/commands/push_v2/definition.js +58 -0
  27. package/src/commands/push_v2/get_app_domain.js +11 -0
  28. package/src/commands/push_v2/get_deployment.js +19 -0
  29. package/src/commands/push_v2/get_push_domain.js +9 -0
  30. package/src/commands/push_v2/get_session_token.js +31 -0
  31. package/src/commands/push_v2/get_settings_file.js +47 -0
  32. package/src/commands/push_v2/handle_initial_deployment.js +24 -0
  33. package/src/commands/push_v2/handle_version_deployment.js +23 -0
  34. package/src/commands/push_v2/index.js +160 -0
  35. package/src/commands/push_v2/prompts/confirm_deployment.js +73 -0
  36. package/src/commands/push_v2/prompts/session_token.js +11 -0
  37. package/src/commands/push_v2/upload_build_to_cdn.js +59 -0
  38. package/src/commands/push_v2/validate_deployment.js +19 -0
  39. package/src/commands/push_v2/validate_push_config.js +18 -0
package/dist/cli.js CHANGED
@@ -1 +1 @@
1
- import c from"chalk";import o from"./commands/index.js";import e from"./lib/parse_args.js";import a from"./lib/parse_options.js";const i=Object.keys(o),n=process.argv.filter(s=>i.includes(s)),m=process.argv.some(s=>["-h","--help"].includes(s));if((m||n.length===0)&&(o.help.command(),process.exit(0)),n.length>1&&(console.log(c.red("Only one command can be called at a time.")),process.exit(0)),n.includes("build")){const s=e(o.build.args),t=a(o.build.options);o.build.command&&o.build.command(s,t)}if(n.includes("create")){const s=e(o.create.args),t=a(o.create.options);s.name||(console.log(c.red("Must pass a <name> for your app to joystick create. Run joystick --help for examples.")),process.exit(0)),o.create.command&&o.create.command(s,t)}if(n.includes("help")){const s=e(o.help.args),t=a(o.help.options);o.help.command&&o.help.command(s,t)}if(n.includes("logout")){const s=e(o.logout.args),t=a(o.logout.options);o.logout.command&&o.logout.command(s,t)}if(n.includes("push")){const s=e(o.push.args),t=a(o.push.options);o.push.command&&o.push.command(s,t)}if(n.includes("start")){const s=e(o.start.args),t=a(o.start.options);o.start.command&&o.start.command(s,t)}if(n.includes("test")){const s=e(o.test.args),t=a(o.test.options);o.test.command&&o.test.command(s,t)}if(n.includes("update")){const s=e(o.update.args),t=a(o.update.options);o.update.command&&o.update.command(s,t)}if(n.includes("use")){const s=e(o.use.args),t=a(o.use.options);o.use.command&&o.use.command(s,t)}
1
+ import e from"chalk";import o from"./commands/index.js";import c from"./lib/parse_args.js";import a from"./lib/parse_options.js";const i=Object.keys(o),n=process.argv.filter(s=>i.includes(s)),m=process.argv.some(s=>["-h","--help"].includes(s));if((m||n.length===0)&&(o.help.command(),process.exit(0)),n.length>1&&(console.log(e.red("Only one command can be called at a time.")),process.exit(0)),n.includes("build")){const s=c(o.build.args),t=a(o.build.options);o.build.command&&o.build.command(s,t)}if(n.includes("create")){const s=c(o.create.args),t=a(o.create.options);s.name||(console.log(e.red("Must pass a <name> for your app to joystick create. Run joystick --help for examples.")),process.exit(0)),o.create.command&&o.create.command(s,t)}if(n.includes("help")){const s=c(o.help.args),t=a(o.help.options);o.help.command&&o.help.command(s,t)}if(n.includes("logout")){const s=c(o.logout.args),t=a(o.logout.options);o.logout.command&&o.logout.command(s,t)}if(n.includes("push")){const s=c(o.push.args),t=a(o.push.options);o.push.command&&o.push.command(s,t)}if(n.includes("push_v2")){const s=c(o.push_v2.args),t=a(o.push_v2.options);o.push_v2.command&&o.push_v2.command(s,t)}if(n.includes("start")){const s=c(o.start.args),t=a(o.start.options);o.start.command&&o.start.command(s,t)}if(n.includes("test")){const s=c(o.test.args),t=a(o.test.options);o.test.command&&o.test.command(s,t)}if(n.includes("update")){const s=c(o.update.args),t=a(o.update.options);o.update.command&&o.update.command(s,t)}if(n.includes("use")){const s=c(o.use.args),t=a(o.use.options);o.use.command&&o.use.command(s,t)}
@@ -1 +1 @@
1
- import o from"./build/definition.js";import r from"./create/definition.js";import m from"./help/definition.js";import t from"./logout/definition.js";import p from"./push/definition.js";import f from"./start/definition.js";import i from"./test/definition.js";import e from"./update/definition.js";import s from"./use/definition.js";const u={build:o,create:r,help:m,logout:t,push:p,start:f,test:i,update:e,use:s};var j=u;export{j as default};
1
+ import o from"./build/definition.js";import r from"./create/definition.js";import m from"./help/definition.js";import t from"./logout/definition.js";import p from"./push/definition.js";import f from"./push_v2/definition.js";import i from"./start/definition.js";import e from"./test/definition.js";import s from"./update/definition.js";import u from"./use/definition.js";const a={build:o,create:r,help:m,logout:t,push:p,push_v2:f,start:i,test:e,update:s,use:u};var j=a;export{j as default};
@@ -0,0 +1 @@
1
+ const s=["https://push-versions-1.cheatcode.co","https://push-versions-2.cheatcode.co","https://push-versions-3.cheatcode.co"];var o=s;export{o as default};
@@ -0,0 +1 @@
1
+ const t=(e="")=>{try{return JSON.parse(e),!0}catch{return!1}};var c=t;export{c as default};
@@ -0,0 +1 @@
1
+ import a from"node-fetch";const t=(e={})=>a(`${e?.push_provision_domain}/api/versions/${e?.domain}`,{method:"POST",headers:{"Content-Type":"application/json","x-push-session-token":e?.session_token},body:JSON.stringify(e?.body)})?.then(async n=>(await n.json())?.data).catch(n=>{console.warn(n)});var r=t;export{r as default};
@@ -0,0 +1 @@
1
+ import n from"./index.js";const[o,d,...e]=process.argv,s={description:"Deploy your Joystick app using Push.",args:{},options:{domain:{flags:{"-d":{set:!!e.includes("-d"),value:!!e.includes("-d")&&e[e.indexOf("-d")+1],parent:"push"},"--domain":{set:!!e.includes("--domain"),value:!!e.includes("--domain")&&e[e.indexOf("--domain")+1],parent:"push"}},description:"The domain of the app Push should deploy to."},environment:{flags:{"-e":{set:!!e.includes("-e"),value:!!e.includes("-e")&&e[e.indexOf("-e")+1],parent:"push"},"--environment":{set:!!e.includes("--environment"),value:!!e.includes("--environment")&&e[e.indexOf("--environment")+1],parent:"push"}},description:"The value you want to use for NODE_ENV in the deployed app (e.g., staging or production). Default is production."},push_server:{flags:{"-s":{set:!!e.includes("-s"),value:!!e.includes("-s")&&e[e.indexOf("-s")+1],parent:"push"},"--server":{set:!!e.includes("--server"),value:!!e.includes("--server")&&e[e.indexOf("--server")+1],parent:"push"}},description:"The Push provision server to target (development or production)."}},command:n};var r=s;export{r as default};
@@ -0,0 +1 @@
1
+ const o=(t="production")=>({development:"http://localhost:2600",staging:"https://staging.push.cheatcode.co",production:"https://push.cheatcode.co"})[t];var e=o;export{e as default};
@@ -0,0 +1 @@
1
+ import a from"node-fetch";const n=(e={})=>a(`${e?.push_domain}/api/deployments/${e?.domain}`,{method:"GET",headers:{"x-push-session-token":e?.session_token,Accept:"application/json"}})?.then(async t=>(await t.json())?.data).catch(t=>{console.warn(t)});var c=n;export{c as default};
@@ -0,0 +1 @@
1
+ const t=(o="production")=>({development:"http://localhost:2603",staging:"https://staging.provision.cheatcode.co",production:"https://provision.cheatcode.co"})[o];var e=t;export{e as default};
@@ -0,0 +1 @@
1
+ import i from"os";import n from"fs";import r from"inquirer";import e from"../../lib/path_exists.js";import p from"./prompts/session_token.js";const{readFile:m,mkdir:a,writeFile:_}=n.promises,f=async()=>{const s=i.homedir(),t=`${s}/.push/session_token`;if(await e(t))return m(t,"utf-8");const{session_token:o}=await r.prompt(p());if(o)return await e(`${s}/.push`)||await a(`${s}/.push`),await _(`${s}/.push/session_token`,o),o};var x=f;export{x as default};
@@ -0,0 +1 @@
1
+ import n from"fs";import i from"../../lib/cli_log.js";import o from"../../lib/is_valid_json_string.js";import r from"../../lib/path_exists.js";const{readFile:a}=n.promises,c=async(t="")=>{const s=`settings.${t}.json`;return await r(s)?a(s):(i(`Could not find a ${s} in your project. Double-check this exists and try again.`,{level:"danger",docs:"https://cheatcode.co/docs/push/config"}),process.exit(0))},_=async t=>{const s=`settings.${t?.environment}.json`,e=await c(t?.environment);return o(e)?JSON.parse(e):(i(`${s} contains invalid JSON. Double-check the syntax and try again.`,{level:"danger",docs:"https://cheatcode.co/docs/push/config"}),process.exit(0))};var m=_;export{m as default};
@@ -0,0 +1 @@
1
+ import t from"node-fetch";const a=async(e={})=>t(`${e?.push_provision_domain}/api/deployments/${e?.deployment?.domain}`,{method:"POST",headers:{"x-push-session-token":e?.session_token,"Content-Type":"application/json"},body:JSON.stringify({environment:e?.environment,build_timestamp:e?.build_timestamp,domain:e?.deployment?.domain,deployment_id:e?.deployment?._id})}).then(async n=>(await n.json())?.data).catch(n=>{console.warn(n)});var m=a;export{m as default};
@@ -0,0 +1 @@
1
+ import t from"node-fetch";const a=async(e={})=>t(`${e?.push_provision_domain}/api/deployments/${e?.deployment?.domain}`,{method:"PUT",headers:{"x-push-session-token":e?.session_token,"Content-Type":"application/json"},body:JSON.stringify({build_timestamp:e?.build_timestamp,domain:e?.deployment?.domain,deployment_id:e?.deployment?._id})}).then(async n=>(await n.json())?.data).catch(n=>{console.warn(n)});var i=a;export{i as default};
@@ -0,0 +1,8 @@
1
+ import p from"chalk";import m from"inquirer";import l from"../../lib/build/index.js";import c from"../../lib/cli_log.js";import a from"./prompts/confirm_deployment.js";import"./create_version.js";import"./get_deployment.js";import _ from"./get_push_domain.js";import u from"./get_settings_file.js";import f from"./get_session_token.js";import"./handle_initial_deployment.js";import"./handle_version_deployment.js";import g from"../../lib/loader.js";import"./upload_build_to_cdn.js";import"./validate_deployment.js";import"./validate_push_config.js";const P=(o="",t="",s={})=>{let e="";for(let n=0;n<s?.errors?.length;n+=1){const r=s?.errors[n];o==="push_config"&&(e+=`${p.yellowBright(`> ${r.field}`)}
2
+ `),e+=`${r?.error}
3
+ ${n+1===s?.errors?.length?"":`
4
+ `}`}c(`${t?`${p.yellowBright(t)}
5
+
6
+
7
+ `:""}${e}`,{level:"danger",docs:{push_config:"https://cheatcode.co/docs/push/config",deployment:"https://cheatcode.co/docs/push/subscription"}[o]}),process.exit(0)},h=async()=>{const o=await f();return o||(c(data?.error?.message,{level:"danger",docs:"https://cheatcode.co/docs/push/cli#authentication"}),process.exit(0))},Q=(o="")=>(c(`settings.${o}.json must contain a push config object. Add this following the docs link below and try again.`,{level:"danger",docs:"https://cheatcode.co/docs/push/config"}),process.exit(0)),y=async(o={},t={})=>{process.loader=new g,process.loader.print("Starting deployment...");const s=await h(),e=t?.environment||"production",r=(await u({environment:e}))?.config?.push||{},w=_(t?.push_server),i={status:"undeployed",deployment_secret:"abc123"},{confirm_deployment:d}=i?.status==="undeployed"?await m.prompt(a(push_config_validation_response?.instances)):{confirm_deployment:!0};if(d){i?.status==="undeployed"&&console.log(`
8
+ `);const v=new Date().toISOString();await l({environment:e,encrypt_build:!0,encryption_key:i?.deployment_secret,silence_confirmation:!0}),console.log("Now do the docker build.")}};var R=y;export{R as default};
@@ -0,0 +1,17 @@
1
+ import _ from"ascii-table";import e from"chalk";const s=(r,[l="",o=[]])=>{r.addRow(`${e.magenta(l)}
2
+ `);for(let t=0;t<o?.length;t+=1){const n=o[t];r.addRow(`${e.yellow(n?.provider)} (${e.green(n?.region)})`,`${e.white(n?.size)} $${e.green(n?.pricePerMonth)}/mo. x${e.blue(n?.quantity)} ${e.white("=")} $${e.green(n?.quantity*n?.pricePerMonth)}/mo.${t+1===o?.length?`
3
+ `:""}`)}},d=(r=[])=>Object.values(r)?.flatMap(o=>o)?.reduce((o=0,t={})=>(o+=t?.pricePerMonth*t?.quantity,o),0),h=(r={})=>{const l=d(r?.load_balancer),o=d(r?.app),t=l+o,n=new _;n.removeBorder(),n.addRow(`${e.blue("Load Balancer")}
4
+ `);const i=Object.entries(r?.load_balancer),c=Object.entries(r?.app);for(let a=0;a<i?.length;a+=1)s(n,i[a]);n.addRow(`
5
+ ${e.blue("App")}
6
+ `);for(let a=0;a<c?.length;a+=1)s(n,c[a]);n.addRow(e.white(`
7
+ ---
8
+ `)),n.addRow(e.green("Monthly Cost"),e.white(`$${e.green(t)}/mo.`)),n.addRow(e.green("Annual Cost"),e.white(`$${e.green(t*12)}/yr.`));const p=t>100;return[{name:"confirm_deployment",type:"confirm",prefix:"",message:`
9
+ ${e.greenBright(">")} Start deployment and provision these instances?`,suffix:`
10
+
11
+ ${n.toString()}
12
+ ${p?`
13
+
14
+ ${e.yellowBright(`!!! >>> These costs are ${e.magenta("high")}. Be absolutely ${e.magenta("CERTAIN")} you want to run this deployment. <<< !!!`)}
15
+
16
+ `:`
17
+ `}`}]};var g=h;export{g as default};
@@ -0,0 +1,5 @@
1
+ import e from"chalk";const o=()=>[{name:"session_token",type:"text",prefix:"",message:`
2
+ ${e.yellowBright(">")} To login to Push, paste your session token below:
3
+ `,suffix:` ${e.yellowBright("Find your session token here:")} https://push.cheatcode.co/account/profile
4
+
5
+ `}];var t=o;export{t as default};
@@ -0,0 +1 @@
1
+ import n from"node-fetch";import i from"form-data";import d from"fs";import m from"lodash";import c from"./cdn_mirrors.js";const{readFile:l}=d.promises,p=async()=>{const a=(await Promise.allSettled(c?.map(async t=>{const o=new AbortController;setTimeout(()=>o.abort(),2e3);const e=await n(`${t}/api/ping`,{signal:o.signal}).catch(r=>({status:503}));return{mirror:t,status:e.status}})))?.map(t=>t.value);return m.orderBy(a,["status","mirror"])[0]},u=async(a="",t={},o="")=>{const e=await p(),r=new i;return r.append("version_tar",await l(".build/build.encrypted.tar.gz"),`${a}.tar.gz`),r.append("build_timestamp",a),r.append("domain",t?.domain),r.append("deployment_id",t?._id),n(`${e?.mirror}/api/versions`,{method:"POST",headers:{...r.getHeaders(),"x-push-session-token":o},body:r}).then(async s=>(await s.json())?.data).catch(s=>{console.warn(s)})};var y=u;export{y as default};
@@ -0,0 +1 @@
1
+ import n from"node-fetch";const t=(a={})=>n(`${a?.push_provision_domain}/api/deployments/${a?.push_config?.domain}/validate`,{method:"POST",headers:{"Content-Type":"application/json","x-push-session-token":a?.session_token},body:JSON.stringify(a?.push_config)})?.then(async e=>(await e.json())?.data).catch(e=>{console.warn(e)});var i=t;export{i as default};
@@ -0,0 +1 @@
1
+ import n from"node-fetch";const o=(t={})=>n(`${t?.push_provision_domain}/api/validate/config`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t?.push_config)})?.then(async a=>(await a.json())?.data).catch(a=>{console.warn(a)});var c=o;export{c as default};
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@joystick.js/cli-canary",
3
3
  "type": "module",
4
- "version": "0.0.0-canary.1648",
5
- "canary_version": "0.0.0-canary.1647",
4
+ "version": "0.0.0-canary.1649",
5
+ "canary_version": "0.0.0-canary.1648",
6
6
  "description": "The CLI for Joystick.",
7
7
  "main": "dist/index.js",
8
8
  "bin": {
package/src/cli.js CHANGED
@@ -68,6 +68,16 @@ if (commands_called.includes('push')) {
68
68
  }
69
69
  }
70
70
 
71
+ // NOTE: Temporary. Eventually will replace block above.
72
+ if (commands_called.includes('push_v2')) {
73
+ const args = parse_args(commands.push_v2.args);
74
+ const options = parse_options(commands.push_v2.options);
75
+
76
+ if (commands.push_v2.command) {
77
+ commands.push_v2.command(args, options);
78
+ }
79
+ }
80
+
71
81
  if (commands_called.includes('start')) {
72
82
  const args = parse_args(commands.start.args);
73
83
  const options = parse_options(commands.start.options);
@@ -3,6 +3,7 @@ import create from './create/definition.js';
3
3
  import help from './help/definition.js';
4
4
  import logout from './logout/definition.js';
5
5
  import push from './push/definition.js';
6
+ import push_v2 from './push_v2/definition.js';
6
7
  import start from './start/definition.js';
7
8
  import test from "./test/definition.js";
8
9
  import update from './update/definition.js';
@@ -14,6 +15,7 @@ const commands = {
14
15
  help,
15
16
  logout,
16
17
  push,
18
+ push_v2,
17
19
  start,
18
20
  test,
19
21
  update,
@@ -0,0 +1,7 @@
1
+ const cdn_mirrors = [
2
+ 'https://push-versions-1.cheatcode.co',
3
+ 'https://push-versions-2.cheatcode.co',
4
+ 'https://push-versions-3.cheatcode.co',
5
+ ];
6
+
7
+ export default cdn_mirrors;
@@ -0,0 +1,10 @@
1
+ const check_if_settings_valid = (settings = '') => {
2
+ try {
3
+ JSON.parse(settings);
4
+ return true;
5
+ } catch (exception) {
6
+ return false;
7
+ }
8
+ };
9
+
10
+ export default check_if_settings_valid;
@@ -0,0 +1,20 @@
1
+ import fetch from 'node-fetch';
2
+
3
+ const create_version = (options = {}) => {
4
+ return fetch(`${options?.push_provision_domain}/api/versions/${options?.domain}`, {
5
+ method: 'POST',
6
+ headers: {
7
+ 'Content-Type': 'application/json',
8
+ 'x-push-session-token': options?.session_token,
9
+ },
10
+ body: JSON.stringify(options?.body),
11
+ })?.then(async (response) => {
12
+ // NOTE: We only care about the response body here so parse it out and return it.
13
+ const data = await response.json();
14
+ return data?.data;
15
+ }).catch((error) => {
16
+ console.warn(error);
17
+ });
18
+ };
19
+
20
+ export default create_version;
@@ -0,0 +1,58 @@
1
+ import push from './index.js';
2
+
3
+ const [_node, _bin, ...raw_args] = process.argv;
4
+
5
+ const definition = {
6
+ description: 'Deploy your Joystick app using Push.',
7
+ args: {},
8
+ options: {
9
+ domain: {
10
+ flags: {
11
+ '-d': {
12
+ set: !!raw_args.includes('-d'),
13
+ value: !!raw_args.includes('-d') && raw_args[raw_args.indexOf('-d') + 1],
14
+ parent: 'push',
15
+ },
16
+ '--domain': {
17
+ set: !!raw_args.includes('--domain'),
18
+ value: !!raw_args.includes('--domain') && raw_args[raw_args.indexOf('--domain') + 1],
19
+ parent: 'push',
20
+ },
21
+ },
22
+ description: 'The domain of the app Push should deploy to.',
23
+ },
24
+ environment: {
25
+ flags: {
26
+ '-e': {
27
+ set: !!raw_args.includes('-e'),
28
+ value: !!raw_args.includes('-e') && raw_args[raw_args.indexOf('-e') + 1],
29
+ parent: 'push',
30
+ },
31
+ '--environment': {
32
+ set: !!raw_args.includes('--environment'),
33
+ value: !!raw_args.includes('--environment') && raw_args[raw_args.indexOf('--environment') + 1],
34
+ parent: 'push',
35
+ },
36
+ },
37
+ description: 'The value you want to use for NODE_ENV in the deployed app (e.g., staging or production). Default is production.',
38
+ },
39
+ push_server: {
40
+ flags: {
41
+ '-s': {
42
+ set: !!raw_args.includes('-s'),
43
+ value: !!raw_args.includes('-s') && raw_args[raw_args.indexOf('-s') + 1],
44
+ parent: 'push',
45
+ },
46
+ '--server': {
47
+ set: !!raw_args.includes('--server'),
48
+ value: !!raw_args.includes('--server') && raw_args[raw_args.indexOf('--server') + 1],
49
+ parent: 'push',
50
+ },
51
+ },
52
+ description: 'The Push provision server to target (development or production).',
53
+ },
54
+ },
55
+ command: push,
56
+ };
57
+
58
+ export default definition;
@@ -0,0 +1,11 @@
1
+ const get_app_domain = (provision_server = 'production') => {
2
+ // NOTE: Rely on provision_server CLI option flag here because that will need
3
+ // to be paired with the appropriate app server.
4
+ return {
5
+ development: 'http://localhost:2600',
6
+ staging: 'https://staging.push.cheatcode.co',
7
+ production: 'https://push.cheatcode.co',
8
+ }[provision_server];
9
+ };
10
+
11
+ export default get_app_domain;
@@ -0,0 +1,19 @@
1
+ import fetch from 'node-fetch';
2
+
3
+ const get_deployment = (options = {}) => {
4
+ return fetch(`${options?.push_domain}/api/deployments/${options?.domain}`, {
5
+ method: 'GET',
6
+ headers: {
7
+ 'x-push-session-token': options?.session_token,
8
+ Accept: 'application/json',
9
+ },
10
+ })?.then(async (response) => {
11
+ // NOTE: We only care about the data in the response here so just return it.
12
+ const data = await response.json();
13
+ return data?.data;
14
+ }).catch((error) => {
15
+ console.warn(error);
16
+ });
17
+ };
18
+
19
+ export default get_deployment;
@@ -0,0 +1,9 @@
1
+ const get_provision_domain = (provision_server = 'production') => {
2
+ return {
3
+ development: 'http://localhost:2603',
4
+ staging: 'https://staging.provision.cheatcode.co',
5
+ production: 'https://provision.cheatcode.co',
6
+ }[provision_server];
7
+ };
8
+
9
+ export default get_provision_domain;
@@ -0,0 +1,31 @@
1
+ import os from 'os';
2
+ import fs from 'fs';
3
+ import inquirer from 'inquirer';
4
+ import path_exists from '../../lib/path_exists.js';
5
+ import session_token_prompt from './prompts/session_token.js';
6
+
7
+ const { readFile, mkdir, writeFile } = fs.promises;
8
+
9
+ const get_session_token = async () => {
10
+ const home_directory = os.homedir();
11
+ const session_token_path = `${home_directory}/.push/session_token`;
12
+ const session_token_exists = await path_exists(session_token_path);
13
+
14
+ if (session_token_exists) {
15
+ return readFile(session_token_path, 'utf-8');
16
+ }
17
+
18
+ const { session_token } = await inquirer.prompt(session_token_prompt());
19
+
20
+ if (session_token) {
21
+ if (!(await path_exists(`${home_directory}/.push`))) {
22
+ await mkdir(`${home_directory}/.push`);
23
+ }
24
+
25
+ await writeFile(`${home_directory}/.push/session_token`, session_token);
26
+
27
+ return session_token;
28
+ }
29
+ };
30
+
31
+ export default get_session_token;
@@ -0,0 +1,47 @@
1
+ import fs from 'fs';
2
+ import cli_log from '../../lib/cli_log.js';
3
+ import is_valid_json_string from '../../lib/is_valid_json_string.js';
4
+ import path_exists from '../../lib/path_exists.js';
5
+
6
+ const { readFile } = fs.promises;
7
+
8
+ const read_settings_file = async (environment = '') => {
9
+ const settings_file_name = `settings.${environment}.json`;
10
+ const settings_file_exists = await path_exists(settings_file_name);
11
+
12
+ if (!settings_file_exists) {
13
+ cli_log(
14
+ `Could not find a ${settings_file_name} in your project. Double-check this exists and try again.`,
15
+ {
16
+ level: "danger",
17
+ docs: "https://cheatcode.co/docs/push/config"
18
+ }
19
+ );
20
+
21
+ return process.exit(0);
22
+ }
23
+
24
+ return readFile(settings_file_name);
25
+ };
26
+
27
+ const get_settings_file = async (options) => {
28
+ const settings_file_name = `settings.${options?.environment}.json`;
29
+ const raw_settings_file = await read_settings_file(options?.environment);
30
+ const settings_file_is_valid_json = is_valid_json_string(raw_settings_file);
31
+
32
+ if (!settings_file_is_valid_json) {
33
+ cli_log(
34
+ `${settings_file_name} contains invalid JSON. Double-check the syntax and try again.`,
35
+ {
36
+ level: "danger",
37
+ docs: "https://cheatcode.co/docs/push/config"
38
+ }
39
+ );
40
+
41
+ return process.exit(0);
42
+ }
43
+
44
+ return JSON.parse(raw_settings_file);
45
+ };
46
+
47
+ export default get_settings_file;
@@ -0,0 +1,24 @@
1
+ import fetch from "node-fetch";
2
+
3
+ const handle_initial_deployment = async (options = {}) => {
4
+ return fetch(`${options?.push_provision_domain}/api/deployments/${options?.deployment?.domain}`, {
5
+ method: "POST",
6
+ headers: {
7
+ "x-push-session-token": options?.session_token,
8
+ 'Content-Type': 'application/json',
9
+ },
10
+ body: JSON.stringify({
11
+ environment: options?.environment,
12
+ build_timestamp: options?.build_timestamp,
13
+ domain: options?.deployment?.domain,
14
+ deployment_id: options?.deployment?._id,
15
+ }),
16
+ }).then(async (response) => {
17
+ const data = await response.json();
18
+ return data?.data;
19
+ }).catch((error) => {
20
+ console.warn(error);
21
+ });
22
+ };
23
+
24
+ export default handle_initial_deployment;
@@ -0,0 +1,23 @@
1
+ import fetch from "node-fetch";
2
+
3
+ const handle_version_deployment = async (options = {}) => {
4
+ return fetch(`${options?.push_provision_domain}/api/deployments/${options?.deployment?.domain}`, {
5
+ method: "PUT",
6
+ headers: {
7
+ "x-push-session-token": options?.session_token,
8
+ 'Content-Type': 'application/json',
9
+ },
10
+ body: JSON.stringify({
11
+ build_timestamp: options?.build_timestamp,
12
+ domain: options?.deployment?.domain,
13
+ deployment_id: options?.deployment?._id,
14
+ }),
15
+ }).then(async (response) => {
16
+ const data = await response.json();
17
+ return data?.data;
18
+ }).catch((error) => {
19
+ console.warn(error);
20
+ });
21
+ };
22
+
23
+ export default handle_version_deployment;
@@ -0,0 +1,160 @@
1
+ import chalk from 'chalk';
2
+ import inquirer from "inquirer";
3
+ import build from '../../lib/build/index.js';
4
+ import cli_log from '../../lib/cli_log.js';
5
+ import confirm_deployment_prompt from './prompts/confirm_deployment.js';
6
+ import create_version from './create_version.js';
7
+ import get_deployment from './get_deployment.js';
8
+ import get_push_domain from './get_push_domain.js';
9
+ import get_settings_file from './get_settings_file.js';
10
+ import get_session_token from './get_session_token.js';
11
+ import handle_initial_deployment from './handle_initial_deployment.js';
12
+ import handle_version_deployment from './handle_version_deployment.js';
13
+ import Loader from '../../lib/loader.js';
14
+ import upload_build_to_cdn from './upload_build_to_cdn.js';
15
+ import validate_deployment from './validate_deployment.js';
16
+ import validate_push_config from './validate_push_config.js';
17
+
18
+ const log_validation_error_response = (validation_type = '', root_error = '', validation_response = {}) => {
19
+ let errors_list = ``;
20
+
21
+ for (let i = 0; i < validation_response?.errors?.length; i += 1) {
22
+ const validation_error = validation_response?.errors[i];
23
+
24
+ if (validation_type === 'push_config') {
25
+ errors_list += `${chalk.yellowBright(`> ${validation_error.field}`)}\n`;
26
+ }
27
+
28
+ errors_list += `${validation_error?.error}\n${i + 1 === validation_response?.errors?.length ? '' : '\n'}`;
29
+ }
30
+
31
+ cli_log(
32
+ `${root_error ? `${chalk.yellowBright(root_error)}\n\n\n` : ''}${errors_list}`,
33
+ {
34
+ level: "danger",
35
+ docs: {
36
+ push_config: "https://cheatcode.co/docs/push/config",
37
+ deployment: "https://cheatcode.co/docs/push/subscription",
38
+ }[validation_type],
39
+ }
40
+ );
41
+
42
+ process.exit(0);
43
+ };
44
+
45
+ const get_session_for_deployment = async () => {
46
+ const session_token = await get_session_token();
47
+
48
+ if (!session_token) {
49
+ cli_log(
50
+ data?.error?.message,
51
+ {
52
+ level: "danger",
53
+ docs: "https://cheatcode.co/docs/push/cli#authentication"
54
+ }
55
+ );
56
+
57
+ return process.exit(0);
58
+ }
59
+
60
+ return session_token;
61
+ };
62
+
63
+ const warn_no_push_config = (environment = '') => {
64
+ cli_log(
65
+ `settings.${environment}.json must contain a push config object. Add this following the docs link below and try again.`,
66
+ {
67
+ level: "danger",
68
+ docs: "https://cheatcode.co/docs/push/config"
69
+ }
70
+ );
71
+
72
+ return process.exit(0);
73
+ };
74
+
75
+ const push = async (args = {}, options = {}) => {
76
+ process.loader = new Loader();
77
+ process.loader.print('Starting deployment...');
78
+
79
+ const session_token = await get_session_for_deployment();
80
+ const environment = options?.environment || 'production';
81
+ const settings = await get_settings_file({ environment });
82
+ const push_settings = settings?.config?.push || {};
83
+ const push_domain = get_push_domain(options?.push_server);
84
+
85
+ const deployment = {
86
+ status: 'undeployed',
87
+ deployment_secret: 'abc123',
88
+ } || await get_deployment({
89
+ domain: settings?.push?.domain,
90
+ session_token,
91
+ push_domain,
92
+ });
93
+
94
+ // NOTE: A bit hacky but removes need for weird if {} statement nesting.
95
+ const { confirm_deployment } = deployment?.status === 'undeployed' ? await inquirer.prompt(
96
+ confirm_deployment_prompt(push_config_validation_response?.instances)
97
+ ) : { confirm_deployment: true };
98
+
99
+ if (confirm_deployment) {
100
+ // NOTE: Do this to create a gap between the confirmation text above (only applies if
101
+ // the deployment is a first-run).
102
+ if (deployment?.status === 'undeployed') {
103
+ console.log('\n');
104
+ }
105
+
106
+ const build_timestamp = new Date().toISOString();
107
+
108
+ await build({
109
+ environment,
110
+ encrypt_build: true,
111
+ encryption_key: deployment?.deployment_secret,
112
+ silence_confirmation: true,
113
+ });
114
+
115
+ console.log('Now do the docker build.');
116
+
117
+ // process.loader.print('Uploading version...');
118
+
119
+ // await upload_build_to_cdn(
120
+ // build_timestamp,
121
+ // deployment,
122
+ // session_token,
123
+ // );
124
+
125
+ // const create_version_response = await create_version({
126
+ // push_domain,
127
+ // domain: settings?.push?.domain,
128
+ // session_token,
129
+ // body: {
130
+ // // NOTE: Endpoint anticipates a stringified version of settings.
131
+ // settings: JSON.stringify(settings),
132
+ // build_timestamp,
133
+ // },
134
+ // });
135
+
136
+ // process.loader.print('Deploying app...');
137
+
138
+ // if (deployment?.status === 'undeployed') {
139
+ // return handle_initial_deployment({
140
+ // push_domain,
141
+ // push_domain,
142
+ // session_token,
143
+ // environment,
144
+ // build_timestamp,
145
+ // deployment,
146
+ // });
147
+ // }
148
+
149
+ // return handle_version_deployment({
150
+ // push_domain,
151
+ // push_domain,
152
+ // session_token,
153
+ // environment,
154
+ // build_timestamp,
155
+ // deployment,
156
+ // });
157
+ }
158
+ };
159
+
160
+ export default push;
@@ -0,0 +1,73 @@
1
+ import AsciiTable from 'ascii-table';
2
+ import chalk from 'chalk';
3
+
4
+ const render_instance_row = (table, [continent_name = '', continent_instances = []]) => {
5
+ table.addRow(`${chalk.magenta(continent_name)}\n`);
6
+
7
+ for (let i = 0; i < continent_instances?.length; i += 1) {
8
+ const instance = continent_instances[i];
9
+ table.addRow(
10
+ `${chalk.yellow(instance?.provider)} (${chalk.green(instance?.region)})`,
11
+ `${chalk.white(instance?.size)} $${chalk.green(instance?.pricePerMonth)}/mo. x${chalk.blue(instance?.quantity)} ${chalk.white('=')} $${chalk.green((instance?.quantity * instance?.pricePerMonth))}/mo.${i + 1 === continent_instances?.length ? '\n' : ''}`,
12
+ );
13
+ }
14
+ };
15
+
16
+ const get_total_monthly_cost_for_instances = (instances = []) => {
17
+ const instances_by_continent = Object.values(instances)?.flatMap((instances_for_continent) => instances_for_continent);
18
+ return instances_by_continent?.reduce((total = 0, instance = {}) => {
19
+ total += instance?.pricePerMonth * instance?.quantity;
20
+ return total;
21
+ }, 0);
22
+ };
23
+
24
+ const confirm_deployment = (instances = {}) => {
25
+ const load_balancer_total_per_month = get_total_monthly_cost_for_instances(instances?.load_balancer);
26
+ const app_total_per_month = get_total_monthly_cost_for_instances(instances?.app);
27
+ const total_monthly_cost = load_balancer_total_per_month + app_total_per_month;
28
+
29
+ const table = new AsciiTable();
30
+
31
+ table.removeBorder();
32
+ table.addRow(`${chalk.blue('Load Balancer')}\n`);
33
+
34
+ const load_balancer_continents = Object.entries(instances?.load_balancer);
35
+ const app_continents = Object.entries(instances?.app);
36
+
37
+ for (let i = 0; i < load_balancer_continents?.length; i += 1) {
38
+ render_instance_row(table, load_balancer_continents[i]);
39
+ }
40
+
41
+ table.addRow(`\n ${chalk.blue('App')}\n`);
42
+
43
+ for (let i = 0; i < app_continents?.length; i += 1) {
44
+ render_instance_row(table, app_continents[i]);
45
+ }
46
+
47
+ table.addRow(chalk.white('\n ---\n'));
48
+
49
+ table.addRow(
50
+ chalk.green('Monthly Cost'),
51
+ chalk.white(`$${chalk.green(total_monthly_cost)}/mo.`)
52
+ );
53
+
54
+ table.addRow(
55
+ chalk.green('Annual Cost'),
56
+ chalk.white(`$${chalk.green(total_monthly_cost * 12)}/yr.`)
57
+ );
58
+
59
+ const is_abnormal = total_monthly_cost > 100;
60
+
61
+ return [{
62
+ name: 'confirm_deployment',
63
+ type: 'confirm',
64
+ prefix: '',
65
+ message: `\n ${chalk.greenBright('>')} Start deployment and provision these instances?`,
66
+ suffix: `
67
+ \n${table.toString()}
68
+ ${is_abnormal ? `\n\n ${chalk.yellowBright(`!!! >>> These costs are ${chalk.magenta('high')}. Be absolutely ${chalk.magenta('CERTAIN')} you want to run this deployment. <<< !!!`)}
69
+ \n ` : '\n'}`,
70
+ }];
71
+ };
72
+
73
+ export default confirm_deployment;
@@ -0,0 +1,11 @@
1
+ import chalk from 'chalk';
2
+
3
+ const session_token = () => [{
4
+ name: 'session_token',
5
+ type: 'text',
6
+ prefix: '',
7
+ message: `\n${chalk.yellowBright('>')} To login to Push, paste your session token below:\n`,
8
+ suffix: ` ${chalk.yellowBright('Find your session token here:')} https://push.cheatcode.co/account/profile\n\n`,
9
+ }];
10
+
11
+ export default session_token;
@@ -0,0 +1,59 @@
1
+ import fetch from "node-fetch";
2
+ import FormData from "form-data";
3
+ import fs from "fs";
4
+ import _ from 'lodash';
5
+ import cdn_mirrors from './cdn_mirrors.js';
6
+
7
+ const { readFile } = fs.promises;
8
+
9
+ const get_first_available_mirror = async () => {
10
+ const results = (await Promise.allSettled(cdn_mirrors?.map(async (mirror) => {
11
+ const controller = new AbortController();
12
+ setTimeout(() => controller.abort(), 2000);
13
+
14
+ const result = await fetch(`${mirror}/api/ping`, { signal: controller.signal }).catch((error) => {
15
+ return { status: 503 };
16
+ });
17
+
18
+ return {
19
+ mirror,
20
+ status: result.status,
21
+ };
22
+ })))?.map((result) => {
23
+ return result.value;
24
+ });
25
+
26
+ return _.orderBy(results, ['status', 'mirror'])[0];
27
+ };
28
+
29
+ const upload_build_to_cdn = async (build_timestamp = '', deployment = {}, session_token = '') => {
30
+ const first_available_mirror = await get_first_available_mirror();
31
+ const form_data = new FormData();
32
+
33
+ form_data.append(
34
+ "version_tar",
35
+ await readFile(`.build/build.encrypted.tar.gz`),
36
+ `${build_timestamp}.tar.gz`
37
+ );
38
+
39
+ form_data.append("build_timestamp", build_timestamp);
40
+ form_data.append("domain", deployment?.domain);
41
+ form_data.append("deployment_id", deployment?._id);
42
+
43
+ return fetch(`${first_available_mirror?.mirror}/api/versions`, {
44
+ method: "POST",
45
+ headers: {
46
+ ...form_data.getHeaders(),
47
+ "x-push-session-token": session_token,
48
+ },
49
+ body: form_data,
50
+ }).then(async (response) => {
51
+ const data = await response.json();
52
+ return data?.data;
53
+ }).catch((error) => {
54
+ console.warn(error);
55
+ });
56
+ };
57
+
58
+ export default upload_build_to_cdn;
59
+
@@ -0,0 +1,19 @@
1
+ import fetch from 'node-fetch';
2
+
3
+ const validate_deployment = (options = {}) => {
4
+ return fetch(`${options?.push_provision_domain}/api/deployments/${options?.push_config?.domain}/validate`, {
5
+ method: 'POST',
6
+ headers: {
7
+ 'Content-Type': 'application/json',
8
+ 'x-push-session-token': options?.session_token,
9
+ },
10
+ body: JSON.stringify(options?.push_config),
11
+ })?.then(async (response) => {
12
+ const data = await response.json();
13
+ return data?.data;
14
+ }).catch((error) => {
15
+ console.warn(error);
16
+ });
17
+ };
18
+
19
+ export default validate_deployment;
@@ -0,0 +1,18 @@
1
+ import fetch from 'node-fetch';
2
+
3
+ const validate_push_config = (options = {}) => {
4
+ return fetch(`${options?.push_provision_domain}/api/validate/config`, {
5
+ method: 'POST',
6
+ headers: {
7
+ 'Content-Type': 'application/json',
8
+ },
9
+ body: JSON.stringify(options?.push_config),
10
+ })?.then(async (response) => {
11
+ const data = await response.json();
12
+ return data?.data;
13
+ }).catch((error) => {
14
+ console.warn(error);
15
+ });
16
+ };
17
+
18
+ export default validate_push_config;