import { RequestError } from "@octokit/request-error" ; import webhooks from "@octokit/webhooks" ; import debug from "debug" ; import * as probot from "probot" ; import { ProbotOctokit } from "probot" ; import * as util from "util" ; import * as config from "./config" ; import { formatFiles } from "./format-files" ; import * as github from "./github" ; import { DevError , logDevError } from "./logging/dev-error" ; import { LoggedError } from "./logging/logged-error" ; import { logUserError , UserError } from "./logging/user-error" ; import * as prettier from "./prettier" ; export interface PullRequestState { readonly baseBranch : string ; readonly headBranch : string ; readonly headOrg : string ; readonly octokit : InstanceType < typeof ProbotOctokit > ; readonly org : string ; prettierConfig : prettier . Options ; prettierIgnore : string ; prettifierConfig : config . Data ; pullRequestId : string ; pullRequestNumber : number ; readonly pullRequestURL : string ; readonly repo : string ; } /** called when this bot gets notified about a new pull request */ export async function onPullRequest ( context ) { let state ; const debugGithub = debug ( "github" ) ; try { state = { octokit : context . octokit , org : context . payload . repository . owner . login , headOrg : context . payload . pull_request . head . repo . owner . login , repo : context . payload . repository . name , headBranch : context . payload . pull_request . head . ref , baseBranch : context . payload . pull_request . base . ref , pullRequestNumber : context . payload . pull_request . number , pullRequestId : context . payload . pull_request . node_id , pullRequestURL : context . payload . pull_request . html_url , prettierConfig : { } , prettifierConfig : config . defaultValues ( ) , prettierIgnore : "" , } ; const repoPrefix = `${ state . org }/${ state . repo }|#${ state . pullRequestNumber }` ; console . log ( `${ repoPrefix }: PULL REQUEST DETECTED` ) ; if ( context . payload . action !== "opened" ) { console . log ( `${ repoPrefix }: PULL REQUEST ACTION IS ${ context . payload . action }, IGNORING` ) ; return ; } try { state = await loadPullRequestContext ( state ) ; } catch ( e ) { if ( e instanceof Error && e . message . startsWith ( "Could not resolve to a Repository with the name" ) ) { console . log ( `${ repoPrefix }: HEAD REPO ${ state . headOrg }/${ state . repo } IS NOT ACCESSIBLE TO THIS BOT, IGNORING` ) ; } else { console . log ( `${ repoPrefix }: CAN'T LOAD PULL REQUEST CONTEXT:` , e ) ; await logDevError ( new DevError ( `${ repoPrefix }: CAN'T LOAD PUSH CONTEXT:` , e , state ) ) ; } return ; } console . log ( `${ repoPrefix }: BOT CONFIG: ${ JSON . stringify ( state . prettifierConfig , null , "__" ) }` ) ; console . log ( `${ repoPrefix }: PRETTIER CONFIG: ${ JSON . stringify ( state . prettierConfig , null , "__" ) }` ) ; console . log ( `${ repoPrefix }: PRETTIER IGNORE: ${ JSON . stringify ( state . prettierIgnore , null , "__" ) }` ) ; const configReader = new config . Reader ( state . prettifierConfig , state . prettierIgnore ) ; if ( configReader . shouldIgnoreBranch ( state . headBranch ) ) { console . log ( `${ repoPrefix }: IGNORING THIS BRANCH PER BOT CONFIG` ) ; return ; } try { var changedFiles = await github . getExistingFilesInPullRequests ( state ) ; } catch ( e ) { console . log ( `${ repoPrefix }: CAN'T LOAD FILES OF PULL REQUEST:` , e ) ; return ; } const { prettifiedFiles , configChange } = await formatFiles ( { configReader , files : changedFiles , prettierConfig : state . prettierConfig , repoPrefix , branch : state . headBranch , octokit : state . octokit , org : state . headOrg , repo : state . repo , } ) ; if ( configChange ) { await github . addComment ( context . octokit , { ... state , issueId : state . pullRequestId , text : "Prettifier-Bot here. The configuration changes made in this pull request look good to me." , } ) ; console . log ( `${ repoPrefix }: ADDED CONFIG CHANGE DEBUG COMMENT` ) ; } if ( prettifiedFiles . length === 0 ) { console . log ( `${ repoPrefix }: ALL ${ changedFiles . length } FILES WERE ALREADY FORMATTED` ) ; return ; } const isPullRequestFromFork = state . headOrg !== state . org ; if ( isPullRequestFromFork ) { if ( configReader . hasCannotPrettifyMessage ( ) ) { const args = { ... state , issueId : state . pullRequestId , text : configReader . cannotPrettifyMessage ( { files : prettifiedFiles . paths ( ) } ) , } ; debugGithub ( `ARGUMENTS FOR CANNOT-PRETTIFY-MESSAGE: ${ util . inspect ( args ) }` ) ; await github . addComment ( context . octokit , args ) ; console . log ( `${ repoPrefix }: ADDED CANNOT-PRETTIFY-MESSAGE ON PULL REQUEST FROM FORK` ) ; } return ; } if ( state . prettifierConfig . fixByCommit ) { const args = { branch : state . headBranch , files : prettifiedFiles . formattedFiles ( ) , message : configReader . commitMessage ( { commitSha : state . headBranch , files : prettifiedFiles . paths ( ) , } ) , octokit : state . octokit , org : state . headOrg , repo : state . repo , } ; try { await github . createCommit ( args ) ; console . log ( `${ repoPrefix }: COMMITTED ${ prettifiedFiles . length } PRETTIFIED FILES` ) ; } catch ( e ) { if ( e instanceof RequestError ) { if ( e . status === 422 ) { if ( e . message . includes ( "Required status check" ) ) { return ; } if ( e . message . includes ( "Update is not a fast forward" ) ) { return ; } } if ( e . status === 403 ) { if ( e . message . includes ( "Resource not accessible by integration" ) ) { return ; } if ( e . message . includes ( "The requested blob is too large to fetch via the API" ) ) { console . log ( `${ repoPrefix }: COMMIT IS TOO LARGE` ) ; return ; } } if ( e . status === 404 ) { return ; } } throw new DevError ( "creating a commit on a freshly opened pull request" , e , args ) ; } } if ( configReader . hasPrettifiedMessage ( ) ) { const args = { ... state , issueId : state . pullRequestId , text : configReader . prettifiedMessage ( { files : prettifiedFiles . paths ( ) } ) , } ; try { await github . addComment ( context . octokit , args ) ; console . log ( `${ repoPrefix }: ADDED PRETTIFIED-MESSAGE` ) ; } catch ( e ) { throw new DevError ( `Cannot comment prettified message into pull request` , e , args ) ; } } } catch ( e ) { if ( e instanceof LoggedError ) { return ; } const errorContext = { ... state , event : "on-pull-request" , payload : context . payload , } ; if ( e instanceof UserError ) { e . enrich ( errorContext ) ; await logUserError ( e , context . octokit ) ; return ; } if ( e instanceof DevError ) { e . enrich ( errorContext ) ; await logDevError ( e ) ; return ; } await logDevError ( new DevError ( "unknown dev error" , e , errorContext ) ) ; } } async function loadPullRequestContext ( state ) { const pullRequestContextData = await github . loadPullRequestContextData ( { branch : state . headBranch , octokit : state . octokit , org : state . headOrg , repo : state . repo , } ) ; state . prettifierConfig = config . parseYML ( pullRequestContextData . prettifierConfig ) ; state . prettierConfig = prettier . loadConfig ( pullRequestContextData ) ; state . prettierIgnore = pullRequestContextData . prettierIgnore ; return state ; }