updateChangelogs.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. #!/usr/bin/env node
  2. import { createReadStream, promises as fs } from 'node:fs'
  3. import { createInterface } from 'node:readline'
  4. import { Buffer } from 'node:buffer'
  5. import process from 'node:process'
  6. const ROOT = new URL('../../', import.meta.url)
  7. const PACKAGES_FOLDER = new URL('./packages/', ROOT)
  8. const releasedDate = new Date().toISOString().slice(0, 10)
  9. const releases = JSON.parse(
  10. await fs.readFile(new URL(process.argv[2], ROOT), 'utf-8'),
  11. )
  12. const uppyRelease = releases.find(({ ident }) => ident === 'uppy')
  13. const changelog = await fs.open(new URL('./CHANGELOG.md', ROOT), 'r+')
  14. const changelogContent = await changelog.readFile()
  15. const mostRecentReleaseHeading = changelogContent.indexOf('\n## ')
  16. function* makeTable (versions) {
  17. const pkgNameMaxLength = Math.max('Package'.length, ...versions.map(pkg => pkg.ident.length))
  18. const pkgVersionMaxLength = Math.max('Version'.length, ...versions.map(pkg => pkg.newVersion.length))
  19. const makeRow = (...cells) => `| ${cells.map((cell, i) => cell[i % 2 ? 'padStart' : 'padEnd'](i % 2 ? pkgVersionMaxLength : pkgNameMaxLength)).join(' | ')} |`
  20. yield makeRow('Package', 'Version', 'Package', 'Version')
  21. yield makeRow(...Array.from({ length:4 }, (_, i) => '-'.repeat(i % 2 ? pkgVersionMaxLength : pkgNameMaxLength)))
  22. const mid = Math.ceil(versions.length / 2)
  23. for (let i = 0; i < mid; i++) {
  24. const left = versions[i] || { ident: '', newVersion: '' }
  25. const right = versions[i + mid] || { ident: '', newVersion: '' }
  26. yield makeRow(left.ident, left.newVersion, right.ident, right.newVersion)
  27. }
  28. }
  29. /**
  30. * Opens the changelog of a given package, creating it if it doesn't exist.
  31. *
  32. * @param {string} pkg Package name
  33. * @returns {Promise<fs.FileHandle>}
  34. */
  35. async function updateSubPackageChangelog (pkg, lines, subsetOfLines) {
  36. const packageReleaseInfo = releases.find(({ ident }) => ident === pkg)
  37. if (packageReleaseInfo == null) {
  38. console.warn(pkg, 'is not being released')
  39. return null
  40. }
  41. const { newVersion } = packageReleaseInfo
  42. const url = new URL(`./${pkg}/CHANGELOG.md`, PACKAGES_FOLDER)
  43. const heading = Buffer.from(`# ${pkg}\n`)
  44. let fh
  45. let oldContent
  46. try {
  47. fh = await fs.open(url, 'r+') // this will throw if the file doesn't exist
  48. oldContent = await fh.readFile()
  49. } catch (e) {
  50. if (e.code !== 'ENOENT') {
  51. throw e
  52. }
  53. // Creates the file if it doesn't exist yet.
  54. fh = await fs.open(url, 'wx')
  55. await fh.writeFile(heading)
  56. }
  57. const { bytesWritten } = await fh.write(`
  58. ## ${newVersion}
  59. Released: ${releasedDate}
  60. Included in: Uppy v${uppyRelease.newVersion}
  61. ${subsetOfLines.map(index => lines[index]).join('\n')}
  62. `, heading.byteLength)
  63. if (oldContent != null) {
  64. await fh.write(oldContent, heading.byteLength, undefined, bytesWritten + heading.byteLength)
  65. }
  66. console.log(`packages/${pkg}/CHANGELOG.md`) // outputing the relative path of the file to git add it.
  67. return fh.close()
  68. }
  69. const subPackagesChangelogs = {}
  70. const lines = []
  71. for await (const line of createInterface({
  72. input: createReadStream(new URL('./CHANGELOG.next.md', ROOT)),
  73. })) {
  74. const index = lines.push(line) - 1
  75. for (const pkg of line.slice(2, line.indexOf(':')).split(',')) {
  76. subPackagesChangelogs[pkg] ??= []
  77. subPackagesChangelogs[pkg].push(index)
  78. }
  79. }
  80. await changelog.write(`
  81. ## ${uppyRelease.newVersion}
  82. Released: ${releasedDate}
  83. ${Array.from(makeTable(releases)).join('\n')}
  84. ${lines.join('\n')}
  85. ${changelogContent.slice(mostRecentReleaseHeading)}`, mostRecentReleaseHeading)
  86. console.log('CHANGELOG.md') // outputing the relative path of the file to git add it.
  87. await changelog.close()
  88. await Promise.all(
  89. Object.entries(subPackagesChangelogs)
  90. .map(([pkg, subsetOfLines]) => updateSubPackageChangelog(pkg, lines, subsetOfLines)),
  91. )