publish-npm.cjs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. /* eslint-env node */
  2. const fs = require("fs")
  3. const path = require("path")
  4. const { execSync } = require("child_process")
  5. const readline = require("readline")
  6. const PACKAGE_NAME = "@roo-code/types"
  7. const BRANCH_NAME = "roo-code-types-v"
  8. const rootDir = path.join(__dirname, "..")
  9. const npmDir = path.join(rootDir, "npm")
  10. const monorepoPackagePath = path.join(rootDir, "package.json")
  11. const npmMetadataPath = path.join(npmDir, "package.metadata.json")
  12. const npmPackagePath = path.join(npmDir, "package.json")
  13. const args = process.argv.slice(2)
  14. const publishOnly = args.includes("--publish-only")
  15. async function confirmPublish() {
  16. const rl = readline.createInterface({
  17. input: process.stdin,
  18. output: process.stdout,
  19. })
  20. return new Promise((resolve) => {
  21. rl.question("\n⚠️ Are you sure you want to publish to npm? (y/n): ", (answer) => {
  22. rl.close()
  23. resolve(answer.toLowerCase() === "y")
  24. })
  25. })
  26. }
  27. function updatePackageVersion(filePath, version) {
  28. try {
  29. const packageContent = JSON.parse(fs.readFileSync(filePath, "utf8"))
  30. const oldVersion = packageContent.version
  31. packageContent.version = version
  32. fs.writeFileSync(filePath, JSON.stringify(packageContent, null, 2) + "\n")
  33. try {
  34. execSync(`npx prettier --write "${filePath}"`, { stdio: "pipe" })
  35. console.log(`✨ Formatted ${path.basename(filePath)} with prettier`)
  36. } catch (prettierError) {
  37. console.warn(`⚠️ Could not format with prettier:`, prettierError.message)
  38. }
  39. const fileName = path.basename(filePath)
  40. console.log(`✅ Updated ${fileName} version: ${oldVersion} → ${version}`)
  41. return oldVersion
  42. } catch (error) {
  43. throw new Error(`Failed to update version in ${path.basename(filePath)}: ${error.message}`)
  44. }
  45. }
  46. function syncVersionToMetadata(version) {
  47. console.log(" 📝 Syncing version to package.metadata.json...")
  48. updatePackageVersion(npmMetadataPath, version)
  49. }
  50. function commitVersionChanges(version) {
  51. try {
  52. console.log(" 📝 Committing version changes to git...")
  53. try {
  54. const status = execSync("git status --porcelain", { encoding: "utf8" })
  55. const relevantChanges = status.split("\n").filter((line) => line.includes("packages/types/npm/package"))
  56. if (relevantChanges.length === 0) {
  57. console.log(" ⚠️ No version changes to commit")
  58. return
  59. }
  60. } catch (error) {
  61. console.warn(" ⚠️ Could not check git status:", error.message)
  62. }
  63. execSync("git add .", { stdio: "pipe" })
  64. const commitMessage = `chore: bump version to v${version}`
  65. execSync(`git commit -m "${commitMessage}"`, { stdio: "pipe" })
  66. console.log(` ✅ Committed: ${commitMessage}`)
  67. } catch (error) {
  68. console.warn(" ⚠️ Could not commit version changes:", error.message)
  69. console.log(" You may need to commit these changes manually.")
  70. }
  71. }
  72. function checkGitHubCLI() {
  73. try {
  74. execSync("gh --version", { stdio: "pipe" })
  75. execSync("gh auth status", { stdio: "pipe" })
  76. return true
  77. } catch (_error) {
  78. return false
  79. }
  80. }
  81. function createPullRequest(branchName, baseBranch, version) {
  82. try {
  83. console.log(` 🔄 Creating pull request...`)
  84. if (!checkGitHubCLI()) {
  85. console.warn(" ⚠️ GitHub CLI not found or not authenticated")
  86. console.log(" Install gh CLI and run: gh auth login")
  87. console.log(" Then manually create PR with: gh pr create")
  88. return
  89. }
  90. const title = `Release: v${version}`
  91. const body = `## 🚀 Release v${version}
  92. This PR contains the version bump for the SDK release v${version}.
  93. ### Changes
  94. - Bumped version from previous to v${version}
  95. - Published to npm as ${PACKAGE_NAME}@${version}
  96. ### Checklist
  97. - [x] Version bumped
  98. - [x] Package published to npm
  99. - [ ] Changelog updated (if applicable)
  100. - [ ] Documentation updated (if applicable)
  101. ---
  102. *This PR was automatically created by the npm publish script.*`
  103. try {
  104. // Create the pull request
  105. const prUrl = execSync(
  106. `gh pr create --base "${baseBranch}" --head "${branchName}" --title "${title}" --body "${body}"`,
  107. { encoding: "utf8", stdio: "pipe" },
  108. ).trim()
  109. console.log(` ✅ Pull request created: ${prUrl}`)
  110. } catch (error) {
  111. if (error.message.includes("already exists")) {
  112. console.log(" ℹ️ Pull request already exists for this branch")
  113. } else {
  114. throw error
  115. }
  116. }
  117. } catch (error) {
  118. console.error(" ❌ Failed to create pull request:", error.message)
  119. console.log(" You can manually create a PR with:")
  120. console.log(` gh pr create --base "${baseBranch}" --head "${branchName}"`)
  121. }
  122. }
  123. function createVersionBranchAndCommit(version) {
  124. try {
  125. const branchName = `${BRANCH_NAME}${version}`
  126. console.log(` 🌿 Creating version branch: ${branchName}...`)
  127. let currentBranch
  128. try {
  129. currentBranch = execSync("git rev-parse --abbrev-ref HEAD", {
  130. encoding: "utf8",
  131. }).trim()
  132. } catch (_error) {
  133. console.warn(" ⚠️ Could not determine current branch")
  134. currentBranch = "main"
  135. }
  136. execSync(`git checkout -b ${branchName}`, { stdio: "pipe" })
  137. console.log(` ✅ Created branch: ${branchName}`)
  138. commitVersionChanges(version)
  139. execSync(`git push --set-upstream origin ${branchName}`, { stdio: "pipe" })
  140. console.log(` ✅ Pushed branch to origin with upstream tracking`)
  141. createPullRequest(branchName, currentBranch, version)
  142. if (currentBranch) {
  143. execSync(`git checkout ${currentBranch}`, { stdio: "pipe" })
  144. console.log(` ✅ Returned to branch: ${currentBranch}`)
  145. }
  146. console.log(` 🎯 Version branch created with commits: ${branchName}`)
  147. } catch (error) {
  148. console.error(" ❌ Failed to create version branch:", error.message)
  149. console.log(" You may need to create the branch manually.")
  150. }
  151. }
  152. function generateNpmPackage() {
  153. try {
  154. console.log(" 📖 Reading monorepo package.json...")
  155. const monorepoPackageContent = fs.readFileSync(monorepoPackagePath, "utf8")
  156. const monorepoPackage = JSON.parse(monorepoPackageContent)
  157. console.log(" 📖 Reading npm package metadata...")
  158. const npmMetadataContent = fs.readFileSync(npmMetadataPath, "utf8")
  159. const npmMetadata = JSON.parse(npmMetadataContent)
  160. console.log(" 🔨 Generating npm package.json...")
  161. const npmPackage = {
  162. ...npmMetadata,
  163. dependencies: monorepoPackage.dependencies || {},
  164. main: "./dist/index.cjs",
  165. module: "./dist/index.js",
  166. types: "./dist/index.d.ts",
  167. exports: {
  168. ".": {
  169. types: "./dist/index.d.ts",
  170. import: "./dist/index.js",
  171. require: {
  172. types: "./dist/index.d.cts",
  173. default: "./dist/index.cjs",
  174. },
  175. },
  176. },
  177. files: ["dist"],
  178. }
  179. const outputContent = JSON.stringify(npmPackage, null, 2) + "\n"
  180. fs.writeFileSync(npmPackagePath, outputContent)
  181. console.log(" ✅ npm/package.json generated successfully")
  182. console.log(` 📦 Package name: ${npmPackage.name}`)
  183. console.log(` 📌 Version: ${npmPackage.version}`)
  184. console.log(` 📚 Dependencies: ${Object.keys(npmPackage.dependencies).length}`)
  185. } catch (error) {
  186. throw new Error(`Failed to generate npm package.json: ${error.message}`)
  187. }
  188. }
  189. async function publish() {
  190. try {
  191. console.log("\n🚀 NPM PUBLISH WORKFLOW")
  192. if (publishOnly) {
  193. console.log("📌 Mode: Publish only (no git operations)")
  194. }
  195. console.log("=".repeat(60))
  196. console.log("\n📦 Step 1: Generating npm package.json...")
  197. generateNpmPackage()
  198. const npmPackage = JSON.parse(fs.readFileSync(npmPackagePath, "utf8"))
  199. const originalVersion = npmPackage.version // Save original version
  200. console.log(`\n📌 Current version: ${npmPackage.version}`)
  201. console.log(`📦 Package name: ${npmPackage.name}`)
  202. console.log("\n📈 Step 2: Bumping version (minor)...")
  203. try {
  204. execSync("npm version minor --no-git-tag-version", {
  205. cwd: npmDir,
  206. stdio: "inherit",
  207. })
  208. } catch (error) {
  209. console.error("❌ Failed to bump version:", error.message)
  210. throw error
  211. }
  212. const updatedPackage = JSON.parse(fs.readFileSync(npmPackagePath, "utf8"))
  213. console.log(`✅ New version: ${updatedPackage.version}`)
  214. console.log("\n🔨 Step 3: Building production bundle...")
  215. console.log(" This may take a moment...")
  216. try {
  217. execSync("NODE_ENV=production pnpm tsup --outDir npm/dist", {
  218. cwd: rootDir,
  219. stdio: "inherit",
  220. })
  221. console.log("✅ Production build complete")
  222. } catch (error) {
  223. console.error("❌ Build failed:", error.message)
  224. throw error
  225. }
  226. console.log("\n" + "=".repeat(60))
  227. console.log("📋 PUBLISH SUMMARY:")
  228. console.log(` Package: ${updatedPackage.name}`)
  229. console.log(` Version: ${updatedPackage.version}`)
  230. console.log(` Registry: ${updatedPackage.publishConfig?.registry || "https://registry.npmjs.org/"}`)
  231. console.log(` Access: ${updatedPackage.publishConfig?.access || "public"}`)
  232. console.log("=".repeat(60))
  233. const confirmed = await confirmPublish()
  234. if (!confirmed) {
  235. console.log("\n❌ Publishing cancelled by user")
  236. console.log("🔙 Reverting version change...")
  237. try {
  238. updatePackageVersion(npmPackagePath, originalVersion)
  239. } catch (revertError) {
  240. console.error("⚠️ Could not revert version:", revertError.message)
  241. console.log(` You may need to manually change version back to ${originalVersion}`)
  242. }
  243. process.exit(0)
  244. }
  245. console.log("\n💾 Step 4: Syncing version to metadata...")
  246. syncVersionToMetadata(updatedPackage.version)
  247. console.log("\n🚀 Step 5: Publishing to npm...")
  248. try {
  249. execSync("npm publish", {
  250. cwd: npmDir,
  251. stdio: "inherit",
  252. })
  253. } catch (error) {
  254. console.error("❌ Publish failed:", error.message)
  255. console.error("💡 The package was built but not published.")
  256. console.error(" You can try publishing manually from the npm directory.")
  257. throw error
  258. }
  259. if (!publishOnly) {
  260. console.log("\n🌿 Step 6: Creating version branch, committing, and opening PR...")
  261. createVersionBranchAndCommit(updatedPackage.version)
  262. } else {
  263. console.log("\n📝 Step 6: Skipping version branch creation (--publish-only mode)")
  264. }
  265. console.log("\n" + "=".repeat(60))
  266. console.log("✅ Successfully published to npm!")
  267. console.log(`🎉 ${updatedPackage.name}@${updatedPackage.version} is now live`)
  268. console.log(`📦 View at: https://www.npmjs.com/package/${updatedPackage.name}`)
  269. if (!publishOnly) {
  270. console.log(`🌿 Version branch: ${BRANCH_NAME}${updatedPackage.version}`)
  271. }
  272. console.log("=".repeat(60) + "\n")
  273. } catch (error) {
  274. console.error("\n❌ Error during publish process:", error.message)
  275. console.error("\n💡 Troubleshooting tips:")
  276. console.error(" 1. Ensure you are logged in to npm: npm whoami")
  277. console.error(" 2. Check your npm permissions for this package")
  278. console.error(" 3. Verify the package name is not already taken")
  279. console.error(" 4. Make sure all dependencies are installed: pnpm install")
  280. process.exit(1)
  281. }
  282. }
  283. async function main() {
  284. await publish()
  285. }
  286. main().catch((error) => {
  287. console.error("Unexpected error:", error)
  288. process.exit(1)
  289. })