1- const core = require ( '@actions/core' ) ;
2- const aws = require ( 'aws-sdk' ) ;
3- const smoketail = require ( 'smoketail' )
1+ const core = require ( "@actions/core" ) ;
2+
3+ const {
4+ ECS ,
5+ waitUntilTasksRunning,
6+ waitUntilTasksStopped,
7+ } = require ( "@aws-sdk/client-ecs" ) ;
8+
9+ const smoketail = require ( "smoketail" ) ;
410
511const main = async ( ) => {
612 try {
713 // Setup AWS clients
8- const ecs = new aws . ECS ( {
9- customUserAgent : ' github-action-aws-ecs-run-task'
14+ const ecs = new ECS ( {
15+ customUserAgent : " github-action-aws-ecs-run-task" ,
1016 } ) ;
1117
1218 // Inputs: Required
13- const cluster = core . getInput ( 'cluster' , { required : true } ) ;
14- const taskDefinition = core . getInput ( 'task-definition' , { required : true } ) ;
15- const subnets = core . getMultilineInput ( 'subnet-ids' , { required : true } ) ;
16- const securityGroups = core . getMultilineInput ( 'security-group-ids' , { required : true } ) ;
19+ const cluster = core . getInput ( "cluster" , { required : true } ) ;
20+ const taskDefinition = core . getInput ( "task-definition" , {
21+ required : true ,
22+ } ) ;
23+ const subnets = core . getMultilineInput ( "subnet-ids" , {
24+ required : true ,
25+ } ) ;
26+ const securityGroups = core . getMultilineInput ( "security-group-ids" , {
27+ required : true ,
28+ } ) ;
1729
1830 // Inputs: Optional
19- const tailLogs = core . getBooleanInput ( 'tail-logs' , { required : false } ) ;
20- const assignPublicIp = core . getInput ( 'assign-public-ip' , { required : false } ) ;
21- const overrideContainer = core . getInput ( 'override-container' , { required : false } ) ;
22- const overrideContainerCommand = core . getMultilineInput ( 'override-container-command' , { required : false } ) ;
23- const overrideContainerEnvironment = core . getMultilineInput ( 'override-container-environment' , { required : false } ) ;
24- const taskStoppedWaitForMaxAttempts = parseInt ( core . getInput ( 'task-stopped-wait-for-max-attempts' , { required : false } ) ) ;
31+ const tailLogs = core . getBooleanInput ( "tail-logs" , { required : false } ) ;
32+ const assignPublicIp = core . getInput ( "assign-public-ip" , {
33+ required : false ,
34+ } ) ;
35+ const overrideContainer = core . getInput ( "override-container" , {
36+ required : false ,
37+ } ) ;
38+ const overrideContainerCommand = core . getMultilineInput (
39+ "override-container-command" ,
40+ { required : false }
41+ ) ;
42+ const overrideContainerEnvironment = core . getMultilineInput (
43+ "override-container-environment" ,
44+ { required : false }
45+ ) ;
2546
2647 // Build Task parameters
2748 const taskRequestParams = {
2849 count : 1 ,
2950 cluster,
3051 taskDefinition,
31- launchType : ' FARGATE' ,
52+ launchType : " FARGATE" ,
3253 networkConfiguration : {
3354 awsvpcConfiguration : {
3455 subnets,
3556 assignPublicIp,
36- securityGroups
57+ securityGroups,
3758 } ,
3859 } ,
3960 } ;
@@ -42,110 +63,155 @@ const main = async () => {
4263 if ( overrideContainer ) {
4364 let overrides = {
4465 name : overrideContainer ,
45- }
66+ } ;
4667
4768 if ( overrideContainerCommand . length ) {
48- core . debug ( `overrideContainer and overrideContainerCommand has been specified. Overriding.` ) ;
69+ core . debug (
70+ `overrideContainer and overrideContainerCommand has been specified. Overriding.`
71+ ) ;
4972
5073 // Iterate over each item in the array and check for line appender character
51- core . debug ( `Parsing overrideContainerCommand and merging line appender strings.` ) ;
74+ core . debug (
75+ `Parsing overrideContainerCommand and merging line appender strings.`
76+ ) ;
5277 overrideContainerCommand . map ( ( x , i , arr ) => {
53- if ( x . endsWith ( '\\' ) ) {
78+ if ( x . endsWith ( "\\" ) ) {
5479 // Remove line appender character
55- arr [ i ] = x . replace ( / \\ $ / , '' )
80+ arr [ i ] = x . replace ( / \\ $ / , "" ) ;
5681
5782 // Check if not the last item in array
5883 if ( arr . length - 1 !== i ) {
5984 // Prepend the current item to the next item and set current item to null
60- arr [ i + 1 ] = arr [ i ] + arr [ i + 1 ]
61- arr [ i ] = null
85+ arr [ i + 1 ] = arr [ i ] + arr [ i + 1 ] ;
86+ arr [ i ] = null ;
6287 }
6388 }
64- } )
89+ } ) ;
6590
6691 // Filter out any null values
67- const parsedCommand = overrideContainerCommand . filter ( x => x )
68- core . debug ( `Resulting command: ${ JSON . stringify ( parsedCommand ) } ` )
92+ const parsedCommand = overrideContainerCommand . filter ( ( x ) => x ) ;
93+ core . debug (
94+ `Resulting command: ${ JSON . stringify ( parsedCommand ) } `
95+ ) ;
6996
70- overrides . command = parsedCommand
97+ overrides . command = parsedCommand ;
7198 }
7299
73100 if ( overrideContainerEnvironment . length ) {
74- core . debug ( `overrideContainer and overrideContainerEnvironment has been specified. Overriding.` ) ;
75- overrides . environment = overrideContainerEnvironment . map ( x => {
76- const parts = x . split ( / = ( .* ) / )
77- return {
78- name : parts [ 0 ] ,
79- value : parts [ 1 ]
101+ core . debug (
102+ `overrideContainer and overrideContainerEnvironment has been specified. Overriding.`
103+ ) ;
104+ overrides . environment = overrideContainerEnvironment . map (
105+ ( x ) => {
106+ const parts = x . split ( / = ( .* ) / ) ;
107+ return {
108+ name : parts [ 0 ] ,
109+ value : parts [ 1 ] ,
110+ } ;
80111 }
81- } )
112+ ) ;
82113 }
83114
84115 taskRequestParams . overrides = {
85116 containerOverrides : [ overrides ] ,
86- }
117+ } ;
87118 }
88119
89120 // Start task
90- core . debug ( JSON . stringify ( taskRequestParams ) )
91- core . debug ( `Starting task.` )
92- let task = await ecs . runTask ( taskRequestParams ) . promise ( ) ;
121+ core . debug ( JSON . stringify ( taskRequestParams ) ) ;
122+ core . debug ( `Starting task.` ) ;
123+ let task = await ecs . runTask ( taskRequestParams ) ;
93124
94125 // Get taskArn and taskId
95126 const taskArn = task . tasks [ 0 ] . taskArn ;
96- const taskId = taskArn . split ( '/' ) . pop ( ) ;
97- core . setOutput ( ' task-arn' , taskArn ) ;
98- core . setOutput ( ' task-id' , taskId ) ;
127+ const taskId = taskArn . split ( "/" ) . pop ( ) ;
128+ core . setOutput ( " task-arn" , taskArn ) ;
129+ core . setOutput ( " task-id" , taskId ) ;
99130 core . info ( `Starting Task with ARN: ${ taskArn } \n` ) ;
100131
101132 // Wait for task to be in running state
102- core . debug ( `Waiting for task to be in running state.` )
103- await ecs . waitFor ( 'tasksRunning' , { cluster, tasks : [ taskArn ] } ) . promise ( ) ;
133+ core . debug ( `Waiting for task to be in running state.` ) ;
134+ await waitUntilTasksRunning (
135+ {
136+ client : ecs ,
137+ maxWaitTime : 200 ,
138+ } ,
139+ { cluster, tasks : [ taskArn ] }
140+ ) ;
104141
105142 // Get logging configuration
106143 let logFilterStream = null ;
107- let logOutput = '' ;
144+ let logOutput = "" ;
108145
109146 if ( tailLogs ) {
110- core . debug ( `Logging enabled. Getting logConfiguration from TaskDefinition.` )
111- let taskDef = await ecs . describeTaskDefinition ( { taskDefinition : taskDefinition } ) . promise ( ) ;
112- taskDef = taskDef . taskDefinition
147+ core . debug (
148+ `Logging enabled. Getting logConfiguration from TaskDefinition.`
149+ ) ;
150+ let taskDef = await ecs . describeTaskDefinition ( {
151+ taskDefinition : taskDefinition ,
152+ } ) ;
153+ taskDef = taskDef . taskDefinition ;
113154
114155 // Iterate all containers in TaskDef and search for given container with awslogs driver
115156 if ( taskDef && taskDef . containerDefinitions ) {
116157 taskDef . containerDefinitions . some ( ( container ) => {
117- core . debug ( `Looking for logConfiguration in container '${ container . name } '.` ) ;
158+ core . debug (
159+ `Looking for logConfiguration in container '${ container . name } '.`
160+ ) ;
118161
119162 // If overrideContainer is passed, we want the logConfiguration for that container
120- if ( overrideContainer && container . name !== overrideContainer ) {
163+ if (
164+ overrideContainer &&
165+ container . name !== overrideContainer
166+ ) {
121167 return false ;
122168 }
123169
124170 // Create a CWLogFilterStream if logOptions are found
125- if ( container . logConfiguration && container . logConfiguration . logDriver === 'awslogs' ) {
126- const logStreamName = [ container . logConfiguration . options [ 'awslogs-stream-prefix' ] , container . name , taskId ] . join ( '/' )
127- core . debug ( `Found matching container with 'awslogs' logDriver. Creating LogStream for '${ logStreamName } '` ) ;
171+ if (
172+ container . logConfiguration &&
173+ container . logConfiguration . logDriver === "awslogs"
174+ ) {
175+ const logStreamName = [
176+ container . logConfiguration . options [
177+ "awslogs-stream-prefix"
178+ ] ,
179+ container . name ,
180+ taskId ,
181+ ] . join ( "/" ) ;
182+ core . debug (
183+ `Found matching container with 'awslogs' logDriver. Creating LogStream for '${ logStreamName } '`
184+ ) ;
128185
129186 logFilterStream = new smoketail . CWLogFilterEventStream (
130187 {
131- logGroupName : container . logConfiguration . options [ 'awslogs-group' ] ,
188+ logGroupName :
189+ container . logConfiguration . options [
190+ "awslogs-group"
191+ ] ,
132192 logStreamNames : [ logStreamName ] ,
133193 startTime : Math . floor ( + new Date ( ) / 1000 ) ,
134194 followInterval : 3000 ,
135- follow : true
195+ follow : true ,
136196 } ,
137- { region : container . logConfiguration . options [ 'awslogs-region' ] }
197+ {
198+ region : container . logConfiguration . options [
199+ "awslogs-region"
200+ ] ,
201+ }
138202 ) ;
139203
140- logFilterStream . on ( ' error' , function ( error ) {
204+ logFilterStream . on ( " error" , function ( error ) {
141205 core . error ( error . message ) ;
142206 core . debug ( error . stack ) ;
143207 } ) ;
144208
145- logFilterStream . on ( 'data' , function ( eventObject ) {
146- const logLine = `${ new Date ( eventObject . timestamp ) . toISOString ( ) } : ${ eventObject . message } `
209+ logFilterStream . on ( "data" , function ( eventObject ) {
210+ const logLine = `${ new Date (
211+ eventObject . timestamp
212+ ) . toISOString ( ) } : ${ eventObject . message } `;
147213 core . info ( logLine ) ;
148- logOutput += logLine + '\n' ;
214+ logOutput += logLine + "\n" ;
149215 } ) ;
150216
151217 return true ;
@@ -156,29 +222,37 @@ const main = async () => {
156222
157223 // Wait for Task to finish
158224 core . debug ( `Waiting for task to finish.` ) ;
159- await ecs . waitFor ( 'tasksStopped' , {
160- cluster,
161- tasks : [ taskArn ] ,
162- $waiter : { delay : 6 , maxAttempts : taskStoppedWaitForMaxAttempts } }
163- ) . promise ( ) ;
225+ await waitUntilTasksStopped (
226+ {
227+ client : ecs ,
228+ minDelay : 6 ,
229+ maxWaitTime : 120 ,
230+ } ,
231+ {
232+ cluster,
233+ tasks : [ taskArn ] ,
234+ }
235+ ) ;
164236
165237 // Close LogStream and store output
166238 if ( logFilterStream !== null ) {
167239 core . debug ( `Closing logStream.` ) ;
168240 logFilterStream . close ( ) ;
169241
170242 // Export log-output
171- core . setOutput ( ' log-output' , logOutput ) ;
243+ core . setOutput ( " log-output" , logOutput ) ;
172244 }
173245
174246 // Describe Task to get Exit Code and Exceptions
175247 core . debug ( `Process exit code and exception.` ) ;
176- task = await ecs . describeTasks ( { cluster, tasks : [ taskArn ] } ) . promise ( ) ;
248+ task = await ecs . describeTasks ( { cluster, tasks : [ taskArn ] } ) ;
177249
178250 // Get exitCode
179251 if ( task . tasks [ 0 ] . containers [ 0 ] . exitCode !== 0 ) {
180- core . info ( `Task failed, see details on Amazon ECS console: https://console.aws.amazon.com/ecs/home?region=${ aws . config . region } #/clusters/${ cluster } /tasks/${ taskId } /details` ) ;
181- core . setFailed ( task . tasks [ 0 ] . stoppedReason )
252+ core . info (
253+ `Task failed, see details on Amazon ECS console: https://console.aws.amazon.com/ecs/home?region=${ ecs . config . region } #/clusters/${ cluster } /tasks/${ taskId } /details`
254+ ) ;
255+ core . setFailed ( task . tasks [ 0 ] . stoppedReason ) ;
182256 }
183257 } catch ( error ) {
184258 core . setFailed ( error . message ) ;
0 commit comments