mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-12 06:26:16 -08:00
ci(refactor): [ENG-IMPACT-01] map workspace changes to required ecosystem checks
This commit is contained in:
1 parent
e0aacc5d6a
commit
6abac891ce
14 files changed
+1070
-9
No files matched your search
@@ -0,0 +1,102 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { execFileSync } from 'node:child_process'
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import process from 'node:process'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { analyzeImpact, readImpactModel, validateImpactWorkflow } from './impact-model.mjs'
|
||||
import { hash } from './releases.mjs'
|
||||
|
||||
const root = fileURLToPath(new URL('../../', import.meta.url))
|
||||
|
||||
export function changedFiles(directory, options = {}) {
|
||||
const git = args => execFileSync('git', args, { cwd: directory, encoding: 'utf8', maxBuffer: 32 * 1024 * 1024, stdio: ['ignore', 'pipe', 'pipe'] }).trimEnd()
|
||||
const head = git(['rev-parse', 'HEAD'])
|
||||
const split = text => text.split('\0').filter(Boolean)
|
||||
const local = [...split(git(['diff', '--name-only', '--no-renames', '-z', 'HEAD', '--'])), ...split(git(['ls-files', '--others', '--exclude-standard', '-z']))]
|
||||
let requested = options.base
|
||||
let origin = requested ? 'explicit-base' : 'worktree'
|
||||
if (!requested && options.eventName) {
|
||||
const event = options.eventPath ? JSON.parse(fs.readFileSync(options.eventPath, 'utf8')) : {}
|
||||
requested = options.eventName === 'pull_request' ? event.pull_request?.base?.sha : options.eventName === 'push' ? event.before : null
|
||||
if (requested && /^0+$/.test(requested))
|
||||
requested = null
|
||||
if (requested)
|
||||
assert(/^[a-f\d]{40}$/.test(requested), 'Invalid GitHub event base SHA')
|
||||
origin = requested ? `github-${options.eventName}` : 'full-repository-event-fallback'
|
||||
}
|
||||
let base = null
|
||||
let mergeBase = null
|
||||
let committed = []
|
||||
if (requested) {
|
||||
try {
|
||||
base = git(['rev-parse', '--verify', '--end-of-options', `${requested}^{commit}`])
|
||||
mergeBase = git(['merge-base', base, head])
|
||||
}
|
||||
catch (error) {
|
||||
if (options.base)
|
||||
throw error
|
||||
origin = 'full-repository-missing-event-base'
|
||||
}
|
||||
if (mergeBase)
|
||||
committed = split(git(['diff', '--name-only', '--no-renames', '-z', mergeBase, head, '--']))
|
||||
}
|
||||
if (origin.startsWith('full-repository-'))
|
||||
committed = split(git(['ls-files', '-z']))
|
||||
return { head, requestedBase: requested || null, base, mergeBase, origin, worktreeChanged: local.length > 0, files: [...new Set([...committed, ...local])].sort() }
|
||||
}
|
||||
|
||||
export function createImpactReport(directory = root, options = {}) {
|
||||
const model = readImpactModel(directory)
|
||||
validateImpactWorkflow(fs.readFileSync(path.join(directory, '.github/workflows/nodejs.yml'), 'utf8'), model)
|
||||
const range = changedFiles(directory, options)
|
||||
const impact = analyzeImpact(model, range.files)
|
||||
const metadata = ['refactor/impact-policy.json', 'package.json', 'yarn.lock', ...model.packages.map(pkg => `packages/${pkg.name}/package.json`)]
|
||||
const inputs = [...new Set([...metadata, ...range.files])].map((file) => {
|
||||
const absolute = path.join(directory, file)
|
||||
if (!fs.existsSync(absolute))
|
||||
return { file, state: 'deleted-or-missing' }
|
||||
if (fs.lstatSync(absolute).isSymbolicLink())
|
||||
return { file, state: 'symlink', sha256: hash(fs.readlinkSync(absolute)) }
|
||||
return { file, state: 'file', sha256: hash(fs.readFileSync(absolute)) }
|
||||
})
|
||||
return {
|
||||
schemaVersion: 1,
|
||||
range,
|
||||
...impact,
|
||||
packages: model.packages.map(pkg => ({ name: pkg.name, version: pkg.version })),
|
||||
edges: model.edges,
|
||||
unresolvedDynamicImports: model.dynamicImports,
|
||||
coveragePackages: model.coveragePackages,
|
||||
installedConsumerPackages: model.policy.installedConsumerPackages,
|
||||
inputs,
|
||||
}
|
||||
}
|
||||
|
||||
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
||||
let base
|
||||
let report = false
|
||||
const args = process.argv.slice(2)
|
||||
for (let index = 0; index < args.length; index++) {
|
||||
if (args[index] === '--report') {
|
||||
report = true
|
||||
}
|
||||
else if (args[index] === '--base') {
|
||||
base = args[++index]
|
||||
assert(base && !base.startsWith('--'), 'Missing --base commit/ref')
|
||||
}
|
||||
else {
|
||||
assert.fail('Use --report and/or --base <commit/ref>')
|
||||
}
|
||||
}
|
||||
const result = createImpactReport(root, { base, eventName: process.env.GITHUB_EVENT_NAME, eventPath: process.env.GITHUB_EVENT_PATH })
|
||||
if (report) {
|
||||
const directory = path.join(root, 'refactor/.cache/ci')
|
||||
fs.mkdirSync(directory, { recursive: true })
|
||||
fs.writeFileSync(path.join(directory, 'impact.json'), `${JSON.stringify(result, null, 2)}\n`)
|
||||
}
|
||||
console.log(`Impact: ${result.files.length} changed paths, ${result.affectedPackages.length}/${result.packages.length} packages; ${result.range.origin}; required jobs ${result.requiredCIJobs.join(', ')}`)
|
||||
if (result.reviewRequired)
|
||||
console.warn(`Impact review: ${result.unknownPaths.length} unclassified paths and ${result.unresolvedDynamicImports.length} dynamic-import files conservatively require the full ecosystem`)
|
||||
console.log(`Installed-consumer gaps among affected packages: ${result.installedConsumerGaps.length}; impact mapping is not release acceptance`)
|
||||
}
|
||||
Reference in new issue
Block a user