@@ -77,7 +77,7 @@ interface StartOptions {
7777 *
7878 * @param {string|undefined } branch server branch to use (default 'master')
7979 * @param {boolean|string|undefined } mountApp bind mount app within server (`true` for autodetect, `false` to disable, or a string to force a path) (default true)
80- * @param {StartOptions|undefined } options Optional parameters to configre the container creation
80+ * @param {StartOptions|undefined } options Optional parameters to configure the container creation
8181 * @return Promise resolving to the IP address of the server
8282 * @throws {Error } If Nextcloud container could not be started
8383 */
@@ -109,19 +109,7 @@ export async function startNextcloud(branch = 'master', mountApp: boolean|string
109109 }
110110
111111 try {
112- // Pulling images
113- console . log ( '\nPulling images… ⏳' )
114- await new Promise ( ( resolve , reject ) => docker . pull ( SERVER_IMAGE , ( _err , stream : Stream ) => {
115- const onFinished = function ( err : Error | null ) {
116- if ( ! err ) {
117- return resolve ( true )
118- }
119- reject ( err )
120- }
121- // https://github.com/apocas/dockerode/issues/357
122- docker . modem . followProgress ( stream , onFinished )
123- } ) )
124- console . log ( '└─ Done' )
112+ await pullImage ( )
125113
126114 // Getting latest image
127115 console . log ( '\nChecking running containers… 🔍' )
@@ -189,8 +177,8 @@ export async function startNextcloud(branch = 'master', mountApp: boolean|string
189177 await container . start ( )
190178
191179 // Set proper permissions for the data folder
192- await runExec ( container , [ 'chown' , '-R' , 'www-data:www-data' , '/var/www/html/data' ] , false , 'root' )
193- await runExec ( container , [ 'chmod' , '0770' , '/var/www/html/data' ] , false , 'root' )
180+ await runExec ( [ 'chown' , '-R' , 'www-data:www-data' , '/var/www/html/data' ] , { container , user : 'root' } )
181+ await runExec ( [ 'chmod' , '0770' , '/var/www/html/data' ] , { container , user : 'root' } )
194182
195183 // Get container's IP
196184 const ip = await getContainerIP ( container )
@@ -207,6 +195,27 @@ export async function startNextcloud(branch = 'master', mountApp: boolean|string
207195 }
208196}
209197
198+ const pullImage = function ( ) {
199+ // Pulling images
200+ console . log ( '\nPulling images… ⏳' )
201+ return new Promise ( ( resolve , reject ) => docker . pull ( SERVER_IMAGE , ( _err , stream : Stream ) => {
202+ const onFinished = function ( err : Error | null ) {
203+ if ( ! err ) {
204+ return resolve ( true )
205+ }
206+ reject ( err )
207+ }
208+ // https://github.com/apocas/dockerode/issues/357
209+ if ( stream ) {
210+ docker . modem . followProgress ( stream , onFinished )
211+ } else {
212+ reject ( 'Failed to open stream' )
213+ }
214+ } ) )
215+ . then ( ( ) => console . log ( '└─ Done' ) )
216+ . catch ( err => console . log ( `└─ 🛑 FAILED! Trying to continue with existing image. (${ err } )` ) )
217+ }
218+
210219/**
211220 * Configure Nextcloud
212221 *
@@ -219,20 +228,20 @@ export const configureNextcloud = async function(apps = ['viewer'], vendoredBran
219228
220229 console . log ( '\nConfiguring Nextcloud…' )
221230 container = container ?? getContainer ( )
222- await runExec ( container , [ 'php' , 'occ' , ' --version'] , true )
231+ await runOcc ( ' --version', { container , verbose : true } )
223232
224233 // Be consistent for screenshots
225- await runExec ( container , [ 'php' , 'occ' , 'config:system:set' , ' default_language', '--value' , ' en'] , true )
226- await runExec ( container , [ 'php' , 'occ' , 'config:system:set' , ' force_language', '--value' , ' en'] , true )
227- await runExec ( container , [ 'php' , 'occ' , 'config:system:set' , ' default_locale', '--value' , ' en_US'] , true )
228- await runExec ( container , [ 'php' , 'occ' , 'config:system:set' , ' force_locale', '--value' , ' en_US'] , true )
229- await runExec ( container , [ 'php' , 'occ' , 'config:system:set' , ' enforce_theme', '--value' , ' light'] , true )
234+ await setSystemConfig ( ' default_language', 'en' , { container } )
235+ await setSystemConfig ( ' force_language', 'en' , { container } )
236+ await setSystemConfig ( ' default_locale', 'en_US' , { container } )
237+ await setSystemConfig ( ' force_locale', 'en_US' , { container } )
238+ await setSystemConfig ( ' enforce_theme', 'light' , { container } )
230239
231240 // Checking apcu
232241 console . log ( '├─ Checking APCu configuration... 👀' )
233- const distributed = await runExec ( container , [ 'php ', 'occ' , 'config:system:get' , 'memcache.distributed' ] )
234- const local = await runExec ( container , [ 'php ', 'occ' , 'config:system:get' , 'memcache.local' ] )
235- const hashing = await runExec ( container , [ 'php ', 'occ' , 'config:system:get' , 'hashing_default_password' ] )
242+ const distributed = await getSystemConfig ( 'memcache.distributed ', { container } )
243+ const local = await getSystemConfig ( 'memcache.local ', { container } )
244+ const hashing = await getSystemConfig ( 'hashing_default_password ', { container } )
236245 if ( ! distributed . includes ( 'Memcache\\APCu' )
237246 || ! local . includes ( 'Memcache\\APCu' )
238247 || ! hashing . includes ( 'true' ) ) {
@@ -242,7 +251,7 @@ export const configureNextcloud = async function(apps = ['viewer'], vendoredBran
242251 console . log ( '│ └─ OK !' )
243252
244253 // Build app list
245- const json = await runExec ( container , [ 'php' , 'occ' , ' app:list', '--output' , 'json' ] , false )
254+ const json = await runOcc ( [ ' app:list', '--output' , 'json' ] , { container } )
246255 // fix dockerode bug returning invalid leading characters
247256 const applist = JSON . parse ( json . substring ( json . indexOf ( '{' ) ) )
248257
@@ -252,14 +261,14 @@ export const configureNextcloud = async function(apps = ['viewer'], vendoredBran
252261 console . log ( `├─ ${ app } version ${ applist . enabled [ app ] } already installed and enabled` )
253262 } else if ( app in applist . disabled ) {
254263 // built in or mounted already as the app under development
255- await runExec ( container , [ 'php' , 'occ' , ' app:enable', '--force' , app ] , true )
264+ await runOcc ( [ ' app:enable', '--force' , app ] , { container , verbose : true } )
256265 } else if ( app in VENDOR_APPS ) {
257266 // apps that are vendored but still missing (i.e. not build in or mounted already)
258- await runExec ( container , [ 'git' , 'clone' , '--depth=1' , `--branch=${ vendoredBranch } ` , VENDOR_APPS [ app ] , `apps/${ app } ` ] , true )
259- await runExec ( container , [ 'php' , 'occ' , ' app:enable', '--force' , app ] , true )
267+ await runExec ( [ 'git' , 'clone' , '--depth=1' , `--branch=${ vendoredBranch } ` , VENDOR_APPS [ app ] , `apps/${ app } ` ] , { container , verbose : true } )
268+ await runOcc ( [ ' app:enable', '--force' , app ] , { container , verbose : true } )
260269 } else {
261270 // try appstore
262- await runExec ( container , [ 'php' , 'occ' , ' app:install', '--force' , app ] , true )
271+ await runOcc ( [ ' app:install', '--force' , app ] , { container , verbose : true } )
263272 }
264273 }
265274 console . log ( '└─ Nextcloud is now ready to use 🎉' )
@@ -274,7 +283,7 @@ export const setupUsers = async function(container?: Container) {
274283 console . log ( '\nCreating test users… 👤' )
275284 const users = [ 'test1' , 'test2' , 'test3' , 'test4' , 'test5' ]
276285 for ( const user of users ) {
277- await runExec ( container ?? getContainer ( ) , [ 'php' , 'occ' , 'user:add' , user , '--password-from-env' ] , true , 'www-data' , [ 'OC_PASS=' + user ] )
286+ await addUser ( user , { container , verbose : true } )
278287 }
279288 console . log ( '└─ Done' )
280289}
@@ -288,7 +297,7 @@ export const setupUsers = async function(container?: Container) {
288297export const createSnapshot = async function ( snapshot ?: string , container ?: Container ) : Promise < string > {
289298 const hash = new Date ( ) . toISOString ( ) . replace ( / [ ^ 0 - 9 ] / g, '' )
290299 console . log ( '\nCreating init DB snapshot…' )
291- await runExec ( container ?? getContainer ( ) , [ 'cp' , '/var/www/html/data/owncloud.db' , `/var/www/html/data/owncloud.db-${ snapshot ?? hash } ` ] , true )
300+ await runExec ( [ 'cp' , '/var/www/html/data/owncloud.db' , `/var/www/html/data/owncloud.db-${ snapshot ?? hash } ` ] , { container , verbose : true } )
292301 console . log ( '└─ Done' )
293302 return snapshot ?? hash
294303}
@@ -300,7 +309,7 @@ export const createSnapshot = async function(snapshot?: string, container?: Cont
300309 */
301310export const restoreSnapshot = async function ( snapshot = 'init' , container ?: Container ) {
302311 console . log ( '\nRestoring DB snapshot…' )
303- await runExec ( container ?? getContainer ( ) , [ 'cp' , `/var/www/html/data/owncloud.db-${ snapshot } ` , '/var/www/html/data/owncloud.db' ] , true )
312+ await runExec ( [ 'cp' , `/var/www/html/data/owncloud.db-${ snapshot } ` , '/var/www/html/data/owncloud.db' ] , { container , verbose : true } )
304313 console . log ( '└─ Done' )
305314}
306315
@@ -356,15 +365,23 @@ export const waitOnNextcloud = async function(ip: string) {
356365 console . log ( '└─ Done' )
357366}
358367
359- const runExec = async function (
360- container : Docker . Container ,
361- command : string [ ] ,
362- verbose = false ,
363- user = 'www-data' ,
364- env : string [ ] = [ ] ,
368+ interface RunExecOptions {
369+ container : Docker . Container ;
370+ user : string ;
371+ env : string [ ] ;
372+ verbose : boolean ;
373+ }
374+
375+ /**
376+ * Execute a command in the container
377+ */
378+ export const runExec = async function (
379+ command : string | string [ ] ,
380+ { container, user= 'www-data' , verbose= false , env= [ ] } : Partial < RunExecOptions > = { } ,
365381) {
382+ container = container || getContainer ( )
366383 const exec = await container . exec ( {
367- Cmd : command ,
384+ Cmd : typeof command === 'string' ? [ command ] : command ,
368385 AttachStdout : true ,
369386 AttachStderr : true ,
370387 User : user ,
@@ -397,6 +414,52 @@ const runExec = async function(
397414 } )
398415}
399416
417+ /**
418+ * Execute an occ command in the container
419+ */
420+ export const runOcc = function (
421+ command : string | string [ ] ,
422+ { container, env= [ ] , verbose= false } : Partial < Omit < RunExecOptions , 'user' > > = { } ,
423+ ) {
424+ const cmdArray = typeof command === 'string' ? [ command ] : command
425+ return runExec ( [ 'php' , 'occ' , ...cmdArray ] , { container, verbose, env } )
426+ }
427+
428+ /**
429+ * Set a Nextcloud system config in the container.
430+ */
431+ export const setSystemConfig = function (
432+ key : string ,
433+ value : string ,
434+ { container } : { container ?: Docker . Container } = { } ,
435+ ) {
436+ return runOcc ( [ 'config:system:set' , key , '--value' , value ] , { container, verbose : true } )
437+ }
438+
439+ /**
440+ * Get a Nextcloud system config value from the container.
441+ */
442+ export const getSystemConfig = function (
443+ key : string ,
444+ { container } : { container ?: Docker . Container } = { } ,
445+ ) {
446+ return runOcc ( [ 'config:system:get' , key ] , { container } )
447+ }
448+
449+
450+ /**
451+ * Add a user to the Nextcloud in the container.
452+ */
453+ export const addUser = function (
454+ user : string ,
455+ { container, env= [ ] , verbose= false } : Partial < Omit < RunExecOptions , 'user' > > = { } ,
456+ ) {
457+ return runOcc (
458+ [ 'user:add' , user , '--password-from-env' ] ,
459+ { container, verbose, env : [ 'OC_PASS=' + user , ...env ] }
460+ )
461+ }
462+
400463const sleep = function ( milliseconds : number ) {
401464 return new Promise ( ( resolve ) => setTimeout ( resolve , milliseconds ) )
402465}
0 commit comments