| 123456789101112131415161718192021222324252627282930313233343536 |
- import fs from 'node:fs';
- import path from 'node:path';
- import crypto from 'node:crypto';
- import {ROOT,request,classRows} from './platform-client.mjs';
- const names=['ksfGetPublicBoard','ksfStartWorkshopSession','ksfSaveWorkshopTask','ksfCreateProjectFromOpportunity','ksfTransitionProject','ksfSaveWorkshopConcept','ksfGetWorkshopProject','ksfGetIngestContract'];
- const manifest=JSON.parse(fs.readFileSync(path.join(ROOT,'dist/manifest.json')));
- const dry=!process.argv.includes('--apply');fs.mkdirSync(path.join(ROOT,'evidence'),{recursive:true});
- const existing=await classRows('Function');const schemas=await request('GET','/schemas');
- const journal={at:new Date().toISOString(),dryRun:dry,functions:[],schemaChanges:0,permissionChanges:0,credentialsChanged:false};
- // Do not overwrite unrelated changes: existing entrypoints must still be the audited v0.1 source.
- for(const name of names){
- const item=manifest.functions.find(x=>x.name===name);if(!item)throw Error('构建缺少目标函数');
- const code=fs.readFileSync(path.join(ROOT,item.file),'utf8');if(crypto.createHash('sha256').update(code).digest('hex')!==item.sha256)throw Error('构建指纹不匹配');
- const matches=existing.filter(x=>x.name===name||x.path===item.path);if(matches.length>1||matches.some(x=>x.path!==item.path||x.name!==name))throw Error('函数名称/路径冲突');
- const row=matches[0];
- if(row&&row.code!==code&&!row.code.includes("release: 'ksf.cloud.v0.1'")&&!row.code.includes('ksf.cloud.v0.1'))throw Error('发现未知函数版本,停止覆盖');
- journal.functions.push({name,path:item.path,sha256:item.sha256,objectId:row?.objectId||null,action:row?(row.code===code?'unchanged':'update'):'create'});
- }
- fs.writeFileSync(path.join(ROOT,'evidence/workshop-deploy-plan.json'),JSON.stringify(journal,null,2));
- if(!dry){
- const backupPath=path.join(ROOT,'evidence/workshop-functions-before-'+Date.now()+'.json');
- fs.writeFileSync(backupPath,JSON.stringify(existing.filter(x=>names.includes(x.name)),null,2));
- for(const item of journal.functions){
- if(item.action==='unchanged')continue;
- const entry=manifest.functions.find(x=>x.name===item.name),code=fs.readFileSync(path.join(ROOT,entry.file),'utf8');
- if(item.objectId)await request('PUT','/classes/Function/'+item.objectId,{code});
- else item.objectId=(await request('POST','/classes/Function',{name:item.name,path:item.path,code,ACL:{}})).objectId;
- const read=await request('GET','/classes/Function/'+item.objectId);if(read.code!==code)throw Error('函数回读不一致');
- item.verified=true;fs.writeFileSync(path.join(ROOT,'evidence/workshop-deployment.json'),JSON.stringify(journal,null,2));
- }
- const after=await request('GET','/schemas');
- for(const before of schemas.results){const now=after.results.find(x=>x.className===before.className);if(JSON.stringify(before.fields)!==JSON.stringify(now.fields)||JSON.stringify(before.classLevelPermissions)!==JSON.stringify(now.classLevelPermissions))throw Error('表结构/权限意外变化');}
- journal.finishedAt=new Date().toISOString();journal.schemaAndPermissionsPreserved=true;
- fs.writeFileSync(path.join(ROOT,'evidence/workshop-deployment.json'),JSON.stringify(journal,null,2));
- }
- console.log(JSON.stringify(journal,null,2));
|