@ethlete/cli 2.0.1-next.2 → 2.1.0-next.4

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/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # @ethlete/cli
2
2
 
3
+ ## 2.1.0-next.4
4
+
5
+ ### Minor Changes
6
+
7
+ - [`b6fe4be`](https://github.com/ethlete-io/ethdk/commit/b6fe4beae010aaa2e0e367d636f6759442983395) Thanks [@TomTomB](https://github.com/TomTomB)! - Add `et api` to run an app's backend from a local checkout, declared in `ethlete.apis.js`. The new
8
+ `ethlete.config.local.json` says where the sibling checkouts on this machine live.
9
+
10
+ ## 2.1.0-next.3
11
+
12
+ ### Minor Changes
13
+
14
+ - [`edea44b`](https://github.com/ethlete-io/ethdk/commit/edea44bf1c494420f02b545202f4b24db9a6395c) Thanks [@TomTomB](https://github.com/TomTomB)! - Update to angular 22
15
+
3
16
  ## 2.0.1-next.2
4
17
 
5
18
  ### Patch Changes
package/README.md CHANGED
@@ -1,11 +1,87 @@
1
- # cli
1
+ # @ethlete/cli
2
2
 
3
- This library was generated with [Nx](https://nx.dev).
3
+ Repo tooling - installs the `et` binary.
4
4
 
5
- ## Building
5
+ ## Installation
6
6
 
7
- Run `nx build cli` to build the library.
7
+ ```bash
8
+ yarn add --dev @ethlete/cli
9
+ ```
8
10
 
9
- ## Running unit tests
11
+ ## `et release`
10
12
 
11
- Run `nx test cli` to execute the unit tests via [Jest](https://jestjs.io).
13
+ ```bash
14
+ yarn et release
15
+ ```
16
+
17
+ Turns pending changesets into a tagged, pushed release commit (version → tag → commit → push).
18
+
19
+ ## `et api`
20
+
21
+ Runs the API an app in this repo talks to, from a checkout on your own machine.
22
+
23
+ ```bash
24
+ yarn et api up hub # start the containers
25
+ yarn et api down hub # stop them
26
+ yarn et api logs hub # follow the API's log
27
+ yarn et api shell hub # a shell in the API container
28
+ yarn et api install hub # an entry from that API's own "exec" map
29
+ yarn et api up hub --host # also print the address other devices can reach
30
+ ```
31
+
32
+ The first container tool that answers is used, in this order: `docker compose`,
33
+ `container compose`, `podman-compose`, `podman compose`.
34
+
35
+ ### Declaring the APIs
36
+
37
+ `ethlete.apis.js` at the repo root describes each API's compose setup. It is a module rather
38
+ than JSON because `env` is a function:
39
+
40
+ ```js
41
+ const { sshKeyPath } = require('@ethlete/cli');
42
+
43
+ module.exports = {
44
+ hub: {
45
+ composeDir: 'development',
46
+ services: ['app', 'database', 'mailhog'],
47
+ execService: 'app',
48
+ port: 8040,
49
+ envFile: '.env',
50
+ setupCommand: 'make setup',
51
+ network: 'shared-fut',
52
+ envKey: 'hubApiEnv',
53
+ examplePath: '../fut-hub-backend',
54
+ env: () => ({ SSH_PRIVATE_KEY: sshKeyPath() }),
55
+ exec: {
56
+ install: ['composer', 'install'],
57
+ 'reset-db': ['make', 'reset-db-with-dev-fixtures'],
58
+ },
59
+ },
60
+ };
61
+ ```
62
+
63
+ Adding an API is an entry in this map. Nothing else changes.
64
+
65
+ ### Where the checkouts live
66
+
67
+ The paths differ per machine, so they live in a gitignored `ethlete.config.local.json` at the
68
+ repo root. `@ethlete/agent-rules` reads the same file, so its skills and `et` agree on where a
69
+ checkout is:
70
+
71
+ ```json
72
+ {
73
+ "sdkSourcePath": "/absolute/path/to/ethlete-sdk",
74
+ "apiRepoPaths": { "hub": "../fut-hub-backend", "*": "../shared-backend" },
75
+ "apiRepoBranches": { "hub": "develop", "*": "main" }
76
+ }
77
+ ```
78
+
79
+ Matching is exact; `"*"` is an explicit shared fallback. A relative path resolves from the repo
80
+ root. These keys used to live in `ethlete-agents.config.local.json`, which is still read as a
81
+ fallback with a warning. Add `ethlete.config.local.json` to your `.gitignore`.
82
+
83
+ ## Documentation
84
+
85
+ Full command reference on the docs site:
86
+
87
+ - [Overview & usage](https://ethlete-sdk-docs.web.app/cli/)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ethlete/cli",
3
- "version": "2.0.1-next.2",
3
+ "version": "2.1.0-next.4",
4
4
  "license": "MIT",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
package/src/index.d.ts CHANGED
@@ -1 +1 @@
1
- export {};
1
+ export * from './lib';
package/src/index.js CHANGED
@@ -2,19 +2,36 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
4
  const lib_1 = require("./lib");
5
+ const USAGE = `et — Ethlete repo tooling
6
+
7
+ et release Turn pending changesets into a tagged, pushed release commit
8
+ et api <cmd> <api> Run an API from this repo's ethlete.apis.js locally
9
+ (up, down, logs, shell, plus that API's own exec entries)
10
+ `;
5
11
  const cli = (args) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
6
- const cliFunctionArg = args[0];
7
- switch (cliFunctionArg) {
12
+ switch (args[0]) {
8
13
  case 'release':
9
- yield (0, lib_1.release)(args);
10
- break;
14
+ // `release` reads its flags from a list that was split on "=" before this switch existed.
15
+ // Keep that shape here so its own parsing is untouched.
16
+ yield (0, lib_1.release)(args.join('=').split('='));
17
+ return 0;
18
+ case 'api':
19
+ return (0, lib_1.apiCommand)({ root: process.cwd(), argv: args.slice(1) });
11
20
  default:
12
- console.log(`No command found named ${cliFunctionArg}. Available commands are: release`);
13
- break;
21
+ console.log(USAGE);
22
+ return args[0] === undefined || args[0] === '--help' || args[0] === '-h' ? 0 : 1;
14
23
  }
15
24
  });
16
- // Get the command line arguments and normalize them
17
- const args = process.argv.slice(2).join('=').split('=');
18
- cli(args);
19
- // Burst cache
25
+ tslib_1.__exportStar(require("./lib"), exports);
26
+ // Guarded so the package can also be imported as a library without running the CLI.
27
+ if (require.main === module) {
28
+ cli(process.argv.slice(2))
29
+ .then((code) => {
30
+ process.exitCode = code;
31
+ })
32
+ .catch((error) => {
33
+ console.error(error instanceof Error ? error.message : error);
34
+ process.exitCode = 1;
35
+ });
36
+ }
20
37
  //# sourceMappingURL=index.js.map
package/src/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../libs/cli/src/index.ts"],"names":[],"mappings":";;;AAAA,+BAAgC;AAEhC,MAAM,GAAG,GAAG,CAAO,IAAc,EAAE,EAAE;IACnC,MAAM,cAAc,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IAE/B,QAAQ,cAAc,EAAE,CAAC;QACvB,KAAK,SAAS;YACZ,MAAM,IAAA,aAAO,EAAC,IAAI,CAAC,CAAC;YACpB,MAAM;QAER;YACE,OAAO,CAAC,GAAG,CAAC,0BAA0B,cAAc,mCAAmC,CAAC,CAAC;YACzF,MAAM;IACV,CAAC;AACH,CAAC,CAAA,CAAC;AAEF,oDAAoD;AACpD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;AAExD,GAAG,CAAC,IAAI,CAAC,CAAC;AAEV,cAAc"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../libs/cli/src/index.ts"],"names":[],"mappings":";;;AAAA,+BAA4C;AAE5C,MAAM,KAAK,GAAG;;;;;CAKb,CAAC;AAEF,MAAM,GAAG,GAAG,CAAO,IAAc,EAAmB,EAAE;IACpD,QAAQ,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QAChB,KAAK,SAAS;YACZ,0FAA0F;YAC1F,wDAAwD;YACxD,MAAM,IAAA,aAAO,EAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;YAEzC,OAAO,CAAC,CAAC;QAEX,KAAK,KAAK;YACR,OAAO,IAAA,gBAAU,EAAC,EAAE,IAAI,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAElE;YACE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAEnB,OAAO,IAAI,CAAC,CAAC,CAAC,KAAK,SAAS,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACrF,CAAC;AACH,CAAC,CAAA,CAAC;AAEF,gDAAsB;AAEtB,oFAAoF;AACpF,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;IAC5B,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;SACvB,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;QACb,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAC1B,CAAC,CAAC;SACD,KAAK,CAAC,CAAC,KAAc,EAAE,EAAE;QACxB,OAAO,CAAC,KAAK,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAC9D,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACvB,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,4 @@
1
+ export declare const apiCommand: ({ root, argv }: {
2
+ root: string;
3
+ argv: string[];
4
+ }) => number;
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.apiCommand = void 0;
4
+ const load_definitions_1 = require("./load-definitions");
5
+ const run_1 = require("./run");
6
+ const apiCommand = ({ root, argv }) => {
7
+ const { found, apis } = (0, load_definitions_1.loadApiDefinitions)(root);
8
+ if (!found) {
9
+ console.error(`No API definitions found. Create ${load_definitions_1.API_DEFINITIONS_FILE_NAMES[0]} in the repo root:\n\n` +
10
+ ` const { sshKeyPath } = require('@ethlete/cli');\n\n` +
11
+ ` module.exports = {\n` +
12
+ ` hub: {\n` +
13
+ ` composeDir: 'development',\n` +
14
+ ` services: ['app', 'database'],\n` +
15
+ ` execService: 'app',\n` +
16
+ ` port: 8040,\n` +
17
+ ` envFile: '.env',\n` +
18
+ ` setupCommand: 'make setup',\n` +
19
+ ` exec: { install: ['composer', 'install'] },\n` +
20
+ ` },\n` +
21
+ ` };`);
22
+ return 1;
23
+ }
24
+ return (0, run_1.runApiCommand)({ apis, argv, root });
25
+ };
26
+ exports.apiCommand = apiCommand;
27
+ //# sourceMappingURL=api-command.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-command.js","sourceRoot":"","sources":["../../../../../../libs/cli/src/lib/api/api-command.ts"],"names":[],"mappings":";;;AAAA,yDAAoF;AACpF,+BAAsC;AAE/B,MAAM,UAAU,GAAG,CAAC,EAAE,IAAI,EAAE,IAAI,EAAoC,EAAE,EAAE;IAC7E,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,IAAA,qCAAkB,EAAC,IAAI,CAAC,CAAC;IAEjD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CACX,oCAAoC,6CAA0B,CAAC,CAAC,CAAC,wBAAwB;YACvF,uDAAuD;YACvD,wBAAwB;YACxB,cAAc;YACd,oCAAoC;YACpC,wCAAwC;YACxC,6BAA6B;YAC7B,qBAAqB;YACrB,0BAA0B;YAC1B,qCAAqC;YACrC,qDAAqD;YACrD,UAAU;YACV,MAAM,CACT,CAAC;QAEF,OAAO,CAAC,CAAC;IACX,CAAC;IAED,OAAO,IAAA,mBAAa,EAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;AAC7C,CAAC,CAAC;AAxBW,QAAA,UAAU,cAwBrB"}
@@ -0,0 +1,17 @@
1
+ export type ComposeTool = {
2
+ engine: string;
3
+ /** The binary and its fixed arguments. Non-empty, so the first entry is always the binary. */
4
+ compose: [string, ...string[]];
5
+ };
6
+ export declare const composeBinary: (compose: string[]) => string[];
7
+ /** Finds the first container tool on this machine that answers `<compose> version`. */
8
+ export declare const resolveComposeTool: (tools?: ComposeTool[]) => ComposeTool | undefined;
9
+ export declare const composeToolNames: (tools?: ComposeTool[]) => string[];
10
+ export declare const engineEnv: (engine: string) => {
11
+ CONTAINERS_REGISTRIES_CONF: string;
12
+ } | {
13
+ CONTAINERS_REGISTRIES_CONF?: undefined;
14
+ };
15
+ export declare const lanAddress: () => string | undefined;
16
+ /** Path of this machine's SSH key, for a compose file that mounts one into the API container. */
17
+ export declare const sshKeyPath: () => string | undefined;
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sshKeyPath = exports.lanAddress = exports.engineEnv = exports.composeToolNames = exports.resolveComposeTool = exports.composeBinary = void 0;
4
+ const child_process_1 = require("child_process");
5
+ const fs_1 = require("fs");
6
+ const os_1 = require("os");
7
+ const path_1 = require("path");
8
+ /**
9
+ * SELinux denies a container read access to a bind mount that carries the host's own label, and API
10
+ * repos mount their checkout without the `:z` that would relabel it. Disabling the label from here
11
+ * keeps those checkouts untouched.
12
+ */
13
+ const PODMAN_COMPOSE_ARGS = ['--podman-run-args=--security-opt label=disable'];
14
+ const COMPOSE_TOOLS = [
15
+ { engine: 'docker', compose: ['docker', 'compose'] },
16
+ { engine: 'container', compose: ['container', 'compose'] },
17
+ { engine: 'podman', compose: ['podman-compose', ...PODMAN_COMPOSE_ARGS] },
18
+ { engine: 'podman', compose: ['podman', 'compose', ...PODMAN_COMPOSE_ARGS] },
19
+ ];
20
+ /**
21
+ * Podman refuses to resolve a short image name such as `mysql:8.0` without a TTY prompt when several
22
+ * unqualified search registries are configured. API compose files use short names, so point podman
23
+ * at one registry and turn the prompt off.
24
+ */
25
+ const PODMAN_REGISTRIES_CONF = `unqualified-search-registries = ["docker.io"]\nshort-name-mode = "disabled"\n`;
26
+ let podmanRegistriesConfPath;
27
+ const writePodmanRegistriesConf = () => {
28
+ podmanRegistriesConfPath !== null && podmanRegistriesConfPath !== void 0 ? podmanRegistriesConfPath : (podmanRegistriesConfPath = (0, path_1.join)((0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), 'ethlete-api-')), 'registries.conf'));
29
+ (0, fs_1.writeFileSync)(podmanRegistriesConfPath, PODMAN_REGISTRIES_CONF, 'utf8');
30
+ return podmanRegistriesConfPath;
31
+ };
32
+ const composeBinary = (compose) => compose.filter((part) => !part.startsWith('--'));
33
+ exports.composeBinary = composeBinary;
34
+ const canRun = (command, args) => {
35
+ const result = (0, child_process_1.spawnSync)(command, args, { stdio: 'ignore' });
36
+ return result.error === undefined && result.status === 0;
37
+ };
38
+ /** Finds the first container tool on this machine that answers `<compose> version`. */
39
+ const resolveComposeTool = (tools = COMPOSE_TOOLS) => tools.find(({ compose }) => {
40
+ const [binary, ...rest] = (0, exports.composeBinary)(compose);
41
+ return binary !== undefined && canRun(binary, [...rest, 'version']);
42
+ });
43
+ exports.resolveComposeTool = resolveComposeTool;
44
+ const composeToolNames = (tools = COMPOSE_TOOLS) => tools.map(({ compose }) => (0, exports.composeBinary)(compose).join(' '));
45
+ exports.composeToolNames = composeToolNames;
46
+ const engineEnv = (engine) => engine === 'podman' ? { CONTAINERS_REGISTRIES_CONF: writePodmanRegistriesConf() } : {};
47
+ exports.engineEnv = engineEnv;
48
+ const lanAddress = () => {
49
+ var _a;
50
+ return (_a = Object.values((0, os_1.networkInterfaces)())
51
+ .flat()
52
+ .find((networkInterface) => (networkInterface === null || networkInterface === void 0 ? void 0 : networkInterface.family) === 'IPv4' && !networkInterface.internal)) === null || _a === void 0 ? void 0 : _a.address;
53
+ };
54
+ exports.lanAddress = lanAddress;
55
+ /** Path of this machine's SSH key, for a compose file that mounts one into the API container. */
56
+ const sshKeyPath = () => [(0, path_1.join)((0, os_1.homedir)(), '.ssh/id_ed25519'), (0, path_1.join)((0, os_1.homedir)(), '.ssh/id_rsa')].find((path) => (0, fs_1.existsSync)(path));
57
+ exports.sshKeyPath = sshKeyPath;
58
+ //# sourceMappingURL=compose.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compose.js","sourceRoot":"","sources":["../../../../../../libs/cli/src/lib/api/compose.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /** One API this repo can run locally. Everything here describes the API's own compose setup. */
2
+ export type ApiDefinition = {
3
+ /** Directory inside the API checkout that holds the compose file, for example `development`. */
4
+ composeDir: string;
5
+ /** Compose services to start. Leave out a service the app does not need, such as a bundled frontend. */
6
+ services: string[];
7
+ /** Service that `shell`, `logs` and every `exec` entry run in. */
8
+ execService: string;
9
+ /** Published host port, not the port inside the container. Used for the printed url. */
10
+ port: number;
11
+ /** File that must exist in `composeDir` before the API can start, for example `.env`. */
12
+ envFile?: string;
13
+ /** Command that creates `envFile`. Named in the error when the file is missing. */
14
+ setupCommand?: string;
15
+ /** External container network to create before the first `up`. */
16
+ network?: string;
17
+ /** Example path shown in the error that asks the developer to configure this API's checkout. */
18
+ examplePath?: string;
19
+ /** localStorage key the app reads to pick its API. Printed by `--host`. */
20
+ envKey?: string;
21
+ /** Extra environment for every compose call. An undefined value is dropped rather than passed as "undefined". */
22
+ env?: () => Record<string, string | undefined>;
23
+ /** Named commands run in `execService`, for example `{ install: ['composer', 'install'] }`. */
24
+ exec?: Record<string, string[]>;
25
+ };
26
+ export type ApiDefinitions = Record<string, ApiDefinition>;
27
+ export declare const BUILT_IN_API_COMMANDS: readonly ["up", "down", "logs", "shell"];
28
+ export declare const apiCommandNames: (api: ApiDefinition) => string[];
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.apiCommandNames = exports.BUILT_IN_API_COMMANDS = void 0;
4
+ exports.BUILT_IN_API_COMMANDS = ['up', 'down', 'logs', 'shell'];
5
+ const apiCommandNames = (api) => { var _a; return [...exports.BUILT_IN_API_COMMANDS, ...Object.keys((_a = api.exec) !== null && _a !== void 0 ? _a : {})]; };
6
+ exports.apiCommandNames = apiCommandNames;
7
+ //# sourceMappingURL=definition.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definition.js","sourceRoot":"","sources":["../../../../../../libs/cli/src/lib/api/definition.ts"],"names":[],"mappings":";;;AA4Ba,QAAA,qBAAqB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAU,CAAC;AAEvE,MAAM,eAAe,GAAG,CAAC,GAAkB,EAAE,EAAE,WAAC,OAAA,CAAC,GAAG,6BAAqB,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,MAAA,GAAG,CAAC,IAAI,mCAAI,EAAE,CAAC,CAAC,CAAA,EAAA,CAAC;AAArG,QAAA,eAAe,mBAAsF"}
@@ -0,0 +1,5 @@
1
+ export * from './api-command';
2
+ export * from './compose';
3
+ export * from './definition';
4
+ export * from './load-definitions';
5
+ export * from './run';
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./api-command"), exports);
5
+ tslib_1.__exportStar(require("./compose"), exports);
6
+ tslib_1.__exportStar(require("./definition"), exports);
7
+ tslib_1.__exportStar(require("./load-definitions"), exports);
8
+ tslib_1.__exportStar(require("./run"), exports);
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../libs/cli/src/lib/api/index.ts"],"names":[],"mappings":";;;AAAA,wDAA8B;AAC9B,oDAA0B;AAC1B,uDAA6B;AAC7B,6DAAmC;AACnC,gDAAsB"}
@@ -0,0 +1,16 @@
1
+ import { ApiDefinitions } from './definition';
2
+ export declare const API_DEFINITIONS_FILE_NAMES: string[];
3
+ export type LoadApiDefinitionsResult = {
4
+ found: true;
5
+ apis: ApiDefinitions;
6
+ fileName: string;
7
+ } | {
8
+ found: false;
9
+ apis?: undefined;
10
+ fileName?: undefined;
11
+ };
12
+ /**
13
+ * Loads the repo's API definitions from `ethlete.apis.js`. The file is a module rather than JSON
14
+ * because a definition's `env` is a function, which JSON cannot hold.
15
+ */
16
+ export declare const loadApiDefinitions: (root: string) => LoadApiDefinitionsResult;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.loadApiDefinitions = exports.API_DEFINITIONS_FILE_NAMES = void 0;
4
+ const fs_1 = require("fs");
5
+ const module_1 = require("module");
6
+ const path_1 = require("path");
7
+ exports.API_DEFINITIONS_FILE_NAMES = ['ethlete.apis.js', 'ethlete.apis.cjs'];
8
+ /**
9
+ * Loads the repo's API definitions from `ethlete.apis.js`. The file is a module rather than JSON
10
+ * because a definition's `env` is a function, which JSON cannot hold.
11
+ */
12
+ const loadApiDefinitions = (root) => {
13
+ var _a;
14
+ const fileName = exports.API_DEFINITIONS_FILE_NAMES.find((candidate) => (0, fs_1.existsSync)((0, path_1.join)(root, candidate)));
15
+ if (!fileName)
16
+ return { found: false };
17
+ const loaded = (0, module_1.createRequire)((0, path_1.join)(root, 'package.json'))((0, path_1.join)(root, fileName));
18
+ const apis = (_a = loaded.default) !== null && _a !== void 0 ? _a : loaded;
19
+ return { found: true, apis, fileName };
20
+ };
21
+ exports.loadApiDefinitions = loadApiDefinitions;
22
+ //# sourceMappingURL=load-definitions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"load-definitions.js","sourceRoot":"","sources":["../../../../../../libs/cli/src/lib/api/load-definitions.ts"],"names":[],"mappings":";;;AAAA,2BAAgC;AAChC,mCAAuC;AACvC,+BAA4B;AAGf,QAAA,0BAA0B,GAAG,CAAC,iBAAiB,EAAE,kBAAkB,CAAC,CAAC;AAKlF;;;GAGG;AACI,MAAM,kBAAkB,GAAG,CAAC,IAAY,EAA4B,EAAE;;IAC3E,MAAM,QAAQ,GAAG,kCAA0B,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,IAAA,eAAU,EAAC,IAAA,WAAI,EAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;IAEnG,IAAI,CAAC,QAAQ;QAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IAEvC,MAAM,MAAM,GAAY,IAAA,sBAAa,EAAC,IAAA,WAAI,EAAC,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,IAAA,WAAI,EAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;IACxF,MAAM,IAAI,GAAG,MAAC,MAAuC,CAAC,OAAO,mCAAK,MAAyB,CAAC;IAE5F,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AACzC,CAAC,CAAC;AATW,QAAA,kBAAkB,sBAS7B"}
@@ -0,0 +1,10 @@
1
+ import { ApiDefinitions } from './definition';
2
+ export type RunApiCommandOptions = {
3
+ apis: ApiDefinitions;
4
+ /** Arguments after the command name, for example `['up', 'hub', '--host']`. */
5
+ argv: string[];
6
+ root?: string;
7
+ /** How the caller is invoked, used in the usage line. */
8
+ invocation?: string;
9
+ };
10
+ export declare const runApiCommand: ({ apis, argv, root, invocation }: RunApiCommandOptions) => number;
@@ -0,0 +1,119 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runApiCommand = void 0;
4
+ const child_process_1 = require("child_process");
5
+ const fs_1 = require("fs");
6
+ const path_1 = require("path");
7
+ const local_config_1 = require("../config/local-config");
8
+ const compose_1 = require("./compose");
9
+ const definition_1 = require("./definition");
10
+ const configHelp = ({ name, api, reason }) => {
11
+ var _a;
12
+ return `${reason}\n\nAdd the path of your ${name} API checkout to ${local_config_1.LOCAL_CONFIG_FILE_NAME} in the repo ` +
13
+ `root. The file is gitignored, so it changes nothing for anyone else:\n\n` +
14
+ ` {\n "apiRepoPaths": {\n "${name}": "${(_a = api.examplePath) !== null && _a !== void 0 ? _a : `../${name}-api`}"\n }\n }`;
15
+ };
16
+ const definedEntries = (record) => Object.fromEntries(Object.entries(record).filter(([, value]) => value !== undefined));
17
+ const isDirectory = (path) => (0, fs_1.existsSync)(path) && (0, fs_1.statSync)(path).isDirectory();
18
+ const runApiCommand = ({ apis, argv, root = process.cwd(), invocation = 'et api' }) => {
19
+ var _a, _b, _c, _d;
20
+ const exposeOnLan = argv.includes('--host');
21
+ const [command, name] = argv.filter((arg) => !arg.startsWith('--'));
22
+ const api = name === undefined ? undefined : apis[name];
23
+ if (command === undefined || name === undefined || !api) {
24
+ console.error(`Usage: ${invocation} <command> <api> [--host]\n\nAPIs: ${Object.keys(apis).join(', ')}`);
25
+ return 1;
26
+ }
27
+ const commands = (0, definition_1.apiCommandNames)(api);
28
+ if (!commands.includes(command)) {
29
+ console.error(`Unknown command "${command}" for the ${name} API.\n\nCommands: ${commands.join(', ')}`);
30
+ return 1;
31
+ }
32
+ const { config, fileName, isLegacy } = (0, local_config_1.readLocalConfig)(root);
33
+ const configured = (0, local_config_1.configuredApiRepoPath)(config, name);
34
+ if (!configured) {
35
+ console.error(configHelp({
36
+ name,
37
+ api,
38
+ reason: fileName
39
+ ? `${fileName} has no apiRepoPaths entry for "${name}".`
40
+ : `${local_config_1.LOCAL_CONFIG_FILE_NAME} does not exist.`,
41
+ }));
42
+ return 1;
43
+ }
44
+ if (isLegacy) {
45
+ console.warn(`${local_config_1.LEGACY_LOCAL_CONFIG_FILE_NAME} still holds "apiRepoPaths". Move it to ${local_config_1.LOCAL_CONFIG_FILE_NAME}.\n`);
46
+ }
47
+ const repoPath = (0, local_config_1.resolveConfiguredPath)(root, configured);
48
+ if (!isDirectory(repoPath)) {
49
+ console.error(`apiRepoPaths.${name} points at ${repoPath}, which is not a directory that exists.`);
50
+ return 1;
51
+ }
52
+ const cwd = (0, path_1.join)(repoPath, api.composeDir);
53
+ if (!isDirectory(cwd)) {
54
+ console.error(`${repoPath} has no ${api.composeDir} directory. Is apiRepoPaths.${name} the right checkout?`);
55
+ return 1;
56
+ }
57
+ if (api.envFile && !(0, fs_1.existsSync)((0, path_1.join)(cwd, api.envFile))) {
58
+ console.error(`Missing ${api.envFile} in ${cwd}.${api.setupCommand ? ` Run "${api.setupCommand}" there first.` : ''}`);
59
+ return 1;
60
+ }
61
+ const tool = (0, compose_1.resolveComposeTool)();
62
+ if (!tool) {
63
+ console.error(`No compose tool found. Tried: ${(0, compose_1.composeToolNames)().join(', ')}.`);
64
+ return 1;
65
+ }
66
+ const { engine, compose } = tool;
67
+ const [binary, ...composePrefix] = compose;
68
+ const env = Object.assign(Object.assign(Object.assign({}, (0, compose_1.engineEnv)(engine)), process.env), definedEntries((_b = (_a = api.env) === null || _a === void 0 ? void 0 : _a.call(api)) !== null && _b !== void 0 ? _b : {}));
69
+ let exitCode = 0;
70
+ const runCompose = (...composeArgs) => {
71
+ var _a;
72
+ const result = (0, child_process_1.spawnSync)(binary, [...composePrefix, ...composeArgs], { cwd, env, stdio: 'inherit' });
73
+ if (result.error) {
74
+ console.error(result.error.message);
75
+ exitCode = 1;
76
+ return;
77
+ }
78
+ exitCode = (_a = result.status) !== null && _a !== void 0 ? _a : 1;
79
+ };
80
+ if (command === 'up') {
81
+ if (api.network) {
82
+ (0, child_process_1.spawnSync)(engine, ['network', 'create', api.network], { stdio: 'ignore' });
83
+ }
84
+ runCompose('up', '-d', ...api.services);
85
+ // podman-compose exits 0 even when it fails to build or pull an image, so the state has to be
86
+ // read back rather than inferred from the exit code.
87
+ console.log(`\n${name} API at http://localhost:${api.port}. State:\n`);
88
+ runCompose('ps');
89
+ if (exposeOnLan) {
90
+ const address = (0, compose_1.lanAddress)();
91
+ if (!address) {
92
+ console.error('\nThis machine has no external IPv4 address, so the API cannot be reached over the network.');
93
+ return 1;
94
+ }
95
+ const envKeyStep = api.envKey
96
+ ? `\n 2. Run localStorage.setItem('${api.envKey}', 'http://${address}:${api.port}') on that device.`
97
+ : '';
98
+ console.log(`\nThe published ports already listen on every interface, so the API also answers on\n` +
99
+ `http://${address}:${api.port}. Two more steps make another device use it:\n\n` +
100
+ ` 1. Serve the app with a host binding of 0.0.0.0.${envKeyStep}\n\n` +
101
+ `A host firewall can still block port ${api.port}.`);
102
+ }
103
+ }
104
+ else if (command === 'down') {
105
+ runCompose('down');
106
+ }
107
+ else if (command === 'logs') {
108
+ runCompose('logs', '-f', api.execService);
109
+ }
110
+ else if (command === 'shell') {
111
+ runCompose('exec', api.execService, 'bash');
112
+ }
113
+ else {
114
+ runCompose('exec', api.execService, ...((_d = (_c = api.exec) === null || _c === void 0 ? void 0 : _c[command]) !== null && _d !== void 0 ? _d : []));
115
+ }
116
+ return exitCode;
117
+ };
118
+ exports.runApiCommand = runApiCommand;
119
+ //# sourceMappingURL=run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.js","sourceRoot":"","sources":["../../../../../../libs/cli/src/lib/api/run.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export * from './local-config';
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./local-config"), exports);
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../../libs/cli/src/lib/config/index.ts"],"names":[],"mappings":";;;AAAA,yDAA+B"}
@@ -0,0 +1,31 @@
1
+ export declare const LOCAL_CONFIG_FILE_NAME = "ethlete.config.local.json";
2
+ /**
3
+ * The file these keys lived in before they moved to `ethlete.config.local.json`. Still read as a
4
+ * fallback so an existing checkout keeps working, and reported so the developer can move them.
5
+ */
6
+ export declare const LEGACY_LOCAL_CONFIG_FILE_NAME = "ethlete-agents.config.local.json";
7
+ /**
8
+ * Machine-local repo topology: where the sibling checkouts this repo works with live. The file is
9
+ * gitignored, so every value is per-machine and never changes what anyone else sees.
10
+ */
11
+ export type LocalConfig = {
12
+ /** Path to a local `ethlete-sdk` checkout, for reading its source or building it into this repo. */
13
+ sdkSourcePath?: string;
14
+ /** Path to the checkout of the API an app talks to, keyed by app name. `"*"` matches any app. */
15
+ apiRepoPaths?: Record<string, string>;
16
+ /** Branch that represents each API's deployed state, keyed by app name. `"*"` matches any app. */
17
+ apiRepoBranches?: Record<string, string>;
18
+ };
19
+ export type LocalConfigSource = {
20
+ config: LocalConfig;
21
+ /** The file the values came from, or `undefined` when neither file exists. */
22
+ fileName?: string;
23
+ /** True when the values came from `LEGACY_LOCAL_CONFIG_FILE_NAME`. */
24
+ isLegacy: boolean;
25
+ };
26
+ /** Reads the local config, falling back to the file the keys used to live in. */
27
+ export declare const readLocalConfig: (root: string) => LocalConfigSource;
28
+ export declare const configuredApiRepoPath: (config: LocalConfig, api: string) => string | undefined;
29
+ export declare const configuredApiRepoBranch: (config: LocalConfig, api: string) => string | undefined;
30
+ /** Resolves a configured path against the repo root, so a relative path in the file works. */
31
+ export declare const resolveConfiguredPath: (root: string, configured: string) => string;
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveConfiguredPath = exports.configuredApiRepoBranch = exports.configuredApiRepoPath = exports.readLocalConfig = exports.LEGACY_LOCAL_CONFIG_FILE_NAME = exports.LOCAL_CONFIG_FILE_NAME = void 0;
4
+ const fs_1 = require("fs");
5
+ const path_1 = require("path");
6
+ exports.LOCAL_CONFIG_FILE_NAME = 'ethlete.config.local.json';
7
+ /**
8
+ * The file these keys lived in before they moved to `ethlete.config.local.json`. Still read as a
9
+ * fallback so an existing checkout keeps working, and reported so the developer can move them.
10
+ */
11
+ exports.LEGACY_LOCAL_CONFIG_FILE_NAME = 'ethlete-agents.config.local.json';
12
+ const readJsonObject = (path) => {
13
+ if (!(0, fs_1.existsSync)(path))
14
+ return undefined;
15
+ let parsed;
16
+ try {
17
+ parsed = JSON.parse((0, fs_1.readFileSync)(path, 'utf8'));
18
+ }
19
+ catch (_a) {
20
+ return undefined;
21
+ }
22
+ if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))
23
+ return undefined;
24
+ return parsed;
25
+ };
26
+ /** Reads the local config, falling back to the file the keys used to live in. */
27
+ const readLocalConfig = (root) => {
28
+ const current = readJsonObject((0, path_1.join)(root, exports.LOCAL_CONFIG_FILE_NAME));
29
+ if (current)
30
+ return { config: current, fileName: exports.LOCAL_CONFIG_FILE_NAME, isLegacy: false };
31
+ const legacy = readJsonObject((0, path_1.join)(root, exports.LEGACY_LOCAL_CONFIG_FILE_NAME));
32
+ if (legacy)
33
+ return { config: legacy, fileName: exports.LEGACY_LOCAL_CONFIG_FILE_NAME, isLegacy: true };
34
+ return { config: {}, isLegacy: false };
35
+ };
36
+ exports.readLocalConfig = readLocalConfig;
37
+ const entryFor = (record, key) => { var _a; return (_a = record === null || record === void 0 ? void 0 : record[key]) !== null && _a !== void 0 ? _a : record === null || record === void 0 ? void 0 : record['*']; };
38
+ const configuredApiRepoPath = (config, api) => entryFor(config.apiRepoPaths, api);
39
+ exports.configuredApiRepoPath = configuredApiRepoPath;
40
+ const configuredApiRepoBranch = (config, api) => entryFor(config.apiRepoBranches, api);
41
+ exports.configuredApiRepoBranch = configuredApiRepoBranch;
42
+ /** Resolves a configured path against the repo root, so a relative path in the file works. */
43
+ const resolveConfiguredPath = (root, configured) => (0, path_1.isAbsolute)(configured) ? configured : (0, path_1.resolve)(root, configured);
44
+ exports.resolveConfiguredPath = resolveConfiguredPath;
45
+ //# sourceMappingURL=local-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-config.js","sourceRoot":"","sources":["../../../../../../libs/cli/src/lib/config/local-config.ts"],"names":[],"mappings":";;;AAAA,2BAA8C;AAC9C,+BAAiD;AAEpC,QAAA,sBAAsB,GAAG,2BAA2B,CAAC;AAElE;;;GAGG;AACU,QAAA,6BAA6B,GAAG,kCAAkC,CAAC;AAuBhF,MAAM,cAAc,GAAG,CAAC,IAAY,EAA2B,EAAE;IAC/D,IAAI,CAAC,IAAA,eAAU,EAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IAExC,IAAI,MAAe,CAAC;IAEpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAA,iBAAY,EAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;IAClD,CAAC;IAAC,WAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,SAAS,CAAC;IAE7F,OAAO,MAAqB,CAAC;AAC/B,CAAC,CAAC;AAEF,iFAAiF;AAC1E,MAAM,eAAe,GAAG,CAAC,IAAY,EAAqB,EAAE;IACjE,MAAM,OAAO,GAAG,cAAc,CAAC,IAAA,WAAI,EAAC,IAAI,EAAE,8BAAsB,CAAC,CAAC,CAAC;IAEnE,IAAI,OAAO;QAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,8BAAsB,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;IAE3F,MAAM,MAAM,GAAG,cAAc,CAAC,IAAA,WAAI,EAAC,IAAI,EAAE,qCAA6B,CAAC,CAAC,CAAC;IAEzE,IAAI,MAAM;QAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,qCAA6B,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAE/F,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;AACzC,CAAC,CAAC;AAVW,QAAA,eAAe,mBAU1B;AAEF,MAAM,QAAQ,GAAG,CAAC,MAA0C,EAAE,GAAW,EAAE,EAAE,WAAC,OAAA,MAAA,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAG,GAAG,CAAC,mCAAI,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAG,GAAG,CAAC,CAAA,EAAA,CAAC;AAEtG,MAAM,qBAAqB,GAAG,CAAC,MAAmB,EAAE,GAAW,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC;AAAjG,QAAA,qBAAqB,yBAA4E;AAEvG,MAAM,uBAAuB,GAAG,CAAC,MAAmB,EAAE,GAAW,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,eAAe,EAAE,GAAG,CAAC,CAAC;AAAtG,QAAA,uBAAuB,2BAA+E;AAEnH,8FAA8F;AACvF,MAAM,qBAAqB,GAAG,CAAC,IAAY,EAAE,UAAkB,EAAE,EAAE,CACxE,IAAA,iBAAU,EAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAA,cAAO,EAAC,IAAI,EAAE,UAAU,CAAC,CAAC;AADrD,QAAA,qBAAqB,yBACgC"}
@@ -1 +1,3 @@
1
+ export * from './api';
2
+ export * from './config';
1
3
  export * from './release';
package/src/lib/index.js CHANGED
@@ -1,5 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./api"), exports);
5
+ tslib_1.__exportStar(require("./config"), exports);
4
6
  tslib_1.__exportStar(require("./release"), exports);
5
7
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../libs/cli/src/lib/index.ts"],"names":[],"mappings":";;;AAAA,oDAA0B"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../libs/cli/src/lib/index.ts"],"names":[],"mappings":";;;AAAA,gDAAsB;AACtB,mDAAyB;AACzB,oDAA0B"}