releases.ts 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253
  1. import { readFileSync } from 'node:fs'
  2. export interface ReleaseManifest {
  3. schemaVersion: 1
  4. releaseVersion: string
  5. webVersion: string
  6. gitTag: string
  7. }
  8. const VERSION_PATTERN = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/
  9. function version(value: unknown, field: string): string {
  10. if (typeof value !== 'string' || !VERSION_PATTERN.test(value)) {
  11. throw new Error(`${field} 必须是有效的语义版本`)
  12. }
  13. return value
  14. }
  15. export function parseReleaseManifest(value: unknown): ReleaseManifest {
  16. if (!value || typeof value !== 'object' || Array.isArray(value)) {
  17. throw new Error('release.json 必须是对象')
  18. }
  19. const input = value as Record<string, unknown>
  20. if (input.schemaVersion !== 1) throw new Error('release.json schemaVersion 必须为 1')
  21. const releaseVersion = version(input.releaseVersion, 'releaseVersion')
  22. const webVersion = version(input.webVersion, 'webVersion')
  23. const gitTag = typeof input.gitTag === 'string' ? input.gitTag : ''
  24. if (releaseVersion !== webVersion) throw new Error('releaseVersion 与 webVersion 必须一致')
  25. if (gitTag !== `v${releaseVersion}`) throw new Error('gitTag 必须与 releaseVersion 一致')
  26. return { schemaVersion: 1, releaseVersion, webVersion, gitTag }
  27. }
  28. export function readReleaseManifest(path: string): ReleaseManifest {
  29. return parseReleaseManifest(JSON.parse(readFileSync(path, 'utf8')))
  30. }
  31. function numericCore(value: string): [number, number, number] {
  32. const [major = 0, minor = 0, patch = 0] = value.split('-', 1)[0]!.split('.').map(Number)
  33. return [major, minor, patch]
  34. }
  35. export function compareReleaseVersions(left: string, right: string): number {
  36. const a = numericCore(left)
  37. const b = numericCore(right)
  38. for (let index = 0; index < 3; index += 1) {
  39. if (a[index] !== b[index]) return a[index]! - b[index]!
  40. }
  41. const leftSuffix = left.includes('-') ? left.slice(left.indexOf('-') + 1) : ''
  42. const rightSuffix = right.includes('-') ? right.slice(right.indexOf('-') + 1) : ''
  43. if (!leftSuffix && rightSuffix) return 1
  44. if (leftSuffix && !rightSuffix) return -1
  45. return leftSuffix.localeCompare(rightSuffix)
  46. }