import { diffLines } from "diff" ; import { Circle } from "./api/circle" ; import { GitHub } from "./api/github" ; import { formatReport } from "./lib/format-report" ; const DUPE_IDENTIFIER = "<!-- DUPE:REPORT -->" ; const jobName = "build" ; const artifactName = "duplicates-report" ; interface DupeReportArgs { owner : string ; repo : string ; buildNum : number ; dryRun ? : boolean ; } export const dupeReport = async ( { owner , repo , buildNum , dryRun = false } ) => { const circle = new Circle ( { owner , repo } ) ; const pullRequestURL = await circle . getPullRequest ( buildNum ) ; /** If a build has no PR associated with it we just skip the dupe report */ if ( ! pullRequestURL ) { console . log ( `Exiting for build ${ owner }/${ repo } #${ buildNum } because there was no PR` ) ; return ; } const github = new GitHub ( { owner , repo , pullRequest : pullRequestURL , dryRun } ) ; let headReport ; try { headReport = await circle . fetchArtifact ( { jobName , artifactName , buildNum } ) ; } catch { console . error ( "failed to read head duplicate report" ) ; } if ( ! headReport ) { console . log ( "local report is empty" ) ; headReport = "" ; } let baseReport ; try { const buildNum = await github . getBaseCommitBuild ( jobName ) ; baseReport = await circle . fetchArtifact ( { jobName , artifactName , buildNum } ) ; } catch { console . warn ( `Failed to get based build, falling back to master` ) ; try { baseReport = await circle . fetchArtifact ( { jobName , artifactName } ) ; } catch ( error ) { console . error ( "failed to fetch base report" ) ; throw error ; } } if ( ! baseReport ) { console . log ( "base report is empty" ) ; baseReport = "" ; } if ( baseReport === "" && headReport === "" ) { console . log ( "Nothing to compare" ) ; return ; } let dupeDiff = "" ; let headerDiff = false ; let hasDiff = false ; let change = 0 ; const masterHeader = baseReport . trim ( ) . split ( "\n" , 5 ) . slice ( 0 , 4 ) . join ( "\n" ) ; const localHeader = headReport . trim ( ) . split ( "\n" , 5 ) . slice ( 0 , 4 ) . join ( "\n" ) ; if ( masterHeader !== localHeader ) { headerDiff = true ; } diffLines ( baseReport . trim ( ) , headReport . trim ( ) , { newlineIsToken : true } ) . forEach ( ( line , lineNumber ) => { let lineStart = " " ; if ( line . added ) { hasDiff = true ; lineStart = "+" ; } else if ( line . removed ) { hasDiff = true ; lineStart = "-" ; } line . value . split ( "\n" ) . forEach ( ( l , lineOffset ) => { lineStart === "+" && change ++ ; lineStart === "-" && change -- ; dupeDiff += lineStart + l + "\n" ; } ) ; } ) ; dupeDiff = "```diff\n" + dupeDiff + "\n```" ; let existingPRComment ; try { existingPRComment = await github . checkForPRCommentWithString ( DUPE_IDENTIFIER ) ; } catch { console . error ( "failed when trying to check to see if message existed on pr" ) ; } console . log ( "diff info" , { hasDiff , headerDiff , change } ) ; if ( ! hasDiff || ( change === 0 && ! headerDiff ) ) { if ( existingPRComment ) { try { await github . deleteComment ( existingPRComment . id ) ; } catch ( error ) { console . error ( `Failed to delete comment ${ existingPRComment . id } in PR ${ pullRequestURL }` , error ) ; } } console . log ( `No change in build #${ buildNum } in PR ${ pullRequestURL }, exiting...` ) ; return ; } let assignees = [ ] ; try { assignees = await github . getAssignees ( ) ; } catch ( error ) { console . error ( "failed to fetch assignees" , error . message ) ; } let prComment = `${ DUPE_IDENTIFIER }\n\n` ; prComment += formatReport ( headReport , dupeDiff , change , assignees ) ; try { if ( existingPRComment ) { await github . updateComment ( existingPRComment . id , prComment ) ; } else { await github . createComment ( prComment ) ; } } catch ( error ) { console . error ( "failed to create dupe report comment with code" , error . status ) ; } } ;