Kaynağa Gözat

chore(release): make release publishing resumable (#14953)

edison 5 gün önce
ebeveyn
işleme
30ba647385
1 değiştirilmiş dosya ile 64 ekleme ve 15 silme
  1. 64 15
      scripts/release.js

+ 64 - 15
scripts/release.js

@@ -73,14 +73,22 @@ const packages = fs
   .readdirSync(path.resolve(__dirname, '../packages'))
   .filter(p => {
     const pkgRoot = path.resolve(__dirname, '../packages', p)
-    if (fs.statSync(pkgRoot).isDirectory()) {
-      const pkg = JSON.parse(
-        fs.readFileSync(path.resolve(pkgRoot, 'package.json'), 'utf-8'),
-      )
-      return !pkg.private
+    const pkgPath = path.resolve(pkgRoot, 'package.json')
+    if (!fs.statSync(pkgRoot).isDirectory() || !fs.existsSync(pkgPath)) {
+      return false
     }
+
+    const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'))
+    return !pkg.private
   })
 
+const sortPackagesForPublishing = (/** @type {string[]} */ packageNames) => [
+  // Publish vue last so users cannot install the new entry package before
+  // the matching internal packages are available.
+  ...packageNames.filter(p => p !== 'vue'),
+  ...packageNames.filter(p => p === 'vue'),
+]
+
 const isCorePackage = (/** @type {string} */ pkgName) => {
   if (!pkgName) return
 
@@ -97,7 +105,7 @@ const isCorePackage = (/** @type {string} */ pkgName) => {
 const keepThePackageName = (/** @type {string} */ pkgName) => pkgName
 
 /** @type {string[]} */
-const skippedPackages = []
+const alreadyPublishedPackages = []
 
 /** @type {ReadonlyArray<import('semver').ReleaseType>} */
 const versionIncrements = [
@@ -124,6 +132,12 @@ const dryRun = async (
 const runIfNotDry = isDryRun ? dryRun : run
 const getPkgRoot = (/** @type {string} */ pkg) =>
   path.resolve(__dirname, '../packages/' + pkg)
+const getPkgManifest = (/** @type {string} */ pkg) =>
+  /** @type {Package} */ (
+    JSON.parse(
+      fs.readFileSync(path.resolve(getPkgRoot(pkg), 'package.json'), 'utf-8'),
+    )
+  )
 const step = (/** @type {string} */ msg) => console.log(pico.cyan(msg))
 
 async function main() {
@@ -252,10 +266,10 @@ async function main() {
     console.log(`\nDry run finished - run git diff to see package changes.`)
   }
 
-  if (skippedPackages.length) {
+  if (alreadyPublishedPackages.length) {
     console.log(
       pico.yellow(
-        `The following packages are skipped and NOT published:\n- ${skippedPackages.join(
+        `The following packages already existed on the registry and were skipped:\n- ${alreadyPublishedPackages.join(
           '\n- ',
         )}`,
       ),
@@ -412,7 +426,7 @@ async function publishPackages(version) {
     additionalPublishFlags.push('--provenance')
   }
 
-  for (const pkg of packages) {
+  for (const pkg of sortPackagesForPublishing(packages)) {
     await publishPackage(pkg, version, additionalPublishFlags)
   }
 }
@@ -423,9 +437,7 @@ async function publishPackages(version) {
  * @param {ReadonlyArray<string>} additionalFlags
  */
 async function publishPackage(pkgName, version, additionalFlags) {
-  if (skippedPackages.includes(pkgName)) {
-    return
-  }
+  const packageName = getPkgManifest(pkgName).name
 
   let releaseTag = null
   if (args.tag) {
@@ -438,7 +450,14 @@ async function publishPackage(pkgName, version, additionalFlags) {
     releaseTag = 'rc'
   }
 
-  step(`Publishing ${pkgName}...`)
+  if (!isDryRun && (await isPackagePublished(packageName, version))) {
+    const pkgVersion = `${packageName}@${version}`
+    console.log(pico.yellow(`Skipping already published: ${pkgVersion}`))
+    alreadyPublishedPackages.push(pkgVersion)
+    return
+  }
+
+  step(`Publishing ${packageName}...`)
   try {
     // Don't change the package manager here as we rely on pnpm to handle
     // workspace:* deps
@@ -457,16 +476,46 @@ async function publishPackage(pkgName, version, additionalFlags) {
         stdio: 'pipe',
       },
     )
-    console.log(pico.green(`Successfully published ${pkgName}@${version}`))
+    console.log(pico.green(`Successfully published ${packageName}@${version}`))
   } catch (/** @type {any} */ e) {
     if (e.message?.match(/previously published/)) {
-      console.log(pico.red(`Skipping already published: ${pkgName}`))
+      const pkgVersion = `${packageName}@${version}`
+      console.log(pico.red(`Skipping already published: ${pkgVersion}`))
+      alreadyPublishedPackages.push(pkgVersion)
     } else {
       throw e
     }
   }
 }
 
+async function isPackagePublished(
+  /** @type {string} */ packageName,
+  /** @type {string} */ version,
+) {
+  try {
+    await run(
+      'npm',
+      [
+        'view',
+        `${packageName}@${version}`,
+        'version',
+        ...(args.registry ? ['--registry', args.registry] : []),
+      ],
+      { stdio: 'pipe' },
+    )
+    return true
+  } catch (/** @type {any} */ e) {
+    if (isPackageNotFoundError(e)) {
+      return false
+    }
+    throw e
+  }
+}
+
+function isPackageNotFoundError(/** @type {Error} */ error) {
+  return /E404|No match found|No matching version|notarget/i.test(error.message)
+}
+
 async function publishOnly() {
   const targetVersion = positionals[0]
   if (targetVersion) {