@@ -50,6 +50,15 @@ type ExistingComment = {
5050 } ;
5151} ;
5252
53+ type WorkflowRun = {
54+ head_sha : string ;
55+ id : number ;
56+ pull_requests ?: Array < { number : number } > ;
57+ run_attempt : number ;
58+ run_number : number ;
59+ workflow_id : number ;
60+ } ;
61+
5362function commenterScript ( ) : string {
5463 const workflow = parse ( readFileSync ( WORKFLOW_PATH , "utf8" ) ) as Workflow ;
5564 const step = workflow . jobs ?. comment ?. steps ?. find (
@@ -71,6 +80,7 @@ async function runCommenter(
7180 liveHeadShaAfter ?: string ;
7281 runHeadSha ?: string ;
7382 runAttempt ?: number ;
83+ workflowRuns ?: WorkflowRun [ ] ;
7484 } = { } ,
7585) {
7686 const script = commenterScript ( ) ;
@@ -93,6 +103,7 @@ async function runCommenter(
93103 rest : {
94104 actions : {
95105 listWorkflowRunArtifacts ( ) { } ,
106+ listWorkflowRuns ( ) { } ,
96107 async downloadArtifact ( ) {
97108 downloadCount += 1 ;
98109 return { data : archiveData } ;
@@ -131,6 +142,19 @@ async function runCommenter(
131142 artifactListCount += 1 ;
132143 return [ { ...artifact , name : artifact . name || ARTIFACT_NAME } ] ;
133144 }
145+ if ( params . workflow_id === 999 ) {
146+ return (
147+ options . workflowRuns ?? [
148+ {
149+ head_sha : options . runHeadSha ?? "head-sha" ,
150+ id : 12345 ,
151+ run_attempt : options . runAttempt ?? 2 ,
152+ run_number : 8 ,
153+ workflow_id : 999 ,
154+ } ,
155+ ]
156+ ) ;
157+ }
134158 if ( params . issue_number === 123 ) {
135159 return options . existingComments ?? [ ] ;
136160 }
@@ -147,6 +171,8 @@ async function runCommenter(
147171 pull_requests : [ { number : 123 } ] ,
148172 repository : { full_name : "openclaw/openclaw" } ,
149173 run_attempt : options . runAttempt ?? 2 ,
174+ run_number : 8 ,
175+ workflow_id : 999 ,
150176 } ,
151177 } ,
152178 repo : {
@@ -444,6 +470,119 @@ describe("iOS Periphery comment workflow", () => {
444470 expect ( result . updatedBodies ) . toEqual ( [ ] ) ;
445471 } ) ;
446472
473+ it ( "does not publish findings from a superseded workflow attempt" , async ( ) => {
474+ const archive = makeZip ( {
475+ "periphery.json" : JSON . stringify ( [
476+ {
477+ kind : "function" ,
478+ location : "Sources/Test.swift:12" ,
479+ name : "unused" ,
480+ } ,
481+ ] ) ,
482+ "periphery.status" : "1\n" ,
483+ } ) ;
484+ const result = await runCommenter (
485+ {
486+ expired : false ,
487+ id : 77 ,
488+ name : "ios-periphery-dead-code-12345-1" ,
489+ size_in_bytes : archive . length ,
490+ } ,
491+ archive ,
492+ {
493+ runAttempt : 1 ,
494+ workflowRuns : [
495+ {
496+ head_sha : "head-sha" ,
497+ id : 12345 ,
498+ run_attempt : 2 ,
499+ run_number : 8 ,
500+ workflow_id : 999 ,
501+ } ,
502+ ] ,
503+ } ,
504+ ) ;
505+
506+ expect ( result . downloadCount ) . toBe ( 1 ) ;
507+ expect ( result . pullGetCount ) . toBe ( 2 ) ;
508+ expect ( result . createdBodies ) . toEqual ( [ ] ) ;
509+ expect ( result . updatedBodies ) . toEqual ( [ ] ) ;
510+ } ) ;
511+
512+ it ( "does not publish findings from a newer run for the same pull request" , async ( ) => {
513+ const archive = makeZip ( {
514+ "periphery.json" : JSON . stringify ( [
515+ {
516+ kind : "function" ,
517+ location : "Sources/Test.swift:12" ,
518+ name : "unused" ,
519+ } ,
520+ ] ) ,
521+ "periphery.status" : "1\n" ,
522+ } ) ;
523+ const result = await runCommenter (
524+ {
525+ expired : false ,
526+ id : 77 ,
527+ name : ARTIFACT_NAME ,
528+ size_in_bytes : archive . length ,
529+ } ,
530+ archive ,
531+ {
532+ workflowRuns : [
533+ {
534+ head_sha : "head-sha" ,
535+ id : 54321 ,
536+ pull_requests : [ { number : 123 } ] ,
537+ run_attempt : 1 ,
538+ run_number : 9 ,
539+ workflow_id : 999 ,
540+ } ,
541+ ] ,
542+ } ,
543+ ) ;
544+
545+ expect ( result . createdBodies ) . toEqual ( [ ] ) ;
546+ expect ( result . updatedBodies ) . toEqual ( [ ] ) ;
547+ } ) ;
548+
549+ it ( "does not treat a run for another pull request as superseding" , async ( ) => {
550+ const archive = makeZip ( {
551+ "periphery.json" : JSON . stringify ( [
552+ {
553+ kind : "function" ,
554+ location : "Sources/Test.swift:12" ,
555+ name : "unused" ,
556+ } ,
557+ ] ) ,
558+ "periphery.status" : "1\n" ,
559+ } ) ;
560+ const result = await runCommenter (
561+ {
562+ expired : false ,
563+ id : 77 ,
564+ name : ARTIFACT_NAME ,
565+ size_in_bytes : archive . length ,
566+ } ,
567+ archive ,
568+ {
569+ workflowRuns : [
570+ {
571+ head_sha : "head-sha" ,
572+ id : 54321 ,
573+ pull_requests : [ { number : 456 } ] ,
574+ run_attempt : 1 ,
575+ run_number : 9 ,
576+ workflow_id : 999 ,
577+ } ,
578+ ] ,
579+ } ,
580+ ) ;
581+
582+ expect ( result . createdBodies ) . toHaveLength ( 1 ) ;
583+ expect ( result . updatedBodies ) . toEqual ( [ ] ) ;
584+ } ) ;
585+
447586 it ( "escapes finding text before creating a PR comment" , async ( ) => {
448587 const longName = `\r\n@octocat|next${ "a" . repeat ( 260 ) } ` ;
449588 const archive = makeZip ( {
0 commit comments