fix(pncc): bind artifacts to one committed tree snapshot
This commit is contained in:
parent
4117696ad8
commit
9e04b0354c
1 changed files with 27 additions and 10 deletions
|
|
@ -25,6 +25,7 @@ const JOURNAL_SCHEMA = 'guanghu.pncc-runtime-journal/v1'
|
||||||
const STATUS_SCHEMA = 'guanghu.pncc-runtime-status/v1'
|
const STATUS_SCHEMA = 'guanghu.pncc-runtime-status/v1'
|
||||||
const MAX_ARTIFACT_BYTES = 2 * 1024 * 1024
|
const MAX_ARTIFACT_BYTES = 2 * 1024 * 1024
|
||||||
const MAX_ARTIFACTS = 256
|
const MAX_ARTIFACTS = 256
|
||||||
|
const MAX_TREE_ENTRIES = 4096
|
||||||
|
|
||||||
function sha256(value) {
|
function sha256(value) {
|
||||||
return createHash('sha256').update(value).digest('hex')
|
return createHash('sha256').update(value).digest('hex')
|
||||||
|
|
@ -74,13 +75,28 @@ function exactGitRoot(repository) {
|
||||||
return candidate
|
return candidate
|
||||||
}
|
}
|
||||||
|
|
||||||
function committedArtifact(repository, head, path) {
|
function committedTree(repository, head) {
|
||||||
|
const raw = runGit(repository, ['ls-tree', '-r', '-z', head], 'PNCC_GIT_TREE_READ', { encoding: 'buffer' })
|
||||||
|
if (!Buffer.isBuffer(raw) || raw.length === 0) fail('PNCC_COMMITTED_TREE_EMPTY')
|
||||||
|
const entries = new Map()
|
||||||
|
for (const record of raw.toString('utf8').split('\0')) {
|
||||||
|
if (!record) continue
|
||||||
|
const tab = record.indexOf('\t')
|
||||||
|
if (tab < 1) fail('PNCC_COMMITTED_TREE_ENTRY_INVALID')
|
||||||
|
const header = record.slice(0, tab).split(' ')
|
||||||
|
const path = record.slice(tab + 1)
|
||||||
|
if (header.length !== 3 || entries.has(path)) fail('PNCC_COMMITTED_TREE_ENTRY_INVALID')
|
||||||
|
entries.set(path, { mode: header[0], type: header[1], object: validateFullHead(header[2]) })
|
||||||
|
if (entries.size > MAX_TREE_ENTRIES) fail('PNCC_COMMITTED_TREE_ENTRY_LIMIT_EXCEEDED')
|
||||||
|
}
|
||||||
|
return entries
|
||||||
|
}
|
||||||
|
|
||||||
|
function committedArtifact(repository, path, tree) {
|
||||||
validateRelativePath(path)
|
validateRelativePath(path)
|
||||||
const treeEntry = String(runGit(repository, ['ls-tree', head, '--', path], 'PNCC_GIT_TREE_ENTRY_READ')).trim()
|
const entry = tree.get(path)
|
||||||
const match = treeEntry.match(/^([0-9]{6}) blob ([0-9a-f]{40})\t(.+)$/u)
|
if (!entry || entry.type !== 'blob') fail('PNCC_COMMITTED_PATH_NOT_EXACT_BLOB', path)
|
||||||
if (!match || match[3] !== path) fail('PNCC_COMMITTED_PATH_NOT_EXACT_BLOB', path)
|
const { mode, object } = entry
|
||||||
const mode = match[1]
|
|
||||||
const object = validateFullHead(match[2].toLowerCase())
|
|
||||||
const type = String(runGit(repository, ['cat-file', '-t', object], 'PNCC_GIT_OBJECT_TYPE_READ')).trim()
|
const type = String(runGit(repository, ['cat-file', '-t', object], 'PNCC_GIT_OBJECT_TYPE_READ')).trim()
|
||||||
if (type !== 'blob') fail('PNCC_COMMITTED_OBJECT_NOT_BLOB', path)
|
if (type !== 'blob') fail('PNCC_COMMITTED_OBJECT_NOT_BLOB', path)
|
||||||
if (!['100644', '100755'].includes(mode)) fail('PNCC_COMMITTED_OBJECT_NOT_REGULAR_FILE', path)
|
if (!['100644', '100755'].includes(mode)) fail('PNCC_COMMITTED_OBJECT_NOT_REGULAR_FILE', path)
|
||||||
|
|
@ -91,8 +107,8 @@ function committedArtifact(repository, head, path) {
|
||||||
return { relativePath: path, gitObjectId: object, sha256: sha256(bytes), byteLength: bytes.length, bytes }
|
return { relativePath: path, gitObjectId: object, sha256: sha256(bytes), byteLength: bytes.length, bytes }
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseManifest(repository, head) {
|
function parseManifest(repository, tree) {
|
||||||
const evidence = committedArtifact(repository, head, MANIFEST_PATH)
|
const evidence = committedArtifact(repository, MANIFEST_PATH, tree)
|
||||||
let manifest
|
let manifest
|
||||||
try {
|
try {
|
||||||
manifest = JSON.parse(evidence.bytes.toString('utf8'))
|
manifest = JSON.parse(evidence.bytes.toString('utf8'))
|
||||||
|
|
@ -123,7 +139,8 @@ export function inspectPersonaRepository(repositoryPath, nodeId) {
|
||||||
const head = validateFullHead(String(runGit(repository, ['rev-parse', 'HEAD'], 'PNCC_GIT_HEAD_READ')).trim().toLowerCase())
|
const head = validateFullHead(String(runGit(repository, ['rev-parse', 'HEAD'], 'PNCC_GIT_HEAD_READ')).trim().toLowerCase())
|
||||||
const dirty = String(runGit(repository, ['status', '--porcelain', '--untracked-files=all'], 'PNCC_GIT_STATUS_READ')).trim()
|
const dirty = String(runGit(repository, ['status', '--porcelain', '--untracked-files=all'], 'PNCC_GIT_STATUS_READ')).trim()
|
||||||
if (dirty) fail('PNCC_REPOSITORY_DIRTY')
|
if (dirty) fail('PNCC_REPOSITORY_DIRTY')
|
||||||
const { manifest, evidence: manifestEvidence } = parseManifest(repository, head)
|
const tree = committedTree(repository, head)
|
||||||
|
const { manifest, evidence: manifestEvidence } = parseManifest(repository, tree)
|
||||||
if (manifest.primaryNode !== nodeId) fail('PNCC_PRIMARY_NODE_MISMATCH')
|
if (manifest.primaryNode !== nodeId) fail('PNCC_PRIMARY_NODE_MISMATCH')
|
||||||
|
|
||||||
const paths = new Set([manifest.brainEntry, manifest.currentCheckpoint, manifest.cognitiveGravity.sourcePath])
|
const paths = new Set([manifest.brainEntry, manifest.currentCheckpoint, manifest.cognitiveGravity.sourcePath])
|
||||||
|
|
@ -135,7 +152,7 @@ export function inspectPersonaRepository(repositoryPath, nodeId) {
|
||||||
}
|
}
|
||||||
if (paths.size > MAX_ARTIFACTS) fail('PNCC_DECLARED_ARTIFACT_LIMIT_EXCEEDED')
|
if (paths.size > MAX_ARTIFACTS) fail('PNCC_DECLARED_ARTIFACT_LIMIT_EXCEEDED')
|
||||||
const artifacts = [...paths].sort().map((path) => {
|
const artifacts = [...paths].sort().map((path) => {
|
||||||
const artifact = committedArtifact(repository, head, path)
|
const artifact = committedArtifact(repository, path, tree)
|
||||||
return { relativePath: artifact.relativePath, gitObjectId: artifact.gitObjectId, sha256: artifact.sha256, byteLength: artifact.byteLength }
|
return { relativePath: artifact.relativePath, gitObjectId: artifact.gitObjectId, sha256: artifact.sha256, byteLength: artifact.byteLength }
|
||||||
})
|
})
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue