@@ -293,12 +293,11 @@ export async function scaleUp(payloads: ActionRequestMessageSQS[]): Promise<stri
293293 const enableOrgLevel = yn ( process . env . ENABLE_ORGANIZATION_RUNNERS , { default : true } ) ;
294294 const maximumRunners = parseInt ( process . env . RUNNERS_MAXIMUM_COUNT || '3' ) ;
295295 const runnerLabels = process . env . RUNNER_LABELS || '' ;
296-
297296 const runnerGroup = process . env . RUNNER_GROUP_NAME || 'Default' ;
298297 const environment = process . env . ENVIRONMENT ;
299298 const ssmTokenPath = process . env . SSM_TOKEN_PATH ;
300299 const subnets = process . env . SUBNET_IDS . split ( ',' ) ;
301- const instanceTypes = process . env . INSTANCE_TYPES . split ( ',' ) ;
300+ let instanceTypes = process . env . INSTANCE_TYPES . split ( ',' ) ;
302301 const instanceTargetCapacityType = process . env . INSTANCE_TARGET_CAPACITY_TYPE ;
303302 const ephemeralEnabled = yn ( process . env . ENABLE_EPHEMERAL_RUNNERS , { default : false } ) ;
304303 const dynamicEc2ConfigEnabled = yn ( process . env . ENABLE_DYNAMIC_EC2_CONFIG , { default : false } ) ;
@@ -341,7 +340,7 @@ export async function scaleUp(payloads: ActionRequestMessageSQS[]): Promise<stri
341340 const validMessages = new Map < string , MessagesWithClient > ( ) ;
342341 const rejectedMessageIds = new Set < string > ( ) ;
343342 for ( const payload of payloads ) {
344- const { eventType, messageId, repositoryName, repositoryOwner } = payload ;
343+ const { eventType, messageId, repositoryName, repositoryOwner, labels } = payload ;
345344 if ( ephemeralEnabled && eventType !== 'workflow_job' ) {
346345 logger . warn (
347346 'Event is not supported in combination with ephemeral runners. Please ensure you have enabled workflow_job events.' ,
@@ -365,7 +364,19 @@ export async function scaleUp(payloads: ActionRequestMessageSQS[]): Promise<stri
365364 continue ;
366365 }
367366
368- const key = enableOrgLevel ? payload . repositoryOwner : `${ payload . repositoryOwner } /${ payload . repositoryName } ` ;
367+ let key = enableOrgLevel ? payload . repositoryOwner : `${ payload . repositoryOwner } /${ payload . repositoryName } ` ;
368+
369+ if ( dynamicEc2ConfigEnabled && labels ?. length ) {
370+ const requestedDynamicEc2Config = labels
371+ . find ( l => l . startsWith ( 'ghr-ec2-' ) )
372+ ?. slice ( 'ghr-ec2-' . length ) ;
373+
374+ if ( requestedDynamicEc2Config ) {
375+ const ec2Hash = ec2LabelsHash ( labels ) ;
376+ key = `${ key } /${ ec2Hash } ` ;
377+ }
378+ }
379+
369380
370381 let entry = validMessages . get ( key ) ;
371382
@@ -405,13 +416,20 @@ export async function scaleUp(payloads: ActionRequestMessageSQS[]): Promise<stri
405416 let scaleUp = 0 ;
406417 const queuedMessages : ActionRequestMessageSQS [ ] = [ ] ;
407418
419+ if ( messages . length > 0 && dynamicEc2ConfigEnabled ) {
420+ const requestedInstanceType = messages [ 0 ] . labels ?. find ( label => label . startsWith ( 'ghr-ec2-instance-type' ) ) ?. replace ( 'ghr-ec2-instance-type' , '' ) ;
421+ instanceTypes = requestedInstanceType ? [ requestedInstanceType ] : instanceTypes ;
422+ }
423+
424+
408425 for ( const message of messages ) {
409426 const messageLogger = logger . createChild ( {
410427 persistentKeys : {
411428 eventType : message . eventType ,
412429 group,
413430 messageId : message . messageId ,
414431 repository : `${ message . repositoryOwner } /${ message . repositoryName } ` ,
432+ labels : message . labels ,
415433 } ,
416434 } ) ;
417435
@@ -702,3 +720,20 @@ async function createJitConfig(
702720
703721 return failedInstances ;
704722}
723+
724+ function ec2LabelsHash ( labels : string [ ] ) : string {
725+ const prefix = 'ghr-ec2-' ;
726+
727+ const input = labels
728+ . filter ( l => l . startsWith ( prefix ) )
729+ . sort ( ) // ensure deterministic hash
730+ . join ( '|' ) ;
731+
732+ let hash = 0 ;
733+ for ( let i = 0 ; i < input . length ; i ++ ) {
734+ hash = ( hash << 5 ) - hash + input . charCodeAt ( i ) ;
735+ hash |= 0 ; // force 32-bit integer
736+ }
737+
738+ return Math . abs ( hash ) . toString ( 36 ) ;
739+ }
0 commit comments