import { posix } from 'path' ; import { getBooleanInput , getInput , info as logInfo , setFailed , setOutput , warning as logWarning , } from '@actions/core' ; import { Credential , errorMessage , isServiceAccountKey , parseCredential , parseDuration , parseKVString , parseKVStringAndFile , presence , } from '@google-github-actions/actions-utils' ; import { CloudFunction , CloudFunctionsClient , SecretEnvVar , SecretVolume , } from './client' ; import { SecretName } from './secret' ; async function run ( ) { try { const name = getInput ( 'name' , { required : true } ) ; const runtime = getInput ( 'runtime' , { required : true } ) ; const description = presence ( getInput ( 'description' ) ) ; const credentials = presence ( getInput ( 'credentials' ) ) ; let projectID = presence ( getInput ( 'project_id' ) ) ; const availableMemoryMb = presence ( getInput ( 'memory_mb' ) ) ; const region = presence ( getInput ( 'region' ) || 'us-central1' ) ; const envVars = presence ( getInput ( 'env_vars' ) ) ; const envVarsFile = presence ( getInput ( 'env_vars_file' ) ) ; const entryPoint = presence ( getInput ( 'entry_point' ) ) ; const sourceDir = presence ( getInput ( 'source_dir' ) ) ; const vpcConnector = presence ( getInput ( 'vpc_connector' ) ) ; const vpcConnectorEgressSettings = presence ( getInput ( 'vpc_connector_egress_settings' ) ) ; const ingressSettings = presence ( getInput ( 'ingress_settings' ) ) ; const serviceAccountEmail = presence ( getInput ( 'service_account_email' ) ) ; const timeout = parseDuration ( getInput ( 'timeout' ) ) ; const maxInstances = presence ( getInput ( 'max_instances' ) ) ; const minInstances = presence ( getInput ( 'min_instances' ) ) ; const eventTriggerType = presence ( getInput ( 'event_trigger_type' ) ) ; const eventTriggerResource = presence ( getInput ( 'event_trigger_resource' ) ) ; const eventTriggerService = presence ( getInput ( 'event_trigger_service' ) ) ; const eventTriggerRetry = getBooleanInput ( 'event_trigger_retry' ) ; const deployTimeout = presence ( getInput ( 'deploy_timeout' ) ) ; const labels = parseKVString ( getInput ( 'labels' ) ) ; const buildEnvVars = presence ( getInput ( 'build_environment_variables' ) ) ; const buildEnvVarsFile = presence ( getInput ( 'build_environment_variables_file' ) ) ; const buildWorkerPool = presence ( getInput ( 'build_worker_pool' ) ) ; const secretEnvVars = parseKVString ( getInput ( 'secret_environment_variables' ) ) ; const secretVols = parseKVString ( getInput ( 'secret_volumes' ) ) ; const dockerRepository = presence ( getInput ( 'docker_repository' ) ) ; const kmsKeyName = presence ( getInput ( 'kms_key_name' ) ) ; let credentialsJSON ; if ( credentials ) { logWarning ( 'The "credentials" input is deprecated. ' + 'Please switch to using google-github-actions/auth which supports both Workload Identity Federation and JSON Key authentication. ' + 'For more details, see https://github.com/google-github-actions/deploy-cloud-functions#authorization' ) ; credentialsJSON = parseCredential ( credentials ) ; } if ( ! projectID ) { if ( credentialsJSON && isServiceAccountKey ( credentialsJSON ) ) { projectID = credentialsJSON ?. project_id ; logInfo ( `Extracted project ID '${ projectID }' from credentials JSON` ) ; } else if ( process . env ?. GCLOUD_PROJECT ) { projectID = process . env . GCLOUD_PROJECT ; logInfo ( `Extracted project ID '${ projectID }' from $GCLOUD_PROJECT` ) ; } } if ( ! sourceDir ) { throw new Error ( `Missing required value 'source_dir'` ) ; } if ( dockerRepository || kmsKeyName ) { if ( ! dockerRepository ) { throw new Error ( `Missing required field 'docker_repository'. This is required when ` + `'kms_key_name' is set.` ) ; } if ( ! kmsKeyName ) { throw new Error ( `Missing required field 'kms_key_name'. This is required when ` + `'docker_repository' is set.` ) ; } } if ( timeout <= 0 ) { throw new Error ( `The 'timeout' parameter must be > 0 seconds (got ${ timeout })` ) ; } const buildEnvironmentVariables = parseKVStringAndFile ( buildEnvVars , buildEnvVarsFile ) ; const environmentVariables = parseKVStringAndFile ( envVars , envVarsFile ) ; const secretEnvironmentVariables = [ ] ; if ( secretEnvVars ) { for ( const [ key , value ] of Object . entries ( secretEnvVars ) ) { const secretRef = new SecretName ( value ) ; secretEnvironmentVariables . push ( { key : key , projectId : secretRef . project , secret : secretRef . name , version : secretRef . version , } ) ; } } const secretVolumes = [ ] ; if ( secretVols ) { for ( const [ key , value ] of Object . entries ( secretVols ) ) { const mountPath = posix . dirname ( key ) ; const pth = posix . basename ( key ) ; const secretRef = new SecretName ( value ) ; secretVolumes . push ( { mountPath : mountPath , projectId : secretRef . project , secret : secretRef . name , versions : [ { path : pth , version : secretRef . version , } , ] , } ) ; } } const client = new CloudFunctionsClient ( { projectID : projectID , location : region , credentials : credentialsJSON , } ) ; const cf = { name : name , runtime : runtime , description : description , availableMemoryMb : availableMemoryMb ? + availableMemoryMb : undefined , buildEnvironmentVariables : buildEnvironmentVariables , buildWorkerPool : buildWorkerPool , dockerRepository : dockerRepository , entryPoint : entryPoint , environmentVariables : environmentVariables , ingressSettings : ingressSettings , kmsKeyName : kmsKeyName , labels : labels , maxInstances : maxInstances ? + maxInstances : undefined , minInstances : minInstances ? + minInstances : undefined , secretEnvironmentVariables : secretEnvironmentVariables , secretVolumes : secretVolumes , serviceAccountEmail : serviceAccountEmail , timeout : `${ timeout }s` , vpcConnector : vpcConnector , vpcConnectorEgressSettings : vpcConnectorEgressSettings , } ; if ( eventTriggerType && eventTriggerResource ) { cf . eventTrigger = { eventType : eventTriggerType , resource : eventTriggerResource , service : eventTriggerService , } ; if ( eventTriggerRetry ) { cf . eventTrigger . failurePolicy = { retry : { } , } ; } } else if ( eventTriggerType || eventTriggerResource || eventTriggerService ) { throw new Error ( `Event triggered functions must define 'event_trigger_type' and 'event_trigger_resource'` ) ; } else { cf . httpsTrigger = { } ; } const resp = await client . deployFromLocalSource ( cf , sourceDir , { timeout : deployTimeout ? + deployTimeout : undefined , onZip : ( sourceDir , zipPath ) => { logInfo ( `Created zip file from '${ sourceDir }' at '${ zipPath }'` ) ; } , onNew : ( ) => { logInfo ( 'Creating new Cloud Function deployment' ) ; } , onExisting : ( ) => { logInfo ( 'Creating new Cloud Function revision' ) ; } , onPoll : ( ( ) : ( ( ) => void ) => { let iteration = 0 ; return ( ) => { if ( iteration === 0 ) { logInfo ( 'Deploying Cloud Function' ) ; } else { logInfo ( `Still deploying Cloud Function (${ iteration }/n)` ) ; } iteration ++ ; } ; } ) ( ) , } ) ; if ( resp . status !== 'ACTIVE' ) { throw new Error ( `Cloud Function deployment finished, but the function not in the ` + `"ACTIVE" status. The current status is "${ resp . status }", which ` + `could indicate a failed deployment. Check the Cloud Function ` + `logs for more information.` ) ; } if ( resp . httpsTrigger ?. url ) { setOutput ( 'url' , resp . httpsTrigger . url ) ; } else { logWarning ( `Output 'url' was not set - only httpsTrigger Cloud Functions return this attribute.` ) ; } setOutput ( 'id' , resp . name ) ; setOutput ( 'status' , resp . status ) ; setOutput ( 'version' , resp . versionId ) ; setOutput ( 'runtime' , resp . runtime ) ; } catch ( err ) { const msg = errorMessage ( err ) ; setFailed ( `google-github-actions/deploy-cloud-functions failed with: ${ msg }` ) ; } } run ( ) ;