mirror of
https://github.com/colbymchenry/codegraph.git
synced 2026-10-26 04:19:33 +08:00
feat: pin reviewed Drupal 0.1.1 while retaining immutable 0.1.0
This commit is contained in:
@@ -11,6 +11,7 @@ const lab=fs.mkdtempSync(path.join(os.tmpdir(),'cg-official-browser-')),corpus=p
|
||||
const project=path.join(lab,'pathauto'),other=path.join(lab,'untouched'),state=path.join(lab,'registry'),artifact=path.join(root,'dist/extensions/drupal.cgext'),bytes=fs.readFileSync(artifact),checks=[],commands=[];
|
||||
fs.mkdirSync(project);fs.mkdirSync(other);fs.writeFileSync(path.join(other,'example.php'),'<?php function untouched() {}');
|
||||
cp.execFileSync('tar',['-x','-C',project],{input:cp.execFileSync('git',['-C',corpus,'archive',pin],{maxBuffer:64*1024*1024})});
|
||||
const extensionVersion=JSON.parse(bytes).package.version;
|
||||
const hash=b=>crypto.createHash('sha256').update(b).digest('hex');
|
||||
function snapshot(){const db=new DatabaseSync(path.join(project,'.codegraph/codegraph.db'),{readOnly:true});try{const nodes=db.prepare('SELECT * FROM nodes ORDER BY id').all().map(({updated_at,...n})=>n),edges=db.prepare('SELECT source,target,kind,metadata,line,col,provenance FROM edges ORDER BY source,target,kind,line,col,metadata').all();return {nodes,edges,pluginEdges:edges.filter(e=>JSON.parse(e.metadata||'{}').synthesizedBy==='drupal'),hash:hash(JSON.stringify({nodes,edges})),config:fs.existsSync(path.join(project,'codegraph.json'))?fs.readFileSync(path.join(project,'codegraph.json'),'utf8'):null};}finally{db.close();}}
|
||||
function verify(data){const definitions=[['/admin/config/search/path/settings','pathauto.routing.yml','buildForm','src/Form/PathautoSettingsForm.php','Drupal route handler'],['/admin/config/search/path/update_bulk','pathauto.routing.yml','buildForm','src/Form/PathautoBulkUpdateForm.php','Drupal route handler'],['getPunctuationCharacters','src/AliasCleaner.php','pathautoPunctuationCharsAlter','tests/modules/pathauto_custom_punctuation_test/src/Hook/PathautoCustomPunctuationTestHooks.php','Drupal hook pathauto_punctuation_chars_alter'],['PathautoGenerator','src/PathautoGenerator.php','AliasCleaner','src/AliasCleaner.php','Drupal service injection']];for(const [from,file,to,target,label]of definitions){const sources=new Set(data.nodes.filter(n=>n.name===from&&n.file_path===file).map(n=>n.id)),targets=new Set(data.nodes.filter(n=>n.name===to&&n.file_path===target).map(n=>n.id));assert.ok(data.pluginEdges.some(e=>sources.has(e.source)&&targets.has(e.target)&&JSON.parse(e.metadata).label===label),'Missing '+label+': '+from);}return definitions;}
|
||||
@@ -24,9 +25,9 @@ let server,bridge,browser,page;
|
||||
const g=await CodeGraph.init(project);try{const r=await g.indexAll();assert.equal(r.filesErrored,0);}finally{g.close();}assert.equal(snapshot().pluginEdges.length,0);
|
||||
bridge=await startExtensionBridge([other,project],origin);browser=await chromium.launch({executablePath:process.env.CHROME_PATH||'/opt/google/chrome/chrome',headless:true,args:['--no-sandbox']});page=await browser.newPage({viewport:{width:1440,height:1000}});page.setDefaultTimeout(60000);let popups=0;page.on('popup',()=>popups++);const pageErrors=[];page.on('pageerror',e=>pageErrors.push(e.message));
|
||||
await page.goto(bridge.connectionUrl);await page.getByRole('button',{name:'Official',exact:true}).click();await page.locator('.extension-card .official').waitFor();assert.equal(await page.locator('.extension-card').count(),1);assert.match(await page.locator('.extension-card .byline').innerText(),/CodeGraph.*OFFICIAL/s);pass('official catalog filter displays trusted CodeGraph Drupal identity');
|
||||
const popupReady=page.waitForEvent('popup');await page.locator('#connect').click();const popup=await popupReady;await popup.getByRole('button',{name:'Allow connection'}).click();await page.getByText('CodeGraph connected',{exact:true}).waitFor();await page.locator('#project').selectOption('1');await page.getByRole('link',{name:'Drupal',exact:true}).click();await page.locator('.compatibility').filter({hasText:'Selected 0.1.0'}).waitFor();assert.equal(await page.locator('.metadata dd.integrity').first().innerText(),'codegraph');await page.getByRole('button',{name:'Install extension'}).click();await page.waitForFunction(()=>state.snapshot?.jobId===1&&!state.snapshot.busy);assert.equal(await page.evaluate(()=>state.snapshot.error),undefined);assert.equal(popups,1);const installed=snapshot(),flows=verify(installed);assert.ok(!CodeGraph.isInitialized(other));pass('one connection and one Install refresh only selected pinned Drupal project; four real route hook and DI edges verified');
|
||||
const popupReady=page.waitForEvent('popup');await page.locator('#connect').click();const popup=await popupReady;await popup.getByRole('button',{name:'Allow connection'}).click();await page.getByText('CodeGraph connected',{exact:true}).waitFor();await page.locator('#project').selectOption('1');await page.getByRole('link',{name:'Drupal',exact:true}).click();await page.locator('.compatibility').filter({hasText:'Selected '+extensionVersion}).waitFor();assert.equal(await page.locator('.metadata dd.integrity').first().innerText(),'codegraph');await page.getByRole('button',{name:'Install extension'}).click();await page.waitForFunction(()=>state.snapshot?.jobId===1&&!state.snapshot.busy);assert.equal(await page.evaluate(()=>state.snapshot.error),undefined);assert.equal(popups,1);const installed=snapshot(),flows=verify(installed);assert.ok(!CodeGraph.isInitialized(other));pass('one connection and one Install refresh only selected pinned Drupal project; four real route hook and DI edges verified');
|
||||
fs.writeFileSync(path.join(out,'graph-proof.json'),JSON.stringify({corpus:'pathauto',revision:pin,artifactSHA:hash(bytes),nodes:installed.nodes.length,edges:installed.edges.length,pluginEdges:installed.pluginEdges.length,hash:installed.hash,expected:flows},null,2));await page.screenshot({path:path.join(out,'desktop-installed.png'),fullPage:true});
|
||||
const manager=new ExtensionManager(project),key='packages/sha256/'+hash(bytes);await server.bucket.put(key,Buffer.from('tampered'));await assert.rejects(()=>manager.installFromRegistry({registry:origin,id:'drupal',version:'0.1.0',replaces:['drupal']}));assert.equal(snapshot().hash,installed.hash);assert.equal(snapshot().config,installed.config);await server.bucket.put(key,bytes);pass('tampered registry artifact fails installation without changing existing graph or config');
|
||||
const manager=new ExtensionManager(project),key='packages/sha256/'+hash(bytes);await server.bucket.put(key,Buffer.from('tampered'));await assert.rejects(()=>manager.installFromRegistry({registry:origin,id:'drupal',version:extensionVersion,replaces:['drupal']}));assert.equal(snapshot().hash,installed.hash);assert.equal(snapshot().config,installed.config);await server.bucket.put(key,bytes);pass('tampered registry artifact fails installation without changing existing graph or config');
|
||||
const incompatible=JSON.parse(bytes);incompatible.package.codegraph.engines='>=9';await assert.rejects(()=>manager.install({bytes:Buffer.from(JSON.stringify(incompatible)),replaces:['drupal']}),/requires/);await assert.rejects(()=>manager.install({bytes:Buffer.from('{}'),replaces:['drupal']}));assert.equal(snapshot().hash,installed.hash);assert.equal(snapshot().config,installed.config);pass('incompatible and malformed installs preserve previously working Drupal graph');
|
||||
await page.getByRole('button',{name:'Disable',exact:true}).click();await page.getByRole('button',{name:'Enable',exact:true}).waitFor();assert.equal(snapshot().pluginEdges.length,0);pass('browser disable removes actual extension contributions');
|
||||
await page.getByRole('button',{name:'Enable',exact:true}).click();await page.getByRole('button',{name:'Disable',exact:true}).waitFor();verify(snapshot());await page.setViewportSize({width:390,height:844});await page.screenshot({path:path.join(out,'mobile-official.png'),fullPage:true});pass('browser enable restores expected graph and mobile official details');
|
||||
|
||||
@@ -5,6 +5,8 @@ const {CodeGraph}=require('../../dist');
|
||||
const {ExtensionManager}=require('../../dist/plugins/manager');
|
||||
Object.assign(process.env,{CODEGRAPH_TELEMETRY:'0',CODEGRAPH_PARSE_WORKERS:'2',CODEGRAPH_RESOLVE_WORKERS:'0',CODEGRAPH_SYNTH_TIMINGS:'all'});
|
||||
const [project,mode,operation,output,artifact='dist/extensions/drupal.cgext']=process.argv.slice(2);
|
||||
const sourceRevision=cp.execFileSync('git',['rev-parse','HEAD'],{encoding:'utf8'}).trim();
|
||||
const sourceDirty=!!cp.execFileSync('git',['status','--porcelain'],{encoding:'utf8'}).trim();
|
||||
const hash=b=>crypto.createHash('sha256').update(b).digest('hex');
|
||||
function graphSnapshot(){const db=new DatabaseSync(path.join(project,'.codegraph/codegraph.db'),{readOnly:true});try{
|
||||
const nodes=db.prepare('SELECT * FROM nodes ORDER BY id').all().map(({updated_at,...n})=>n);
|
||||
@@ -18,6 +20,6 @@ function graphSnapshot(){const db=new DatabaseSync(path.join(project,'.codegraph
|
||||
else {const graph=CodeGraph.isInitialized(project)?await CodeGraph.open(project):await CodeGraph.init(project);try{result=operation==='rebuild'?await graph.refreshPluginIndex():operation==='sync'?await graph.sync():await graph.indexAll();}finally{graph.close();}}
|
||||
assert.ok(!result?.filesErrored && result?.success!==false,JSON.stringify(result));
|
||||
const measured={wallMs:performance.now()-started,cpu:process.cpuUsage(cpu),resources:process.resourceUsage()};
|
||||
const record={revision:cp.execFileSync('git',['rev-parse','HEAD'],{encoding:'utf8'}).trim(),dirty:!!cp.execFileSync('git',['status','--porcelain'],{encoding:'utf8'}).trim(),project,mode,operation,artifactSHA:mode==='external'?hash(fs.readFileSync(artifact)):null,node:process.version,platform:process.platform,...measured,graph:graphSnapshot()};
|
||||
const record={revision:sourceRevision,dirty:sourceDirty,project,mode,operation,artifactSHA:mode==='external'?hash(fs.readFileSync(artifact)):null,node:process.version,platform:process.platform,...measured,graph:graphSnapshot()};
|
||||
fs.mkdirSync(path.dirname(output),{recursive:true});fs.writeFileSync(output,JSON.stringify(record,null,2));console.log(JSON.stringify(record));
|
||||
})().catch(e=>{console.error(e);process.exitCode=1;});
|
||||
|
||||
Reference in New Issue
Block a user