rbxts-orchestra 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/run.js CHANGED
@@ -32,7 +32,7 @@ var import_yargs = __toESM(require("yargs"));
32
32
  // package.json
33
33
  var package_default = {
34
34
  name: "rbxts-orchestra",
35
- version: "0.1.0",
35
+ version: "0.1.2",
36
36
  description: "A build orchestrator.",
37
37
  main: "src/index.ts",
38
38
  bin: {
@@ -44,7 +44,8 @@ var package_default = {
44
44
  },
45
45
  files: [
46
46
  "dist",
47
- "bin/run"
47
+ "bin/run",
48
+ "src"
48
49
  ],
49
50
  scripts: {
50
51
  build: "tsup-node && tsc",
@@ -77,9 +78,10 @@ var package_default = {
77
78
  },
78
79
  dependencies: {
79
80
  consola: "^3.2.3",
81
+ jiti: "^2.6.1",
80
82
  picocolors: "^1.0.1",
81
- yargs: "^17.7.2",
82
- jiti: "^2.6.1"
83
+ "rbxts-orchestra": "^0.1.0",
84
+ yargs: "^17.7.2"
83
85
  }
84
86
  };
85
87
 
@@ -173,6 +175,7 @@ async function handler(argv2) {
173
175
  });
174
176
  await orchestra.runScripts(argv2.script, delimitedArgv).catch((code) => {
175
177
  logger.error(`Failed to run script: exit code ${code}`);
178
+ process2.exit(code);
176
179
  });
177
180
  }
178
181
 
@@ -32,6 +32,7 @@ export async function handler(argv) {
32
32
  });
33
33
  await orchestra.runScripts(argv.script, delimitedArgv).catch((code) => {
34
34
  logger.error(`Failed to run script: exit code ${code}`);
35
+ process.exit(code);
35
36
  });
36
37
  }
37
38
  //# sourceMappingURL=runScript.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"runScript.js","sourceRoot":"","sources":["../../../src/commands/runScript.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AAGxC,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,OAAO,EAAE,SAAS,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAC;AAOpE,MAAM,CAAC,MAAM,OAAO,GAAG,cAAc,CAAC;AACtC,MAAM,CAAC,MAAM,QAAQ,GAAG,iDAAiD,CAAC;AAE1E,MAAM,UAAU,OAAO,CAAC,KAAoB;IAC3C,OAAO,KAAK;SACV,UAAU,CAAC,QAAQ,EAAE;QACrB,IAAI,EAAE,QAAQ;QACd,WAAW,EAAE,aAAa;KAC1B,CAAC;SACD,MAAM,CAAC,QAAQ,EAAE;QACjB,IAAI,EAAE,QAAQ;QACd,KAAK,EAAE,GAAG;QACV,WAAW,EAAE,mCAAmC;QAChD,OAAO,EAAE,GAAG,yBAAyB,EAAE;KACvC,CAAC;SACD,MAAM,CAAC,QAAQ,EAAE,CAAC,KAAa,EAAE,EAAE;QACnC,IAAI,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YACvB,OAAO,KAAK,CAAC;QACd,CAAC;QACD,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,KAAK,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,IAAiC;IAC9D,MAAM,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC;IACpE,MAAM,aAAa,GAAG,cAAc,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,cAAc,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACjG,MAAM,SAAS,GAAG,MAAM,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;QACxE,MAAM,CAAC,KAAK,CAAC,iCAAiC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;QAC7D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC,CAAC,CAAC;IAEH,MAAM,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE;QACrE,MAAM,CAAC,KAAK,CAAC,mCAAmC,IAAI,EAAE,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"runScript.js","sourceRoot":"","sources":["../../../src/commands/runScript.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AAGxC,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,OAAO,EAAE,SAAS,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAC;AAOpE,MAAM,CAAC,MAAM,OAAO,GAAG,cAAc,CAAC;AACtC,MAAM,CAAC,MAAM,QAAQ,GAAG,iDAAiD,CAAC;AAE1E,MAAM,UAAU,OAAO,CAAC,KAAoB;IAC3C,OAAO,KAAK;SACV,UAAU,CAAC,QAAQ,EAAE;QACrB,IAAI,EAAE,QAAQ;QACd,WAAW,EAAE,aAAa;KAC1B,CAAC;SACD,MAAM,CAAC,QAAQ,EAAE;QACjB,IAAI,EAAE,QAAQ;QACd,KAAK,EAAE,GAAG;QACV,WAAW,EAAE,mCAAmC;QAChD,OAAO,EAAE,GAAG,yBAAyB,EAAE;KACvC,CAAC;SACD,MAAM,CAAC,QAAQ,EAAE,CAAC,KAAa,EAAE,EAAE;QACnC,IAAI,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YACvB,OAAO,KAAK,CAAC;QACd,CAAC;QACD,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,KAAK,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,IAAiC;IAC9D,MAAM,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,IAAI,IAAI,CAAC,CAAC;IACpE,MAAM,aAAa,GAAG,cAAc,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,cAAc,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACjG,MAAM,SAAS,GAAG,MAAM,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;QACxE,MAAM,CAAC,KAAK,CAAC,iCAAiC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;QAC7D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC,CAAC,CAAC;IAEH,MAAM,SAAS,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE;QACrE,MAAM,CAAC,KAAK,CAAC,mCAAmC,IAAI,EAAE,CAAC,CAAC;QACxD,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACpB,CAAC,CAAC,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rbxts-orchestra",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "A build orchestrator.",
5
5
  "main": "src/index.ts",
6
6
  "bin": {
@@ -12,7 +12,8 @@
12
12
  },
13
13
  "files": [
14
14
  "dist",
15
- "bin/run"
15
+ "bin/run",
16
+ "src"
16
17
  ],
17
18
  "scripts": {
18
19
  "build": "tsup-node && tsc",
@@ -45,8 +46,9 @@
45
46
  },
46
47
  "dependencies": {
47
48
  "consola": "^3.2.3",
49
+ "jiti": "^2.6.1",
48
50
  "picocolors": "^1.0.1",
49
- "yargs": "^17.7.2",
50
- "jiti": "^2.6.1"
51
+ "rbxts-orchestra": "^0.1.0",
52
+ "yargs": "^17.7.2"
51
53
  }
52
54
  }
@@ -0,0 +1,3 @@
1
+ import * as run from "./runScript";
2
+
3
+ export const commands = [run];
@@ -0,0 +1,48 @@
1
+ import { isAbsolute, join } from "node:path";
2
+ import * as process from "node:process";
3
+ import { ArgumentsCamelCase, Argv } from "yargs";
4
+
5
+ import { logger } from "../logger";
6
+ import { Orchestra, ORCHESTRA_CONFIG_FILENAME } from "../orchestra";
7
+
8
+ interface RunArgv {
9
+ script: string;
10
+ config: string;
11
+ }
12
+
13
+ export const command = "run <script>";
14
+ export const describe = "Runs build scripts for all orchestrated places.";
15
+
16
+ export function builder(yargs: Argv<RunArgv>): Argv {
17
+ return yargs
18
+ .positional("script", {
19
+ type: "string",
20
+ description: "script name",
21
+ })
22
+ .option("config", {
23
+ type: "string",
24
+ alias: "c",
25
+ description: "Path to the orchestra config file",
26
+ default: `${ORCHESTRA_CONFIG_FILENAME}`,
27
+ })
28
+ .coerce("config", (value: string) => {
29
+ if (isAbsolute(value)) {
30
+ return value;
31
+ }
32
+ return join(process.cwd(), value);
33
+ });
34
+ }
35
+
36
+ export async function handler(argv: ArgumentsCamelCase<RunArgv>) {
37
+ const delimiterIndex = process.argv.findIndex((arg) => arg == "--");
38
+ const delimitedArgv = delimiterIndex !== -1 ? process.argv.slice(delimiterIndex + 1) : undefined;
39
+ const orchestra = await Orchestra.fromFileConfig(argv.config).catch(() => {
40
+ logger.error(`Failed to find config file at ${argv.config}`);
41
+ process.exit(1);
42
+ });
43
+
44
+ await orchestra.runScripts(argv.script, delimitedArgv).catch((code) => {
45
+ logger.error(`Failed to run script: exit code ${code}`);
46
+ process.exit(code);
47
+ });
48
+ }
@@ -0,0 +1,45 @@
1
+ import { execSync } from "child_process";
2
+ import { Environments, Value } from "src/orchestra";
3
+
4
+ export function semverString<E extends Environments>(
5
+ /**
6
+ * The base semver string (e.g `1.0.0`)
7
+ */
8
+ baseSemver: string,
9
+ /**
10
+ * A list of environments in which to include the short hash of the
11
+ * currently checked out commit in the semver string
12
+ */
13
+ commitHashEnvironments?: (keyof E)[],
14
+ /**
15
+ * A list of environments in which to include the name of the
16
+ * local user in the semver string
17
+ */
18
+ userEnvironments?: (keyof E)[],
19
+ ): Value<E, string> {
20
+ return (orchestra) => {
21
+ let shortCommitHash, localUsername;
22
+ try {
23
+ localUsername = execSync("whoami", {
24
+ stdio: "pipe",
25
+ })
26
+ .toString()
27
+ .trim();
28
+ shortCommitHash = execSync("git rev-parse --short HEAD", {
29
+ stdio: "pipe",
30
+ })
31
+ .toString()
32
+ .trim();
33
+ } catch {}
34
+ let semver = baseSemver;
35
+
36
+ if (localUsername && userEnvironments?.includes(orchestra.environment)) {
37
+ semver += `-${localUsername}`;
38
+ }
39
+ if (shortCommitHash && commitHashEnvironments?.includes(orchestra.environment)) {
40
+ semver += `+${shortCommitHash}`;
41
+ }
42
+
43
+ return semver;
44
+ };
45
+ }
@@ -0,0 +1,64 @@
1
+ import { spawn } from "child_process";
2
+ import { resolve } from "path";
3
+
4
+ import { Environments, PlaceConfig, Script, val, Value } from "../orchestra/config";
5
+ import { Orchestra } from "../orchestra/orchestra";
6
+
7
+ interface SpawnConfig {
8
+ /**
9
+ * Whether to wait until the process exits before continuing to the next script
10
+ */
11
+ waitForExit: boolean;
12
+ /**
13
+ * Whether to ignore a non-zero exit code
14
+ */
15
+ ignoreNonZeroExitCode: boolean;
16
+ }
17
+
18
+ const DEFAULT_SPAWN_CONFIG: SpawnConfig = {
19
+ waitForExit: true,
20
+ ignoreNonZeroExitCode: false,
21
+ };
22
+
23
+ function generateEnv<E extends Environments>(
24
+ orchestra: Orchestra<E>,
25
+ place?: PlaceConfig<E>,
26
+ ): Record<string, string | undefined> {
27
+ return {
28
+ ...process.env,
29
+ ORCHESTRA_DIR: resolve("."),
30
+ RELEASE_NUMBER: orchestra.resolveConfig(orchestra.config.releaseNumber),
31
+ RELEASE_NAME: orchestra.resolveConfig(orchestra.config.releaseName),
32
+ PLACE_ID: place && orchestra.resolveConfig(place.placeId)?.toString(),
33
+ EXPERIENCE_ID: place && orchestra.resolveConfig(place.experienceId)?.toString(),
34
+ };
35
+ }
36
+
37
+ export function sh<E extends Environments>(cmd: string, config: Partial<SpawnConfig> = {}): Value<E, Script<E>> {
38
+ const fullConfig = {
39
+ ...DEFAULT_SPAWN_CONFIG,
40
+ ...config,
41
+ };
42
+
43
+ return val((orchestra, place, argv = []) => {
44
+ return new Promise((resolvePromise, reject) => {
45
+ const child = spawn(`${cmd}`, argv, {
46
+ cwd: place ? resolve(orchestra.resolveConfig(place.rootDir)) : undefined,
47
+ stdio: "inherit",
48
+ env: generateEnv(orchestra, place),
49
+ });
50
+
51
+ if (fullConfig.waitForExit) {
52
+ child.on("exit", (code) => {
53
+ if (code !== 0 && !fullConfig.ignoreNonZeroExitCode) reject(code);
54
+ resolvePromise();
55
+ });
56
+ } else {
57
+ resolvePromise();
58
+ }
59
+ });
60
+ });
61
+ }
62
+ export function script<E extends Environments>(path: string, config: Partial<SpawnConfig> = {}): Value<E, Script<E>> {
63
+ return sh(`${resolve(path)}`, config);
64
+ }
package/src/logger.ts ADDED
@@ -0,0 +1,3 @@
1
+ import { createConsola } from "consola";
2
+
3
+ export const logger = createConsola({});
@@ -0,0 +1,117 @@
1
+ import { Orchestra } from "./orchestra";
2
+
3
+ /**
4
+ * A value that is resolved using the Orchestra context
5
+ */
6
+ export type Value<E extends Environments, T> = (orchestra: Orchestra<E>) => T;
7
+
8
+ /**
9
+ * Allow either a raw value or a computed one
10
+ */
11
+ export type MaybeValue<E extends Environments, T> = T | Value<E, T>;
12
+
13
+ /**
14
+ * Resolve a MaybeValue at runtime
15
+ */
16
+ export function resolveValue<E extends Environments, T>(value: MaybeValue<E, T>, orchestra: Orchestra<E>): T {
17
+ return typeof value === "function" ? (value as Value<E, T>)(orchestra) : value;
18
+ }
19
+
20
+ /**
21
+ * Helper to wrap static values
22
+ */
23
+ export function val<E extends Environments, T>(value: T): Value<E, T> {
24
+ return () => value;
25
+ }
26
+
27
+ /**
28
+ * Environment-based value helper
29
+ */
30
+ export function env<V extends Record<string, unknown>>(values: V) {
31
+ return <E extends { [K in keyof V]: EnvironmentConfig }>(orchestra: Orchestra<E>): V[keyof V] => {
32
+ return values[orchestra.environment as keyof V];
33
+ };
34
+ }
35
+
36
+ export type Script<E extends Environments> = (
37
+ orchestra: Orchestra<E>,
38
+ place?: PlaceConfig<E>,
39
+ argv?: string[],
40
+ ) => Promise<void> | void;
41
+
42
+ /**
43
+ * A configuration for a place managed by an Orchestra
44
+ */
45
+ export interface PlaceConfig<E extends Environments> {
46
+ rootDir: MaybeValue<E, string>;
47
+ placeId: MaybeValue<E, number>;
48
+ experienceId: MaybeValue<E, number>;
49
+
50
+ scripts: {
51
+ /**
52
+ * A callback that runs when this place is built by the Orchestra
53
+ */
54
+ [k: string]: MaybeValue<E, Script<E>>;
55
+ };
56
+ }
57
+
58
+ /**
59
+ * A configuration for a specific environment within an Orchestra
60
+ */
61
+ export interface EnvironmentConfig {
62
+ /**
63
+ * Whether to use this environment as the default if no other environments are chosen
64
+ */
65
+ default?: boolean;
66
+
67
+ /**
68
+ * The NODE_ENV value that will result in this environment being chosen
69
+ */
70
+ nodeEnv?: string;
71
+ }
72
+
73
+ /**
74
+ * A list of environments available to an Orchestra
75
+ */
76
+ export type Environments = {
77
+ [k: string]: EnvironmentConfig;
78
+ };
79
+
80
+ /**
81
+ * A configuration for an Orchestra
82
+ */
83
+ export interface OrchestraConfig<E extends Environments> {
84
+ /**
85
+ * Semantic version
86
+ */
87
+ releaseNumber: string;
88
+
89
+ /**
90
+ * Human-friendly release name
91
+ */
92
+ releaseName: string;
93
+
94
+ /**
95
+ * Available environments
96
+ */
97
+ environments: E;
98
+
99
+ scripts?: {
100
+ /**
101
+ * Callback that runs before any scripts are run
102
+ */
103
+ prepare?: MaybeValue<E, Script<E>>;
104
+ } & Record<string, MaybeValue<E, Script<E>>>;
105
+
106
+ /**
107
+ * Places in this experience
108
+ */
109
+ places: [PlaceConfig<E>, ...PlaceConfig<E>[]];
110
+ }
111
+
112
+ /**
113
+ * Create an Orchestra config
114
+ */
115
+ export function orchestraConfig<const E extends Environments>(experience: OrchestraConfig<E>) {
116
+ return experience;
117
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./config";
2
+ export * from "./orchestra";
@@ -0,0 +1,52 @@
1
+ import assert from "assert";
2
+ import { createJiti } from "jiti";
3
+ import { resolve } from "path";
4
+
5
+ import { Environments, MaybeValue, OrchestraConfig, resolveValue } from "./config";
6
+
7
+ const jiti = createJiti(__filename);
8
+
9
+ export const ORCHESTRA_CONFIG_FILENAME = "orchestra.config.ts";
10
+
11
+ export class Orchestra<E extends Environments> {
12
+ public static async fromDirectory(directoryPath: string): Promise<Orchestra<never>> {
13
+ return await Orchestra.fromFileConfig(resolve(directoryPath, ORCHESTRA_CONFIG_FILENAME));
14
+ }
15
+
16
+ public static async fromFileConfig(filePath: string): Promise<Orchestra<never>> {
17
+ const config = await jiti.import(resolve(filePath), {
18
+ default: true,
19
+ });
20
+
21
+ return new Orchestra(config as OrchestraConfig<never>);
22
+ }
23
+
24
+ public readonly environment: keyof E;
25
+
26
+ constructor(public readonly config: OrchestraConfig<E>) {
27
+ for (const [environmentName, environmentConfig] of Object.entries(config.environments)) {
28
+ if (this!.environment) continue;
29
+ const NODE_ENV = process.env.NODE_ENV ?? "unknown";
30
+ if (environmentConfig.default) {
31
+ this.environment = environmentName;
32
+ } else if (NODE_ENV == environmentConfig.nodeEnv) {
33
+ this.environment = environmentName;
34
+ }
35
+ }
36
+ assert(this!.environment, `Orchestra confiag is invalid, no default or otherwise valid environments provided`);
37
+ }
38
+
39
+ public async runScripts(scriptName: string, argv?: string[]) {
40
+ await this.resolveConfig(this.config.scripts?.prepare)?.(this, undefined, argv);
41
+
42
+ await this.resolveConfig(this.config.scripts?.[scriptName])?.(this, undefined, argv);
43
+
44
+ for (const place of this.config.places) {
45
+ await this.resolveConfig(place.scripts[scriptName])?.(this, place, argv);
46
+ }
47
+ }
48
+
49
+ public resolveConfig<T>(config: MaybeValue<E, T>): T {
50
+ return resolveValue(config, this);
51
+ }
52
+ }