@@ -305,14 +305,22 @@ test('Android APK repack signs the output and preserves its package id', (t) =>
305305 assert . match ( mismatch . stderr , / d i d n o t p r e s e r v e t h e s o u r c e s i g n i n g c e r t i f i c a t e / ) ;
306306} ) ;
307307
308- test ( 'producer maps each platform to its resolved lookup and matrix artifact name' , ( t ) => {
308+ test ( 'producer maps each platform to its resolved lookup and matrix artifact name, and gates has-work when both are cached ' , ( t ) => {
309309 const workflow = parse ( fs . readFileSync ( '.github/workflows/test-app-build-cache.yml' , 'utf8' ) ) ;
310310 const fingerprintStep = workflow . jobs . fingerprint . steps . find ( ( step ) => step . id === 'fingerprint' ) ;
311+ // #2034: an empty `include` matrix is legal JSON but GitHub Actions rejects it as
312+ // `strategy.matrix`, so `release` must be skipped -- not handed a zero-length
313+ // matrix -- whenever both fixtures are already cached.
314+ assert . equal (
315+ workflow . jobs . fingerprint . outputs [ 'has-work' ] ,
316+ '${{ steps.fingerprint.outputs.has-work }}' ,
317+ ) ;
318+ assert . equal ( workflow . jobs . release . if , "needs.fingerprint.outputs.has-work == 'true'" ) ;
319+
311320 const tempRoot = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , 'fixture-producer-name-' ) ) ;
312321 t . after ( ( ) => fs . rmSync ( tempRoot , { force : true , recursive : true } ) ) ;
313322 const actionDir = path . join ( tempRoot , '.github/actions/setup-fixture-app' ) ;
314323 const binDir = path . join ( tempRoot , 'bin' ) ;
315- const outputPath = path . join ( tempRoot , 'output' ) ;
316324 const resolverLog = path . join ( tempRoot , 'resolver-calls' ) ;
317325 const nodeLog = path . join ( tempRoot , 'node-calls' ) ;
318326 fs . mkdirSync ( actionDir , { recursive : true } ) ;
@@ -326,140 +334,64 @@ test('producer maps each platform to its resolved lookup and matrix artifact nam
326334 '' ,
327335 ] . join ( '\n' ) ,
328336 ) ;
329- fs . writeFileSync (
330- path . join ( binDir , 'node' ) ,
331- [ '#!/bin/sh' , 'printf "%s\\n" "$*" >> "$TEST_NODE_LOG"' , '' ] . join ( '\n' ) ,
332- ) ;
337+ const writeNodeStub = ( cached ) =>
338+ fs . writeFileSync (
339+ path . join ( binDir , 'node' ) ,
340+ [
341+ '#!/bin/sh' ,
342+ 'printf "%s\\n" "$*" >> "$TEST_NODE_LOG"' ,
343+ cached ? 'printf "111"' : 'true' ,
344+ '' ,
345+ ] . join ( '\n' ) ,
346+ ) ;
347+ writeNodeStub ( false ) ;
333348 fs . chmodSync ( path . join ( binDir , 'node' ) , 0o755 ) ;
349+
334350 const run = fingerprintStep . run
335351 . replaceAll ( '${{ github.event.pull_request.head.sha || github.sha }}' , 'current-head' )
336352 . replaceAll ( '${{ github.repository }}' , 'octo/repo' )
337353 . replaceAll ( '${{ github.event_name }}' , 'pull_request' )
338354 . replaceAll ( '${{ github.event.pull_request.head.repo.full_name }}' , 'octo/repo' ) ;
339- const result = spawnSync ( 'bash' , [ '-c' , run ] , {
340- cwd : tempRoot ,
341- encoding : 'utf8' ,
342- env : {
343- ...process . env ,
344- GITHUB_OUTPUT : outputPath ,
345- PATH : `${ binDir } :${ process . env . PATH } ` ,
346- TEST_NODE_LOG : nodeLog ,
347- TEST_RESOLVER_LOG : resolverLog ,
348- } ,
349- } ) ;
350- assert . equal ( result . status , 0 , result . stderr ) ;
351- const outputLines = fs . readFileSync ( outputPath , 'utf8' ) . trim ( ) . split ( '\n' ) ;
352- const matrixLine = outputLines . find ( ( line ) => line . startsWith ( 'matrix=' ) ) ;
353- const matrix = JSON . parse ( matrixLine . slice ( 'matrix=' . length ) ) ;
354- assert . deepEqual (
355- matrix . include . map ( ( { platform, artifactName } ) => ( { platform, artifactName } ) ) ,
356- [
357- { platform : 'ios' , artifactName : 'fingerprint.ios-hash.ios' } ,
358- { platform : 'android' , artifactName : 'fingerprint.android-hash.android' } ,
359- ] ,
360- ) ;
361- assert . deepEqual (
362- outputLines . find ( ( line ) => line . startsWith ( 'has-work=' ) ) ,
363- 'has-work=true' ,
364- ) ;
365- assert . deepEqual ( fs . readFileSync ( resolverLog , 'utf8' ) . trim ( ) . split ( '\n' ) , [ 'ios' , 'android' ] ) ;
366- assert . deepEqual ( fs . readFileSync ( nodeLog , 'utf8' ) . trim ( ) . split ( '\n' ) , [
367- '.github/actions/setup-fixture-app/trusted-artifact.mjs find octo/repo fingerprint.ios-hash.ios current-head' ,
368- '.github/actions/setup-fixture-app/trusted-artifact.mjs find octo/repo fingerprint.android-hash.android current-head' ,
369- ] ) ;
370- } ) ;
371-
372- test ( 'fingerprint matrix covers all four cache combinations and gates has-work on both-cached' , ( t ) => {
373- const workflow = parse ( fs . readFileSync ( '.github/workflows/test-app-build-cache.yml' , 'utf8' ) ) ;
374- const fingerprintStep = workflow . jobs . fingerprint . steps . find ( ( step ) => step . id === 'fingerprint' ) ;
375- // #2034: an empty `include` matrix is legal JSON but GitHub Actions rejects
376- // it as `strategy.matrix`, so the `release` job must be skipped -- not
377- // handed a zero-length matrix -- whenever both fixtures are cached.
378- assert . equal (
379- workflow . jobs . fingerprint . outputs [ 'has-work' ] ,
380- '${{ steps.fingerprint.outputs.has-work }}' ,
381- ) ;
382- assert . equal ( workflow . jobs . release . if , "needs.fingerprint.outputs.has-work == 'true'" ) ;
383-
384- const run = fingerprintStep . run
385- . replaceAll ( '${{ github.event.pull_request.head.sha || github.sha }}' , 'current-head' )
386- . replaceAll ( '${{ github.repository }}' , 'octo/repo' )
387- . replaceAll ( '${{ github.event_name }}' , 'push' )
388- . replaceAll ( '${{ github.event.pull_request.head.repo.full_name }}' , '' ) ;
389-
390- const tempRoot = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , 'fixture-matrix-combinations-' ) ) ;
391- t . after ( ( ) => fs . rmSync ( tempRoot , { force : true , recursive : true } ) ) ;
392- const actionDir = path . join ( tempRoot , '.github/actions/setup-fixture-app' ) ;
393- const binDir = path . join ( tempRoot , 'bin' ) ;
394- fs . mkdirSync ( actionDir , { recursive : true } ) ;
395- fs . mkdirSync ( binDir ) ;
396- fs . writeFileSync (
397- path . join ( actionDir , 'resolve-artifact-name.sh' ) ,
398- [ '#!/bin/sh' , 'printf "fingerprint.%s-hash.%s\\n" "$1" "$1"' , '' ] . join ( '\n' ) ,
399- ) ;
400- fs . chmodSync ( path . join ( actionDir , 'resolve-artifact-name.sh' ) , 0o755 ) ;
401- fs . writeFileSync (
402- path . join ( binDir , 'node' ) ,
403- [
404- '#!/bin/sh' ,
405- // Invoked as: node trusted-artifact.mjs find <repo> <name> <sha>
406- 'name="$4"' ,
407- 'case "$name" in' ,
408- ' *.ios) [ "$TEST_IOS_CACHED" = 1 ] && printf "111"; true ;;' ,
409- ' *.android) [ "$TEST_ANDROID_CACHED" = 1 ] && printf "222"; true ;;' ,
410- 'esac' ,
411- '' ,
412- ] . join ( '\n' ) ,
413- ) ;
414- fs . chmodSync ( path . join ( binDir , 'node' ) , 0o755 ) ;
415-
416- const runFingerprint = ( iosCached , androidCached ) => {
417- const outputPath = path . join ( tempRoot , `output-${ iosCached } -${ androidCached } ` ) ;
355+ const runFingerprint = ( outputName ) => {
356+ const outputPath = path . join ( tempRoot , outputName ) ;
418357 const result = spawnSync ( 'bash' , [ '-c' , run ] , {
419358 cwd : tempRoot ,
420359 encoding : 'utf8' ,
421360 env : {
422361 ...process . env ,
423362 GITHUB_OUTPUT : outputPath ,
424363 PATH : `${ binDir } :${ process . env . PATH } ` ,
425- TEST_ANDROID_CACHED : androidCached ? '1' : '0' ,
426- TEST_IOS_CACHED : iosCached ? '1' : '0' ,
364+ TEST_NODE_LOG : nodeLog ,
365+ TEST_RESOLVER_LOG : resolverLog ,
427366 } ,
428367 } ) ;
429368 assert . equal ( result . status , 0 , result . stderr ) ;
430369 const lines = fs . readFileSync ( outputPath , 'utf8' ) . trim ( ) . split ( '\n' ) ;
431- const matrixLine = lines . find ( ( line ) => line . startsWith ( 'matrix=' ) ) ;
432- const hasWorkLine = lines . find ( ( line ) => line . startsWith ( 'has-work=' ) ) ;
433370 return {
434- hasWork : hasWorkLine . slice ( 'has-work=' . length ) ,
435- matrix : JSON . parse ( matrixLine . slice ( 'matrix=' . length ) ) ,
371+ hasWork : lines . find ( ( line ) => line . startsWith ( 'has-work=' ) ) ,
372+ matrix : JSON . parse ( lines . find ( ( line ) => line . startsWith ( 'matrix=' ) ) . slice ( 'matrix=' . length ) ) ,
436373 } ;
437374 } ;
438375
439- const bothCached = runFingerprint ( true , true ) ;
440- assert . deepEqual ( bothCached . matrix , { include : [ ] } ) ;
441- assert . equal ( bothCached . hasWork , 'false' ) ;
442-
443- const iosOnlyCached = runFingerprint ( true , false ) ;
376+ const neitherCached = runFingerprint ( 'output-neither-cached' ) ;
444377 assert . deepEqual (
445- iosOnlyCached . matrix . include . map ( ( entry ) => entry . platform ) ,
446- [ 'android' ] ,
447- ) ;
448- assert . equal ( iosOnlyCached . hasWork , 'true' ) ;
449-
450- const androidOnlyCached = runFingerprint ( false , true ) ;
451- assert . deepEqual (
452- androidOnlyCached . matrix . include . map ( ( entry ) => entry . platform ) ,
453- [ 'ios' ] ,
378+ neitherCached . matrix . include . map ( ( { platform, artifactName } ) => ( { platform, artifactName } ) ) ,
379+ [
380+ { platform : 'ios' , artifactName : 'fingerprint.ios-hash.ios' } ,
381+ { platform : 'android' , artifactName : 'fingerprint.android-hash.android' } ,
382+ ] ,
454383 ) ;
455- assert . equal ( androidOnlyCached . hasWork , 'true' ) ;
384+ assert . equal ( neitherCached . hasWork , 'has-work=true' ) ;
385+ assert . deepEqual ( fs . readFileSync ( resolverLog , 'utf8' ) . trim ( ) . split ( '\n' ) , [ 'ios' , 'android' ] ) ;
386+ assert . deepEqual ( fs . readFileSync ( nodeLog , 'utf8' ) . trim ( ) . split ( '\n' ) , [
387+ '.github/actions/setup-fixture-app/trusted-artifact.mjs find octo/repo fingerprint.ios-hash.ios current-head' ,
388+ '.github/actions/setup-fixture-app/trusted-artifact.mjs find octo/repo fingerprint.android-hash.android current-head' ,
389+ ] ) ;
456390
457- const neitherCached = runFingerprint ( false , false ) ;
458- assert . deepEqual (
459- neitherCached . matrix . include . map ( ( entry ) => entry . platform ) ,
460- [ 'ios' , 'android' ] ,
461- ) ;
462- assert . equal ( neitherCached . hasWork , 'true' ) ;
391+ writeNodeStub ( true ) ;
392+ const bothCached = runFingerprint ( 'output-both-cached' ) ;
393+ assert . deepEqual ( bothCached . matrix , { include : [ ] } ) ;
394+ assert . equal ( bothCached . hasWork , 'has-work=false' ) ;
463395} ) ;
464396
465397test ( 'artifact name resolver scopes both platforms and rejects invalid output' , ( t ) => {
0 commit comments