@rpcbase/cli 0.73.0 → 0.74.0

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/package.json CHANGED
@@ -1,37 +1,24 @@
1
1
  {
2
2
  "name": "@rpcbase/cli",
3
- "version": "0.73.0",
4
- "license": "SSPL-1.0",
3
+ "version": "0.74.0",
4
+ "type": "module",
5
+ "main": "./index.js",
5
6
  "bin": {
6
- "rb": "./bin.js"
7
+ "rb": "./src/index.js"
7
8
  },
8
- "publishConfig": {
9
- "registry": "https://registry.npmjs.org/"
10
- },
11
- "files": [
12
- "src/",
13
- "eslint/",
14
- "stylelint/",
15
- "!src/**/*.test.js"
16
- ],
17
9
  "scripts": {
18
- "test": "echo \"no test specified exiting with code 0\" && exit 0"
10
+ "test": "echo \"Error: no test specified\" && exit 1",
11
+ "release": "wireit"
19
12
  },
20
13
  "wireit": {
21
- "apply-version": {
22
- "command": "node ../../scripts/prflow/apply-prerelease-versions.js $BRANCH_NAME"
23
- },
24
14
  "release": {
25
- "command": "npm publish --tag $NPM_RELEASE_CHANNEL | tee publish-output.txt",
26
- "dependencies": [
27
- "apply-version"
28
- ],
15
+ "command": "../../scripts/publish.js",
16
+ "dependencies": [],
29
17
  "files": [
30
- "package.json"
31
- ],
32
- "output": [
33
- "publish-output.txt"
18
+ "package.json",
19
+ "index.js"
34
20
  ],
21
+ "output": [],
35
22
  "env": {
36
23
  "NPM_RELEASE_CHANNEL": {
37
24
  "external": true
@@ -40,28 +27,8 @@
40
27
  }
41
28
  },
42
29
  "dependencies": {
43
- "@babel/core": "7.24.9",
44
- "@babel/eslint-parser": "7.25.0",
45
- "@babel/plugin-syntax-flow": "7.24.7",
46
- "@babel/plugin-transform-react-jsx": "7.24.7",
47
- "@rpcbase/agent": "0.42.0",
48
- "bluebird": "3.7.2",
49
- "concurrently": "8.2.2",
50
- "debug": "4.3.6",
51
- "dotenv": "16.4.5",
52
- "eslint": "8.57.0",
53
- "lint-staged": "15.2.7",
54
- "octokit": "3.1.2",
55
- "parse-dotenv": "2.1.0",
56
- "picocolors": "1.0.1",
57
- "semver": "7.6.0",
58
- "stylelint": "16.7.0",
59
- "stylelint-config-standard": "36.0.0",
60
- "stylelint-config-standard-scss": "13.1.0",
61
- "validator": "13.12.0",
30
+ "dotenv": "16.5.0",
31
+ "validator": "13.15.0",
62
32
  "yargs": "17.7.2"
63
- },
64
- "devDependencies": {
65
- "jest": "29.7.0"
66
33
  }
67
34
  }
@@ -0,0 +1,137 @@
1
+ import { execSync } from "child_process";
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import os from "os";
5
+ import validator from "validator";
6
+
7
+ export const deploy = async (argv) => {
8
+ const log = (message) => {
9
+ if (argv.verbose) console.log(message);
10
+ };
11
+
12
+ const {
13
+ RB_DEPLOY_HOST,
14
+ RB_DEPLOY_USER,
15
+ RB_DEPLOY_KEY,
16
+ RB_DEPLOY_DIR,
17
+ } = argv.env || {};
18
+
19
+ if (
20
+ !RB_DEPLOY_HOST ||
21
+ !validator.isURL(RB_DEPLOY_HOST, { require_tld: false })
22
+ ) {
23
+ throw new Error(
24
+ "Missing or invalid required environment variable: RB_DEPLOY_HOST",
25
+ );
26
+ }
27
+
28
+ if (!RB_DEPLOY_USER || !validator.isLength(RB_DEPLOY_USER, { min: 1 })) {
29
+ throw new Error(
30
+ "Missing or invalid required environment variable: RB_DEPLOY_USER",
31
+ );
32
+ }
33
+
34
+ if (!RB_DEPLOY_KEY || !validator.isLength(RB_DEPLOY_KEY, { min: 1 })) {
35
+ throw new Error(
36
+ "Missing or invalid required environment variable: RB_DEPLOY_KEY",
37
+ );
38
+ }
39
+
40
+ if (!RB_DEPLOY_DIR || !validator.isLength(RB_DEPLOY_DIR, { min: 1 })) {
41
+ throw new Error(
42
+ "Missing or invalid required environment variable: RB_DEPLOY_DIR",
43
+ );
44
+ }
45
+
46
+ const host = RB_DEPLOY_HOST;
47
+ const user = RB_DEPLOY_USER;
48
+ const deployDir = RB_DEPLOY_DIR;
49
+ let keyPath = RB_DEPLOY_KEY;
50
+
51
+ let tempKeyPath = null;
52
+
53
+ // Create an SSH function that can be reused
54
+ const ssh = async (command, stdioInherit = false) => {
55
+ const sshCommand = `ssh -i "${keyPath}" -o StrictHostKeyChecking=no ${user}@${host} "${command}"`;
56
+ log(`${command}`);
57
+ const out = execSync(sshCommand, {
58
+ stdio: (argv.verbose || stdioInherit) ? "inherit" : "pipe",
59
+ })
60
+ const res = out?.toString()?.trim() || ""
61
+ return res
62
+ };
63
+
64
+ try {
65
+ if (keyPath.startsWith("~")) keyPath = keyPath.replace("~", os.homedir());
66
+
67
+ if (!fs.existsSync(keyPath)) {
68
+ // try to treat RB_DEPLOY_KEY as base64-encoded key content
69
+ const keyContent = Buffer.from(RB_DEPLOY_KEY, "base64").toString("utf-8");
70
+ if (!keyContent.includes("-----BEGIN")) {
71
+ throw new Error(
72
+ "Decoded content does not appear to be a valid SSH key",
73
+ );
74
+ }
75
+ tempKeyPath = path.join(os.tmpdir(), `deploy-key-${Date.now()}`);
76
+ fs.writeFileSync(tempKeyPath, keyContent);
77
+ fs.chmodSync(tempKeyPath, "400");
78
+ keyPath = tempKeyPath;
79
+ }
80
+
81
+ log(`Connecting to ${host} as ${user} using key at ${keyPath}`);
82
+ const res = await ssh("echo connection-ok");
83
+ console.log(`${user}@${host}`, res)
84
+ await ssh(`mkdir -p ~/apps/${deployDir}`);
85
+
86
+ const excludeList = [
87
+ ".aider*",
88
+ ".husky/",
89
+ ".github/",
90
+ ".git/",
91
+ ".wireit/",
92
+ "coverage/",
93
+ "node_modules/",
94
+ ".gitignore",
95
+ "*.css.map",
96
+ "*.env*",
97
+ "*.js.map",
98
+ "*.md",
99
+ ];
100
+
101
+ const excludeArgs = excludeList.map((p) => `--exclude='${p}'`).join(" ");
102
+ const rsyncCmd = [
103
+ "rsync -avz -C",
104
+ excludeArgs,
105
+ "--delete",
106
+ `-e "ssh -i '${keyPath}' -o StrictHostKeyChecking=no"`,
107
+ ".",
108
+ `${user}@${host}:~/apps/${deployDir}/`,
109
+ ].join(" ");
110
+
111
+ log(`Running: ${rsyncCmd}`);
112
+ execSync(rsyncCmd, { stdio: argv.verbose ? "inherit" : "ignore" });
113
+
114
+ await ssh(`cd ~/apps/${deployDir}/infrastructure && [ -f package.json ] && yarn || true`);
115
+
116
+ log("Restarting application...");
117
+ await ssh(`cd ~/apps/${deployDir}/infrastructure && node ctrl.js down prod`, true);
118
+ log("Application stopped. Starting it up again...");
119
+ await ssh(`cd ~/apps/${deployDir}/infrastructure && node ctrl.js up prod`, true);
120
+
121
+ log("Deployment complete ✅");
122
+ } catch (error) {
123
+ console.error(
124
+ `Deployment failed: ${error instanceof Error ? error.message : error}`,
125
+ );
126
+ throw error;
127
+ } finally {
128
+ if (tempKeyPath) {
129
+ try {
130
+ fs.unlinkSync(tempKeyPath);
131
+ log(`Removed temporary key file: ${tempKeyPath}`);
132
+ } catch (cleanupError) {
133
+ console.warn(`Failed to remove temporary key file: ${cleanupError}`);
134
+ }
135
+ }
136
+ }
137
+ };
@@ -1,13 +1,14 @@
1
- /* @flow */
2
- const path = require("path")
3
- const {execSync} = require("child_process");
1
+ import path from "path";
2
+ import { execSync } from "child_process";
3
+ import { fileURLToPath } from "url"
4
4
 
5
- const wait_for = () => {
5
+
6
+ export const waitFor = () => {
6
7
  process.title = "rb-wait-for"
7
8
 
8
9
  const args = process.argv.slice(3).join(" ")
9
10
 
10
- const script_path = path.join(__dirname, "./wait-for.sh")
11
+ const script_path = path.join(path.dirname(fileURLToPath(import.meta.url)), "./wait-for.sh")
11
12
 
12
13
  const command = `${script_path} ${args}`
13
14
 
@@ -17,5 +18,3 @@ const wait_for = () => {
17
18
  console.error(`wait-for execution failed: ${error}`)
18
19
  }
19
20
  }
20
-
21
- module.exports = wait_for
@@ -1,4 +1,4 @@
1
- #!/bin/sh
1
+ #!/usr/bin/env sh
2
2
 
3
3
  # original script: https://github.com/eficode/wait-for/blob/master/wait-for
4
4
 
package/src/index.js ADDED
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env node
2
+
3
+ import yargs from "yargs/yargs"
4
+ import { hideBin } from 'yargs/helpers'
5
+ import dotenv from 'dotenv'
6
+ import fs from 'fs'
7
+ import {deploy} from "./cmd-deploy.js"
8
+ import {waitFor} from "./cmd-wait-for/index.js"
9
+
10
+ yargs(hideBin(process.argv))
11
+ .option("env", {
12
+ describe: "Path to environment file",
13
+ type: "array",
14
+ global: true,
15
+ coerce: (envFiles) => {
16
+ if (!envFiles || envFiles.length === 0) return {};
17
+
18
+ const envVars = {};
19
+
20
+ envFiles.forEach(filePath => {
21
+ if (fs.existsSync(filePath)) {
22
+ const parsed = dotenv.parse(fs.readFileSync(filePath));
23
+ Object.assign(envVars, parsed);
24
+ } else {
25
+ console.warn(`Warning: Environment file ${filePath} not found.`);
26
+ }
27
+ });
28
+
29
+ // Then apply process.env variables to override file-based variables
30
+ Object.assign(envVars, process.env);
31
+
32
+ return envVars;
33
+ }
34
+ })
35
+ .command(
36
+ "test",
37
+ "test command",
38
+ (yargs) => {
39
+ return yargs
40
+ .option("verbose", {
41
+ alias: "v",
42
+ describe: "Run with verbose logging",
43
+ type: "boolean",
44
+ });
45
+ },
46
+ async(argv) => {
47
+ console.log("IT WORKS!")
48
+ },
49
+ )
50
+ .command(
51
+ "deploy",
52
+ "Deploy the application",
53
+ (yargs) => {
54
+ return yargs
55
+ .option("verbose", {
56
+ alias: "v",
57
+ describe: "Run with verbose logging",
58
+ type: "boolean",
59
+ });
60
+ },
61
+ async(argv) => {
62
+ await deploy(argv)
63
+ },
64
+ )
65
+ .command("wait-for", "Waits for the specified service to become available", () => {}, (argv) => {
66
+ waitFor()
67
+ })
68
+ .demandCommand(1, "You need to specify a command")
69
+ // .strict()
70
+ .help()
71
+ .parse();
package/src/log.js ADDED
File without changes
package/bin.js DELETED
@@ -1,129 +0,0 @@
1
- #!/usr/bin/env node
2
- /* @flow */
3
- process.title = "rb-cli"
4
- // load .env
5
- const path = require("path")
6
- require("dotenv").config({path: path.join(process.cwd(), "./.env")})
7
- //
8
- const yargs = require("yargs/yargs")
9
- const {hideBin} = require("yargs/helpers")
10
-
11
- const increment_pkg = require("./src/versions/increment-pkg")
12
- const lint = require("./src/lint")
13
- const lint_staged = require("./src/lint-staged")
14
- const print_versions = require("./src/print_versions")
15
- const run_agent = require("./src/run_agent")
16
- const start_command = require("./src/start_command")
17
- const stylelint = require("./src/stylelint")
18
- const sync_dotenv = require("./src/sync-dotenv")
19
- const update = require("./src/update")
20
- const wait_for = require("./src/wait-for")
21
-
22
-
23
- yargs(hideBin(process.argv))
24
- .command(["start", "*"], "Starts client, server, and infrastructure", (yargs) => {
25
- yargs
26
- .option("coverage", {
27
- alias: "c",
28
- type: "boolean",
29
- default: false,
30
- description: "Adds coverage instrumentation (always disabled in production)"
31
- })
32
- .option("verbose", {
33
- type: "boolean",
34
- default: false,
35
- description: "Run with verbose logging"
36
- })
37
- .option("debug", {
38
- type: "boolean",
39
- default: false,
40
- description: "Run with debug logging"
41
- })
42
- }, (argv) => {
43
- if (argv.version) return print_versions()
44
- start_command(argv)
45
- })
46
- .command(["lint"], "Run eslint", (yargs) => {}, (argv) => {
47
- lint(argv)
48
- })
49
- .command(["stylelint"], "Run stylelint", (yargs) => {}, (argv) => {
50
- stylelint(argv)
51
- })
52
- .command(["lint-staged"], "Run lint-staged", (yargs) => {}, (argv) => {
53
- lint_staged(argv)
54
- })
55
- // TODO: rm this
56
- .command(["agent"], "Run the agent", (yargs) => {
57
- // yargs
58
- // .option("option1", {
59
- // alias: "o",
60
- // type: "boolean",
61
- // default: false,
62
- // description: "Option description"
63
- // })
64
- }, (argv) => {
65
- if (argv.version) return print_versions()
66
- run_agent(argv)
67
- })
68
- // Update
69
- .command(["update", "up"], "Update rb dependencies to latest version", (yargs) => {
70
- yargs
71
- .option("dry-run", {
72
- type: "boolean",
73
- default: false,
74
- description: "Print output without writing any files"
75
- })
76
- .option("all", {
77
- type: "boolean",
78
- default: false,
79
- description: "Doesn't filter @rpcbase/ dependencies, updates everything"
80
- })
81
- }, async(argv) => {
82
- if (argv.version) return print_versions()
83
- await update(argv)
84
- })
85
- // Increment pkg
86
- .command(["increment-pkg <files...>"], "Bump a package version if necessary by checking its source on github", (yargs) => {
87
- }, async(argv) => {
88
- if (argv.version) return print_versions()
89
- await increment_pkg(argv)
90
- })
91
- // sync-dotenv
92
- .command("sync-dotenv [source_env] [dest_env]", "Compare and sync dotenv files", (yargs) => {
93
- yargs
94
- .option("write", {
95
- type: "boolean",
96
- default: false,
97
- description: "Write the ouput to dest env file"
98
- })
99
- .positional("source_env", {
100
- describe: "source .env file",
101
- type: "string"
102
- })
103
- .positional("dest_env", {
104
- describe: "destination env file",
105
- type: "string"
106
- })
107
- }, async(argv) => {
108
- if (argv.version) return print_versions()
109
- await sync_dotenv(argv.source_env, argv.dest_env)
110
- })
111
- // wait-for
112
- .command("wait-for", "", () => {}, (argv) => {
113
- wait_for()
114
- })
115
- // Ping
116
- .command("ping", "Ping", () => {}, (argv) => {
117
- if (argv.version) return print_versions()
118
- console.log("PING")
119
- })
120
- // Global options
121
- .version(false)
122
- .option("version", {
123
- alias: "v",
124
- type: "boolean",
125
- description: "Displays the @rpcbase/cli version"
126
- })
127
- .help("h")
128
- .alias("h", "help")
129
- .parse()
@@ -1,42 +0,0 @@
1
- /* @flow */
2
- const assert = require("assert")
3
- const {execSync} = require("child_process")
4
- const os = require("os")
5
- const colors = require("picocolors")
6
-
7
-
8
- const get_networks = () => {
9
- const out = execSync("docker network ls --format=json")
10
- const nets = out.toString().trim().split("\n")
11
- .filter(l => !!l)
12
- .map((l) => JSON.parse(l))
13
-
14
- return nets
15
- }
16
-
17
- const docker_runtime_check = () => {
18
- // check docker is running
19
- try {
20
- execSync("docker version --format=json")
21
- } catch (err) {
22
- console.log(colors.cyan("docker"), "exited with error")
23
- process.exit(1)
24
- }
25
-
26
- // check network exists and create it if not
27
- const {RB_APP_NAME} = process.env
28
- assert(RB_APP_NAME, "missing RB_APP_NAME in env")
29
-
30
- const net_name = `${RB_APP_NAME}-network`
31
-
32
- const nets = get_networks()
33
-
34
- const target_net = nets.find((n) => n.Name === net_name)
35
-
36
- if (!target_net) {
37
- execSync(`docker network create ${net_name}`)
38
- console.log("created docker network:", net_name)
39
- }
40
- }
41
-
42
- module.exports = docker_runtime_check
@@ -1,12 +0,0 @@
1
- /* @flow */
2
- const colors = require("picocolors")
3
-
4
- const exit_with_message = (message = "", code = 1) => {
5
- console.log(colors.red("error:"), message)
6
-
7
- // TODO: sentry(?) report error
8
-
9
- process.exit(code)
10
- }
11
-
12
- module.exports = exit_with_message
@@ -1,19 +0,0 @@
1
- /* @flow */
2
- const path = require("path")
3
-
4
- const get_root_dir = (run_configs) => {
5
- let project_root_dir
6
- if (run_configs[0].type === "server") {
7
- project_root_dir = path.join(run_configs[0].working_dir, "../../")
8
- } else if (run_configs[0].type === "client") {
9
- project_root_dir = path.join(run_configs[0].working_dir, "../")
10
- }
11
-
12
- if (!project_root_dir) {
13
- throw new Error("unable to find project root dir")
14
- }
15
-
16
- return project_root_dir
17
- }
18
-
19
- module.exports = get_root_dir
@@ -1,78 +0,0 @@
1
- /* @flow */
2
- const path = require("path")
3
- const fs = require("fs")
4
- const dotenv = require("dotenv")
5
- const child_process = require("child_process")
6
- const util = require("util")
7
-
8
- const get_root_dir = require("./get_root_dir")
9
-
10
-
11
- const exec = util.promisify(child_process.exec)
12
-
13
- const TAILSCALE_PATH = "/Applications/Tailscale.app/Contents/MacOS/Tailscale"
14
-
15
- const has_tailscale = process.platform === "darwin" && fs.existsSync(TAILSCALE_PATH)
16
-
17
-
18
- const get_port = (run_configs) => {
19
- const project_root_dir = get_root_dir(run_configs)
20
-
21
- const server_env_path = path.join(project_root_dir, "./server/server/.env")
22
-
23
- const server_env_buf = fs.readFileSync(server_env_path)
24
- const parsed_env = dotenv.parse(server_env_buf)
25
-
26
- const server_port = parsed_env.SERVER_PORT
27
- const wss_port = parsed_env.WSS_PORT
28
-
29
- return {server_port, wss_port}
30
- }
31
-
32
-
33
- const check_status = async() => {
34
- try {
35
- const {stdout, stderr} = await exec(`${TAILSCALE_PATH} funnel status --json`)
36
- const out = JSON.parse(stdout)
37
- return typeof out === "object"
38
- } catch (err) {
39
- //
40
- }
41
- return false
42
- }
43
-
44
- const start = async(run_configs) => {
45
- if (!has_tailscale) return
46
-
47
- const should_ts_start = await check_status()
48
-
49
- if (!should_ts_start) return
50
-
51
- const {server_port, wss_port} = get_port(run_configs)
52
- try {
53
- await exec(`${TAILSCALE_PATH} funnel --bg --set-path / --https=443 http://127.0.0.1:${server_port}`, {stdio: "inherit"})
54
- if (wss_port) {
55
- await exec(`${TAILSCALE_PATH} funnel --bg --set-path /wss --https=${wss_port} http://127.0.0.1:${wss_port}`, {stdio: "inherit"})
56
- }
57
- } catch (err) {
58
- console.log("error starting tailscale, is the tailscale app running ?")
59
- }
60
- }
61
-
62
-
63
- const stop = async(run_configs) => {
64
- if (!has_tailscale) return
65
-
66
- const {server_port, wss_port} = get_port(run_configs)
67
- try {
68
- await exec(`${TAILSCALE_PATH} funnel --https=443 off`, {stdio: "inherit"})
69
- if (wss_port) {
70
- await exec(`${TAILSCALE_PATH} funnel --https=${wss_port} off`)
71
- }
72
- } catch (err) {
73
- // console.log("TAILSCALE ERR", err)
74
- console.log("error stopping tailscale, it probably wasn't initally running. you can ignore it")
75
- }
76
- }
77
-
78
- module.exports = {start, stop}
@@ -1,17 +0,0 @@
1
- const path = require("path")
2
- const {spawn} = require("child_process")
3
-
4
- const {hideBin} = require("yargs/helpers")
5
-
6
- module.exports = () => {
7
- const args = hideBin(process.argv).slice(1) // remove first arg
8
-
9
- const package_dir = path.dirname(require.resolve("lint-staged/package.json"))
10
- const bin_path = path.join(package_dir, "./bin/lint-staged.js")
11
-
12
- const ps = spawn(bin_path, args, { stdio: "inherit" })
13
-
14
- ps.on("close", (code) => {
15
- process.exit(code)
16
- })
17
- }
package/src/lint.js DELETED
@@ -1,17 +0,0 @@
1
- const path = require("path")
2
- const {spawn} = require("child_process")
3
-
4
- const {hideBin} = require("yargs/helpers")
5
-
6
- module.exports = () => {
7
- const args = hideBin(process.argv).slice(1) //remove first arg (our rb command 'lint')
8
-
9
- const eslint_path = path.dirname(require.resolve("eslint/package.json"))
10
- const bin_path = path.join(eslint_path, "./bin/eslint.js")
11
-
12
- const ps = spawn(bin_path, args, { stdio: "inherit" })
13
-
14
- ps.on("close", (code) => {
15
- process.exit(code)
16
- })
17
- }
@@ -1,15 +0,0 @@
1
- /* @flow */
2
- const {execSync} = require("child_process")
3
-
4
- const colors = require("picocolors")
5
-
6
- const package = require("../package.json")
7
-
8
- const print_versions = () => {
9
- console.log(`${colors.cyan("rb")} v${package.version}`)
10
-
11
- const node_out = execSync("node -v")
12
- console.log(`${colors.green("node")} ${node_out.toString().trim()}`)
13
- }
14
-
15
- module.exports = print_versions
package/src/run_agent.js DELETED
@@ -1,17 +0,0 @@
1
- /* @flow */
2
- const path = require("path")
3
- const fs = require("fs")
4
-
5
- const {run} = require("@rpcbase/agent")
6
-
7
- const run_agent = async() => {
8
- process.title = "rb-agent"
9
-
10
- const cwd = process.cwd()
11
- const proj_parent_dir = cwd// path.join(cwd, "../../")
12
- const proj_prefix = path.basename(proj_parent_dir)
13
-
14
- run(proj_prefix)
15
- }
16
-
17
- module.exports = run_agent