2727 */
2828
2929import { execFileSync } from "node:child_process" ;
30+ import { existsSync } from "node:fs" ;
3031import { mkdtemp , rm , mkdir , writeFile , copyFile } from "node:fs/promises" ;
3132import { join , dirname } from "node:path" ;
3233import { fileURLToPath } from "node:url" ;
@@ -74,7 +75,12 @@ const coreFeatures = [scaffolderPlugin];
7475type Status = "pass" | "fail-load" | "fail-start" | "fail-bundle" | "error" ;
7576type Report = {
7677 cliVersion : string ;
77- backend : { total : number ; loaded : number ; errors : PluginError [ ] } ;
78+ backend : {
79+ total : number ;
80+ loaded : number ;
81+ skipped : string [ ] ;
82+ errors : PluginError [ ] ;
83+ } ;
7884 backendStart : { ok : boolean ; skipped ?: boolean ; error ?: string } ;
7985 frontend : { total : number ; valid : number ; errors : PluginError [ ] } ;
8086 status : Status ;
@@ -169,15 +175,25 @@ async function main(): Promise<number> {
169175 console . error ( "Provide --dynamic-plugins <dynamic-plugins.yaml>." ) ;
170176 return 2 ;
171177 }
178+ if ( ! existsSync ( dynamicPlugins ) ) {
179+ console . error ( `dynamic-plugins file not found: ${ dynamicPlugins } ` ) ;
180+ return 2 ;
181+ }
172182
173- const cliVersion = run ( process . execPath , [ CLI_BIN , "--version" ] ) ;
174- console . log ( `▶ install CLI: ${ CLI } @${ cliVersion } ` ) ;
175-
176- const tempDir = await mkdtemp ( join ( tmpdir ( ) , "native-smoke-" ) ) ;
177- const root = join ( tempDir , "dynamic-plugins-root" ) ;
178- await mkdir ( root , { recursive : true } ) ;
183+ // Declared outside the try so the catch/finally can see them even if setup fails.
184+ let cliVersion = "unknown" ;
185+ let tempDir : string | undefined ;
179186
180187 try {
188+ // Everything fallible lives in the try, so any failure still writes a results.json
189+ // (status: error) instead of exiting with no report.
190+ cliVersion = run ( process . execPath , [ CLI_BIN , "--version" ] ) ;
191+ console . log ( `▶ install CLI: ${ CLI } @${ cliVersion } ` ) ;
192+
193+ tempDir = await mkdtemp ( join ( tmpdir ( ) , "native-smoke-" ) ) ;
194+ const root = join ( tempDir , "dynamic-plugins-root" ) ;
195+ await mkdir ( root , { recursive : true } ) ;
196+
181197 await extractPlugins ( root , dynamicPlugins ) ;
182198
183199 const manifest = discoverPlugins ( root ) ;
@@ -187,18 +203,38 @@ async function main(): Promise<number> {
187203
188204 // Let extracted plugins (under a temp dir) resolve their @backstage/* peers here.
189205 patchModuleResolution ( HARNESS_NODE_MODULES ) ;
206+
207+ const skipped = manifest . backend
208+ . filter ( ( p ) => KNOWN_FAILURES . has ( p . dirName ) )
209+ . map ( ( p ) => p . dirName ) ;
210+ if ( skipped . length > 0 ) {
211+ console . warn (
212+ `⚠ skipped ${ skipped . length } known-failure backend plugin(s): ${ skipped . join ( ", " ) } ` ,
213+ ) ;
214+ }
215+
190216 const backendPlugins = manifest . backend . filter (
191217 ( p ) => ! KNOWN_FAILURES . has ( p . dirName ) ,
192218 ) ;
193219 const { loaded, errors : loadErrors } = loadBackendPlugins ( backendPlugins ) ;
194220 const start = await startBackend ( loaded ) ;
195221 const frontend = validateFrontends ( manifest . frontend ) ;
196222
223+ // A workspace whose only backend plugin is a known failure would otherwise pass
224+ // silently having validated nothing — make that visible.
225+ if ( loaded . length === 0 && manifest . frontend . length === 0 ) {
226+ console . warn (
227+ `⚠ nothing validated: 0 plugins loaded ` +
228+ `(${ manifest . backend . length } backend found, ${ skipped . length } skipped)` ,
229+ ) ;
230+ }
231+
197232 const report : Report = {
198233 cliVersion,
199234 backend : {
200235 total : manifest . backend . length ,
201236 loaded : loaded . length ,
237+ skipped,
202238 errors : loadErrors ,
203239 } ,
204240 backendStart : start ,
@@ -216,8 +252,9 @@ async function main(): Promise<number> {
216252 : String ( start . ok ) ;
217253 console . log ( `▶ report → ${ out } (status: ${ report . status } )` ) ;
218254 console . log (
219- ` backend loaded ${ report . backend . loaded } /${ report . backend . total } , ` +
220- `start=${ startLabel } , frontend ${ frontend . valid } /${ manifest . frontend . length } ` ,
255+ ` backend loaded ${ report . backend . loaded } /${ report . backend . total } ` +
256+ ( skipped . length ? ` (${ skipped . length } skipped)` : "" ) +
257+ `, start=${ startLabel } , frontend ${ frontend . valid } /${ manifest . frontend . length } ` ,
221258 ) ;
222259 return report . status === "pass" ? 0 : 1 ;
223260 } catch ( err ) {
@@ -226,7 +263,7 @@ async function main(): Promise<number> {
226263 const message = err instanceof Error ? err . message : String ( err ) ;
227264 const report : Report = {
228265 cliVersion,
229- backend : { total : 0 , loaded : 0 , errors : [ ] } ,
266+ backend : { total : 0 , loaded : 0 , skipped : [ ] , errors : [ ] } ,
230267 backendStart : { ok : false , error : message } ,
231268 frontend : { total : 0 , valid : 0 , errors : [ ] } ,
232269 status : "error" ,
@@ -235,7 +272,7 @@ async function main(): Promise<number> {
235272 console . error ( `▶ report → ${ out } (status: error)\n${ message } ` ) ;
236273 return 1 ;
237274 } finally {
238- await rm ( tempDir , { recursive : true , force : true } ) ;
275+ if ( tempDir ) await rm ( tempDir , { recursive : true , force : true } ) ;
239276 }
240277}
241278
0 commit comments