33const YAML = require ( 'yaml' )
44const path = require ( 'path' )
55const fs = require ( 'fs' )
6+ const fetch = require ( 'node-fetch' )
67process . env . DISABLE_BOX_BANNER = true
78const simplify = require ( 'simplify-sdk' )
89const { options } = require ( 'yargs' ) ;
@@ -50,6 +51,63 @@ var argv = yargs.usage('simplify-pipeline create|list [stage] [options]')
5051
5152showBoxBanner ( )
5253
54+ function parseIncludes ( yamlObject ) {
55+ return new Promise ( ( resolve , reject ) => {
56+ let stages = [ ...yamlObject . stages ]
57+ if ( yamlObject . include ) {
58+ let remoteUrls = [ ]
59+ yamlObject . include . map ( item => {
60+ if ( item . local ) {
61+ if ( fs . existsSync ( path . resolve ( item . local ) ) ) {
62+ const ymlObj = fs . readFileSync ( path . resolve ( item . local ) ) . toString ( )
63+ const includedObj = YAML . parse ( ymlObj )
64+ yamlObject = { ...yamlObject , ...includedObj }
65+ if ( includedObj . stages ) {
66+ stages = [ ...stages , ...includedObj . stages ]
67+ }
68+ }
69+ } else if ( item . template ) {
70+ remoteUrls . push ( `https://gitlab.com/gitlab-org/gitlab/-/raw/master/lib/gitlab/ci/templates/${ item . template } ` )
71+ }
72+ } )
73+ function arrayBuffer2String ( buf , callback ) {
74+ var bb = new BlobBuilder ( ) ;
75+ bb . append ( buf ) ;
76+ var f = new FileReader ( ) ;
77+ f . onload = function ( e ) {
78+ callback ( e . target . result )
79+ }
80+ f . readAsText ( bb . getBlob ( ) ) ;
81+ }
82+ /** fetching remote templates from GitLab... */
83+ if ( remoteUrls . length > 0 ) {
84+ Promise . all ( remoteUrls . map ( ( url ) => fetch ( url ) ) ) . then ( ( responses ) => {
85+ return Promise . all ( responses . map ( ( res ) => res . text ( ) ) ) . then ( ( buffers ) => {
86+ return buffers . map ( ( buffer ) => {
87+ return YAML . parse ( buffer )
88+ } ) ;
89+ } ) ;
90+ } ) . then ( ( finalObjects ) => {
91+ yamlObject . stages = [ ...new Set ( stages ) ]
92+ finalObjects . map ( m => {
93+ if ( m . stages ) {
94+ stages = [ ...stages , ...m . stages ]
95+ }
96+ yamlObject = { ...yamlObject , ...m }
97+ } )
98+ yamlObject . stages = [ ...new Set ( stages ) ]
99+ resolve ( yamlObject )
100+ } ) ;
101+ } else {
102+ yamlObject . stages = [ ...new Set ( stages ) ]
103+ resolve ( yamlObject )
104+ }
105+ } else {
106+ resolve ( yamlObject )
107+ }
108+ } )
109+ }
110+
53111var cmdOPS = ( argv . _ [ 0 ] || 'create' ) . toUpperCase ( )
54112var optCMD = ( argv . _ . length > 1 ? argv . _ [ 1 ] : undefined )
55113var index = - 1
@@ -60,109 +118,123 @@ if (!fs.existsSync(path.resolve(`${filename}`))) {
60118 process . exit ( )
61119}
62120const file = fs . readFileSync ( path . resolve ( `${ filename } ` ) , 'utf8' )
63- const yamlObject = YAML . parse ( file )
64- if ( cmdOPS == 'CREATE' ) {
65- if ( ! optCMD ) {
66- index = readlineSync . keyInSelect ( yamlObject . stages , `Select a stage to execute ?` , {
67- cancel : `${ CBRIGHT } None${ CRESET } - (Escape)`
68- } )
69- } else {
70- index = yamlObject . stages . indexOf ( optCMD )
71- }
72- let dockerfileContent = [ 'FROM scratch' ]
73- if ( index >= 0 ) {
74- const executedStage = yamlObject . stages [ index ]
75- let dockerComposeContent = { version : '3.8' , services : { } , volumes : { } }
76- dockerComposeContent . volumes [ `shared` ] = {
77- "driver" : "local" ,
78- "driver_opts" : {
79- "type" : "none" ,
80- "device" : "$PWD" ,
81- "o" : "bind"
121+ let yamlObject = YAML . parse ( file )
122+ if ( yamlObject . include ) {
123+ parseIncludes ( yamlObject ) . then ( result => {
124+ yamlObject = result
125+ if ( cmdOPS == 'CREATE' ) {
126+ if ( ! optCMD ) {
127+ index = readlineSync . keyInSelect ( yamlObject . stages , `Select a stage to execute ?` , {
128+ cancel : `${ CBRIGHT } None${ CRESET } - (Escape)`
129+ } )
130+ } else {
131+ index = yamlObject . stages . indexOf ( optCMD )
82132 }
83- }
84- let stageExecutionChains = [ ]
85- let dockerCacheVolumes = [ ]
86- let dockerBaseContent = [ `FROM ${ yamlObject . image } ` , 'WORKDIR /source' , 'VOLUME /source' , 'COPY . /source' ]
87- let dockerBasePath = `${ projectName } /Dockerfile`
88- const baseImage = `base-${ yamlObject . image . split ( ':' ) [ 0 ] } `
89- const baseDirName = path . dirname ( path . resolve ( dockerBasePath ) )
90- if ( ! fs . existsSync ( baseDirName ) ) {
91- fs . mkdirSync ( baseDirName , { recursive : true } )
92- }
93- if ( yamlObject . cache && yamlObject . cache . paths && yamlObject . cache . paths . length ) {
94- dockerCacheVolumes . push ( )
95- }
96- fs . writeFileSync ( dockerBasePath , dockerBaseContent . join ( '\n' ) )
97- dockerComposeContent . services [ baseImage ] = { build : { context : `../` , dockerfile : `${ projectName } /Dockerfile` , args : { } } }
98-
99- const variables = simplify . getContentArgs ( yamlObject . variables , { ...process . env } )
100- Object . keys ( yamlObject ) . map ( ( key , idx ) => {
101- if ( yamlObject [ key ] . stage === executedStage ) {
102- const stageName = `${ idx } -${ executedStage } .${ key } `
103- dockerComposeContent . services [ key ] = { build : { context : `../` , dockerfile : `${ projectName } /${ stageName } .Dockerfile` , args : { } } , volumes : [ `shared:/source` ] }
104- stageExecutionChains . push ( `${ stageName } ` )
105- dockerfileContent = [ `FROM ${ projectName . replace ( / \. / g, '' ) } _${ baseImage } ` , 'WORKDIR /source' ]
106- let dockerCommands = [ ]
107- let dockerBeforeCommands = [ ]
133+ let dockerfileContent = [ 'FROM scratch' ]
134+ if ( index >= 0 ) {
135+ const executedStage = yamlObject . stages [ index ]
136+ let dockerComposeContent = { version : '3.8' , services : { } , volumes : { } }
137+ dockerComposeContent . volumes [ `shared` ] = {
138+ "driver" : "local" ,
139+ "driver_opts" : {
140+ "type" : "none" ,
141+ "device" : "$PWD" ,
142+ "o" : "bind"
143+ }
144+ }
145+ let stageExecutionChains = [ ]
146+ let dockerCacheVolumes = [ ]
147+ let dockerBaseContent = [ `FROM ${ yamlObject . image } ` , 'WORKDIR /source' , 'VOLUME /source' , 'COPY . /source' ]
148+ let dockerBasePath = `${ projectName } /Dockerfile`
149+ const baseImage = `base-${ yamlObject . image . split ( ':' ) [ 0 ] } `
150+ const baseDirName = path . dirname ( path . resolve ( dockerBasePath ) )
151+ if ( ! fs . existsSync ( baseDirName ) ) {
152+ fs . mkdirSync ( baseDirName , { recursive : true } )
153+ }
154+ if ( yamlObject . cache && yamlObject . cache . paths && yamlObject . cache . paths . length ) {
155+ dockerCacheVolumes . push ( )
156+ }
157+ fs . writeFileSync ( dockerBasePath , dockerBaseContent . join ( '\n' ) )
158+ dockerComposeContent . services [ baseImage ] = { build : { context : `../` , dockerfile : `${ projectName } /Dockerfile` , args : { } } }
159+
160+ const variables = simplify . getContentArgs ( yamlObject . variables , { ...process . env } )
161+ Object . keys ( yamlObject ) . map ( ( key , idx ) => {
162+ if ( yamlObject [ key ] . stage === executedStage ) {
163+ const stageName = `${ idx } -${ executedStage } .${ key } `
164+ dockerComposeContent . services [ key ] = { build : { context : `../` , dockerfile : `${ projectName } /${ stageName } .Dockerfile` , args : { } } , volumes : [ `shared:/source` ] }
165+ stageExecutionChains . push ( `${ stageName } ` )
166+ dockerfileContent = [ `FROM ${ projectName . replace ( / \. / g, '' ) } _${ baseImage } ` , 'WORKDIR /source' ]
167+ let dockerCommands = [ ]
168+ let dockerBeforeCommands = [ ]
169+
170+ simplify . getContentArgs ( yamlObject [ key ] . before_script , { ...process . env } , { ...variables } ) . map ( script => {
171+ let scriptContent = script
172+ if ( script . startsWith ( 'export ' ) ) {
173+ let dockerOpts = 'ENV'
174+ scriptContent = script . replace ( 'export ' , '' )
175+ const argKeyValue = scriptContent . split ( '=' )
176+ dockerComposeContent . services [ key ] . build . args [ `${ argKeyValue [ 0 ] . trim ( ) } ` ] = `${ argKeyValue [ 1 ] . trim ( ) } `
177+ scriptContent = `${ argKeyValue [ 0 ] . trim ( ) } ="${ argKeyValue [ 1 ] . trim ( ) } "`
178+ dockerfileContent . push ( `${ dockerOpts } ${ scriptContent } ` )
179+ } else {
180+ dockerBeforeCommands . push ( `${ scriptContent } ` )
181+ }
182+ } )
108183
109- simplify . getContentArgs ( yamlObject [ key ] . before_script , { ...process . env } , { ...variables } ) . map ( script => {
110- let scriptContent = script
111- if ( script . startsWith ( 'export ' ) ) {
112- let dockerOpts = 'ENV'
113- scriptContent = script . replace ( 'export ' , '' )
114- const argKeyValue = scriptContent . split ( '=' )
115- dockerComposeContent . services [ key ] . build . args [ `${ argKeyValue [ 0 ] . trim ( ) } ` ] = `${ argKeyValue [ 1 ] . trim ( ) } `
116- scriptContent = `${ argKeyValue [ 0 ] . trim ( ) } ="${ argKeyValue [ 1 ] . trim ( ) } "`
117- dockerfileContent . push ( `${ dockerOpts } ${ scriptContent } ` )
118- } else {
119- dockerBeforeCommands . push ( `${ scriptContent } ` )
184+ simplify . getContentArgs ( yamlObject [ key ] . script , { ...process . env } , { ...variables } ) . map ( script => {
185+ let scriptContent = script
186+ if ( script . startsWith ( 'export ' ) ) {
187+ let dockerOpts = 'ENV'
188+ scriptContent = script . replace ( 'export ' , '' )
189+ const argKeyValue = scriptContent . split ( '=' )
190+ dockerComposeContent . services [ key ] . build . args [ `${ argKeyValue [ 0 ] . trim ( ) } ` ] = `${ argKeyValue [ 1 ] . trim ( ) } `
191+ scriptContent = `${ argKeyValue [ 0 ] . trim ( ) } ="${ argKeyValue [ 1 ] . trim ( ) } "`
192+ dockerfileContent . push ( `${ dockerOpts } ${ scriptContent } ` )
193+ } else {
194+ dockerCommands . push ( `${ scriptContent } ` )
195+ }
196+ } )
197+ dockerfileContent . push ( `RUN ${ dockerBeforeCommands . join ( ' && ' ) } ` )
198+ dockerfileContent . push ( `RUN ${ dockerCommands . join ( ' && ' ) } ` )
199+ let dockerfilePath = `${ projectName } /${ stageName } .Dockerfile`
200+ const pathDirName = path . dirname ( path . resolve ( dockerfilePath ) )
201+ if ( ! fs . existsSync ( pathDirName ) ) {
202+ fs . mkdirSync ( pathDirName , { recursive : true } )
203+ }
204+ fs . writeFileSync ( dockerfilePath , dockerfileContent . join ( '\n' ) )
120205 }
121206 } )
122-
123- simplify . getContentArgs ( yamlObject [ key ] . script , { ...process . env } , { ...variables } ) . map ( script => {
124- let scriptContent = script
125- if ( script . startsWith ( 'export ' ) ) {
126- let dockerOpts = 'ENV'
127- scriptContent = script . replace ( 'export ' , '' )
128- const argKeyValue = scriptContent . split ( '=' )
129- dockerComposeContent . services [ key ] . build . args [ `${ argKeyValue [ 0 ] . trim ( ) } ` ] = `${ argKeyValue [ 1 ] . trim ( ) } `
130- scriptContent = `${ argKeyValue [ 0 ] . trim ( ) } ="${ argKeyValue [ 1 ] . trim ( ) } "`
131- dockerfileContent . push ( `${ dockerOpts } ${ scriptContent } ` )
132- } else {
133- dockerCommands . push ( `${ scriptContent } ` )
207+ let dockerComposePath = `${ projectName } /docker-compose.${ executedStage } .yml`
208+ fs . writeFileSync ( dockerComposePath , YAML . stringify ( dockerComposeContent ) )
209+ console . log ( `Created ${ projectName } docker-compose for stage '${ optCMD } ' cached to '${ projectName } ' volume` )
210+ fs . writeFileSync ( `pipeline.bash` , [
211+ '#!/bin/bash' ,
212+ `cd ${ projectName } ` ,
213+ `docker volume rm ${ projectName . replace ( / \. / g, '' ) } _shared > /dev/null` ,
214+ `docker-compose -f docker-compose.$1.yml --project-name ${ projectName } up --force-recreate`
215+ ] . join ( '\n' ) )
216+ }
217+ } else if ( cmdOPS == 'LIST' ) {
218+ if ( ! optCMD ) {
219+ yamlObject . stages . map ( ( cmd , idx ) => {
220+ console . log ( `\t- ${ CPROMPT } ${ cmd . toLowerCase ( ) } ${ CRESET } ` )
221+ } )
222+ } else {
223+ Object . keys ( yamlObject ) . map ( ( key , idx ) => {
224+ if ( yamlObject [ key ] . stage === optCMD ) {
225+ const stageName = `[${ optCMD } ] ${ key } `
226+ console . log ( `\t- ${ CPROMPT } ${ stageName . toLowerCase ( ) } ${ CRESET } ` )
134227 }
135228 } )
136- dockerfileContent . push ( `RUN ${ dockerBeforeCommands . join ( ' && ' ) } ` )
137- dockerfileContent . push ( `RUN ${ dockerCommands . join ( ' && ' ) } ` )
138- let dockerfilePath = `${ projectName } /${ stageName } .Dockerfile`
139- const pathDirName = path . dirname ( path . resolve ( dockerfilePath ) )
140- if ( ! fs . existsSync ( pathDirName ) ) {
141- fs . mkdirSync ( pathDirName , { recursive : true } )
142- }
143- fs . writeFileSync ( dockerfilePath , dockerfileContent . join ( '\n' ) )
144229 }
145- } )
146- let dockerComposePath = `${ projectName } /docker-compose.${ executedStage } .yml`
147- fs . writeFileSync ( dockerComposePath , YAML . stringify ( dockerComposeContent ) )
148- console . log ( `Created ${ projectName } docker-compose for stage '${ optCMD } ' cached to '${ projectName } ' volume` )
149- fs . writeFileSync ( `pipeline.bash` , [
150- '#!/bin/bash' ,
151- `cd ${ projectName } ` ,
152- `docker volume rm ${ projectName . replace ( / \. / g, '' ) } _shared > /dev/null` ,
153- `docker-compose -f docker-compose.$1.yml --project-name ${ projectName } up --force-recreate`
154- ] . join ( '\n' ) )
155- }
156- } else if ( cmdOPS == 'LIST' ) {
157- yamlObject . stages . map ( ( cmd , idx ) => {
158- console . log ( `\t- ${ CPROMPT } ${ cmd . toLowerCase ( ) } ${ CRESET } ` )
159- } )
160- } else {
161- yargs . showHelp ( )
162- console . log ( `\n` , ` * ${ CBRIGHT } Supported command list${ CRESET } :` , '\n' )
163- OPT_COMMANDS . map ( ( cmd , idx ) => {
164- console . log ( `\t- ${ CPROMPT } ${ cmd . name . toLowerCase ( ) } ${ CRESET } : ${ cmd . desc } ` )
230+ } else {
231+ yargs . showHelp ( )
232+ console . log ( `\n` , ` * ${ CBRIGHT } Supported command list${ CRESET } :` , '\n' )
233+ OPT_COMMANDS . map ( ( cmd , idx ) => {
234+ console . log ( `\t- ${ CPROMPT } ${ cmd . name . toLowerCase ( ) } ${ CRESET } : ${ cmd . desc } ` )
235+ } )
236+ console . log ( `\n` )
237+ process . exit ( 0 )
238+ }
165239 } )
166- console . log ( `\n` )
167- process . exit ( 0 )
168240}
0 commit comments