@dbx-tools/core 0.6.89 → 0.6.91
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/README.md +68 -12
- package/index.ts +4 -1
- package/lib/index.d.ts +4 -1
- package/lib/index.js +4 -2
- package/lib/src/bundle.d.ts +38 -0
- package/lib/src/bundle.js +177 -0
- package/lib/src/config.d.ts +53 -30
- package/lib/src/config.js +269 -98
- package/lib/src/file.d.ts +8 -0
- package/lib/src/file.js +16 -1
- package/lib/src/project.d.ts +7 -4
- package/lib/src/project.js +52 -35
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/bundle.ts +211 -0
- package/src/config.ts +296 -122
- package/src/file.ts +19 -0
- package/src/project.ts +57 -42
package/README.md
CHANGED
|
@@ -15,6 +15,8 @@ Key features:
|
|
|
15
15
|
arrays.
|
|
16
16
|
- Workspace/project root discovery from package-manager files, git metadata, and
|
|
17
17
|
the current working directory.
|
|
18
|
+
- Asynchronous Databricks bundle App-resource streaming with native filesystem
|
|
19
|
+
traversal and CLI-resolved configuration.
|
|
18
20
|
- Safe filesystem stat and project naming helpers for CLIs and projen synth.
|
|
19
21
|
- Layered config from process env, environment-specific `.env` files, and the
|
|
20
22
|
Databricks bundle's App `config.env`.
|
|
@@ -90,18 +92,39 @@ const timeoutMs = config.positiveInt(undefined, "TIMEOUT_MS", 30_000, {
|
|
|
90
92
|
});
|
|
91
93
|
```
|
|
92
94
|
|
|
93
|
-
Resolution is lazy and follows process env, `.env`,
|
|
94
|
-
configuration
|
|
95
|
-
produce names such as
|
|
95
|
+
Resolution is lazy and follows constant `data`, process env, `.env`, Databricks
|
|
96
|
+
bundle configuration, then `app.yaml` / `app.yml` env values. The default
|
|
97
|
+
`DBX_TOOLS` scope and an optional capability prefix produce names such as
|
|
98
|
+
`DBX_TOOLS_TUNNEL_PUBLIC_DOMAIN`, then
|
|
96
99
|
`TUNNEL_PUBLIC_DOMAIN`, then `PUBLIC_DOMAIN`. `.env.production` and `.env.prod`
|
|
97
100
|
are checked before `.env` when `NODE_ENV=production`; development uses
|
|
98
101
|
`.env.development` and `.env.dev`.
|
|
99
102
|
|
|
103
|
+
Pass one config map or an array of maps through `data`; the first matching value
|
|
104
|
+
wins before environment lookup by default. When a custom `sources` order omits
|
|
105
|
+
`config`, passed data is still read and is appended last. This lets a caller put
|
|
106
|
+
env first without accidentally discarding its fallback config:
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
const endpoint = config.resolveValue("lakebaseEndpoint", {
|
|
110
|
+
data: { LAKEBASE_ENDPOINT: flags.endpoint },
|
|
111
|
+
sources: ["env", "dotenv", "bundle", "app"],
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
`resolveValue()` tries the exact, uppercase, and tokenized-uppercase forms from
|
|
116
|
+
`environmentKeys()` before applying normal scope and prefix expansion.
|
|
117
|
+
|
|
100
118
|
Bundle lookup runs `databricks bundle validate --output json` only after earlier
|
|
101
119
|
sources miss. It reads literal values from the single App's `config.env`, accepts
|
|
102
|
-
usable partial JSON from a failed validation, and
|
|
103
|
-
|
|
104
|
-
|
|
120
|
+
usable partial JSON from a failed validation, and honors the active Databricks
|
|
121
|
+
profile. Parsed dotenv records are cached by file path; parsed bundle output is
|
|
122
|
+
cached by bundle path plus profile. App YAML lookup runs only if the bundle did
|
|
123
|
+
not resolve the key and reads `env[].value` entries. Bundle `value_from` and App
|
|
124
|
+
YAML `valueFrom` references resolve supported fields from their named
|
|
125
|
+
`sql_warehouse`, `genie_space`, or `postgres` resource. Config-file discovery and parsed results
|
|
126
|
+
are single-attempt per source key: found paths, missing files, empty records,
|
|
127
|
+
invalid records, and `undefined` results all cache. Root bundle `variables` are NOT a
|
|
105
128
|
config source: they are authoring inputs interpolated into the bundle's own
|
|
106
129
|
targets, resources, and paths, so reading one as a process setting resolves names
|
|
107
130
|
the deployed App never sees. Reference a variable from `config.env` to make it
|
|
@@ -113,13 +136,14 @@ contains a bundle, and bundle reads are enabled, validation runs. This keeps
|
|
|
113
136
|
pre-boot callers such as `@dbx-tools/appkit` auto-configuration on the same
|
|
114
137
|
deterministic path as CLIs and ordinary Node consumers.
|
|
115
138
|
|
|
116
|
-
Deployed Apps skip dotenv and
|
|
139
|
+
Deployed Apps skip dotenv, bundle, and app YAML lookup after `isDatabricksAppEnv()`
|
|
117
140
|
recognizes the required App name, HTTP(S) host, and valid port. Set
|
|
118
141
|
`DBX_TOOLS_DATABRICKS_APP_ENV=true` or `false` to force that result; unrecognized
|
|
119
142
|
values leave automatic detection in place.
|
|
120
143
|
|
|
121
|
-
`DBX_TOOLS_CONFIG_DOTENV
|
|
122
|
-
those file sources on or off. A
|
|
144
|
+
`DBX_TOOLS_CONFIG_DOTENV`, `DBX_TOOLS_CONFIG_BUNDLE`, and
|
|
145
|
+
`DBX_TOOLS_CONFIG_APP` independently force those file sources on or off. A
|
|
146
|
+
recognized boolean takes precedence over App
|
|
123
147
|
runtime detection, so `true` can enable a local source inside an App and `false`
|
|
124
148
|
can suppress it during local development. Absent or unrecognized values keep
|
|
125
149
|
the default: read files outside an App and skip them inside one. Bundle reads
|
|
@@ -130,6 +154,31 @@ Use `config.string()`, `boolean()`, `positiveNumber()`, `positiveInt()`,
|
|
|
130
154
|
`port()`, and `list()` to normalize typed options and text-based configuration
|
|
131
155
|
through one rule. `config.port()` accepts only TCP ports from 1 through 65535;
|
|
132
156
|
`config.ENV_ONLY` disables file fallbacks for exact environment reads.
|
|
157
|
+
`config.flattenBundleEnv()`, `flattenAppEnv()`, and `getBundlePath()` expose the
|
|
158
|
+
same parsing logic for callers that already have parsed configuration data.
|
|
159
|
+
|
|
160
|
+
## Discover Bundle App Resources
|
|
161
|
+
|
|
162
|
+
```ts
|
|
163
|
+
import { bundle } from "@dbx-tools/core";
|
|
164
|
+
|
|
165
|
+
for await (const resource of bundle.appResources(projectRoot, cwd)) {
|
|
166
|
+
consume(resource);
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
`appResources()` returns an async generator. It checks bundle files in the
|
|
171
|
+
optional working directory's ancestor chain first, then traverses the project
|
|
172
|
+
boundary with Node's asynchronous native filesystem APIs. It yields each `resources.apps` entry from
|
|
173
|
+
`databricks bundle validate --output json`, so includes, targets, variables,
|
|
174
|
+
and relative source paths are resolved by the Databricks CLI. It does not
|
|
175
|
+
require `app.yml` or `app.yaml`. Each resource includes its App `config` and the
|
|
176
|
+
complete resolved bundle `data`.
|
|
177
|
+
|
|
178
|
+
A failed validation that still returns partial App resources attaches
|
|
179
|
+
`bundleFailure` to those resources. Failed bundles with no App resources yield
|
|
180
|
+
nothing. Missing, invalid, or out-of-bound project paths also yield nothing.
|
|
181
|
+
Malformed successful CLI output throws while the generator is consumed.
|
|
133
182
|
|
|
134
183
|
## Run Commands
|
|
135
184
|
|
|
@@ -254,19 +303,26 @@ import { project } from "@dbx-tools/core";
|
|
|
254
303
|
const root = project.root();
|
|
255
304
|
const name = project.name();
|
|
256
305
|
const origins = [...project.resolveProjectRoots(process.cwd())];
|
|
306
|
+
const cwd = project.resolveWorkingDirectory("");
|
|
257
307
|
```
|
|
258
308
|
|
|
259
309
|
`project.root()` checks npm/pnpm workspace roots, git top-level, and cwd.
|
|
260
310
|
`project.name()` prefers package metadata, then git remote name, then directory
|
|
261
|
-
basename. `project.
|
|
311
|
+
basename. `project.resolveWorkingDirectory()` normalizes blank, null, omitted,
|
|
312
|
+
relative, and absolute cwd values for cache decisions. Project subprocess probes
|
|
313
|
+
share a command/argument result cache only when that resolved path is the live
|
|
314
|
+
process cwd; another directory executes directly. Empty command results are
|
|
315
|
+
cached too.
|
|
262
316
|
|
|
263
317
|
## Modules
|
|
264
318
|
|
|
265
319
|
- `exec` - async/sync process spawning, stdio handling, abort wiring, and shlex.
|
|
266
320
|
- `bin` - executable download, optional archive extraction, selection, and
|
|
267
321
|
atomic installation.
|
|
268
|
-
- `
|
|
269
|
-
|
|
322
|
+
- `bundle` - asynchronous streaming of CLI-resolved Databricks bundle App resources.
|
|
323
|
+
- `project` - cwd normalization, root discovery, project naming, and git-remote
|
|
324
|
+
parsing.
|
|
325
|
+
- `file` - best-effort stat and parsed-record caching by caller-defined source key.
|
|
270
326
|
- `brand` - YAML/JSON discovery, parsing, validation, and asset path resolution.
|
|
271
327
|
- `config` - scoped environment, dotenv, and validated Databricks bundle lookup,
|
|
272
328
|
including runtime detection and typed coercion helpers.
|
package/index.ts
CHANGED
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
// Regenerated from the exporting modules in ./src.
|
|
3
3
|
// Hand edits are overwritten on the next watch; this file is read-only.
|
|
4
4
|
|
|
5
|
+
export const PACKAGE_IDENTIFIER = "@dbx-tools/core";
|
|
5
6
|
export * as bin from "./src/bin.ts";
|
|
6
7
|
export * as brand from "./src/brand.ts";
|
|
8
|
+
export * as bundle from "./src/bundle.ts";
|
|
7
9
|
export * as config from "./src/config.ts";
|
|
8
10
|
export * as exec from "./src/exec.ts";
|
|
9
11
|
export * as file from "./src/file.ts";
|
|
@@ -13,7 +15,8 @@ export * as project from "./src/project.ts";
|
|
|
13
15
|
export type { BinContext, BinSelectionContext, BinSelector, BinVersionOutput, BinVersionParser, BinOptions, BinUrl } from "./src/bin.ts";
|
|
14
16
|
export { BrandContextSchema, defaultBrandContext, parseBrandContext, brandContextJsonSchema, brandContextPrompt } from "./src/brand.ts";
|
|
15
17
|
export type { BrandContext, BrandContextInput } from "./src/brand.ts";
|
|
16
|
-
export { MAX_TCP_PORT, ENV_ONLY, valueSchema, bundleResourceSchema, bundleEnvEntrySchema, bundleAppSchema } from "./src/config.ts";
|
|
18
|
+
export { MAX_TCP_PORT, ENV_ONLY, valueSchema, bundleResourceSchema, bundleEnvEntrySchema, bundleAppSchema, appEnvEntrySchema, appSchema } from "./src/config.ts";
|
|
19
|
+
export type { ConfigMapValue, ConfigData, ConfigSource, ConfigOptions, ConfigFile } from "./src/config.ts";
|
|
17
20
|
export { COMMAND_NOT_FOUND_EXIT_CODE } from "./src/exec.ts";
|
|
18
21
|
export type { ExecStdio, LineHandler, StdioOption, ExecResult, ChildProcessResult, ExecOptions, SyncExecStdio, SyncExecOptions, SpawnArgs } from "./src/exec.ts";
|
|
19
22
|
export type { FileLockBackend, FileLockAcquisition, FileLockOptions } from "./src/file-lock.ts";
|
package/lib/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
export declare const PACKAGE_IDENTIFIER = "@dbx-tools/core";
|
|
1
2
|
export * as bin from "./src/bin.ts";
|
|
2
3
|
export * as brand from "./src/brand.ts";
|
|
4
|
+
export * as bundle from "./src/bundle.ts";
|
|
3
5
|
export * as config from "./src/config.ts";
|
|
4
6
|
export * as exec from "./src/exec.ts";
|
|
5
7
|
export * as file from "./src/file.ts";
|
|
@@ -9,7 +11,8 @@ export * as project from "./src/project.ts";
|
|
|
9
11
|
export type { BinContext, BinSelectionContext, BinSelector, BinVersionOutput, BinVersionParser, BinOptions, BinUrl } from "./src/bin.ts";
|
|
10
12
|
export { BrandContextSchema, defaultBrandContext, parseBrandContext, brandContextJsonSchema, brandContextPrompt } from "./src/brand.ts";
|
|
11
13
|
export type { BrandContext, BrandContextInput } from "./src/brand.ts";
|
|
12
|
-
export { MAX_TCP_PORT, ENV_ONLY, valueSchema, bundleResourceSchema, bundleEnvEntrySchema, bundleAppSchema } from "./src/config.ts";
|
|
14
|
+
export { MAX_TCP_PORT, ENV_ONLY, valueSchema, bundleResourceSchema, bundleEnvEntrySchema, bundleAppSchema, appEnvEntrySchema, appSchema } from "./src/config.ts";
|
|
15
|
+
export type { ConfigMapValue, ConfigData, ConfigSource, ConfigOptions, ConfigFile } from "./src/config.ts";
|
|
13
16
|
export { COMMAND_NOT_FOUND_EXIT_CODE } from "./src/exec.ts";
|
|
14
17
|
export type { ExecStdio, LineHandler, StdioOption, ExecResult, ChildProcessResult, ExecOptions, SyncExecStdio, SyncExecOptions, SpawnArgs } from "./src/exec.ts";
|
|
15
18
|
export type { FileLockBackend, FileLockAcquisition, FileLockOptions } from "./src/file-lock.ts";
|
package/lib/index.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
// GENERATED by projen watch - DO NOT EDIT.
|
|
2
2
|
// Regenerated from the exporting modules in ./src.
|
|
3
3
|
// Hand edits are overwritten on the next watch; this file is read-only.
|
|
4
|
+
export const PACKAGE_IDENTIFIER = "@dbx-tools/core";
|
|
4
5
|
export * as bin from "./src/bin.js";
|
|
5
6
|
export * as brand from "./src/brand.js";
|
|
7
|
+
export * as bundle from "./src/bundle.js";
|
|
6
8
|
export * as config from "./src/config.js";
|
|
7
9
|
export * as exec from "./src/exec.js";
|
|
8
10
|
export * as file from "./src/file.js";
|
|
@@ -10,6 +12,6 @@ export * as fileLock from "./src/file-lock.js";
|
|
|
10
12
|
export * as processLock from "./src/process-lock.js";
|
|
11
13
|
export * as project from "./src/project.js";
|
|
12
14
|
export { BrandContextSchema, defaultBrandContext, parseBrandContext, brandContextJsonSchema, brandContextPrompt } from "./src/brand.js";
|
|
13
|
-
export { MAX_TCP_PORT, ENV_ONLY, valueSchema, bundleResourceSchema, bundleEnvEntrySchema, bundleAppSchema } from "./src/config.js";
|
|
15
|
+
export { MAX_TCP_PORT, ENV_ONLY, valueSchema, bundleResourceSchema, bundleEnvEntrySchema, bundleAppSchema, appEnvEntrySchema, appSchema } from "./src/config.js";
|
|
14
16
|
export { COMMAND_NOT_FOUND_EXIT_CODE } from "./src/exec.js";
|
|
15
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
17
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwyQ0FBMkM7QUFDM0MsbURBQW1EO0FBQ25ELHdFQUF3RTtBQUV4RSxNQUFNLENBQUMsTUFBTSxrQkFBa0IsR0FBRyxpQkFBaUIsQ0FBQztBQUNwRCxPQUFPLEtBQUssR0FBRyxNQUFNLGNBQWMsQ0FBQztBQUNwQyxPQUFPLEtBQUssS0FBSyxNQUFNLGdCQUFnQixDQUFDO0FBQ3hDLE9BQU8sS0FBSyxNQUFNLE1BQU0saUJBQWlCLENBQUM7QUFDMUMsT0FBTyxLQUFLLE1BQU0sTUFBTSxpQkFBaUIsQ0FBQztBQUMxQyxPQUFPLEtBQUssSUFBSSxNQUFNLGVBQWUsQ0FBQztBQUN0QyxPQUFPLEtBQUssSUFBSSxNQUFNLGVBQWUsQ0FBQztBQUN0QyxPQUFPLEtBQUssUUFBUSxNQUFNLG9CQUFvQixDQUFDO0FBQy9DLE9BQU8sS0FBSyxXQUFXLE1BQU0sdUJBQXVCLENBQUM7QUFDckQsT0FBTyxLQUFLLE9BQU8sTUFBTSxrQkFBa0IsQ0FBQztBQUU1QyxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsbUJBQW1CLEVBQUUsaUJBQWlCLEVBQUUsc0JBQXNCLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUV4SSxPQUFPLEVBQUUsWUFBWSxFQUFFLFFBQVEsRUFBRSxXQUFXLEVBQUUsb0JBQW9CLEVBQUUsb0JBQW9CLEVBQUUsZUFBZSxFQUFFLGlCQUFpQixFQUFFLFNBQVMsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBRWpLLE9BQU8sRUFBRSwyQkFBMkIsRUFBRSxNQUFNLGVBQWUsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIEdFTkVSQVRFRCBieSBwcm9qZW4gd2F0Y2ggLSBETyBOT1QgRURJVC5cbi8vIFJlZ2VuZXJhdGVkIGZyb20gdGhlIGV4cG9ydGluZyBtb2R1bGVzIGluIC4vc3JjLlxuLy8gSGFuZCBlZGl0cyBhcmUgb3ZlcndyaXR0ZW4gb24gdGhlIG5leHQgd2F0Y2g7IHRoaXMgZmlsZSBpcyByZWFkLW9ubHkuXG5cbmV4cG9ydCBjb25zdCBQQUNLQUdFX0lERU5USUZJRVIgPSBcIkBkYngtdG9vbHMvY29yZVwiO1xuZXhwb3J0ICogYXMgYmluIGZyb20gXCIuL3NyYy9iaW4udHNcIjtcbmV4cG9ydCAqIGFzIGJyYW5kIGZyb20gXCIuL3NyYy9icmFuZC50c1wiO1xuZXhwb3J0ICogYXMgYnVuZGxlIGZyb20gXCIuL3NyYy9idW5kbGUudHNcIjtcbmV4cG9ydCAqIGFzIGNvbmZpZyBmcm9tIFwiLi9zcmMvY29uZmlnLnRzXCI7XG5leHBvcnQgKiBhcyBleGVjIGZyb20gXCIuL3NyYy9leGVjLnRzXCI7XG5leHBvcnQgKiBhcyBmaWxlIGZyb20gXCIuL3NyYy9maWxlLnRzXCI7XG5leHBvcnQgKiBhcyBmaWxlTG9jayBmcm9tIFwiLi9zcmMvZmlsZS1sb2NrLnRzXCI7XG5leHBvcnQgKiBhcyBwcm9jZXNzTG9jayBmcm9tIFwiLi9zcmMvcHJvY2Vzcy1sb2NrLnRzXCI7XG5leHBvcnQgKiBhcyBwcm9qZWN0IGZyb20gXCIuL3NyYy9wcm9qZWN0LnRzXCI7XG5leHBvcnQgdHlwZSB7IEJpbkNvbnRleHQsIEJpblNlbGVjdGlvbkNvbnRleHQsIEJpblNlbGVjdG9yLCBCaW5WZXJzaW9uT3V0cHV0LCBCaW5WZXJzaW9uUGFyc2VyLCBCaW5PcHRpb25zLCBCaW5VcmwgfSBmcm9tIFwiLi9zcmMvYmluLnRzXCI7XG5leHBvcnQgeyBCcmFuZENvbnRleHRTY2hlbWEsIGRlZmF1bHRCcmFuZENvbnRleHQsIHBhcnNlQnJhbmRDb250ZXh0LCBicmFuZENvbnRleHRKc29uU2NoZW1hLCBicmFuZENvbnRleHRQcm9tcHQgfSBmcm9tIFwiLi9zcmMvYnJhbmQudHNcIjtcbmV4cG9ydCB0eXBlIHsgQnJhbmRDb250ZXh0LCBCcmFuZENvbnRleHRJbnB1dCB9IGZyb20gXCIuL3NyYy9icmFuZC50c1wiO1xuZXhwb3J0IHsgTUFYX1RDUF9QT1JULCBFTlZfT05MWSwgdmFsdWVTY2hlbWEsIGJ1bmRsZVJlc291cmNlU2NoZW1hLCBidW5kbGVFbnZFbnRyeVNjaGVtYSwgYnVuZGxlQXBwU2NoZW1hLCBhcHBFbnZFbnRyeVNjaGVtYSwgYXBwU2NoZW1hIH0gZnJvbSBcIi4vc3JjL2NvbmZpZy50c1wiO1xuZXhwb3J0IHR5cGUgeyBDb25maWdNYXBWYWx1ZSwgQ29uZmlnRGF0YSwgQ29uZmlnU291cmNlLCBDb25maWdPcHRpb25zLCBDb25maWdGaWxlIH0gZnJvbSBcIi4vc3JjL2NvbmZpZy50c1wiO1xuZXhwb3J0IHsgQ09NTUFORF9OT1RfRk9VTkRfRVhJVF9DT0RFIH0gZnJvbSBcIi4vc3JjL2V4ZWMudHNcIjtcbmV4cG9ydCB0eXBlIHsgRXhlY1N0ZGlvLCBMaW5lSGFuZGxlciwgU3RkaW9PcHRpb24sIEV4ZWNSZXN1bHQsIENoaWxkUHJvY2Vzc1Jlc3VsdCwgRXhlY09wdGlvbnMsIFN5bmNFeGVjU3RkaW8sIFN5bmNFeGVjT3B0aW9ucywgU3Bhd25BcmdzIH0gZnJvbSBcIi4vc3JjL2V4ZWMudHNcIjtcbmV4cG9ydCB0eXBlIHsgRmlsZUxvY2tCYWNrZW5kLCBGaWxlTG9ja0FjcXVpc2l0aW9uLCBGaWxlTG9ja09wdGlvbnMgfSBmcm9tIFwiLi9zcmMvZmlsZS1sb2NrLnRzXCI7XG5leHBvcnQgdHlwZSB7IFByb2plY3RDb250ZXh0IH0gZnJvbSBcIi4vc3JjL3Byb2plY3QudHNcIjtcbiJdfQ==
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Streaming discovery of Databricks bundle App resources.
|
|
3
|
+
*
|
|
4
|
+
* Bundle files are found with native filesystem traversal inside a hard project
|
|
5
|
+
* boundary. Each candidate is resolved through `databricks bundle validate
|
|
6
|
+
* --output json` so includes, targets, variables, and App configuration follow
|
|
7
|
+
* the Databricks CLI's semantics.
|
|
8
|
+
*
|
|
9
|
+
* @module
|
|
10
|
+
*/
|
|
11
|
+
type AppResource = {
|
|
12
|
+
/** Absolute path to the bundle's root configuration file. */
|
|
13
|
+
bundlePath: string;
|
|
14
|
+
/** Resource key under `resources.apps`. */
|
|
15
|
+
key: string;
|
|
16
|
+
/** Absolute source directory resolved from `source_code_path`. */
|
|
17
|
+
sourceCodePath?: string;
|
|
18
|
+
/** App resource returned by `databricks bundle validate`. */
|
|
19
|
+
config: Record<string, unknown>;
|
|
20
|
+
/** Complete resolved bundle returned by `databricks bundle validate`. */
|
|
21
|
+
data: Record<string, unknown>;
|
|
22
|
+
/** Validation failure associated with partial bundle output. */
|
|
23
|
+
bundleFailure?: Error;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Stream every App resource in bundles under `projectBoundary`.
|
|
27
|
+
*
|
|
28
|
+
* `cwd` defaults to the current directory and may name a file or directory.
|
|
29
|
+
* Bundle files in its ancestor chain are checked before the rest of the project
|
|
30
|
+
* boundary. No App YAML file is required.
|
|
31
|
+
*
|
|
32
|
+
* A missing or invalid boundary/cwd, or a cwd outside the boundary, yields
|
|
33
|
+
* nothing. A non-zero validation with partial JSON attaches
|
|
34
|
+
* `bundleFailure` to each App resource parsed from that bundle. A failed
|
|
35
|
+
* validation with no App resources yields nothing.
|
|
36
|
+
*/
|
|
37
|
+
export declare function appResources(projectBoundary: string, cwd?: string): AsyncGenerator<AppResource, void, void>;
|
|
38
|
+
export {};
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Streaming discovery of Databricks bundle App resources.
|
|
3
|
+
*
|
|
4
|
+
* Bundle files are found with native filesystem traversal inside a hard project
|
|
5
|
+
* boundary. Each candidate is resolved through `databricks bundle validate
|
|
6
|
+
* --output json` so includes, targets, variables, and App configuration follow
|
|
7
|
+
* the Databricks CLI's semantics.
|
|
8
|
+
*
|
|
9
|
+
* @module
|
|
10
|
+
*/
|
|
11
|
+
import { opendir, realpath, stat } from "node:fs/promises";
|
|
12
|
+
import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
13
|
+
import { json, object, string } from "@dbx-tools/shared-core";
|
|
14
|
+
import { spawn } from "./exec.js";
|
|
15
|
+
import { resolveWorkingDirectory } from "./project.js";
|
|
16
|
+
import { project } from "../index.js";
|
|
17
|
+
const BUNDLE_FILE_NAMES = new Set(["databricks.yml", "databricks.yaml"]);
|
|
18
|
+
const IGNORED_DIRECTORIES = [
|
|
19
|
+
/^\..*/,
|
|
20
|
+
"node_modules",
|
|
21
|
+
"dist",
|
|
22
|
+
"build",
|
|
23
|
+
"out",
|
|
24
|
+
"target",
|
|
25
|
+
"tmp",
|
|
26
|
+
"vendor",
|
|
27
|
+
];
|
|
28
|
+
/**
|
|
29
|
+
* Stream every App resource in bundles under `projectBoundary`.
|
|
30
|
+
*
|
|
31
|
+
* `cwd` defaults to the current directory and may name a file or directory.
|
|
32
|
+
* Bundle files in its ancestor chain are checked before the rest of the project
|
|
33
|
+
* boundary. No App YAML file is required.
|
|
34
|
+
*
|
|
35
|
+
* A missing or invalid boundary/cwd, or a cwd outside the boundary, yields
|
|
36
|
+
* nothing. A non-zero validation with partial JSON attaches
|
|
37
|
+
* `bundleFailure` to each App resource parsed from that bundle. A failed
|
|
38
|
+
* validation with no App resources yields nothing.
|
|
39
|
+
*/
|
|
40
|
+
export async function* appResources(projectBoundary, cwd) {
|
|
41
|
+
const boundary = await resolveDirectory(projectBoundary);
|
|
42
|
+
const startDirectory = await resolveStartDirectory(cwd);
|
|
43
|
+
if (!boundary || !startDirectory || !isWithinOrEqual(startDirectory, boundary)) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
for await (const bundlePath of bundlePaths(boundary, startDirectory)) {
|
|
47
|
+
const validated = await validateBundle(bundlePath);
|
|
48
|
+
for (const [key, config] of Object.entries(bundleApps(validated.data))) {
|
|
49
|
+
if (!object.isRecord(config))
|
|
50
|
+
continue;
|
|
51
|
+
yield {
|
|
52
|
+
bundlePath,
|
|
53
|
+
key,
|
|
54
|
+
config,
|
|
55
|
+
data: validated.data,
|
|
56
|
+
...object.optional("sourceCodePath", resolveSourceCodePath(bundlePath, config.source_code_path)),
|
|
57
|
+
...object.optional("bundleFailure", validated.bundleFailure),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async function resolveDirectory(value) {
|
|
63
|
+
try {
|
|
64
|
+
const resolved = await realpath(resolve(value));
|
|
65
|
+
return (await stat(resolved)).isDirectory() ? resolved : undefined;
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async function resolveStartDirectory(cwd) {
|
|
72
|
+
try {
|
|
73
|
+
const resolved = await realpath(resolveWorkingDirectory(cwd));
|
|
74
|
+
const info = await stat(resolved);
|
|
75
|
+
if (info.isDirectory())
|
|
76
|
+
return resolved;
|
|
77
|
+
if (info.isFile())
|
|
78
|
+
return dirname(resolved);
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function resolveSourceCodePath(bundlePath, sourceCodePath) {
|
|
86
|
+
if (typeof sourceCodePath !== "string")
|
|
87
|
+
return undefined;
|
|
88
|
+
return isAbsolute(sourceCodePath) ? sourceCodePath : resolve(dirname(bundlePath), sourceCodePath);
|
|
89
|
+
}
|
|
90
|
+
async function* bundlePaths(boundary, startDirectory) {
|
|
91
|
+
const seen = new Set();
|
|
92
|
+
let current = startDirectory;
|
|
93
|
+
while (isWithinOrEqual(current, boundary)) {
|
|
94
|
+
for (const filename of BUNDLE_FILE_NAMES) {
|
|
95
|
+
const candidate = join(current, filename);
|
|
96
|
+
if (!(await isFile(candidate)))
|
|
97
|
+
continue;
|
|
98
|
+
const resolved = await realpath(candidate);
|
|
99
|
+
if (seen.has(resolved))
|
|
100
|
+
continue;
|
|
101
|
+
seen.add(resolved);
|
|
102
|
+
yield resolved;
|
|
103
|
+
}
|
|
104
|
+
if (current === boundary)
|
|
105
|
+
break;
|
|
106
|
+
current = dirname(current);
|
|
107
|
+
}
|
|
108
|
+
yield* descendBundlePaths(boundary, seen);
|
|
109
|
+
}
|
|
110
|
+
async function* descendBundlePaths(directory, seen) {
|
|
111
|
+
const entries = await opendir(directory);
|
|
112
|
+
for await (const entry of entries) {
|
|
113
|
+
const candidate = join(directory, entry.name);
|
|
114
|
+
if (entry.isDirectory()) {
|
|
115
|
+
const ignored = IGNORED_DIRECTORIES.some((ignore) => {
|
|
116
|
+
if (ignore instanceof RegExp)
|
|
117
|
+
return ignore.test(entry.name);
|
|
118
|
+
return ignore === entry.name;
|
|
119
|
+
});
|
|
120
|
+
if (!ignored) {
|
|
121
|
+
yield* descendBundlePaths(candidate, seen);
|
|
122
|
+
}
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (!entry.isFile() || !BUNDLE_FILE_NAMES.has(entry.name))
|
|
126
|
+
continue;
|
|
127
|
+
const resolved = await realpath(candidate);
|
|
128
|
+
if (seen.has(resolved))
|
|
129
|
+
continue;
|
|
130
|
+
seen.add(resolved);
|
|
131
|
+
yield resolved;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
async function validateBundle(bundlePath) {
|
|
135
|
+
const result = await spawn("databricks", ["bundle", "validate", "--output", "json"], {
|
|
136
|
+
cwd: dirname(bundlePath),
|
|
137
|
+
stdin: "ignore",
|
|
138
|
+
stdout: "capture",
|
|
139
|
+
stderr: "capture",
|
|
140
|
+
});
|
|
141
|
+
const detail = string.trimToNull(result.stderr) ?? string.trimToNull(result.stdout);
|
|
142
|
+
const bundleFailure = result.exitCode === 0
|
|
143
|
+
? undefined
|
|
144
|
+
: new Error(`databricks bundle validate failed for ${bundlePath} (exit ${result.exitCode})${detail ? `: ${detail}` : ""}`);
|
|
145
|
+
const parsed = json.parseRecord(result.stdout);
|
|
146
|
+
if (!parsed) {
|
|
147
|
+
if (bundleFailure)
|
|
148
|
+
return { data: {}, bundleFailure };
|
|
149
|
+
throw new Error(`Databricks CLI returned invalid bundle JSON for ${bundlePath}`);
|
|
150
|
+
}
|
|
151
|
+
return { data: parsed, ...object.optional("bundleFailure", bundleFailure) };
|
|
152
|
+
}
|
|
153
|
+
function bundleApps(bundle) {
|
|
154
|
+
const resources = object.isRecord(bundle.resources) ? bundle.resources : undefined;
|
|
155
|
+
return object.isRecord(resources?.apps) ? resources.apps : {};
|
|
156
|
+
}
|
|
157
|
+
async function isFile(path) {
|
|
158
|
+
try {
|
|
159
|
+
return (await stat(path)).isFile();
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function isWithinOrEqual(candidate, parent) {
|
|
166
|
+
const path = relative(parent, candidate);
|
|
167
|
+
return path === "" || (path !== ".." && !path.startsWith(`..${sep}`) && !isAbsolute(path));
|
|
168
|
+
}
|
|
169
|
+
if (import.meta.main) {
|
|
170
|
+
const path = process.argv[2];
|
|
171
|
+
if (!path)
|
|
172
|
+
throw new Error("Usage: bun bundle.ts <path>");
|
|
173
|
+
for await (const resource of appResources(project.root(), path)) {
|
|
174
|
+
console.dir(resource, { depth: null });
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnVuZGxlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2J1bmRsZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7Ozs7Ozs7O0dBU0c7QUFFSCxPQUFPLEVBQUUsT0FBTyxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUMzRCxPQUFPLEVBQUUsT0FBTyxFQUFFLFVBQVUsRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLE9BQU8sRUFBRSxHQUFHLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFDOUUsT0FBTyxFQUFFLElBQUksRUFBRSxNQUFNLEVBQUUsTUFBTSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7QUFDOUQsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLFdBQVcsQ0FBQztBQUNsQyxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFDdkQsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUV0QyxNQUFNLGlCQUFpQixHQUFHLElBQUksR0FBRyxDQUFDLENBQUMsZ0JBQWdCLEVBQUUsaUJBQWlCLENBQUMsQ0FBQyxDQUFDO0FBQ3pFLE1BQU0sbUJBQW1CLEdBQUc7SUFDMUIsT0FBTztJQUNQLGNBQWM7SUFDZCxNQUFNO0lBQ04sT0FBTztJQUNQLEtBQUs7SUFDTCxRQUFRO0lBQ1IsS0FBSztJQUNMLFFBQVE7Q0FDQSxDQUFDO0FBaUJYOzs7Ozs7Ozs7OztHQVdHO0FBQ0gsTUFBTSxDQUFDLEtBQUssU0FBUyxDQUFDLENBQUMsWUFBWSxDQUNqQyxlQUF1QixFQUN2QixHQUFZO0lBRVosTUFBTSxRQUFRLEdBQUcsTUFBTSxnQkFBZ0IsQ0FBQyxlQUFlLENBQUMsQ0FBQztJQUN6RCxNQUFNLGNBQWMsR0FBRyxNQUFNLHFCQUFxQixDQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQ3hELElBQUksQ0FBQyxRQUFRLElBQUksQ0FBQyxjQUFjLElBQUksQ0FBQyxlQUFlLENBQUMs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SUFBSSxDQUFDLElBQUksQ0FBQyxVQUFVLENBQUMsS0FBSyxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7QUFDN0YsQ0FBQztBQUVELElBQUksTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsQ0FBQztJQUNyQixNQUFNLElBQUksR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQzdCLElBQUksQ0FBQyxJQUFJO1FBQUUsTUFBTSxJQUFJLEtBQUssQ0FBQyw2QkFBNkIsQ0FBQyxDQUFDO0lBQzFELElBQUksS0FBSyxFQUFFLE1BQU0sUUFBUSxJQUFJLFlBQVksQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFHLEVBQUUsSUFBSSxDQUFDLEVBQUUsQ0FBQztRQUNqRSxPQUFPLENBQUMsR0FBRyxDQUFDLFFBQVEsRUFBRSxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBQ3pDLENBQUM7QUFDSCxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBTdHJlYW1pbmcgZGlzY292ZXJ5IG9mIERhdGFicmlja3MgYnVuZGxlIEFwcCByZXNvdXJjZXMuXG4gKlxuICogQnVuZGxlIGZpbGVzIGFyZSBmb3VuZCB3aXRoIG5hdGl2ZSBmaWxlc3lzdGVtIHRyYXZlcnNhbCBpbnNpZGUgYSBoYXJkIHByb2plY3RcbiAqIGJvdW5kYXJ5LiBFYWNoIGNhbmRpZGF0ZSBpcyByZXNvbHZlZCB0aHJvdWdoIGBkYXRhYnJpY2tzIGJ1bmRsZSB2YWxpZGF0ZVxuICogLS1vdXRwdXQganNvbmAgc28gaW5jbHVkZXMsIHRhcmdldHMsIHZhcmlhYmxlcywgYW5kIEFwcCBjb25maWd1cmF0aW9uIGZvbGxvd1xuICogdGhlIERhdGFicmlja3MgQ0xJJ3Mgc2VtYW50aWNzLlxuICpcbiAqIEBtb2R1bGVcbiAqL1xuXG5pbXBvcnQgeyBvcGVuZGlyLCByZWFscGF0aCwgc3RhdCB9IGZyb20gXCJub2RlOmZzL3Byb21pc2VzXCI7XG5pbXBvcnQgeyBkaXJuYW1lLCBpc0Fic29sdXRlLCBqb2luLCByZWxhdGl2ZSwgcmVzb2x2ZSwgc2VwIH0gZnJvbSBcIm5vZGU6cGF0aFwiO1xuaW1wb3J0IHsganNvbiwgb2JqZWN0LCBzdHJpbmcgfSBmcm9tIFwiQGRieC10b29scy9zaGFyZWQtY29yZVwiO1xuaW1wb3J0IHsgc3Bhd24gfSBmcm9tIFwiLi9leGVjLnRzXCI7XG5pbXBvcnQgeyByZXNvbHZlV29ya2luZ0RpcmVjdG9yeSB9IGZyb20gXCIuL3Byb2plY3QudHNcIjtcbmltcG9ydCB7IHByb2plY3QgfSBmcm9tIFwiLi4vaW5kZXgudHNcIjtcblxuY29uc3QgQlVORExFX0ZJTEVfTkFNRVMgPSBuZXcgU2V0KFtcImRhdGFicmlja3MueW1sXCIsIFwiZGF0YWJyaWNrcy55YW1sXCJdKTtcbmNvbnN0IElHTk9SRURfRElSRUNUT1JJRVMgPSBbXG4gIC9eXFwuLiovLFxuICBcIm5vZGVfbW9kdWxlc1wiLFxuICBcImRpc3RcIixcbiAgXCJidWlsZFwiLFxuICBcIm91dFwiLFxuICBcInRhcmdldFwiLFxuICBcInRtcFwiLFxuICBcInZlbmRvclwiLFxuXSBhcyBjb25zdDtcblxudHlwZSBBcHBSZXNvdXJjZSA9IHtcbiAgLyoqIEFic29sdXRlIHBhdGggdG8gdGhlIGJ1bmRsZSdzIHJvb3QgY29uZmlndXJhdGlvbiBmaWxlLiAqL1xuICBidW5kbGVQYXRoOiBzdHJpbmc7XG4gIC8qKiBSZXNvdXJjZSBrZXkgdW5kZXIgYHJlc291cmNlcy5hcHBzYC4gKi9cbiAga2V5OiBzdHJpbmc7XG4gIC8qKiBBYnNvbHV0ZSBzb3VyY2UgZGlyZWN0b3J5IHJlc29sdmVkIGZyb20gYHNvdXJjZV9jb2RlX3BhdGhgLiAqL1xuICBzb3VyY2VDb2RlUGF0aD86IHN0cmluZztcbiAgLyoqIEFwcCByZXNvdXJjZSByZXR1cm5lZCBieSBgZGF0YWJyaWNrcyBidW5kbGUgdmFsaWRhdGVgLiAqL1xuICBjb25maWc6IFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xuICAvKiogQ29tcGxldGUgcmVzb2x2ZWQgYnVuZGxlIHJldHVybmVkIGJ5IGBkYXRhYnJpY2tzIGJ1bmRsZSB2YWxpZGF0ZWAuICovXG4gIGRhdGE6IFJlY29yZDxzdHJpbmcsIHVua25vd24+O1xuICAvKiogVmFsaWRhdGlvbiBmYWlsdXJlIGFzc29jaWF0ZWQgd2l0aCBwYXJ0aWFsIGJ1bmRsZSBvdXRwdXQuICovXG4gIGJ1bmRsZUZhaWx1cmU/OiBFcnJvcjtcbn07XG5cbi8qKlxuICogU3RyZWFtIGV2ZXJ5IEFwcCByZXNvdXJjZSBpbiBidW5kbGVzIHVuZGVyIGBwcm9qZWN0Qm91bmRhcnlgLlxuICpcbiAqIGBjd2RgIGRlZmF1bHRzIHRvIHRoZSBjdXJyZW50IGRpcmVjdG9yeSBhbmQgbWF5IG5hbWUgYSBmaWxlIG9yIGRpcmVjdG9yeS5cbiAqIEJ1bmRsZSBmaWxlcyBpbiBpdHMgYW5jZXN0b3IgY2hhaW4gYXJlIGNoZWNrZWQgYmVmb3JlIHRoZSByZXN0IG9mIHRoZSBwcm9qZWN0XG4gKiBib3VuZGFyeS4gTm8gQXBwIFlBTUwgZmlsZSBpcyByZXF1aXJlZC5cbiAqXG4gKiBBIG1pc3Npbmcgb3IgaW52YWxpZCBib3VuZGFyeS9jd2QsIG9yIGEgY3dkIG91dHNpZGUgdGhlIGJvdW5kYXJ5LCB5aWVsZHNcbiAqIG5vdGhpbmcuIEEgbm9uLXplcm8gdmFsaWRhdGlvbiB3aXRoIHBhcnRpYWwgSlNPTiBhdHRhY2hlc1xuICogYGJ1bmRsZUZhaWx1cmVgIHRvIGVhY2ggQXBwIHJlc291cmNlIHBhcnNlZCBmcm9tIHRoYXQgYnVuZGxlLiBBIGZhaWxlZFxuICogdmFsaWRhdGlvbiB3aXRoIG5vIEFwcCByZXNvdXJjZXMgeWllbGRzIG5vdGhpbmcuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiogYXBwUmVzb3VyY2VzKFxuICBwcm9qZWN0Qm91bmRhcnk6IHN0cmluZyxcbiAgY3dkPzogc3RyaW5nLFxuKTogQXN5bmNHZW5lcmF0b3I8QXBwUmVzb3VyY2UsIHZvaWQsIHZvaWQ+IHtcbiAgY29uc3QgYm91bmRhcnkgPSBhd2FpdCByZXNvbHZlRGlyZWN0b3J5KHByb2plY3RCb3VuZGFyeSk7XG4gIGNvbnN0IHN0YXJ0RGlyZWN0b3J5ID0gYXdhaXQgcmVzb2x2ZVN0YXJ0RGlyZWN0b3J5KGN3ZCk7XG4gIGlmICghYm91bmRhcnkgfHwgIXN0YXJ0RGlyZWN0b3J5IHx8ICFpc1dpdGhpbk9yRXF1YWwoc3RhcnREaXJlY3RvcnksIGJvdW5kYXJ5KSkge1xuICAgIHJldHVybjtcbiAgfVxuXG4gIGZvciBhd2FpdCAoY29uc3QgYnVuZGxlUGF0aCBvZiBidW5kbGVQYXRocyhib3VuZGFyeSwgc3RhcnREaXJlY3RvcnkpKSB7XG4gICAgY29uc3QgdmFsaWRhdGVkID0gYXdhaXQgdmFsaWRhdGVCdW5kbGUoYnVuZGxlUGF0aCk7XG4gICAgZm9yIChjb25zdCBba2V5LCBjb25maWddIG9mIE9iamVjdC5lbnRyaWVzKGJ1bmRsZUFwcHModmFsaWRhdGVkLmRhdGEpKSkge1xuICAgICAgaWYgKCFvYmplY3QuaXNSZWNvcmQoY29uZmlnKSkgY29udGludWU7XG4gICAgICB5aWVsZCB7XG4gICAgICAgIGJ1bmRsZVBhdGgsXG4gICAgICAgIGtleSxcbiAgICAgICAgY29uZmlnLFxuICAgICAgICBkYXRhOiB2YWxpZGF0ZWQuZGF0YSxcbiAgICAgICAgLi4ub2JqZWN0Lm9wdGlvbmFsKFxuICAgICAgICAgIFwic291cmNlQ29kZVBhdGhcIixcbiAgICAgICAgICByZXNvbHZlU291cmNlQ29kZVBhdGgoYnVuZGxlUGF0aCwgY29uZmlnLnNvdXJjZV9jb2RlX3BhdGgpLFxuICAgICAgICApLFxuICAgICAgICAuLi5vYmplY3Qub3B0aW9uYWwoXCJidW5kbGVGYWlsdXJlXCIsIHZhbGlkYXRlZC5idW5kbGVGYWlsdXJlKSxcbiAgICAgIH07XG4gICAgfVxuICB9XG59XG5cbmFzeW5jIGZ1bmN0aW9uIHJlc29sdmVEaXJlY3RvcnkodmFsdWU6IHN0cmluZyk6IFByb21pc2U8c3RyaW5nIHwgdW5kZWZpbmVkPiB7XG4gIHRyeSB7XG4gICAgY29uc3QgcmVzb2x2ZWQgPSBhd2FpdCByZWFscGF0aChyZXNvbHZlKHZhbHVlKSk7XG4gICAgcmV0dXJuIChhd2FpdCBzdGF0KHJlc29sdmVkKSkuaXNEaXJlY3RvcnkoKSA/IHJlc29sdmVkIDogdW5kZWZpbmVkO1xuICB9IGNhdGNoIHtcbiAgICByZXR1cm4gdW5kZWZpbmVkO1xuICB9XG59XG5cbmFzeW5jIGZ1bmN0aW9uIHJlc29sdmVTdGFydERpcmVjdG9yeShjd2Q/OiBzdHJpbmcpOiBQcm9taXNlPHN0cmluZyB8IHVuZGVmaW5lZD4ge1xuICB0cnkge1xuICAgIGNvbnN0IHJlc29sdmVkID0gYXdhaXQgcmVhbHBhdGgocmVzb2x2ZVdvcmtpbmdEaXJlY3RvcnkoY3dkKSk7XG4gICAgY29uc3QgaW5mbyA9IGF3YWl0IHN0YXQocmVzb2x2ZWQpO1xuICAgIGlmIChpbmZvLmlzRGlyZWN0b3J5KCkpIHJldHVybiByZXNvbHZlZDtcbiAgICBpZiAoaW5mby5pc0ZpbGUoKSkgcmV0dXJuIGRpcm5hbWUocmVzb2x2ZWQpO1xuICAgIHJldHVybiB1bmRlZmluZWQ7XG4gIH0gY2F0Y2gge1xuICAgIHJldHVybiB1bmRlZmluZWQ7XG4gIH1cbn1cblxuZnVuY3Rpb24gcmVzb2x2ZVNvdXJjZUNvZGVQYXRoKGJ1bmRsZVBhdGg6IHN0cmluZywgc291cmNlQ29kZVBhdGg6IHVua25vd24pOiBzdHJpbmcgfCB1bmRlZmluZWQge1xuICBpZiAodHlwZW9mIHNvdXJjZUNvZGVQYXRoICE9PSBcInN0cmluZ1wiKSByZXR1cm4gdW5kZWZpbmVkO1xuICByZXR1cm4gaXNBYnNvbHV0ZShzb3VyY2VDb2RlUGF0aCkgPyBzb3VyY2VDb2RlUGF0aCA6IHJlc29sdmUoZGlybmFtZShidW5kbGVQYXRoKSwgc291cmNlQ29kZVBhdGgpO1xufVxuXG5hc3luYyBmdW5jdGlvbiogYnVuZGxlUGF0aHMoXG4gIGJvdW5kYXJ5OiBzdHJpbmcsXG4gIHN0YXJ0RGlyZWN0b3J5OiBzdHJpbmcsXG4pOiBBc3luY0dlbmVyYXRvcjxzdHJpbmcsIHZvaWQsIHZvaWQ+IHtcbiAgY29uc3Qgc2VlbiA9IG5ldyBTZXQ8c3RyaW5nPigpO1xuICBsZXQgY3VycmVudCA9IHN0YXJ0RGlyZWN0b3J5O1xuICB3aGlsZSAoaXNXaXRoaW5PckVxdWFsKGN1cnJlbnQsIGJvdW5kYXJ5KSkge1xuICAgIGZvciAoY29uc3QgZmlsZW5hbWUgb2YgQlVORExFX0ZJTEVfTkFNRVMpIHtcbiAgICAgIGNvbnN0IGNhbmRpZGF0ZSA9IGpvaW4oY3VycmVudCwgZmlsZW5hbWUpO1xuICAgICAgaWYgKCEoYXdhaXQgaXNGaWxlKGNhbmRpZGF0ZSkpKSBjb250aW51ZTtcbiAgICAgIGNvbnN0IHJlc29sdmVkID0gYXdhaXQgcmVhbHBhdGgoY2FuZGlkYXRlKTtcbiAgICAgIGlmIChzZWVuLmhhcyhyZXNvbHZlZCkpIGNvbnRpbnVlO1xuICAgICAgc2Vlbi5hZGQocmVzb2x2ZWQpO1xuICAgICAgeWllbGQgcmVzb2x2ZWQ7XG4gICAgfVxuICAgIGlmIChjdXJyZW50ID09PSBib3VuZGFyeSkgYnJlYWs7XG4gICAgY3VycmVudCA9IGRpcm5hbWUoY3VycmVudCk7XG4gIH1cblxuICB5aWVsZCogZGVzY2VuZEJ1bmRsZVBhdGhzKGJvdW5kYXJ5LCBzZWVuKTtcbn1cblxuYXN5bmMgZnVuY3Rpb24qIGRlc2NlbmRCdW5kbGVQYXRocyhcbiAgZGlyZWN0b3J5OiBzdHJpbmcsXG4gIHNlZW46IFNldDxzdHJpbmc+LFxuKTogQXN5bmNHZW5lcmF0b3I8c3RyaW5nLCB2b2lkLCB2b2lkPiB7XG4gIGNvbnN0IGVudHJpZXMgPSBhd2FpdCBvcGVuZGlyKGRpcmVjdG9yeSk7XG4gIGZvciBhd2FpdCAoY29uc3QgZW50cnkgb2YgZW50cmllcykge1xuICAgIGNvbnN0IGNhbmRpZGF0ZSA9IGpvaW4oZGlyZWN0b3J5LCBlbnRyeS5uYW1lKTtcbiAgICBpZiAoZW50cnkuaXNEaXJlY3RvcnkoKSkge1xuICAgICAgY29uc3QgaWdub3JlZCA9IElHTk9SRURfRElSRUNUT1JJRVMuc29tZSgoaWdub3JlKSA9PiB7XG4gICAgICAgIGlmIChpZ25vcmUgaW5zdGFuY2VvZiBSZWdFeHApIHJldHVybiBpZ25vcmUudGVzdChlbnRyeS5uYW1lKTtcbiAgICAgICAgcmV0dXJuIGlnbm9yZSA9PT0gZW50cnkubmFtZTtcbiAgICAgIH0pO1xuICAgICAgaWYgKCFpZ25vcmVkKSB7XG4gICAgICAgIHlpZWxkKiBkZXNjZW5kQnVuZGxlUGF0aHMoY2FuZGlkYXRlLCBzZWVuKTtcbiAgICAgIH1cbiAgICAgIGNvbnRpbnVlO1xuICAgIH1cbiAgICBpZiAoIWVudHJ5LmlzRmlsZSgpIHx8ICFCVU5ETEVfRklMRV9OQU1FUy5oYXMoZW50cnkubmFtZSkpIGNvbnRpbnVlO1xuICAgIGNvbnN0IHJlc29sdmVkID0gYXdhaXQgcmVhbHBhdGgoY2FuZGlkYXRlKTtcbiAgICBpZiAoc2Vlbi5oYXMocmVzb2x2ZWQpKSBjb250aW51ZTtcbiAgICBzZWVuLmFkZChyZXNvbHZlZCk7XG4gICAgeWllbGQgcmVzb2x2ZWQ7XG4gIH1cbn1cblxuYXN5bmMgZnVuY3Rpb24gdmFsaWRhdGVCdW5kbGUoXG4gIGJ1bmRsZVBhdGg6IHN0cmluZyxcbik6IFByb21pc2U8UGljazxBcHBSZXNvdXJjZSwgXCJkYXRhXCIgfCBcImJ1bmRsZUZhaWx1cmVcIj4+IHtcbiAgY29uc3QgcmVzdWx0ID0gYXdhaXQgc3Bhd24oXCJkYXRhYnJpY2tzXCIsIFtcImJ1bmRsZVwiLCBcInZhbGlkYXRlXCIsIFwiLS1vdXRwdXRcIiwgXCJqc29uXCJdLCB7XG4gICAgY3dkOiBkaXJuYW1lKGJ1bmRsZVBhdGgpLFxuICAgIHN0ZGluOiBcImlnbm9yZVwiLFxuICAgIHN0ZG91dDogXCJjYXB0dXJlXCIsXG4gICAgc3RkZXJyOiBcImNhcHR1cmVcIixcbiAgfSk7XG4gIGNvbnN0IGRldGFpbCA9IHN0cmluZy50cmltVG9OdWxsKHJlc3VsdC5zdGRlcnIpID8/IHN0cmluZy50cmltVG9OdWxsKHJlc3VsdC5zdGRvdXQpO1xuICBjb25zdCBidW5kbGVGYWlsdXJlID1cbiAgICByZXN1bHQuZXhpdENvZGUgPT09IDBcbiAgICAgID8gdW5kZWZpbmVkXG4gICAgICA6IG5ldyBFcnJvcihcbiAgICAgICAgICBgZGF0YWJyaWNrcyBidW5kbGUgdmFsaWRhdGUgZmFpbGVkIGZvciAke2J1bmRsZVBhdGh9IChleGl0ICR7cmVzdWx0LmV4aXRDb2RlfSkke1xuICAgICAgICAgICAgZGV0YWlsID8gYDogJHtkZXRhaWx9YCA6IFwiXCJcbiAgICAgICAgICB9YCxcbiAgICAgICAgKTtcblxuICBjb25zdCBwYXJzZWQgPSBqc29uLnBhcnNlUmVjb3JkKHJlc3VsdC5zdGRvdXQpO1xuICBpZiAoIXBhcnNlZCkge1xuICAgIGlmIChidW5kbGVGYWlsdXJlKSByZXR1cm4geyBkYXRhOiB7fSwgYnVuZGxlRmFpbHVyZSB9O1xuICAgIHRocm93IG5ldyBFcnJvcihgRGF0YWJyaWNrcyBDTEkgcmV0dXJuZWQgaW52YWxpZCBidW5kbGUgSlNPTiBmb3IgJHtidW5kbGVQYXRofWApO1xuICB9XG4gIHJldHVybiB7IGRhdGE6IHBhcnNlZCwgLi4ub2JqZWN0Lm9wdGlvbmFsKFwiYnVuZGxlRmFpbHVyZVwiLCBidW5kbGVGYWlsdXJlKSB9O1xufVxuXG5mdW5jdGlvbiBidW5kbGVBcHBzKGJ1bmRsZTogUmVjb3JkPHN0cmluZywgdW5rbm93bj4pOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiB7XG4gIGNvbnN0IHJlc291cmNlcyA9IG9iamVjdC5pc1JlY29yZChidW5kbGUucmVzb3VyY2VzKSA/IGJ1bmRsZS5yZXNvdXJjZXMgOiB1bmRlZmluZWQ7XG4gIHJldHVybiBvYmplY3QuaXNSZWNvcmQocmVzb3VyY2VzPy5hcHBzKSA/IHJlc291cmNlcy5hcHBzIDoge307XG59XG5cbmFzeW5jIGZ1bmN0aW9uIGlzRmlsZShwYXRoOiBzdHJpbmcpOiBQcm9taXNlPGJvb2xlYW4+IHtcbiAgdHJ5IHtcbiAgICByZXR1cm4gKGF3YWl0IHN0YXQocGF0aCkpLmlzRmlsZSgpO1xuICB9IGNhdGNoIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cbn1cblxuZnVuY3Rpb24gaXNXaXRoaW5PckVxdWFsKGNhbmRpZGF0ZTogc3RyaW5nLCBwYXJlbnQ6IHN0cmluZyk6IGJvb2xlYW4ge1xuICBjb25zdCBwYXRoID0gcmVsYXRpdmUocGFyZW50LCBjYW5kaWRhdGUpO1xuICByZXR1cm4gcGF0aCA9PT0gXCJcIiB8fCAocGF0aCAhPT0gXCIuLlwiICYmICFwYXRoLnN0YXJ0c1dpdGgoYC4uJHtzZXB9YCkgJiYgIWlzQWJzb2x1dGUocGF0aCkpO1xufVxuXG5pZiAoaW1wb3J0Lm1ldGEubWFpbikge1xuICBjb25zdCBwYXRoID0gcHJvY2Vzcy5hcmd2WzJdO1xuICBpZiAoIXBhdGgpIHRocm93IG5ldyBFcnJvcihcIlVzYWdlOiBidW4gYnVuZGxlLnRzIDxwYXRoPlwiKTtcbiAgZm9yIGF3YWl0IChjb25zdCByZXNvdXJjZSBvZiBhcHBSZXNvdXJjZXMocHJvamVjdC5yb290KCkhLCBwYXRoKSkge1xuICAgIGNvbnNvbGUuZGlyKHJlc291cmNlLCB7IGRlcHRoOiBudWxsIH0pO1xuICB9XG59XG4iXX0=
|
package/lib/src/config.d.ts
CHANGED
|
@@ -1,29 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Layered configuration lookup: environment, `.env`,
|
|
2
|
+
* Layered configuration lookup: environment, `.env`, bundle, and `app.yaml`.
|
|
3
3
|
*
|
|
4
4
|
* Every package resolves settings the same way: take the caller's value, else
|
|
5
5
|
* an environment variable, else a default. Local development adds two fallback
|
|
6
|
-
* locations: `.env` files
|
|
7
|
-
* `databricks.yml`.
|
|
6
|
+
* locations: `.env` files, one App's `resources.apps.<app>.config.env` in
|
|
7
|
+
* `databricks.yml`, and literal env values in `app.yaml` / `app.yml`.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
9
|
+
* {@link values} is LAZY (an `object.Sequence`), so `databricks bundle validate`
|
|
10
|
+
* is only spawned when the environment and `.env` both missed; app YAML is only
|
|
11
|
+
* read after the bundle source also missed.
|
|
10
12
|
*
|
|
11
|
-
*
|
|
12
|
-
* validate` is only spawned when the environment and `.env` both missed.
|
|
13
|
-
* - the parsed `.env` and bundle are cached through {@link context.cached}, so
|
|
14
|
-
* the spawn happens once per working directory and a `cwd` change misses
|
|
15
|
-
* rather than returning another project's config.
|
|
16
|
-
*
|
|
17
|
-
* Inside a deployed Databricks App both file sources are skipped by default
|
|
13
|
+
* Inside a deployed Databricks App all three file sources are skipped by default
|
|
18
14
|
* ({@link isDatabricksAppEnv}): the platform has already turned them into real
|
|
19
15
|
* environment variables, there is no bundle to validate, and the `databricks`
|
|
20
16
|
* CLI is not on the image. Boolean environment overrides can force either file
|
|
21
17
|
* source on or off when a tool needs different behavior.
|
|
22
18
|
*
|
|
23
|
-
* Only the single app's `config.env`
|
|
24
|
-
*
|
|
25
|
-
* targets, resources, and paths),
|
|
26
|
-
* names the deployed app never sees.
|
|
19
|
+
* Only the single bundle app's `config.env` and literal app YAML `env[].value`
|
|
20
|
+
* entries are consulted. Root bundle `variables` are not: they are authoring
|
|
21
|
+
* inputs for the bundle itself (interpolated into targets, resources, and paths),
|
|
22
|
+
* so treating one as a process setting resolves names the deployed app never sees.
|
|
27
23
|
*
|
|
28
24
|
* Node-only (`child_process`, `fs`, `process`).
|
|
29
25
|
*
|
|
@@ -31,22 +27,30 @@
|
|
|
31
27
|
*/
|
|
32
28
|
import { z } from "zod";
|
|
33
29
|
type ConfigKey = string | readonly string[];
|
|
30
|
+
export type ConfigMapValue = string | readonly string[] | null | undefined;
|
|
31
|
+
export type ConfigData = Readonly<Record<string, ConfigMapValue>>;
|
|
34
32
|
/** Where a value may come from, consulted in the order given. */
|
|
35
|
-
type ConfigSource = "env" | "dotenv" | "bundle";
|
|
36
|
-
interface ConfigOptions {
|
|
33
|
+
export type ConfigSource = "config" | "env" | "dotenv" | "bundle" | "app";
|
|
34
|
+
export interface ConfigOptions {
|
|
37
35
|
/**
|
|
38
36
|
* Outermost namespaces tried before each key. Defaults to `DBX_TOOLS`.
|
|
39
37
|
*/
|
|
40
38
|
scope?: string | readonly string[];
|
|
41
39
|
/** Capability namespaces inserted after the scope and before each key. */
|
|
42
40
|
prefix?: string | readonly string[];
|
|
43
|
-
/**
|
|
41
|
+
/** Constant config maps read by the `config` source. */
|
|
42
|
+
data?: ConfigData | readonly ConfigData[];
|
|
43
|
+
/** Parsed bundle data used instead of loading `databricks.yml`. */
|
|
44
|
+
bundleData?: ConfigFile | Record<string, unknown>;
|
|
45
|
+
/** Parsed app YAML data used instead of loading `app.yaml`. */
|
|
46
|
+
appData?: ConfigFile | Record<string, unknown>;
|
|
47
|
+
/** Sources in precedence order. Default: `config`, `env`, `dotenv`, `bundle`, `app`. */
|
|
44
48
|
sources?: ConfigSource | readonly ConfigSource[];
|
|
45
49
|
/** Directory to resolve `.env` and the bundle from. Default: `process.cwd()`. */
|
|
46
50
|
cwd?: string;
|
|
47
51
|
}
|
|
48
52
|
/** A config file found on disk, with its parsed contents. */
|
|
49
|
-
interface ConfigFile {
|
|
53
|
+
export interface ConfigFile {
|
|
50
54
|
path: string;
|
|
51
55
|
data: Record<string, unknown>;
|
|
52
56
|
}
|
|
@@ -58,13 +62,7 @@ export declare const ENV_ONLY: {
|
|
|
58
62
|
sources: "env";
|
|
59
63
|
};
|
|
60
64
|
export declare const valueSchema: z.ZodString;
|
|
61
|
-
/**
|
|
62
|
-
* The GENERIC shape of a bundle resource: a name, and whatever else the resource
|
|
63
|
-
* type carries. Deliberately unopinionated and `passthrough()` - the concrete
|
|
64
|
-
* resource kinds (`sql_warehouse`, `genie_space`, `postgres`, ...) are
|
|
65
|
-
* Databricks-App concepts that belong to the package that resolves them, so
|
|
66
|
-
* node-appkit `.extend()`s this rather than this module knowing about them.
|
|
67
|
-
*/
|
|
65
|
+
/** A named bundle or App resource; concrete resource fields pass through. */
|
|
68
66
|
export declare const bundleResourceSchema: z.ZodObject<{
|
|
69
67
|
name: z.ZodOptional<z.ZodString>;
|
|
70
68
|
}, z.core.$loose>;
|
|
@@ -86,6 +84,27 @@ export declare const bundleAppSchema: z.ZodObject<{
|
|
|
86
84
|
name: z.ZodOptional<z.ZodString>;
|
|
87
85
|
}, z.core.$loose>>>;
|
|
88
86
|
}, z.core.$strip>;
|
|
87
|
+
export declare const appEnvEntrySchema: z.ZodObject<{
|
|
88
|
+
name: z.ZodString;
|
|
89
|
+
value: z.ZodOptional<z.ZodString>;
|
|
90
|
+
valueFrom: z.ZodOptional<z.ZodString>;
|
|
91
|
+
}, z.core.$strip>;
|
|
92
|
+
export declare const appSchema: z.ZodObject<{
|
|
93
|
+
env: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
94
|
+
name: z.ZodString;
|
|
95
|
+
value: z.ZodOptional<z.ZodString>;
|
|
96
|
+
valueFrom: z.ZodOptional<z.ZodString>;
|
|
97
|
+
}, z.core.$strip>>>;
|
|
98
|
+
resources: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
99
|
+
name: z.ZodOptional<z.ZodString>;
|
|
100
|
+
}, z.core.$loose>>>;
|
|
101
|
+
}, z.core.$strip>;
|
|
102
|
+
/** Flatten the single bundle App's `config.env`, resolving resource references. */
|
|
103
|
+
export declare function flattenBundleEnv(data: unknown): Record<string, string>;
|
|
104
|
+
/** Flatten `app.yaml` env entries, resolving `valueFrom` resource references. */
|
|
105
|
+
export declare function flattenAppEnv(data: unknown): Record<string, string>;
|
|
106
|
+
/** Walk a dot-separated path through parsed bundle data. */
|
|
107
|
+
export declare function getBundlePath(data: Record<string, unknown>, path: string): string | undefined;
|
|
89
108
|
/**
|
|
90
109
|
* Detect a Databricks App runtime from its required name, host, and port.
|
|
91
110
|
*
|
|
@@ -95,6 +114,8 @@ export declare const bundleAppSchema: z.ZodObject<{
|
|
|
95
114
|
* absent or unrecognized override falls back to structural detection.
|
|
96
115
|
*/
|
|
97
116
|
export declare function isDatabricksAppEnv(source?: Record<string, string | undefined>): boolean;
|
|
117
|
+
/** Exact, uppercase, and tokenized-uppercase names for a human-friendly key. */
|
|
118
|
+
export declare function environmentKeys(name: string): readonly string[];
|
|
98
119
|
/**
|
|
99
120
|
* The first value that resolves for `input`, or `undefined`.
|
|
100
121
|
*
|
|
@@ -102,6 +123,8 @@ export declare function isDatabricksAppEnv(source?: Record<string, string | unde
|
|
|
102
123
|
* const domain = config.text(["TUNNEL_PUBLIC_DOMAIN", "PUBLIC_DOMAIN"]);
|
|
103
124
|
*/
|
|
104
125
|
export declare function text(input: ConfigKey, options?: ConfigOptions): string | undefined;
|
|
126
|
+
/** Resolve a human-friendly name through {@link environmentKeys} and {@link text}. */
|
|
127
|
+
export declare function resolveValue(name: string, options?: ConfigOptions): string | undefined;
|
|
105
128
|
/**
|
|
106
129
|
* The PRIMARY (fully-scoped) name for `input` - what to print in a log line or an
|
|
107
130
|
* error, so the message names the variable a reader should set. Do not index
|
|
@@ -156,10 +179,10 @@ export declare function list(configured: string | readonly string[] | undefined
|
|
|
156
179
|
* or a deployed App without an explicit override, there is no bundle, or the
|
|
157
180
|
* CLI produces no JSON.
|
|
158
181
|
*
|
|
159
|
-
*
|
|
160
|
-
*
|
|
161
|
-
* lookups do not rerun validation and changing either cannot return another
|
|
162
|
-
* context's bundle.
|
|
182
|
+
* Parsed validation output is cached by bundle path and Databricks profile.
|
|
183
|
+
*
|
|
163
184
|
*/
|
|
164
185
|
export declare function bundleFile(cwd?: string | null): ConfigFile | undefined;
|
|
186
|
+
/** The parsed `app.yaml` / `app.yml` for `cwd`, when local App config reads are enabled. */
|
|
187
|
+
export declare function appFile(cwd?: string | null): ConfigFile | undefined;
|
|
165
188
|
export {};
|