Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
39c9cba
feat(appkit): appkit plugin add with --auth-mode and --set resource c…
MarioCadenas Oct 20, 2026
401af38
fix(appkit): plugin add OBO files auth, genie scopes, lakebase derive…
MarioCadenas Oct 20, 2026
48756d9
fix(appkit): plugin add sets aiSearch OBO auth on its default index
MarioCadenas Oct 20, 2026
e482fc5
fix(appkit): plugin add writes aiSearch index columns so production s…
MarioCadenas Oct 20, 2026
4a9e0df
fix(appkit): plugin add agents OBO, .env placeholders, defaults, app …
MarioCadenas Oct 20, 2026
62260e2
fix(appkit): plugin add agents OBO follows the model endpoint only
MarioCadenas Oct 20, 2026
02279f3
fix(appkit): plugin add limits aiSearch columns to the index's synced…
MarioCadenas Oct 20, 2026
7d2149c
fix(appkit): plugin add honors the columnsToIndex alias for aiSearch …
MarioCadenas Oct 20, 2026
ebcc5a4
fix(shared): plugin registration extends an existing import from the …
MarioCadenas Oct 27, 2026
be9e6e3
fix(shared): plugin add type-only import detection, agents model on s…
MarioCadenas Oct 27, 2026
63d4e99
fix(shared): plugin add prints required config with --no-register, re…
MarioCadenas Oct 27, 2026
19c0167
feat(shared): appkit plugin add takes several plugins or a data plan …
MarioCadenas Oct 28, 2026
14024af
fix(shared): plugin add plans agents last; registry add skips the bun…
MarioCadenas Oct 28, 2026
c5284b1
ci: install the pnpm template smoke test with a fresh store
MarioCadenas Oct 28, 2026
435c1fd
fix(shared): plugin add sets files auth per volume for mixed modes, m…
MarioCadenas Oct 28, 2026
7b87caa
fix(shared): plugin add --from-plan accepts set keys with the plugin …
MarioCadenas Oct 29, 2026
1bbef8b
refactor(shared): simplify plugin add helpers
MarioCadenas Nov 5, 2026
421bee3
refactor(shared): trim plugin add comments to one line each
MarioCadenas Nov 5, 2026
038e71a
refactor(shared): return a Set from validSetKeys, inline byName
MarioCadenas Nov 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 134 additions & 0 deletions packages/shared/src/cli/commands/plugin/add/add.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import fs from "node:fs";
import os from "node:os";
import path from "node:path";

import { expect, test } from "vitest";

import { envConflicts, pluginConfig, toSource } from "./add";
import type { ManifestPlugin, ManifestResource } from "./plan";

const index: ManifestResource = {
type: "vector_search_index",
alias: "Index",
resourceKey: "vector-search-index",
scope: "vector-search",
};
const plugin = (name: string): ManifestPlugin => ({
name,
package: "@databricks/appkit",
resources: { required: [index], optional: [] },
});
const obo = { "vector_search_index:vector-search-index": "obo" } as const;

test("aiSearch sets columns and OBO auth on the default index", () => {
const config = pluginConfig(plugin("aiSearch"), [index], obo, ["id", "t"]);
expect(toSource(config)).toBe(
`{ indexes: { default: { columns: ["id", "t"], auth: "on-behalf-of-user" } } }`,
);
expect(pluginConfig(plugin("aiSearch"), [index], {}, ["id"])).toEqual({
indexes: { default: { columns: ["id"] } },
});
});

test("files OBO sets the plugin-level auth", () => {
expect(pluginConfig(plugin("files"), [index], obo)).toEqual({
auth: "on-behalf-of-user",
});
});

test("no config for sp or plugins without an auth option", () => {
expect(pluginConfig(plugin("files"), [index], {})).toBeUndefined();
expect(pluginConfig(plugin("analytics"), [index], obo)).toBeUndefined();
});

test("toSource escapes strings and quotes non-identifier keys", () => {
expect(toSource({ "a-b": ['x"); evil()'] })).toBe(
`{ "a-b": ["x\\"); evil()"] }`,
);
});

test("agents OBO sets plugin auth; resources that can't be OBO don't block it", () => {
const experiment: ManifestResource = {
type: "experiment",
alias: "Experiment",
resourceKey: "agents-mlflow-experiment",
};
const endpoint: ManifestResource = {
type: "serving_endpoint",
alias: "LLM",
resourceKey: "agents-serving-endpoint",
scope: "model-serving",
};
expect(
pluginConfig(plugin("agents"), [endpoint, experiment], {
"serving_endpoint:agents-serving-endpoint": "obo",
}),
).toEqual({
auth: "on-behalf-of-user",
});
});

test("agents auth follows the model endpoint, not the skills volume", () => {
const endpoint: ManifestResource = {
type: "serving_endpoint",
alias: "LLM",
resourceKey: "agents-serving-endpoint",
scope: "model-serving",
};
const skills: ManifestResource = {
type: "volume",
alias: "Skills",
resourceKey: "agents-skills",
scope: "files",
};
const modelObo = {
"serving_endpoint:agents-serving-endpoint": "obo",
} as const;
expect(pluginConfig(plugin("agents"), [endpoint, skills], modelObo)).toEqual({
auth: "on-behalf-of-user",
});
expect(
pluginConfig(plugin("agents"), [endpoint, skills], {
"volume:agents-skills": "obo",
}),
).toBeUndefined();
});

test("envConflicts flags env names bound to another resource or value", () => {
const cwd = fs.mkdtempSync(path.join(os.tmpdir(), "plugin-add-"));
fs.writeFileSync(path.join(cwd, ".env"), "A=old\nB=\n");
fs.writeFileSync(
path.join(cwd, "app.yaml"),
"env:\n - name: C\n valueFrom: serving-endpoint\n - name: D\n valueFrom: same\n",
);
const plan = {
dotEnv: [
{ env: "A", value: "new" },
{ env: "B", value: "x" },
],
appYamlEnv: [
{ name: "C", valueFrom: "agents-serving-endpoint" },
{ name: "D", valueFrom: "same" },
],
bundleVariables: [],
resourceBindings: [],
unverifiedTypes: [],
};
expect(envConflicts(cwd, plan)).toEqual(["A", "C"]);
fs.rmSync(cwd, { recursive: true, force: true });
});

test("files with mixed modes sets auth on the OBO volumes only", () => {
const vol = (key: string, env: string): ManifestResource => ({
type: "volume",
alias: key,
resourceKey: key,
scope: "files",
fields: { path: { env } },
});
const priv = vol("files", "DATABRICKS_VOLUME_FILES");
const exp = vol("exports", "DATABRICKS_VOLUME_EXPORTS");
expect(
pluginConfig(plugin("files"), [priv, exp], { "volume:files": "obo" }),
).toEqual({ volumes: { files: { auth: "on-behalf-of-user" } } });
});
Loading
Loading