import { Octokit } from '@octokit/rest' ; import { createPullRequest , Changes } from 'code-suggester' ; import { request } from 'gaxios' ; import { v4 as uuid } from 'uuid' ; import { PolicyResult , GitHubRepo , Logger } from './policy' ; import { Endpoints } from '@octokit/types' ; type PullRequests = Endpoints [ 'GET /repos/{owner}/{repo}/pulls' ] [ 'response' ] [ 'data' ] ; export const cocUrl = 'https://raw.githubusercontent.com/googleapis/.github/master/CODE_OF_CONDUCT.md' ; export const securityUrl = 'https://raw.githubusercontent.com/googleapis/.github/master/SECURITY.md' ; export const contributingUrl = 'https://raw.githubusercontent.com/googleapis/.github/master/CONTRIBUTING.md' ; const fileCache = new Map < string , string > ( ) ; param url returns export async function cachedGet ( url ) { let contents = fileCache . get ( url ) ; if ( contents ) { return contents ; } const res = await request < string > ( { url : url , responseType : 'text' , } ) ; contents = res . data ; fileCache . set ( url , contents ) ; return contents ; } export class Changer { private octokit : Octokit ; private repo : GitHubRepo ; private owner : string ; private name : string ; private prsCache ? : PullRequests ; constructor ( octokit , repo , private logger = console ) { this . octokit = octokit ; this . repo = repo ; const [ owner , name ] = repo . full_name . split ( '/' ) ; this . owner = owner ; this . name = name ; } param title returns private async hasMatchingPR ( title ) { if ( ! this . prsCache ) { this . prsCache = await this . octokit . paginate ( this . octokit . pulls . list , { owner : this . owner , repo : this . name , state : 'open' , per_page : 100 , } ) ; } const matches = this . prsCache . filter ( x => x . title === title ) ; return matches . length > 0 ; } param title param sourceUrl param targetPath async addFile ( title , sourceUrl , targetPath ) { if ( await this . hasMatchingPR ( title ) ) { return ; } const content = await cachedGet ( sourceUrl ) ; const changes = new Map ( [ [ targetPath , { content , mode : '100644' , } , ] , ] ) ; try { await createPullRequest ( this . octokit , changes , { title , message : title , description : title , upstreamOwner : this . owner , upstreamRepo : this . name , fork : false , primary : this . repo . default_branch , retry : 0 , branch : `policy-bot-${ uuid ( ) }` , } ) ; } catch ( e ) { const err = e as Error ; if ( err . message === 'Branch not found' ) { this . logger . warn ( `Branch ${ this . repo . default_branch } does not exist on repository ${ this . owner }/${ this . name }.` ) ; } else { throw err ; } } } param result async submitFixes ( result ) { if ( ! result . hasCodeOfConduct ) { const title = 'chore: add a Code of Conduct' ; await this . addFile ( title , cocUrl , 'CODE_OF_CONDUCT.md' ) ; } if ( ! result . hasSecurityPolicy ) { const title = 'chore: add SECURITY.md' ; await this . addFile ( title , securityUrl , 'SECURITY.md' ) ; } if ( ! result . hasContributing ) { const title = 'chore: add CONTRIBUTING.md' ; await this . addFile ( title , contributingUrl , 'CONTRIBUTING.md' ) ; } } } export function getChanger ( octokit , repo , logger = console ) { return new Changer ( octokit , repo , logger ) ; }