@vaharoni/devops 1.2.7 → 1.2.9

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.
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Encrypts a plaintext string using AES-256-GCM with the given key.
3
+ * @param plaintext
4
+ * @param key - The key to use for encryption. If not provided, the key will be derived from the MONOREPO_BASE_SECRET environment variable.
5
+ * @returns The encrypted ciphertext.
6
+ */
7
+ export declare function encryptAes256Gcm(plaintext: string, key?: Buffer): string;
8
+ /**
9
+ * Decrypts a ciphertext string using AES-256-GCM with the given key.
10
+ * @param ciphertext - The ciphertext to decrypt.
11
+ * @param key - The key to use for decryption. If not provided, the key will be derived from the MONOREPO_BASE_SECRET environment variable.
12
+ * @returns The decrypted plaintext.
13
+ */
14
+ export declare function decryptAes256Gcm(ciphertext: string, key?: Buffer): string;
15
+ //# sourceMappingURL=aes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aes.d.ts","sourceRoot":"","sources":["../../../src/app-support/crypto/aes.ts"],"names":[],"mappings":"AAaA;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAqBxE;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAiBzE"}
@@ -0,0 +1,56 @@
1
+ import crypto from "crypto";
2
+ const ALGORITHM = "aes-256-gcm";
3
+ const IV_LENGTH = 12; // GCM recommended IV length
4
+ const AUTH_TAG_LENGTH = 16;
5
+ function getKey() {
6
+ const keyStr = process.env.MONOREPO_BASE_SECRET;
7
+ if (!keyStr)
8
+ throw new Error("MONOREPO_BASE_SECRET not set");
9
+ // The secret is 32 random bytes stored as hex (64 chars) - decode it directly
10
+ return Buffer.from(keyStr, "hex");
11
+ }
12
+ /**
13
+ * Encrypts a plaintext string using AES-256-GCM with the given key.
14
+ * @param plaintext
15
+ * @param key - The key to use for encryption. If not provided, the key will be derived from the MONOREPO_BASE_SECRET environment variable.
16
+ * @returns The encrypted ciphertext.
17
+ */
18
+ export function encryptAes256Gcm(plaintext, key) {
19
+ key ??= getKey();
20
+ const iv = crypto.randomBytes(IV_LENGTH);
21
+ const cipher = crypto.createCipheriv(ALGORITHM, key, iv, {
22
+ authTagLength: AUTH_TAG_LENGTH,
23
+ });
24
+ const encrypted = Buffer.concat([
25
+ cipher.update(plaintext, "utf8"),
26
+ cipher.final(),
27
+ ]);
28
+ const authTag = cipher.getAuthTag();
29
+ // Format: iv:authTag:ciphertext (all base64)
30
+ return [
31
+ iv.toString("base64"),
32
+ authTag.toString("base64"),
33
+ encrypted.toString("base64"),
34
+ ].join(":");
35
+ }
36
+ /**
37
+ * Decrypts a ciphertext string using AES-256-GCM with the given key.
38
+ * @param ciphertext - The ciphertext to decrypt.
39
+ * @param key - The key to use for decryption. If not provided, the key will be derived from the MONOREPO_BASE_SECRET environment variable.
40
+ * @returns The decrypted plaintext.
41
+ */
42
+ export function decryptAes256Gcm(ciphertext, key) {
43
+ key ??= getKey();
44
+ const [ivB64, authTagB64, encryptedB64] = ciphertext.split(":");
45
+ const iv = Buffer.from(ivB64, "base64");
46
+ const authTag = Buffer.from(authTagB64, "base64");
47
+ const encrypted = Buffer.from(encryptedB64, "base64");
48
+ const decipher = crypto.createDecipheriv(ALGORITHM, key, iv, {
49
+ authTagLength: AUTH_TAG_LENGTH,
50
+ });
51
+ decipher.setAuthTag(authTag);
52
+ return Buffer.concat([
53
+ decipher.update(encrypted),
54
+ decipher.final(),
55
+ ]).toString("utf8");
56
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=aes.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aes.spec.d.ts","sourceRoot":"","sources":["../../../src/app-support/crypto/aes.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,58 @@
1
+ import { beforeEach, describe, expect, it } from 'vitest';
2
+ import { encryptAes256Gcm, decryptAes256Gcm } from './aes';
3
+ describe('AES-256-GCM encrypt and decrypt', () => {
4
+ beforeEach(() => {
5
+ // AES-256 requires a 32-byte key (64 hex characters)
6
+ process.env.MONOREPO_BASE_SECRET = '1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef';
7
+ });
8
+ it('should encrypt and decrypt a simple string', () => {
9
+ const plaintext = 'hello world';
10
+ const encrypted = encryptAes256Gcm(plaintext);
11
+ const decrypted = decryptAes256Gcm(encrypted);
12
+ expect(decrypted).toEqual(plaintext);
13
+ });
14
+ it('should encrypt and decrypt an empty string', () => {
15
+ const plaintext = '';
16
+ const encrypted = encryptAes256Gcm(plaintext);
17
+ const decrypted = decryptAes256Gcm(encrypted);
18
+ expect(decrypted).toEqual(plaintext);
19
+ });
20
+ it('should encrypt and decrypt unicode text', () => {
21
+ const plaintext = '你好世界 🌍 héllo';
22
+ const encrypted = encryptAes256Gcm(plaintext);
23
+ const decrypted = decryptAes256Gcm(encrypted);
24
+ expect(decrypted).toEqual(plaintext);
25
+ });
26
+ it('should produce different ciphertexts for the same plaintext (random IV)', () => {
27
+ const plaintext = 'same input';
28
+ const encrypted1 = encryptAes256Gcm(plaintext);
29
+ const encrypted2 = encryptAes256Gcm(plaintext);
30
+ expect(encrypted1).not.toEqual(encrypted2);
31
+ // But both should decrypt to the same value
32
+ expect(decryptAes256Gcm(encrypted1)).toEqual(plaintext);
33
+ expect(decryptAes256Gcm(encrypted2)).toEqual(plaintext);
34
+ });
35
+ it('should produce ciphertext in expected format (iv:authTag:ciphertext)', () => {
36
+ const encrypted = encryptAes256Gcm('test');
37
+ const parts = encrypted.split(':');
38
+ expect(parts).toHaveLength(3);
39
+ // All parts should be valid base64
40
+ parts.forEach((part) => {
41
+ expect(() => Buffer.from(part, 'base64')).not.toThrow();
42
+ });
43
+ });
44
+ it('should throw on tampered ciphertext', () => {
45
+ const encrypted = encryptAes256Gcm('sensitive data');
46
+ const [iv, authTag, ciphertext] = encrypted.split(':');
47
+ const tamperedCiphertext = `${iv}:${authTag}:${ciphertext.slice(0, -1)}x`;
48
+ expect(() => decryptAes256Gcm(tamperedCiphertext)).toThrow();
49
+ });
50
+ it('should throw on tampered auth tag', () => {
51
+ const encrypted = encryptAes256Gcm('sensitive data');
52
+ const [iv, _authTag, ciphertext] = encrypted.split(':');
53
+ // Use a completely different auth tag (all zeros)
54
+ const fakeAuthTag = Buffer.alloc(16, 0).toString('base64');
55
+ const tamperedTag = `${iv}:${fakeAuthTag}:${ciphertext}`;
56
+ expect(() => decryptAes256Gcm(tamperedTag)).toThrow();
57
+ });
58
+ });
@@ -1,3 +1,4 @@
1
+ export { encryptAes256Gcm as encrypt, decryptAes256Gcm as decrypt } from "./aes";
1
2
  /**
2
3
  * A simple token generation/verification class that relies on the subject field of an internal JWT-like token. It can:
3
4
  * - generate a short-lived (60s) token with the given subject
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/app-support/crypto/index.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,qBAAa,aAAa;IACL,OAAO,EAAE,MAAM;gBAAf,OAAO,EAAE,MAAM;IAElC,QAAQ;IAIR,aAAa,CAAC,KAAK,EAAE,MAAM;IAO3B,uBAAuB,CAAC,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI;CAO5D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/app-support/crypto/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,IAAI,OAAO,EAAE,gBAAgB,IAAI,OAAO,EAAE,MAAM,OAAO,CAAC;AAEjF;;;;;;GAMG;AACH,qBAAa,aAAa;IACL,OAAO,EAAE,MAAM;gBAAf,OAAO,EAAE,MAAM;IAElC,QAAQ;IAIR,aAAa,CAAC,KAAK,EAAE,MAAM;IAO3B,uBAAuB,CAAC,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI;CAO5D"}
@@ -1,4 +1,5 @@
1
1
  import { generateInternalAuthToken, parseInternalAuthTokenOrThrow } from "./internal-token";
2
+ export { encryptAes256Gcm as encrypt, decryptAes256Gcm as decrypt } from "./aes";
2
3
  /**
3
4
  * A simple token generation/verification class that relies on the subject field of an internal JWT-like token. It can:
4
5
  * - generate a short-lived (60s) token with the given subject
@@ -1,8 +1,8 @@
1
1
  import chalk from "chalk";
2
2
  import { execSync, spawn } from "child_process";
3
3
  import fs from "fs";
4
- import { globSync } from "glob";
5
4
  import { allSupportedEnvs } from "../libs/k8s-constants";
5
+ import { globEnvYamlFiles } from "../libs/discovery";
6
6
  export class CLICommandParser {
7
7
  command;
8
8
  args;
@@ -207,7 +207,7 @@ export class CommandExecutor {
207
207
  _checkEnvYamlFiles() {
208
208
  if (!this.checkEnvYaml)
209
209
  return;
210
- const envYamlFiles = globSync("**/env.yaml");
210
+ const envYamlFiles = globEnvYamlFiles();
211
211
  const checkEnvCmd = new CommandExecutor(`devops env _validate ${envYamlFiles.join(" ")}`, { env: this.env, quiet: true, checkEnvYaml: false });
212
212
  checkEnvCmd.exec();
213
213
  }
@@ -1,6 +1,6 @@
1
- import { globSync } from "glob";
2
1
  import { deleteMonorepoSecret, getMonorepoSecretStr, setMonorepoSecret, } from "../../libs/k8s-secrets-manager";
3
2
  import { CombinedEnvValidator } from "../../libs/validate-env";
3
+ import { globEnvYamlFiles } from "../../libs/discovery";
4
4
  import { CLICommandParser, dotEnvFilesForEnv, printUsageAndExit, } from "../common";
5
5
  const oneLiner = "Commands to manipulate env variables";
6
6
  const keyExamples = `
@@ -28,7 +28,7 @@ function run(cmdObj) {
28
28
  const [command, ...rest] = cmdObj.args;
29
29
  switch (command) {
30
30
  case "validate": {
31
- const envYamlFiles = globSync("**/env.yaml");
31
+ const envYamlFiles = globEnvYamlFiles();
32
32
  // We have to have a _validate so that we go through a CommandExecutor which injects env variables into the process
33
33
  cmdObj
34
34
  .executorFromEnv(`devops env _validate ${envYamlFiles.join(" ")}`, { quiet: false })
@@ -2,4 +2,5 @@ import type { SupportedLanguages, WorkspaceIndex } from "../../types";
2
2
  export declare function workspaces(): WorkspaceIndex;
3
3
  export declare function workspaceDirectoryForLanguage(language: SupportedLanguages): Record<string, import("../..").PackageData>;
4
4
  export declare function getWorkspace(workspaceName: string): import("../..").WorkspaceIndexEntry;
5
+ export declare function globEnvYamlFiles(): string[];
5
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/libs/discovery/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAKtE,wBAAgB,UAAU,mBA8BzB;AAED,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE,kBAAkB,+CASzE;AAED,wBAAgB,YAAY,CAAC,aAAa,EAAE,MAAM,uCAQjD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/libs/discovery/index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAOtE,wBAAgB,UAAU,mBA8BzB;AAED,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE,kBAAkB,+CASzE;AAED,wBAAgB,YAAY,CAAC,aAAa,EAAE,MAAM,uCAQjD;AAED,wBAAgB,gBAAgB,IAAI,MAAM,EAAE,CAQ3C"}
@@ -1,6 +1,9 @@
1
1
  import chalk from "chalk";
2
+ import { globSync } from "glob";
3
+ import path from "path";
2
4
  import { nodeWorkspaces } from "./process-package-json";
3
5
  import { pythonWorkspaces } from "./process-pyproject-toml";
6
+ const rootPath = process.env.MONOREPO_ROOT || process.cwd();
4
7
  const _workspaces = {};
5
8
  let _workspacesLoaded = false;
6
9
  export function workspaces() {
@@ -53,3 +56,10 @@ export function getWorkspace(workspaceName) {
53
56
  }
54
57
  return workspace;
55
58
  }
59
+ export function globEnvYamlFiles() {
60
+ const allWorkspaces = workspaces();
61
+ const workspacePaths = [
62
+ ...new Set(Object.values(allWorkspaces).map((w) => w.rootPath)),
63
+ ];
64
+ return workspacePaths.flatMap((wsPath) => globSync(path.join(rootPath, wsPath, "**/env.yaml")));
65
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vaharoni/devops",
3
3
  "type": "module",
4
- "version": "1.2.7",
4
+ "version": "1.2.9",
5
5
  "description": "Devops utility",
6
6
  "main": "./dist/index.js",
7
7
  "types": "./dist/index.d.ts",
@@ -0,0 +1,66 @@
1
+ import { beforeEach, describe, expect, it } from 'vitest';
2
+ import { encryptAes256Gcm, decryptAes256Gcm } from './aes';
3
+
4
+ describe('AES-256-GCM encrypt and decrypt', () => {
5
+ beforeEach(() => {
6
+ // AES-256 requires a 32-byte key (64 hex characters)
7
+ process.env.MONOREPO_BASE_SECRET = '1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef';
8
+ });
9
+
10
+ it('should encrypt and decrypt a simple string', () => {
11
+ const plaintext = 'hello world';
12
+ const encrypted = encryptAes256Gcm(plaintext);
13
+ const decrypted = decryptAes256Gcm(encrypted);
14
+ expect(decrypted).toEqual(plaintext);
15
+ });
16
+
17
+ it('should encrypt and decrypt an empty string', () => {
18
+ const plaintext = '';
19
+ const encrypted = encryptAes256Gcm(plaintext);
20
+ const decrypted = decryptAes256Gcm(encrypted);
21
+ expect(decrypted).toEqual(plaintext);
22
+ });
23
+
24
+ it('should encrypt and decrypt unicode text', () => {
25
+ const plaintext = '你好世界 🌍 héllo';
26
+ const encrypted = encryptAes256Gcm(plaintext);
27
+ const decrypted = decryptAes256Gcm(encrypted);
28
+ expect(decrypted).toEqual(plaintext);
29
+ });
30
+
31
+ it('should produce different ciphertexts for the same plaintext (random IV)', () => {
32
+ const plaintext = 'same input';
33
+ const encrypted1 = encryptAes256Gcm(plaintext);
34
+ const encrypted2 = encryptAes256Gcm(plaintext);
35
+ expect(encrypted1).not.toEqual(encrypted2);
36
+ // But both should decrypt to the same value
37
+ expect(decryptAes256Gcm(encrypted1)).toEqual(plaintext);
38
+ expect(decryptAes256Gcm(encrypted2)).toEqual(plaintext);
39
+ });
40
+
41
+ it('should produce ciphertext in expected format (iv:authTag:ciphertext)', () => {
42
+ const encrypted = encryptAes256Gcm('test');
43
+ const parts = encrypted.split(':');
44
+ expect(parts).toHaveLength(3);
45
+ // All parts should be valid base64
46
+ parts.forEach((part) => {
47
+ expect(() => Buffer.from(part, 'base64')).not.toThrow();
48
+ });
49
+ });
50
+
51
+ it('should throw on tampered ciphertext', () => {
52
+ const encrypted = encryptAes256Gcm('sensitive data');
53
+ const [iv, authTag, ciphertext] = encrypted.split(':');
54
+ const tamperedCiphertext = `${iv}:${authTag}:${ciphertext.slice(0, -1)}x`;
55
+ expect(() => decryptAes256Gcm(tamperedCiphertext)).toThrow();
56
+ });
57
+
58
+ it('should throw on tampered auth tag', () => {
59
+ const encrypted = encryptAes256Gcm('sensitive data');
60
+ const [iv, _authTag, ciphertext] = encrypted.split(':');
61
+ // Use a completely different auth tag (all zeros)
62
+ const fakeAuthTag = Buffer.alloc(16, 0).toString('base64');
63
+ const tamperedTag = `${iv}:${fakeAuthTag}:${ciphertext}`;
64
+ expect(() => decryptAes256Gcm(tamperedTag)).toThrow();
65
+ });
66
+ });
@@ -0,0 +1,66 @@
1
+ import crypto from "crypto";
2
+
3
+ const ALGORITHM = "aes-256-gcm";
4
+ const IV_LENGTH = 12; // GCM recommended IV length
5
+ const AUTH_TAG_LENGTH = 16;
6
+
7
+ function getKey(): Buffer {
8
+ const keyStr = process.env.MONOREPO_BASE_SECRET;
9
+ if (!keyStr) throw new Error("MONOREPO_BASE_SECRET not set");
10
+ // The secret is 32 random bytes stored as hex (64 chars) - decode it directly
11
+ return Buffer.from(keyStr, "hex");
12
+ }
13
+
14
+ /**
15
+ * Encrypts a plaintext string using AES-256-GCM with the given key.
16
+ * @param plaintext
17
+ * @param key - The key to use for encryption. If not provided, the key will be derived from the MONOREPO_BASE_SECRET environment variable.
18
+ * @returns The encrypted ciphertext.
19
+ */
20
+ export function encryptAes256Gcm(plaintext: string, key?: Buffer): string {
21
+ key ??= getKey();
22
+ const iv = crypto.randomBytes(IV_LENGTH);
23
+
24
+ const cipher = crypto.createCipheriv(ALGORITHM, key, iv, {
25
+ authTagLength: AUTH_TAG_LENGTH,
26
+ });
27
+
28
+ const encrypted = Buffer.concat([
29
+ cipher.update(plaintext, "utf8"),
30
+ cipher.final(),
31
+ ]);
32
+
33
+ const authTag = cipher.getAuthTag();
34
+
35
+ // Format: iv:authTag:ciphertext (all base64)
36
+ return [
37
+ iv.toString("base64"),
38
+ authTag.toString("base64"),
39
+ encrypted.toString("base64"),
40
+ ].join(":");
41
+ }
42
+
43
+ /**
44
+ * Decrypts a ciphertext string using AES-256-GCM with the given key.
45
+ * @param ciphertext - The ciphertext to decrypt.
46
+ * @param key - The key to use for decryption. If not provided, the key will be derived from the MONOREPO_BASE_SECRET environment variable.
47
+ * @returns The decrypted plaintext.
48
+ */
49
+ export function decryptAes256Gcm(ciphertext: string, key?: Buffer): string {
50
+ key ??= getKey();
51
+ const [ivB64, authTagB64, encryptedB64] = ciphertext.split(":");
52
+
53
+ const iv = Buffer.from(ivB64, "base64");
54
+ const authTag = Buffer.from(authTagB64, "base64");
55
+ const encrypted = Buffer.from(encryptedB64, "base64");
56
+
57
+ const decipher = crypto.createDecipheriv(ALGORITHM, key, iv, {
58
+ authTagLength: AUTH_TAG_LENGTH,
59
+ });
60
+ decipher.setAuthTag(authTag);
61
+
62
+ return Buffer.concat([
63
+ decipher.update(encrypted),
64
+ decipher.final(),
65
+ ]).toString("utf8");
66
+ }
@@ -1,4 +1,5 @@
1
1
  import { generateInternalAuthToken, parseInternalAuthTokenOrThrow } from "./internal-token";
2
+ export { encryptAes256Gcm as encrypt, decryptAes256Gcm as decrypt } from "./aes";
2
3
 
3
4
  /**
4
5
  * A simple token generation/verification class that relies on the subject field of an internal JWT-like token. It can:
package/src/cli/common.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import chalk from "chalk";
2
2
  import { execSync, spawn, type StdioOptions } from "child_process";
3
3
  import fs from "fs";
4
- import { globSync } from "glob";
5
4
  import { allSupportedEnvs } from "../libs/k8s-constants";
5
+ import { globEnvYamlFiles } from "../libs/discovery";
6
6
 
7
7
  type ParsedArgs<TBoolKeys extends readonly string[], TParamKeys extends readonly string[]> = {
8
8
  args: string[];
@@ -294,7 +294,7 @@ export class CommandExecutor {
294
294
 
295
295
  _checkEnvYamlFiles() {
296
296
  if (!this.checkEnvYaml) return;
297
- const envYamlFiles = globSync("**/env.yaml");
297
+ const envYamlFiles = globEnvYamlFiles();
298
298
  const checkEnvCmd = new CommandExecutor(
299
299
  `devops env _validate ${envYamlFiles.join(" ")}`,
300
300
  { env: this.env, quiet: true, checkEnvYaml: false }
@@ -1,10 +1,10 @@
1
- import { globSync } from "glob";
2
1
  import {
3
2
  deleteMonorepoSecret,
4
3
  getMonorepoSecretStr,
5
4
  setMonorepoSecret,
6
5
  } from "../../libs/k8s-secrets-manager";
7
6
  import { CombinedEnvValidator } from "../../libs/validate-env";
7
+ import { globEnvYamlFiles } from "../../libs/discovery";
8
8
  import {
9
9
  CLICommandParser,
10
10
  dotEnvFilesForEnv,
@@ -38,7 +38,7 @@ function run(cmdObj: CLICommandParser) {
38
38
  const [command, ...rest] = cmdObj.args;
39
39
  switch (command) {
40
40
  case "validate": {
41
- const envYamlFiles = globSync("**/env.yaml");
41
+ const envYamlFiles = globEnvYamlFiles();
42
42
 
43
43
  // We have to have a _validate so that we go through a CommandExecutor which injects env variables into the process
44
44
  cmdObj
@@ -1,8 +1,12 @@
1
1
  import chalk from "chalk";
2
+ import { globSync } from "glob";
3
+ import path from "path";
2
4
  import { nodeWorkspaces } from "./process-package-json";
3
5
  import { pythonWorkspaces } from "./process-pyproject-toml";
4
6
  import type { SupportedLanguages, WorkspaceIndex } from "../../types";
5
7
 
8
+ const rootPath = process.env.MONOREPO_ROOT || process.cwd();
9
+
6
10
  const _workspaces: WorkspaceIndex = {};
7
11
  let _workspacesLoaded = false;
8
12
 
@@ -58,3 +62,13 @@ export function getWorkspace(workspaceName: string) {
58
62
  }
59
63
  return workspace;
60
64
  }
65
+
66
+ export function globEnvYamlFiles(): string[] {
67
+ const allWorkspaces = workspaces();
68
+ const workspacePaths = [
69
+ ...new Set(Object.values(allWorkspaces).map((w) => w.rootPath)),
70
+ ];
71
+ return workspacePaths.flatMap((wsPath) =>
72
+ globSync(path.join(rootPath, wsPath, "**/env.yaml"))
73
+ );
74
+ }
package/dist/test.d.ts DELETED
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"test.d.ts","sourceRoot":"","sources":["../src/test.ts"],"names":[],"mappings":""}
package/dist/test.js DELETED
@@ -1 +0,0 @@
1
- export {};
package/src/test.ts DELETED
File without changes