@@ -2,6 +2,7 @@ import {TemplateContext} from "../../templates/template-context.js";
22import {
33 BasicExpressionToken ,
44 MappingToken ,
5+ NullToken ,
56 ScalarToken ,
67 StringToken ,
78 TemplateToken
@@ -20,11 +21,20 @@ export function convertSteps(context: TemplateContext, steps: TemplateToken): St
2021 }
2122
2223 const idBuilder = new IdBuilder ( ) ;
24+ const backgroundStepIds = new Set < string > ( ) ;
2325
2426 const result : Step [ ] = [ ] ;
2527 for ( const item of steps ) {
26- const step = handleTemplateTokenErrors ( steps , context , undefined , ( ) => convertStep ( context , idBuilder , item ) ) ;
28+ const step = handleTemplateTokenErrors ( steps , context , undefined , ( ) =>
29+ convertStep ( context , idBuilder , backgroundStepIds , item )
30+ ) ;
2731 if ( step ) {
32+ if ( "background" in step && step . background ) {
33+ if ( step . id ) {
34+ backgroundStepIds . add ( step . id . toLowerCase ( ) ) ;
35+ }
36+ }
37+
2838 result . push ( step ) ;
2939 }
3040 }
@@ -37,6 +47,12 @@ export function convertSteps(context: TemplateContext, steps: TemplateToken): St
3747 let id = "" ;
3848 if ( isActionStep ( step ) ) {
3949 id = createActionStepId ( step ) ;
50+ } else if ( "wait" in step ) {
51+ id = "wait" ;
52+ } else if ( "wait-all" in step ) {
53+ id = "wait-all" ;
54+ } else if ( "cancel" in step ) {
55+ id = "cancel" ;
4056 }
4157
4258 if ( ! id ) {
@@ -50,13 +66,22 @@ export function convertSteps(context: TemplateContext, steps: TemplateToken): St
5066 return result ;
5167}
5268
53- function convertStep ( context : TemplateContext , idBuilder : IdBuilder , step : TemplateToken ) : Step | undefined {
69+ function convertStep (
70+ context : TemplateContext ,
71+ idBuilder : IdBuilder ,
72+ backgroundStepIds : Set < string > ,
73+ step : TemplateToken
74+ ) : Step | undefined {
5475 const mapping = step . assertMapping ( "steps item" ) ;
5576
5677 let run : ScalarToken | undefined ;
5778 let id : StringToken | undefined ;
5879 let name : ScalarToken | undefined ;
5980 let uses : StringToken | undefined ;
81+ let background : boolean | undefined ;
82+ let wait : StringToken [ ] | undefined ;
83+ let waitAll : boolean | undefined ;
84+ let cancel : StringToken | undefined ;
6085 let continueOnError : boolean | ScalarToken | undefined ;
6186 let env : MappingToken | undefined ;
6287 let ifCondition : BasicExpressionToken | undefined ;
@@ -81,6 +106,19 @@ function convertStep(context: TemplateContext, idBuilder: IdBuilder, step: Templ
81106 case "uses" :
82107 uses = item . value . assertString ( "steps item uses" ) ;
83108 break ;
109+ case "background" :
110+ background = item . value . assertBoolean ( "steps item background" ) . value ;
111+ break ;
112+ case "wait" :
113+ wait = convertWaitTargets ( context , backgroundStepIds , item . value , id ) ;
114+ break ;
115+ case "wait-all" :
116+ waitAll = convertWaitAllValue ( context , item . value ) ;
117+ break ;
118+ case "cancel" :
119+ cancel = item . value . assertString ( "steps item cancel" ) ;
120+ validateTargetStepId ( context , backgroundStepIds , cancel , id ) ;
121+ break ;
84122 case "env" :
85123 env = item . value . assertMapping ( "step env" ) ;
86124 break ;
@@ -103,7 +141,8 @@ function convertStep(context: TemplateContext, idBuilder: IdBuilder, step: Templ
103141 if : ifCondition || new BasicExpressionToken ( undefined , undefined , "success()" , undefined , undefined , undefined ) ,
104142 "continue-on-error" : continueOnError ,
105143 env,
106- run
144+ run,
145+ background
107146 } ;
108147 }
109148
@@ -114,10 +153,38 @@ function convertStep(context: TemplateContext, idBuilder: IdBuilder, step: Templ
114153 if : ifCondition || new BasicExpressionToken ( undefined , undefined , "success()" , undefined , undefined , undefined ) ,
115154 "continue-on-error" : continueOnError ,
116155 env,
117- uses
156+ uses,
157+ background
118158 } ;
119159 }
120- context . error ( step , "Expected uses or run to be defined" ) ;
160+
161+ if ( wait ) {
162+ return {
163+ id : id ?. value || "" ,
164+ name : name || createSyntheticStepName ( "Wait" ) ,
165+ "continue-on-error" : continueOnError ,
166+ wait
167+ } ;
168+ }
169+
170+ if ( waitAll !== undefined ) {
171+ return {
172+ id : id ?. value || "" ,
173+ name : name || createSyntheticStepName ( "Wait for all" ) ,
174+ "continue-on-error" : continueOnError ,
175+ "wait-all" : waitAll
176+ } ;
177+ }
178+
179+ if ( cancel ) {
180+ return {
181+ id : id ?. value || "" ,
182+ name : name || createSyntheticStepName ( "Cancel" ) ,
183+ "continue-on-error" : continueOnError ,
184+ cancel
185+ } ;
186+ }
187+ context . error ( step , "Expected one of uses, run, wait, wait-all, or cancel to be defined" ) ;
121188}
122189
123190function createActionStepId ( step : ActionStep ) : string {
@@ -144,3 +211,57 @@ function createActionStepId(step: ActionStep): string {
144211
145212 return "" ;
146213}
214+
215+ function createSyntheticStepName ( value : string ) : ScalarToken {
216+ return new StringToken ( undefined , undefined , value , undefined , undefined , undefined ) ;
217+ }
218+
219+ function convertWaitTargets (
220+ context : TemplateContext ,
221+ backgroundStepIds : Set < string > ,
222+ token : TemplateToken ,
223+ ownStepId ?: StringToken
224+ ) : StringToken [ ] {
225+ if ( token instanceof StringToken ) {
226+ validateTargetStepId ( context , backgroundStepIds , token , ownStepId ) ;
227+ return [ token ] ;
228+ }
229+
230+ const sequence = token . assertSequence ( "steps item wait" ) ;
231+ const targets : StringToken [ ] = [ ] ;
232+ for ( let i = 0 ; i < sequence . count ; i ++ ) {
233+ const target = sequence . get ( i ) . assertString ( "steps item wait item" ) ;
234+ validateTargetStepId ( context , backgroundStepIds , target , ownStepId ) ;
235+ targets . push ( target ) ;
236+ }
237+
238+ return targets ;
239+ }
240+
241+ function convertWaitAllValue ( context : TemplateContext , token : TemplateToken ) : boolean {
242+ if ( token instanceof NullToken ) {
243+ return true ;
244+ }
245+
246+ const value = token . assertBoolean ( "steps item wait-all" ) . value ;
247+ if ( ! value ) {
248+ context . error ( token , "The value of 'wait-all' must be true or omitted" ) ;
249+ }
250+
251+ return true ;
252+ }
253+
254+ function validateTargetStepId (
255+ context : TemplateContext ,
256+ backgroundStepIds : Set < string > ,
257+ target : StringToken ,
258+ ownStepId ?: StringToken
259+ ) {
260+ if ( target . value . startsWith ( "__" ) ) {
261+ context . error ( target , `The identifier '${ target . value } ' is invalid. IDs starting with '__' are reserved.` ) ;
262+ } else if ( ownStepId && target . value . toLowerCase ( ) === ownStepId . value . toLowerCase ( ) ) {
263+ context . error ( target , `Step '${ ownStepId . value } ' cannot reference itself` ) ;
264+ } else if ( ! backgroundStepIds . has ( target . value . toLowerCase ( ) ) ) {
265+ context . error ( target , `Step references unknown background step ID '${ target . value } '` ) ;
266+ }
267+ }
0 commit comments