diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dce91a4c7..5be1863c5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -190,18 +190,19 @@ jobs: id: version env: BRANCH: ${{ github.head_ref }} + PR_NUMBER: ${{ github.event.pull_request.number }} run: | SANITIZED_BRANCH=$(echo "$BRANCH" | sed 's/[^a-zA-Z0-9-]/-/g' | sed 's/--*/-/g' | sed 's/^-//;s/-$//') + SHORTSHA=$(git rev-parse --short HEAD) CURRENT_VERSION=$(node -p "require('./packages/appkit/package.json').version") - PR_VERSION="${CURRENT_VERSION}-${SANITIZED_BRANCH}" + PR_VERSION="${CURRENT_VERSION}-pr.${SHORTSHA}-${SANITIZED_BRANCH}-${PR_NUMBER}" echo "version=$PR_VERSION" >> "$GITHUB_OUTPUT" - pnpm exec tsx tools/sync-versions.ts "$PR_VERSION" - name: Build SDK tarballs - run: pnpm pack:sdk + run: pnpm pack:prerelease - name: Prepare template artifact - run: pnpm exec tsx tools/prepare-template-artifact.ts --version "${{ steps.version.outputs.version }}" + run: pnpm exec tsx tools/prepare-template-artifact.ts - name: Install template dependencies working-directory: pr-template @@ -209,13 +210,13 @@ jobs: - name: Create zip artifact working-directory: pr-template - run: zip -r ../pr-template.zip . -x 'node_modules/*' + run: zip -r "../appkit-template-${{ steps.version.outputs.version }}.zip" . -x 'node_modules/*' - name: Upload artifact uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: appkit-template-${{ steps.version.outputs.version }} - path: pr-template.zip + path: appkit-template-${{ steps.version.outputs.version }}.zip docs-build: name: Docs Build diff --git a/.github/workflows/prepare-release.yml b/.github/workflows/prepare-release.yml index 7da2ab38c..6da4894c7 100644 --- a/.github/workflows/prepare-release.yml +++ b/.github/workflows/prepare-release.yml @@ -150,7 +150,6 @@ jobs: - name: Prepare template artifact run: > pnpm exec tsx tools/prepare-template-artifact.ts - --version "${{ needs.prepare.outputs.version }}" --tarball-dir release-artifacts --output-dir template-artifact diff --git a/package.json b/package.json index eeb45fbfc..4cccc54c7 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "lint:fix": "biome lint --write .", "lint": "biome lint .", "pack:sdk": "pnpm build && pnpm --filter=docs build && pnpm -r tarball", + "pack:prerelease": "pnpm build && pnpm --filter=docs build && pnpm -r --filter='@databricks/*' tarball:prerelease", "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", "release:dry": "release-it --dry-run", diff --git a/packages/appkit-ui/package.json b/packages/appkit-ui/package.json index ba45ec331..6ad186917 100644 --- a/packages/appkit-ui/package.json +++ b/packages/appkit-ui/package.json @@ -52,6 +52,7 @@ "clean": "rm -rf dist tmp", "dist": "tsx ../../tools/dist-appkit.ts", "tarball": "rm -rf tmp && pnpm dist && npm pack ./tmp --pack-destination ./tmp", + "tarball:prerelease": "rm -rf tmp && SHORTSHA=$(git rev-parse --short HEAD) && pnpm dist --prerelease $SHORTSHA && npm pack ./tmp --pack-destination ./tmp", "typecheck": "tsc --noEmit" }, "dependencies": { diff --git a/packages/appkit/package.json b/packages/appkit/package.json index 135765612..c380af173 100644 --- a/packages/appkit/package.json +++ b/packages/appkit/package.json @@ -54,6 +54,7 @@ "clean": "rm -rf dist tmp", "dist": "tsx ../../tools/dist-appkit.ts", "tarball": "rm -rf tmp && pnpm dist && npm pack ./tmp --pack-destination ./tmp", + "tarball:prerelease": "rm -rf tmp && SHORTSHA=$(git rev-parse --short HEAD) && pnpm dist --prerelease $SHORTSHA && npm pack ./tmp --pack-destination ./tmp", "typecheck": "tsc --noEmit" }, "dependencies": { diff --git a/packages/lakebase/package.json b/packages/lakebase/package.json index 305c13670..3fdbfecc1 100644 --- a/packages/lakebase/package.json +++ b/packages/lakebase/package.json @@ -43,6 +43,7 @@ "clean": "rm -rf dist tmp", "dist": "tsx ../../tools/dist-lakebase.ts", "tarball": "rm -rf tmp && pnpm dist && npm pack ./tmp --pack-destination ./tmp", + "tarball:prerelease": "rm -rf tmp && SHORTSHA=$(git rev-parse --short HEAD) && pnpm dist --prerelease $SHORTSHA && npm pack ./tmp --pack-destination ./tmp", "typecheck": "tsc --noEmit", "release:dry": "release-it --dry-run", "release:sbom": "pnpm exec cdxgen -t js --no-recurse --required-only -o tmp/sbom.cdx.json ." diff --git a/tools/dist-appkit.ts b/tools/dist-appkit.ts index 4bfe587da..810d55d55 100644 --- a/tools/dist-appkit.ts +++ b/tools/dist-appkit.ts @@ -1,7 +1,14 @@ import fs from "node:fs"; import path from "node:path"; +import { parseArgs } from "node:util"; const __dirname = path.dirname(new URL(import.meta.url).pathname); +const { values } = parseArgs({ + options: { + prerelease: { type: "string" }, + }, +}); +const prerelease = values.prerelease; // Read CLI dependencies from shared package const sharedPkgPath = path.join(__dirname, "../packages/shared/package.json"); @@ -16,6 +23,10 @@ const pkg = JSON.parse(fs.readFileSync("package.json", "utf-8")); // "shared" is intentionally excluded: it is bundled directly into appkit/appkit-ui via noExternal. const WORKSPACE_PACKAGE_REPLACEMENTS = ["@databricks/lakebase"]; +if (prerelease) { + pkg.version = `${pkg.version}-pr.${prerelease}`; +} + delete pkg.dependencies.shared; for (const depName of WORKSPACE_PACKAGE_REPLACEMENTS) { diff --git a/tools/dist-lakebase.ts b/tools/dist-lakebase.ts index e34a7c413..e1bd82c1f 100644 --- a/tools/dist-lakebase.ts +++ b/tools/dist-lakebase.ts @@ -1,11 +1,21 @@ import fs from "node:fs"; import path from "node:path"; +import { parseArgs } from "node:util"; const __dirname = path.dirname(new URL(import.meta.url).pathname); +const { values } = parseArgs({ + options: { + prerelease: { type: "string" }, + }, +}); +const prerelease = values.prerelease; fs.mkdirSync("tmp", { recursive: true }); const pkg = JSON.parse(fs.readFileSync("package.json", "utf-8")); +if (prerelease) { + pkg.version = `${pkg.version}-pr.${prerelease}`; +} pkg.exports = pkg.publishConfig.exports; delete pkg.publishConfig.exports; diff --git a/tools/prepare-template-artifact.ts b/tools/prepare-template-artifact.ts index b47e322f8..8df7cde78 100644 --- a/tools/prepare-template-artifact.ts +++ b/tools/prepare-template-artifact.ts @@ -5,17 +5,16 @@ * Copies the template/ directory into a staging folder, bundles the SDK tarballs, * and rewrites package.json to use `file:` references for appkit and appkit-ui. * - * When `packages/lakebase/tmp/databricks-lakebase-.tgz` exists (e.g. after + * 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 ] + * tsx tools/prepare-template-artifact.ts [--tarball-dir ] [--output-dir ] * * Options: - * --version Required. Version string used to locate tarball filenames. * --tarball-dir Optional. Single directory containing both tarballs. * Defaults to packages/appkit/tmp/ and packages/appkit-ui/tmp/. * --output-dir Optional. Staging directory name. Defaults to "pr-template". @@ -26,6 +25,7 @@ import { cpSync, existsSync, mkdirSync, + readdirSync, readFileSync, writeFileSync, } from "node:fs"; @@ -36,34 +36,46 @@ const ROOT = process.cwd(); const { values } = parseArgs({ options: { - version: { type: "string" }, "tarball-dir": { type: "string" }, "output-dir": { type: "string", default: "pr-template" }, }, strict: true, }); -const version = values.version; -if (!version) { - console.error( - "Usage: tsx tools/prepare-template-artifact.ts --version [--tarball-dir ] [--output-dir ]", - ); - process.exit(1); -} - const tarballDir = values["tarball-dir"]; // biome-ignore lint/style/noNonNullAssertion: default value guarantees this is defined const outputDir = values["output-dir"]!; 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`; +function getTarballName( + dir: string, + packageName: string, + required = true, +): string { + const pattern = new RegExp( + `^databricks-${packageName}-\\d+\\.\\d+\\.\\d+(?:-pr\\.[0-9a-f]+)?\\.tgz$`, + "i", + ); + if (!existsSync(dir)) { + if (!required) { + return ""; + } + console.error(`Expected tarball directory to exist: ${dir}`); + process.exit(1); + } + const matches = readdirSync(dir).filter((entry) => pattern.test(entry)); + if (!required && matches.length === 0) { + return ""; + } + if (matches.length !== 1) { + console.error( + `Expected exactly one ${pattern.source} tarball in ${dir}, found ${matches.length}`, + ); + process.exit(1); + } + return matches[0]; +} // 1. Copy template into staging directory mkdirSync(STAGING_DIR, { recursive: true }); @@ -71,19 +83,29 @@ cpSync(join(ROOT, "template"), STAGING_DIR, { recursive: true }); console.log(`✓ Copied template/ → ${outputDir}/`); // 2. Copy tarballs into staging directory -const appkitSrc = tarballDir - ? join(ROOT, tarballDir, APPKIT_TARBALL) - : join(ROOT, "packages/appkit/tmp", APPKIT_TARBALL); -const appkitUiSrc = tarballDir - ? join(ROOT, tarballDir, APPKIT_UI_TARBALL) - : join(ROOT, "packages/appkit-ui/tmp", APPKIT_UI_TARBALL); +const appkitDir = tarballDir + ? join(ROOT, tarballDir) + : join(ROOT, "packages/appkit/tmp"); +const appkitUiDir = tarballDir + ? join(ROOT, tarballDir) + : join(ROOT, "packages/appkit-ui/tmp"); +const lakebaseDir = tarballDir + ? join(ROOT, tarballDir) + : join(ROOT, "packages/lakebase/tmp"); + +const APPKIT_TARBALL = getTarballName(appkitDir, "appkit"); +const APPKIT_UI_TARBALL = getTarballName(appkitUiDir, "appkit-ui"); +const LAKEBASE_TARBALL = getTarballName(lakebaseDir, "lakebase", false); + +const appkitSrc = join(appkitDir, APPKIT_TARBALL); +const appkitUiSrc = join(appkitUiDir, APPKIT_UI_TARBALL); 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)) { +const lakebaseSrc = LAKEBASE_TARBALL ? join(lakebaseDir, LAKEBASE_TARBALL) : ""; +if (LAKEBASE_TARBALL && lakebaseSrc && existsSync(lakebaseSrc)) { copyFileSync(lakebaseSrc, join(STAGING_DIR, LAKEBASE_TARBALL)); console.log(`✓ Copied ${LAKEBASE_TARBALL}`); } @@ -93,13 +115,13 @@ 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)) { +if (lakebaseSrc && existsSync(lakebaseSrc) && LAKEBASE_TARBALL) { pkg.overrides = pkg.overrides ?? {}; pkg.overrides["@databricks/lakebase"] = `file:./${LAKEBASE_TARBALL}`; } writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`); console.log( - existsSync(lakebaseSrc) + lakebaseSrc && existsSync(lakebaseSrc) ? "✓ Rewrote package.json (appkit/appkit-ui file: deps; @databricks/lakebase override)" : "✓ Rewrote package.json dependencies to file: references (no local lakebase pack)", );