Skip to content

Commit b9a6bd8

Browse files
authored
chore: introduce script to generate static app templates (#136)
* chore: introduce script to generate static app templates * chore: add Lakebase values * chore: fix generator * chore: remove lakebase-specific templates from generator Move appkit-lakebase template and lakebase set values to the lakebase PR (lakebase-plugin-4-final), which introduces the required postgres resource fields. Signed-off-by: Pawel Kosiec <pawel.kosiec@databricks.com> * chore: add appkit-lakebase template without resource set values Resource set values (lakebase.postgres.branch, lakebase.postgres.database) will be added once the lakebase postgres resource schema is available. Signed-off-by: Pawel Kosiec <pawel.kosiec@databricks.com> * chore: add appkit-genie template to generator Signed-off-by: Pawel Kosiec <pawel.kosiec@databricks.com> * chore: address PR #136 review comments - Add genie feature to appkit-all-in-one template - Replace analytics-specific placeholder reminder with generic one * chore: revert unnecessary type parameter on useAnalyticsQuery Signed-off-by: Pawel Kosiec <pawel.kosiec@databricks.com> * chore: improve the script --------- Signed-off-by: Pawel Kosiec <pawel.kosiec@databricks.com>
1 parent 93e9de5 commit b9a6bd8

8 files changed

Lines changed: 334 additions & 27 deletions

File tree

CONTRIBUTING.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,20 @@ export DATABRICKS_APP_NAME=your-app-name # The name of the app to deploy. If not
9898
export DATABRICKS_WORKSPACE_DIR=your-workspace-dir # The source workspace directory to deploy the app from. It will be used to construct the absolute path: /Workspace/Users/{your-username}/{workspace-dir}
9999
```
100100

101+
## Generating App templates
102+
103+
To generate app templates, run the following command:
104+
105+
```bash
106+
pnpm generate:app-templates
107+
```
108+
109+
By default, the command will generate app templates in the `../app-templates` directory, assuming that you have the [`app-templates`](https://github.com/databricks/app-templates) repository cloned in the same parent directory as this one.
110+
111+
You can override the output directory by setting the `APP_TEMPLATES_OUTPUT_DIR` environment variable.
112+
113+
By default, the command will use the `databricks` CLI to generate the app templates. You can override the CLI by setting the `DATABRICKS_CLI` environment variable to provide a different binary name or path.
114+
101115
## Contributing to AppKit documentation
102116

103117
The `docs/` directory contains the AppKit documentation site, built with Docusaurus.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
"build:watch": "pnpm -r --filter=!dev-playground --filter=!docs build:watch",
1111
"check:fix": "biome check --write .",
1212
"check": "biome check .",
13+
"generate:app-templates": "tsx tools/generate-app-templates.ts",
1314
"check:licenses": "tsx tools/check-licenses.ts",
1415
"build:notice": "tsx tools/build-notice.ts > NOTICE.md",
1516
"deploy:playground": "pnpm pack:sdk && tsx tools/playground/deploy-playground.ts",

template/README.md

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
1-
# Minimal Databricks App
1+
# {{.projectName}}
22

3-
A minimal Databricks App powered by Databricks AppKit, featuring React, TypeScript, and Tailwind CSS.
3+
A Databricks App powered by [AppKit](https://databricks.github.io/appkit/), featuring React, TypeScript, and Tailwind CSS.
4+
5+
**Enabled plugins:**
6+
{{- if .plugins.analytics}}
7+
- **Analytics** -- SQL query execution against Databricks SQL Warehouses
8+
{{- end}}
9+
{{- if .plugins.lakebase}}
10+
- **Lakebase** -- Fully managed Postgres database for transactional (OLTP) workloads on Databricks
11+
{{- end}}
12+
- **Server** -- Express HTTP server with static file serving and Vite dev mode
413

514
## Prerequisites
615

@@ -15,7 +24,7 @@ A minimal Databricks App powered by Databricks AppKit, featuring React, TypeScri
1524
For local development, configure your environment variables by creating a `.env` file:
1625

1726
```bash
18-
cp env.example .env
27+
cp .env.example .env
1928
```
2029

2130
Edit `.env` and set the environment variables you need:
@@ -25,6 +34,12 @@ DATABRICKS_HOST=https://your-workspace.cloud.databricks.com
2534
DATABRICKS_APP_PORT=8000
2635
# ... other environment variables, depending on the plugins you use
2736
```
37+
{{- if .plugins.lakebase}}
38+
39+
#### Lakebase Configuration
40+
41+
The Lakebase plugin requires additional environment variables for PostgreSQL connectivity. To learn how to configure the Lakebase plugin, see the [Lakebase plugin documentation](https://databricks.github.io/appkit/docs/plugins/lakebase).
42+
{{- end}}
2843

2944
### CLI Authentication
3045

@@ -57,7 +72,7 @@ client_secret = prod-client-secret
5772
Deploy using a specific profile:
5873

5974
```bash
60-
databricks bundle deploy -t prod --profile production
75+
databricks bundle deploy --profile production
6176
```
6277

6378
**Note:** Personal Access Tokens (PATs) are legacy authentication. OAuth is strongly recommended for better security.
@@ -90,7 +105,7 @@ npm run build
90105

91106
This creates:
92107

93-
- `dist/server/` - Compiled server code
108+
- `dist/server.js` - Compiled server bundle
94109
- `client/dist/` - Bundled client assets
95110

96111
### Production
@@ -126,13 +141,13 @@ Update `databricks.yml` with your workspace settings:
126141

127142
```yaml
128143
targets:
129-
dev:
144+
default:
130145
workspace:
131146
host: https://your-workspace.cloud.databricks.com
132-
variables:
133-
warehouse_id: your-warehouse-id
134147
```
135148
149+
Make sure to replace all placeholder values in `databricks.yml` with your actual resource IDs.
150+
136151
### 2. Validate Bundle
137152

138153
```bash
@@ -141,10 +156,10 @@ databricks bundle validate
141156

142157
### 3. Deploy
143158

144-
Deploy to the development target:
159+
Deploy to the default target:
145160

146161
```bash
147-
databricks bundle deploy -t dev
162+
databricks bundle deploy
148163
```
149164

150165
### 4. Run
@@ -174,6 +189,10 @@ databricks bundle deploy -t prod
174189
* server.ts # Server entry point
175190
* routes/ # Routes
176191
* shared/ # Shared types
192+
{{- if .plugins.analytics}}
193+
* config/ # Configuration
194+
* queries/ # SQL query files
195+
{{- end}}
177196
* databricks.yml # Bundle configuration
178197
* app.yaml # App configuration
179198
* .env.example # Environment variables example
File renamed without changes.

template/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "{{.projectName}}",
33
"version": "1.0.0",
4-
"main": "build/index.js",
4+
"main": "dist/server.js",
55
"type": "module",
66
"scripts": {
77
"start": "NODE_ENV=production node --env-file-if-exists=./.env ./dist/server.js",

template/playwright.config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export default defineConfig({
88
workers: process.env.CI ? 1 : undefined,
99
reporter: 'html',
1010
use: {
11-
baseURL: `http://localhost:${process.env.PORT || 8000}`,
11+
baseURL: `http://localhost:${process.env.DATABRICKS_APP_PORT || process.env.PORT || 8000}`,
1212
trace: 'on-first-retry',
1313
},
1414
projects: [
@@ -19,7 +19,7 @@ export default defineConfig({
1919
],
2020
webServer: {
2121
command: 'npm run dev',
22-
url: `http://localhost:${process.env.PORT || 8000}`,
22+
url: `http://localhost:${process.env.DATABRICKS_APP_PORT || process.env.PORT || 8000}`,
2323
reuseExistingServer: !process.env.CI,
2424
timeout: 120 * 1000,
2525
},

template/tests/smoke.spec.ts

Lines changed: 60 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,77 @@ import { test, expect } from '@playwright/test';
22
import { writeFileSync, mkdirSync } from 'node:fs';
33
import { join } from 'node:path';
44

5+
// ── Templated configuration (resolved by `databricks apps init`) ────────────
6+
const APP_CONFIG = {
7+
name: '{{.projectName}}',
8+
plugins: [
9+
{{- if .plugins.analytics}}
10+
'analytics',
11+
{{- end}}
12+
{{- if .plugins.lakebase}}
13+
'lakebase',
14+
{{- end}}
15+
],
16+
} as const;
17+
18+
interface PluginPage {
19+
navLabel: string;
20+
path: string;
21+
expectedTexts: string[];
22+
}
23+
24+
const PLUGIN_PAGES: Record<string, PluginPage> = {
25+
analytics: {
26+
navLabel: 'Analytics',
27+
path: '/analytics',
28+
expectedTexts: ['SQL Query Result', 'Sales Data Filter'],
29+
},
30+
lakebase: {
31+
navLabel: 'Lakebase',
32+
path: '/lakebase',
33+
expectedTexts: ['Todo List'],
34+
},
35+
};
36+
37+
const enabledPages = Object.entries(PLUGIN_PAGES).filter(
38+
([key]) => APP_CONFIG.plugins.includes(key),
39+
);
40+
41+
// ── Tests ───────────────────────────────────────────────────────────────────
42+
543
let testArtifactsDir: string;
644
let consoleLogs: string[] = [];
745
let consoleErrors: string[] = [];
846
let pageErrors: string[] = [];
947
let failedRequests: string[] = [];
1048

11-
test('smoke test - app loads and displays data', async ({ page }) => {
12-
// Navigate to the app
49+
test('smoke test - app loads and displays home page', async ({ page }) => {
1350
await page.goto('/');
1451

15-
// ⚠️ UPDATE THESE SELECTORS after customizing App.tsx:
16-
// - Change heading name to match your app title
17-
// - Change data selector to match your primary data display
18-
await expect(page.getByRole('heading', { name: 'Minimal Databricks App' })).toBeVisible();
19-
await expect(page.getByText('hello world', { exact: true })).toBeVisible({ timeout: 30000 });
20-
21-
// Wait for health check to complete (wait for "OK" status)
22-
await expect(page.getByText('OK')).toBeVisible({ timeout: 30000 });
52+
await expect(page.getByRole('heading', { name: APP_CONFIG.name })).toBeVisible();
53+
await expect(
54+
page.getByRole('heading', { name: 'Welcome to your Databricks App' }),
55+
).toBeVisible();
56+
await expect(page.getByText('Getting Started')).toBeVisible();
2357

24-
// Verify console logs were captured
25-
expect(consoleLogs.length).toBeGreaterThan(0);
26-
expect(consoleErrors.length).toBe(0);
27-
expect(pageErrors.length).toBe(0);
58+
await expect(page.getByRole('link', { name: 'Home' })).toBeVisible();
59+
for (const [, plugin] of enabledPages) {
60+
await expect(page.getByRole('link', { name: plugin.navLabel })).toBeVisible();
61+
}
2862
});
2963

64+
for (const [name, plugin] of enabledPages) {
65+
test(`smoke test - ${name} page loads`, async ({ page }) => {
66+
await page.goto(plugin.path);
67+
68+
for (const text of plugin.expectedTexts) {
69+
await expect(page.getByText(text)).toBeVisible();
70+
}
71+
});
72+
}
73+
74+
// ── Lifecycle hooks ─────────────────────────────────────────────────────────
75+
3076
test.beforeEach(async ({ page }) => {
3177
consoleLogs = [];
3278
consoleErrors = [];

0 commit comments

Comments
 (0)