| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- import { spawnSync } from 'node:child_process'
- import { readFile } from 'node:fs/promises'
- import { dirname, join, resolve } from 'node:path'
- import { fileURLToPath } from 'node:url'
- const scriptDir = dirname(fileURLToPath(import.meta.url))
- const sourceRoot = resolve(scriptDir, '..')
- const branchRoles = await readJson(join(sourceRoot, 'ecosystem', 'branch-roles.json'))
- const wrapperManifest = await readJson(join(sourceRoot, 'ecosystem', 'wrappers.json'))
- const rootPackage = await readJson(join(sourceRoot, 'package.json'))
- function assert(condition, message) {
- if (!condition) {
- throw new Error(message)
- }
- }
- async function readJson(path) {
- return JSON.parse(await readFile(path, 'utf8'))
- }
- function runGit(args) {
- const result = spawnSync('git', args, {
- cwd: sourceRoot,
- encoding: 'utf8',
- stdio: 'pipe'
- })
- if (result.status !== 0) {
- throw new Error(`Command failed: git ${args.join(' ')}\n${result.stderr || result.stdout}`)
- }
- return result.stdout.trim()
- }
- function repositoryPath(url) {
- const normalized = url
- .replace(/^git\+/, '')
- .replace(/\.git$/, '')
- .replace(/^https?:\/\//, '')
- .replace(/^git@([^:]+):/, '$1/')
- const segments = normalized.split('/')
- return segments.slice(-2).join('/')
- }
- function assertPublicRepository(url, expectedOrg, label) {
- const path = repositoryPath(url)
- const [org] = path.split('/')
- assert(org === expectedOrg, `${label} must live under ${expectedOrg}, got ${url}`)
- }
- assert(branchRoles.schemaVersion === 1, 'branch-roles.json schemaVersion must be 1')
- assert(branchRoles.currentSourceBranch, 'branch-roles.json must declare currentSourceBranch')
- assert(branchRoles.sourceRemote?.name === 'origin', 'Private source remote must be named origin')
- assert(branchRoles.sourceRemote?.visibility === 'private', 'Source remote must be marked private')
- assert(
- branchRoles.sourceRemote?.url === wrapperManifest.corePackage.aggregateRepository,
- 'branch-roles source remote must match wrappers.json corePackage.aggregateRepository'
- )
- assert(
- wrapperManifest.corePackage.visibility === 'public-source',
- 'wrappers.json corePackage.visibility must be public-source'
- )
- const originUrl = runGit(['remote', 'get-url', branchRoles.sourceRemote.name])
- assert(
- originUrl.replace(/\.git$/, '') === branchRoles.sourceRemote.url.replace(/\.git$/, ''),
- `git origin must point to ${branchRoles.sourceRemote.url}, got ${originUrl}`
- )
- const currentBranch = runGit(['branch', '--show-current'])
- const rolesByName = new Map(branchRoles.branches.map(branch => [branch.name, branch]))
- assert(rolesByName.has('main'), 'branch roles must include main')
- assert(rolesByName.has('v2'), 'branch roles must include v2')
- assert(rolesByName.has('v3'), 'branch roles must include v3')
- assert(rolesByName.has(currentBranch), `current branch ${currentBranch} must be declared in branch roles`)
- const mainRole = rolesByName.get('main')
- assert(mainRole.role === 'private-aggregate-workspace', 'main branch role must be private-aggregate-workspace')
- assert(mainRole.packageName === rootPackage.name, `main branch package must be ${rootPackage.name}`)
- assert(
- mainRole.sourcePolicy === 'private-complete-original-workspace',
- 'main branch source policy must keep the complete original workspace private'
- )
- const wrappersById = new Map(wrapperManifest.wrappers.map(wrapper => [wrapper.id, wrapper]))
- const compatibilityPackagesByName = new Map(
- (wrapperManifest.compatibilityPackages || []).map(compatibilityPackage => [
- compatibilityPackage.packageName,
- compatibilityPackage
- ])
- )
- for (const branchName of ['v2', 'v3']) {
- const role = rolesByName.get(branchName)
- assert(role.wrapperId, `${branchName} branch must declare wrapperId`)
- const wrapper = wrappersById.get(role.wrapperId)
- assert(wrapper, `${branchName} wrapperId ${role.wrapperId} must exist in wrappers.json`)
- assert(role.packageName === wrapper.packageName, `${branchName} packageName must match wrappers.json`)
- assert(
- role.sourcePolicy === 'component-source-exported-publicly',
- `${branchName} source policy must export component source publicly`
- )
- for (const compatibilityPackage of role.compatibilityPackages || []) {
- assert(
- wrapper.historicalPackages.includes(compatibilityPackage),
- `${branchName} compatibility package ${compatibilityPackage} must be declared in wrappers.json`
- )
- const compatibilityPackageEntry = compatibilityPackagesByName.get(compatibilityPackage)
- assert(
- compatibilityPackageEntry,
- `${branchName} compatibility package ${compatibilityPackage} must have a compatibilityPackages entry`
- )
- assert(
- compatibilityPackageEntry.targetPackage === wrapper.packageName,
- `${branchName} compatibility package ${compatibilityPackage} must target ${wrapper.packageName}`
- )
- assert(
- compatibilityPackageEntry.packageDir,
- `${branchName} compatibility package ${compatibilityPackage} must declare packageDir`
- )
- }
- }
- for (const wrapper of wrapperManifest.wrappers) {
- for (const historicalPackage of wrapper.historicalPackages || []) {
- assert(
- compatibilityPackagesByName.has(historicalPackage),
- `Historical package ${historicalPackage} must have a compatibilityPackages entry`
- )
- }
- }
- assert(
- wrapperManifest.sourceBranch === branchRoles.currentSourceBranch,
- `wrappers.json sourceBranch must match branch-roles currentSourceBranch ${branchRoles.currentSourceBranch}`
- )
- assert(
- branchRoles.targetBranchModel?.main === mainRole.role,
- 'targetBranchModel.main must match the declared main branch role'
- )
- assert(
- branchRoles.targetBranchModel?.v2 === rolesByName.get('v2')?.role,
- 'targetBranchModel.v2 must match the declared v2 branch role'
- )
- assert(
- branchRoles.targetBranchModel?.v3 === rolesByName.get('v3')?.role,
- 'targetBranchModel.v3 must match the declared v3 branch role'
- )
- assert(branchRoles.publicOrganization === wrapperManifest.organization, 'Public organization must match wrappers.json')
- assert(
- branchRoles.publicMainRepository?.sourcePolicy === 'public-open-source-main-repository',
- 'Open-source main repository must publish source, demo code, documentation, and release artifacts'
- )
- assertPublicRepository(branchRoles.publicMainRepository.github, branchRoles.publicOrganization, 'Open-source main GitHub repository')
- assertPublicRepository(branchRoles.publicMainRepository.gitee, branchRoles.publicOrganization, 'Open-source main Gitee repository')
- const publicMainRepo = repositoryPath(branchRoles.publicMainRepository.github)
- assertPublicRepository(
- wrapperManifest.corePackage.github,
- wrapperManifest.organization,
- `${wrapperManifest.corePackage.packageName} GitHub repository`
- )
- assertPublicRepository(
- wrapperManifest.corePackage.gitee,
- wrapperManifest.organization,
- `${wrapperManifest.corePackage.packageName} Gitee repository`
- )
- assert(
- repositoryPath(wrapperManifest.corePackage.github) !== publicMainRepo,
- `${wrapperManifest.corePackage.packageName} must use a dedicated core repository, not the open-source main repository`
- )
- for (const wrapper of wrapperManifest.wrappers) {
- assertPublicRepository(wrapper.github, wrapperManifest.organization, `${wrapper.packageName} GitHub repository`)
- assertPublicRepository(wrapper.gitee, wrapperManifest.organization, `${wrapper.packageName} Gitee repository`)
- assert(
- repositoryPath(wrapper.github) !== publicMainRepo,
- `${wrapper.packageName} must use a dedicated component repository, not the open-source main repository`
- )
- }
- console.log(
- `Verified ${branchRoles.branches.length} branch roles, aggregate origin, and ${wrapperManifest.wrappers.length + 1} public core/component repositories.`
- )
|