From 3534f9e3fc8c6f3149a711aedfdd3900a72ee397 Mon Sep 17 00:00:00 2001 From: Dotta Date: Fri, 11 Sep 2026 16:27:30 -0500 Subject: [PATCH] fix: clean Pi qualification home after shutdown failure Co-Authored-By: Paperclip --- .../scripts/verify-pi-provider-launch.mjs | 9 +++++++-- .../scripts/verify-pi-provider-launch.test.mjs | 10 ++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/packages/paperclip-runner/scripts/verify-pi-provider-launch.mjs b/packages/paperclip-runner/scripts/verify-pi-provider-launch.mjs index 8311b73163..a7668741b5 100644 --- a/packages/paperclip-runner/scripts/verify-pi-provider-launch.mjs +++ b/packages/paperclip-runner/scripts/verify-pi-provider-launch.mjs @@ -67,7 +67,12 @@ try { } finally { clearTimeout(forceTimer); clearTimeout(deadlineTimer); - await lease.close(); + try { + await lease.close(); + } finally { + // A failed verifier aborts this disposable image-build step. Remove its + // temporary HOME even when shutdown or lease cleanup reports a failure. + await rm(root, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 }); + } } - await rm(root, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 }); } diff --git a/packages/paperclip-runner/scripts/verify-pi-provider-launch.test.mjs b/packages/paperclip-runner/scripts/verify-pi-provider-launch.test.mjs index 3dc6a989f7..34280ff89f 100644 --- a/packages/paperclip-runner/scripts/verify-pi-provider-launch.test.mjs +++ b/packages/paperclip-runner/scripts/verify-pi-provider-launch.test.mjs @@ -7,7 +7,7 @@ import {join,basename,dirname} from 'node:path'; import {fileURLToPath} from 'node:url'; const verifier=fileURLToPath(new URL('./verify-pi-provider-launch.mjs',import.meta.url)); -for(const ignoreTerm of [false,true]) test('Pi qualification waits for process closure before removing HOME (ignore SIGTERM='+ignoreTerm+')',async(t)=>{ +for(const {ignoreTerm, suppressClose} of [{ignoreTerm:false,suppressClose:false},{ignoreTerm:true,suppressClose:false},{ignoreTerm:false,suppressClose:true}]) test('Pi qualification cleans HOME after bounded shutdown (ignore SIGTERM='+ignoreTerm+', suppress close='+suppressClose+')',async(t)=>{ if(process.platform==='win32'){t.skip('The image verifier uses POSIX process groups');return;} const root=await mkdtemp(join(tmpdir(),'pi-qualification-cleanup-test-')); try{ @@ -24,6 +24,10 @@ export async function verifyQualifiedAcpxInstallation(){return {openCommand:asyn return {spawn:(_args,options)=>{ child=spawn(${JSON.stringify(process.execPath)},[${JSON.stringify(join(root,'provider.mjs'))}],{...options,stdio:['pipe','pipe','pipe']}); child.once('close',()=>{closed=true;}); + if (${suppressClose}) { + const once=child.once.bind(child); + child.once=(event,listener)=>event==='close'?child:once(event,listener); + } return child; },close:async()=>{assert(closed,'lease was closed before the provider completed its shutdown writes');await writeFile(${JSON.stringify(join(root,'lease-closed'))},String(child.signalCode??child.exitCode));}}; }};} @@ -38,7 +42,9 @@ process.on('SIGTERM',()=>{if(${ignoreTerm})return;setTimeout(()=>{fs.mkdirSync(p let stdout='',stderr='';child.stdout.on('data',b=>stdout+=b);child.stderr.on('data',b=>stderr+=b); const timer=setTimeout(()=>child.kill('SIGKILL'),15000); let code;try{code=await new Promise((resolve,reject)=>{child.once('error',reject);child.once('close',resolve);});}finally{clearTimeout(timer);} - assert.equal(code,0,stderr);assert.match(stdout,/Verified Pi ACP/); + if(suppressClose){assert.notEqual(code,0);assert.match(stderr,/Pi qualification process did not close after SIGKILL/);} + else assert.equal(code,0,stderr); + assert.match(stdout,/Verified Pi ACP/); if(!ignoreTerm)assert.equal(await readFile(join(root,'shutdown-finished'),'utf8'),'finished'); assert.equal(await readFile(join(root,'lease-closed'),'utf8'),ignoreTerm?'SIGKILL':'0'); const home=await readFile(join(root,'home-path'),'utf8');await assert.rejects(readFile(join(home,'.pi','last-write')), {code:'ENOENT'});