From bff9778b810d81507df3ff7f5b0d7586438839b6 Mon Sep 17 00:00:00 2001 From: MarioCadenas Date: Thu, 30 Apr 2026 16:21:22 +0200 Subject: [PATCH 1/3] ci: vendor lakebase in PR template artifact only PR template installs pulled @databricks/lakebase from npm because dist-appkit replaces workspace:* with a semver for the packed appkit tarball. Add APPKIT_VENDOR_LAKEBASE=1 path in dist-appkit that embeds the lakebase .tgz from packages/lakebase/tmp and uses a file: dependency; release and pack:sdk leave the variable unset so behavior stays semver-only. Introduce pnpm pack:pr-template (lakebase tarball, then vendored appkit pack) and use it from the pr-template-artifact job instead of pack:sdk. --- .github/workflows/ci.yml | 4 ++-- package.json | 1 + tools/dist-appkit.ts | 31 +++++++++++++++++++++++++++++- tools/prepare-template-artifact.ts | 4 +++- 4 files changed, 36 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a9fffe54f..0988691c2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -158,8 +158,8 @@ jobs: echo "version=$PR_VERSION" >> "$GITHUB_OUTPUT" pnpm exec tsx tools/sync-versions.ts "$PR_VERSION" - - name: Build SDK tarballs - run: pnpm pack:sdk + - name: Build SDK tarballs for PR template + run: pnpm pack:pr-template - name: Prepare template artifact run: pnpm exec tsx tools/prepare-template-artifact.ts --version "${{ steps.version.outputs.version }}" diff --git a/package.json b/package.json index 0d3af43fc..1a02173ab 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "format": "biome format --write .", "lint:fix": "biome lint --write .", "lint": "biome lint .", + "pack:pr-template": "pnpm build && pnpm --filter=docs build && pnpm --filter=@databricks/lakebase tarball && APPKIT_VENDOR_LAKEBASE=1 pnpm --filter=@databricks/appkit tarball && pnpm --filter=@databricks/appkit-ui tarball", "pack:sdk": "pnpm build && pnpm --filter=docs build && pnpm -r tarball", "prepare": "husky", "release:sbom": "pnpm exec cdxgen -t js --no-recurse --required-only -o packages/appkit/tmp/sbom.cdx.json packages/appkit && pnpm exec cdxgen -t js --no-recurse --required-only -o packages/appkit-ui/tmp/sbom.cdx.json packages/appkit-ui", diff --git a/tools/dist-appkit.ts b/tools/dist-appkit.ts index 4bfe587da..58990c0e4 100644 --- a/tools/dist-appkit.ts +++ b/tools/dist-appkit.ts @@ -14,10 +14,14 @@ const pkg = JSON.parse(fs.readFileSync("package.json", "utf-8")); // Packages that are workspace-local but published separately — replace workspace:* with real version. // "shared" is intentionally excluded: it is bundled directly into appkit/appkit-ui via noExternal. +// When APPKIT_VENDOR_LAKEBASE=1 (PR template artifact only), embed the lakebase .tgz from +// packages/lakebase/tmp/ into tmp/ and depend on file:./… so installs stay off the registry. const WORKSPACE_PACKAGE_REPLACEMENTS = ["@databricks/lakebase"]; delete pkg.dependencies.shared; +const vendorLakebase = process.env.APPKIT_VENDOR_LAKEBASE === "1"; + for (const depName of WORKSPACE_PACKAGE_REPLACEMENTS) { if (pkg.dependencies?.[depName] === "workspace:*") { const pkgDirName = depName.split("/").pop() ?? depName; @@ -26,7 +30,32 @@ for (const depName of WORKSPACE_PACKAGE_REPLACEMENTS) { `../packages/${pkgDirName}/package.json`, ); const depPkg = JSON.parse(fs.readFileSync(depPkgPath, "utf-8")); - pkg.dependencies[depName] = `${depPkg.version}`; + const depVersion = depPkg.version as string; + + if (vendorLakebase) { + const packedName = `databricks-${pkgDirName}-${depVersion}.tgz`; + const tarballSrc = path.join( + __dirname, + "../packages", + pkgDirName, + "tmp", + packedName, + ); + if (!fs.existsSync(tarballSrc)) { + console.error( + `APPKIT_VENDOR_LAKEBASE is set but missing ${tarballSrc}. Run: pnpm --filter=@databricks/lakebase tarball`, + ); + process.exit(1); + } + fs.copyFileSync(tarballSrc, path.join("tmp", packedName)); + pkg.dependencies[depName] = `file:./${packedName}`; + pkg.files = pkg.files ?? []; + if (!pkg.files.includes(packedName)) { + pkg.files.push(packedName); + } + } else { + pkg.dependencies[depName] = `${depVersion}`; + } } } diff --git a/tools/prepare-template-artifact.ts b/tools/prepare-template-artifact.ts index df2e9274a..0b32fc03c 100644 --- a/tools/prepare-template-artifact.ts +++ b/tools/prepare-template-artifact.ts @@ -4,7 +4,9 @@ * * Copies the template/ directory into a staging folder, bundles the SDK tarballs, * and rewrites package.json to use `file:` references so the template can be - * tested against a specific version of appkit/appkit-ui. + * tested against a specific version of appkit/appkit-ui. For a local PR-template-style + * pack, run `pnpm pack:pr-template` (bundles lakebase into the appkit tarball via + * APPKIT_VENDOR_LAKEBASE); release flows keep using `pnpm pack:sdk`. * * Usage: * tsx tools/prepare-template-artifact.ts --version [--tarball-dir ] [--output-dir ] From 07bd4d121de9fba9179269da9edad915815f42a7 Mon Sep 17 00:00:00 2001 From: MarioCadenas Date: Thu, 30 Apr 2026 16:30:18 +0200 Subject: [PATCH 2/3] fix: ship vendored lakebase under dist/vendor for npm pack MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit npm pack on packages/appkit/tmp honors parent .gitignore; tmp/ is ignored so root-level databricks-lakebase-*.tgz was dropped from the tarball. Install then failed to resolve file:./… next to package.json. Stage the lakebase .tgz under dist/vendor/ (already included via files dist/) and point the file: dependency there. --- tools/dist-appkit.ts | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/tools/dist-appkit.ts b/tools/dist-appkit.ts index 58990c0e4..e9176f5c7 100644 --- a/tools/dist-appkit.ts +++ b/tools/dist-appkit.ts @@ -15,13 +15,18 @@ const pkg = JSON.parse(fs.readFileSync("package.json", "utf-8")); // Packages that are workspace-local but published separately — replace workspace:* with real version. // "shared" is intentionally excluded: it is bundled directly into appkit/appkit-ui via noExternal. // When APPKIT_VENDOR_LAKEBASE=1 (PR template artifact only), embed the lakebase .tgz from -// packages/lakebase/tmp/ into tmp/ and depend on file:./… so installs stay off the registry. +// packages/lakebase/tmp/ under tmp/dist/vendor/ and depend on file:./dist/vendor/… . +// Root-level .tgz under tmp/ can be omitted by npm pack because packages/appkit/.gitignore +// lists "tmp", so packlist skips them; "dist" is already in "files". const WORKSPACE_PACKAGE_REPLACEMENTS = ["@databricks/lakebase"]; delete pkg.dependencies.shared; const vendorLakebase = process.env.APPKIT_VENDOR_LAKEBASE === "1"; +/** Set when vendoring: copy lakebase tgz into tmp/dist/vendor/ after dist/ is staged. */ +let vendorLakebaseCopy: { src: string; destName: string } | null = null; + for (const depName of WORKSPACE_PACKAGE_REPLACEMENTS) { if (pkg.dependencies?.[depName] === "workspace:*") { const pkgDirName = depName.split("/").pop() ?? depName; @@ -47,12 +52,8 @@ for (const depName of WORKSPACE_PACKAGE_REPLACEMENTS) { ); process.exit(1); } - fs.copyFileSync(tarballSrc, path.join("tmp", packedName)); - pkg.dependencies[depName] = `file:./${packedName}`; - pkg.files = pkg.files ?? []; - if (!pkg.files.includes(packedName)) { - pkg.files.push(packedName); - } + vendorLakebaseCopy = { src: tarballSrc, destName: packedName }; + pkg.dependencies[depName] = `file:./dist/vendor/${packedName}`; } else { pkg.dependencies[depName] = `${depVersion}`; } @@ -86,6 +87,15 @@ fs.writeFileSync("tmp/package.json", JSON.stringify(pkg, null, 2)); fs.cpSync("dist", "tmp/dist", { recursive: true }); +if (vendorLakebaseCopy) { + const vendorDir = "tmp/dist/vendor"; + fs.mkdirSync(vendorDir, { recursive: true }); + fs.copyFileSync( + vendorLakebaseCopy.src, + path.join(vendorDir, vendorLakebaseCopy.destName), + ); +} + if (fs.existsSync("bin")) { fs.cpSync("bin", "tmp/bin", { recursive: true }); } From e64187b91a365b88b385d9a276427a592647280d Mon Sep 17 00:00:00 2001 From: MarioCadenas Date: Thu, 30 Apr 2026 16:40:20 +0200 Subject: [PATCH 3/3] fix(pr-template): pin lakebase via template overrides, not appkit pack npm pack on packages/appkit/tmp still drops vendored .tgz under dist/ because the staging tree lives under a gitignored tmp path. Revert APPKIT_VENDOR_LAKEBASE / pack:pr-template. prepare-template-artifact copies packages/lakebase/tmp/*.tgz into the staging dir when present and sets overrides so npm install resolves @databricks/lakebase from the template root. Release template jobs without a local lakebase pack keep registry resolution. --- .github/workflows/ci.yml | 4 +-- package.json | 1 - tools/dist-appkit.ts | 41 +----------------------------- tools/prepare-template-artifact.ts | 34 +++++++++++++++++++++---- 4 files changed, 32 insertions(+), 48 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0988691c2..a9fffe54f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -158,8 +158,8 @@ jobs: echo "version=$PR_VERSION" >> "$GITHUB_OUTPUT" pnpm exec tsx tools/sync-versions.ts "$PR_VERSION" - - name: Build SDK tarballs for PR template - run: pnpm pack:pr-template + - name: Build SDK tarballs + run: pnpm pack:sdk - name: Prepare template artifact run: pnpm exec tsx tools/prepare-template-artifact.ts --version "${{ steps.version.outputs.version }}" diff --git a/package.json b/package.json index 1a02173ab..0d3af43fc 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,6 @@ "format": "biome format --write .", "lint:fix": "biome lint --write .", "lint": "biome lint .", - "pack:pr-template": "pnpm build && pnpm --filter=docs build && pnpm --filter=@databricks/lakebase tarball && APPKIT_VENDOR_LAKEBASE=1 pnpm --filter=@databricks/appkit tarball && pnpm --filter=@databricks/appkit-ui tarball", "pack:sdk": "pnpm build && pnpm --filter=docs build && pnpm -r tarball", "prepare": "husky", "release:sbom": "pnpm exec cdxgen -t js --no-recurse --required-only -o packages/appkit/tmp/sbom.cdx.json packages/appkit && pnpm exec cdxgen -t js --no-recurse --required-only -o packages/appkit-ui/tmp/sbom.cdx.json packages/appkit-ui", diff --git a/tools/dist-appkit.ts b/tools/dist-appkit.ts index e9176f5c7..4bfe587da 100644 --- a/tools/dist-appkit.ts +++ b/tools/dist-appkit.ts @@ -14,19 +14,10 @@ const pkg = JSON.parse(fs.readFileSync("package.json", "utf-8")); // Packages that are workspace-local but published separately — replace workspace:* with real version. // "shared" is intentionally excluded: it is bundled directly into appkit/appkit-ui via noExternal. -// When APPKIT_VENDOR_LAKEBASE=1 (PR template artifact only), embed the lakebase .tgz from -// packages/lakebase/tmp/ under tmp/dist/vendor/ and depend on file:./dist/vendor/… . -// Root-level .tgz under tmp/ can be omitted by npm pack because packages/appkit/.gitignore -// lists "tmp", so packlist skips them; "dist" is already in "files". const WORKSPACE_PACKAGE_REPLACEMENTS = ["@databricks/lakebase"]; delete pkg.dependencies.shared; -const vendorLakebase = process.env.APPKIT_VENDOR_LAKEBASE === "1"; - -/** Set when vendoring: copy lakebase tgz into tmp/dist/vendor/ after dist/ is staged. */ -let vendorLakebaseCopy: { src: string; destName: string } | null = null; - for (const depName of WORKSPACE_PACKAGE_REPLACEMENTS) { if (pkg.dependencies?.[depName] === "workspace:*") { const pkgDirName = depName.split("/").pop() ?? depName; @@ -35,28 +26,7 @@ for (const depName of WORKSPACE_PACKAGE_REPLACEMENTS) { `../packages/${pkgDirName}/package.json`, ); const depPkg = JSON.parse(fs.readFileSync(depPkgPath, "utf-8")); - const depVersion = depPkg.version as string; - - if (vendorLakebase) { - const packedName = `databricks-${pkgDirName}-${depVersion}.tgz`; - const tarballSrc = path.join( - __dirname, - "../packages", - pkgDirName, - "tmp", - packedName, - ); - if (!fs.existsSync(tarballSrc)) { - console.error( - `APPKIT_VENDOR_LAKEBASE is set but missing ${tarballSrc}. Run: pnpm --filter=@databricks/lakebase tarball`, - ); - process.exit(1); - } - vendorLakebaseCopy = { src: tarballSrc, destName: packedName }; - pkg.dependencies[depName] = `file:./dist/vendor/${packedName}`; - } else { - pkg.dependencies[depName] = `${depVersion}`; - } + pkg.dependencies[depName] = `${depPkg.version}`; } } @@ -87,15 +57,6 @@ fs.writeFileSync("tmp/package.json", JSON.stringify(pkg, null, 2)); fs.cpSync("dist", "tmp/dist", { recursive: true }); -if (vendorLakebaseCopy) { - const vendorDir = "tmp/dist/vendor"; - fs.mkdirSync(vendorDir, { recursive: true }); - fs.copyFileSync( - vendorLakebaseCopy.src, - path.join(vendorDir, vendorLakebaseCopy.destName), - ); -} - if (fs.existsSync("bin")) { fs.cpSync("bin", "tmp/bin", { recursive: true }); } diff --git a/tools/prepare-template-artifact.ts b/tools/prepare-template-artifact.ts index 0b32fc03c..b47e322f8 100644 --- a/tools/prepare-template-artifact.ts +++ b/tools/prepare-template-artifact.ts @@ -3,10 +3,13 @@ * Prepares a template artifact for testing or release. * * Copies the template/ directory into a staging folder, bundles the SDK tarballs, - * and rewrites package.json to use `file:` references so the template can be - * tested against a specific version of appkit/appkit-ui. For a local PR-template-style - * pack, run `pnpm pack:pr-template` (bundles lakebase into the appkit tarball via - * APPKIT_VENDOR_LAKEBASE); release flows keep using `pnpm pack:sdk`. + * and rewrites package.json to use `file:` references for appkit and appkit-ui. + * + * When `packages/lakebase/tmp/databricks-lakebase-.tgz` exists (e.g. after + * `pnpm pack:sdk` on a dev checkout), that tarball is copied into the staging folder and + * `overrides["@databricks/lakebase"]` is set so the template install uses the local pack + * instead of the registry. Release jobs that only download appkit tarballs skip this + * branch and keep the default semver resolution from the appkit tarball. * * Usage: * tsx tools/prepare-template-artifact.ts --version [--tarball-dir ] [--output-dir ] @@ -21,6 +24,7 @@ import { copyFileSync, cpSync, + existsSync, mkdirSync, readFileSync, writeFileSync, @@ -55,6 +59,12 @@ const STAGING_DIR = join(ROOT, outputDir); const APPKIT_TARBALL = `databricks-appkit-${version}.tgz`; const APPKIT_UI_TARBALL = `databricks-appkit-ui-${version}.tgz`; +const lakebasePkg = JSON.parse( + readFileSync(join(ROOT, "packages/lakebase/package.json"), "utf-8"), +) as { version: string }; +const lakebaseVersion = lakebasePkg.version; +const LAKEBASE_TARBALL = `databricks-lakebase-${lakebaseVersion}.tgz`; + // 1. Copy template into staging directory mkdirSync(STAGING_DIR, { recursive: true }); cpSync(join(ROOT, "template"), STAGING_DIR, { recursive: true }); @@ -72,10 +82,24 @@ copyFileSync(appkitSrc, join(STAGING_DIR, APPKIT_TARBALL)); copyFileSync(appkitUiSrc, join(STAGING_DIR, APPKIT_UI_TARBALL)); console.log(`✓ Copied ${APPKIT_TARBALL} and ${APPKIT_UI_TARBALL}`); +const lakebaseSrc = join(ROOT, "packages/lakebase/tmp", LAKEBASE_TARBALL); +if (existsSync(lakebaseSrc)) { + copyFileSync(lakebaseSrc, join(STAGING_DIR, LAKEBASE_TARBALL)); + console.log(`✓ Copied ${LAKEBASE_TARBALL}`); +} + // 3. Rewrite package.json dependencies to point at the local tarballs const pkgPath = join(STAGING_DIR, "package.json"); const pkg = JSON.parse(readFileSync(pkgPath, "utf-8")); pkg.dependencies["@databricks/appkit"] = `file:./${APPKIT_TARBALL}`; pkg.dependencies["@databricks/appkit-ui"] = `file:./${APPKIT_UI_TARBALL}`; +if (existsSync(lakebaseSrc)) { + pkg.overrides = pkg.overrides ?? {}; + pkg.overrides["@databricks/lakebase"] = `file:./${LAKEBASE_TARBALL}`; +} writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`); -console.log("✓ Rewrote package.json dependencies to file: references"); +console.log( + existsSync(lakebaseSrc) + ? "✓ Rewrote package.json (appkit/appkit-ui file: deps; @databricks/lakebase override)" + : "✓ Rewrote package.json dependencies to file: references (no local lakebase pack)", +);