import { Octokit } from '@octokit/rest' ; import { logger } from 'gcf-utils' ; import * as child_process from 'child_process' ; export const exec = function ( command , token ) : Promise < { stdout : string ; stderr : string ; } > { return new Promise ( ( resolve , reject ) => { child_process . exec ( command , { env : { ... process . env , GITHUB_TOKEN : token , } , } , ( error , stdout , stderr ) => { if ( stdout ) { logger . info ( stdout ) ; } if ( stderr ) { logger . warn ( stderr ) ; } if ( error ) { reject ( error ) ; } else { resolve ( { stdout , stderr } ) ; } } ) ; } ) ; } ; export const ALLOWED_ORGANIZATIONS = [ 'googleapis' , 'GoogleCloudPlatform' ] ; export const FAILED_LABEL = 'autorelease: failed' ; export const TAGGED_LABEL = 'autorelease: tagged' ; export const TRIGGERED_LABEL = 'autorelease: triggered' ; export const PUBLISHED_LABEL = 'autorelease: published' ; export interface Repository { owner : string ; repo : string ; } interface BasicPullRequest { owner : string ; repo : string ; number : number ; } export interface PullRequest { html_url : string ; number : number ; state : string ; labels : { name ? : string ; } [ ] ; merge_commit_sha : string | null ; user ? : { login ? : string | undefined ; } | null ; base : { repo : { owner ? : { login ? : string | null ; } | null ; name : string ; } ; } ; closed_at : string | null ; } const LAUNCH_DATE = new Date ( '2021-08-01' ) ; function isReleasePullRequest ( pullRequest ) { return ( pullRequest . state === 'closed' && ! ! pullRequest . merge_commit_sha && pullRequest . labels . some ( label => { return label . name === TAGGED_LABEL ; } ) && ! pullRequest . labels . some ( label => { return label . name === TRIGGERED_LABEL ; } ) && ! ! pullRequest . closed_at && new Date ( pullRequest . closed_at ) > LAUNCH_DATE ) ; } export async function findPendingReleasePullRequests ( octokit , repository , maxNumber = 5 ) { const listGenerator = octokit . paginate . iterator ( 'GET /repos/{owner}/{repo}/pulls' , { owner : repository . owner , repo : repository . repo , state : 'closed' , sort : 'updated' , direction : 'desc' , } ) ; const found = [ ] ; for await ( const listResponse of listGenerator ) { for ( const pullRequest of listResponse . data ) { if ( isReleasePullRequest ( pullRequest ) ) { found . push ( pullRequest ) ; if ( found . length >= maxNumber ) { break ; } } } } return found ; } export async function triggerKokoroJob ( pullRequestUrl , token ) { logger . info ( `triggering job for ${ pullRequestUrl }` ) ; const command = `python3 -m autorelease trigger-single --pull=${ pullRequestUrl }` ; logger . debug ( `command: ${ command }` ) ; try { const { stdout , stderr } = await exec ( command , token ) ; if ( stdout ) { logger . info ( stdout ) ; } if ( stderr ) { logger . warn ( stderr ) ; } return { stdout , stderr } ; } catch ( e ) { logger . error ( `error executing command: ${ command }` , e ) ; throw e ; } } export async function markTriggered ( octokit , pullRequest ) { logger . info ( 'adding `autorelease: triggered` label' ) ; await octokit . issues . addLabels ( { owner : pullRequest . owner , repo : pullRequest . repo , issue_number : pullRequest . number , labels : [ TRIGGERED_LABEL ] , } ) ; } export async function markFailed ( octokit , pullRequest ) { logger . info ( 'adding `autorelease: failed` label' ) ; await octokit . issues . addLabels ( { owner : pullRequest . owner , repo : pullRequest . repo , issue_number : pullRequest . number , labels : [ FAILED_LABEL ] , } ) ; } export async function cleanupPublished ( octokit , pullRequest ) { logger . info ( 'adding `autorelease: failed` label' ) ; let success = true ; for ( const name of [ TAGGED_LABEL , TRIGGERED_LABEL ] ) { try { await octokit . issues . removeLabel ( { owner : pullRequest . owner , repo : pullRequest . repo , issue_number : pullRequest . number , name , } ) ; } catch ( err ) { logger . warn ( `failed to remove label ${ name }` ) ; success = false ; } } return success ; }