mwdd_mediawiki.go 16.5 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/*Package cmd is used for command line.

Copyright © 2020 Addshore

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package cmd

import (
	"fmt"
22
	"os"
23
24
25
26
	"os/user"
	"path/filepath"
	"strings"
	"time"
Addshore's avatar
Addshore committed
27
28

	"gerrit.wikimedia.org/r/mediawiki/tools/cli/internal/exec"
29
	"gerrit.wikimedia.org/r/mediawiki/tools/cli/internal/mediawiki"
Addshore's avatar
Addshore committed
30
	"gerrit.wikimedia.org/r/mediawiki/tools/cli/internal/mwdd"
31
	"github.com/briandowns/spinner"
32
	"github.com/manifoldco/promptui"
33
34
35
36
	"github.com/spf13/cobra"
)

var mwddMediawikiCmd = &cobra.Command{
37
38
39
40
	Use:     "mediawiki",
	Short:   "MediaWiki service",
	Aliases: []string{"mw"},
	RunE:    nil,
41
42
43
44
	PersistentPreRun: func(cmd *cobra.Command, args []string) {
		cmd.Parent().Parent().PersistentPreRun(cmd, args)
		mwdd := mwdd.DefaultForUser()
		mwdd.EnsureReady()
45
46
47
48

		usr, _ := user.Current()
		usrDir := usr.HomeDir

Addshore's avatar
Addshore committed
49
		if mwdd.Env().Missing("MEDIAWIKI_VOLUMES_CODE") {
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68

			// Try to autodetect if we are in a MediaWiki directory at all
			suggestedMwDir, err := os.Getwd()
			if err != nil {
				panic(err)
			}
			for {
				_, checkError := mediawiki.ForDirectory(suggestedMwDir)
				if checkError == nil {
					break
				}
				suggestedMwDir = filepath.Dir(suggestedMwDir)
				if suggestedMwDir == "/" {
					suggestedMwDir = "~/dev/git/gerrit/mediawiki/core"
					break
				}
			}

			// Prompt the user for a directory or confirmation
69
			dirPrompt := promptui.Prompt{
Addshore's avatar
Addshore committed
70
				Label:   "What directory would you like to store MediaWiki source code in?",
71
				Default: suggestedMwDir,
72
73
74
75
76
77
78
79
80
81
82
83
84
85
			}
			value, err := dirPrompt.Run()

			// Deal with people entering ~/ paths and them not be handled
			if value == "~" {
				// In case of "~", which won't be caught by the "else if"
				value = usrDir
			} else if strings.HasPrefix(value, "~/") {
				// Use strings.HasPrefix so we don't match paths like
				// "/something/~/something/"
				value = filepath.Join(usrDir, value[2:])
			}

			if err == nil {
Addshore's avatar
Addshore committed
86
				mwdd.Env().Set("MEDIAWIKI_VOLUMES_CODE", value)
87
88
89
90
			} else {
				fmt.Println("Can't continue without a MediaWiki code directory")
				os.Exit(1)
			}
91
92
93
94

		}

		// Default the mediawiki container to a .composer directory in the running users home dir
Addshore's avatar
Addshore committed
95
		if !mwdd.Env().Has("MEDIAWIKI_VOLUMES_DOT_COMPOSER") {
96
97
98
99
100
101
102
103
			usrComposerDirectory := usrDir + "/.composer"
			if _, err := os.Stat(usrComposerDirectory); os.IsNotExist(err) {
				err := os.Mkdir(usrComposerDirectory, 0755)
				if err != nil {
					fmt.Println("Failed to create directory needed for a composer cache")
					os.Exit(1)
				}
			}
104
			mwdd.Env().Set("MEDIAWIKI_VOLUMES_DOT_COMPOSER", usrDir+"/.composer")
105
106
107
108
109
110
111
112
		}

		setupOpts := mediawiki.CloneSetupOpts{}
		mediawiki, _ := mediawiki.ForDirectory(mwdd.Env().Get("MEDIAWIKI_VOLUMES_CODE"))

		// TODO ask a question about what remotes you want to end up using? https vs ssh!
		// TODO ask if they want to get any more skins and extensions?
		// TODO async cloning of repos for speed!
Addshore's avatar
Addshore committed
113
		if !mediawiki.MediaWikiIsPresent() {
114
115
116
117
118
119
120
			cloneMwPrompt := promptui.Prompt{
				Label:     "MediaWiki code not detected in " + mwdd.Env().Get("MEDIAWIKI_VOLUMES_CODE") + ". Do you want to clone it now?",
				IsConfirm: true,
			}
			_, err := cloneMwPrompt.Run()
			setupOpts.GetMediaWiki = err == nil
		}
Addshore's avatar
Addshore committed
121
		if !mediawiki.VectorIsPresent() {
122
123
124
125
126
127
128
			cloneMwPrompt := promptui.Prompt{
				Label:     "Vector skin is not detected in " + mwdd.Env().Get("MEDIAWIKI_VOLUMES_CODE") + ". Do you want to clone it from Gerrit?",
				IsConfirm: true,
			}
			_, err := cloneMwPrompt.Run()
			setupOpts.GetVector = err == nil
		}
Addshore's avatar
Addshore committed
129
		if setupOpts.GetMediaWiki || setupOpts.GetVector {
130
			cloneFromGithubPrompt := promptui.Prompt{
131
132
133
				Label:     "Do you want to clone from Github for extra speed? (your git remotes will be switched to Gerrit after download)",
				IsConfirm: true,
			}
134
			_, err := cloneFromGithubPrompt.Run()
135
			setupOpts.UseGithub = err == nil
136
137

			cloneShallowPrompt := promptui.Prompt{
138
139
140
				Label:     "Do you want to use shallow clones for extra speed? (You can fetch all history later using `git fetch --unshallow`)",
				IsConfirm: true,
			}
141
			_, err = cloneShallowPrompt.Run()
142
143
			setupOpts.UseShallow = err == nil

144
			finalRemoteTypePrompt := promptui.Prompt{
Addshore's avatar
Addshore committed
145
				Label:   "How do you want to interact with Gerrit for the cloned repositores? (http or ssh)",
146
147
148
				Default: "ssh",
			}
			remoteType, err := finalRemoteTypePrompt.Run()
Addshore's avatar
Addshore committed
149
			if err != nil || (remoteType != "ssh" && remoteType != "http") {
150
151
152
153
				fmt.Println("Invalid Gerrit interaction type chosen.")
				os.Exit(1)
			}
			setupOpts.GerritInteractionType = remoteType
Addshore's avatar
Addshore committed
154
			if remoteType == "ssh" {
155
				gerritUsernamePrompt := promptui.Prompt{
Addshore's avatar
Addshore committed
156
					Label: "What is your Gerrit username?",
157
158
				}
				gerritUsername, err := gerritUsernamePrompt.Run()
Addshore's avatar
Addshore committed
159
				if err != nil || len(gerritUsername) < 1 {
160
161
162
163
164
165
					fmt.Println("Gerrit username required for ssh interaction type.")
					os.Exit(1)
				}
				setupOpts.GerritUsername = gerritUsername
			}
			setupOpts.UseShallow = err == nil
166
167
		}

Addshore's avatar
Addshore committed
168
		if setupOpts.GetMediaWiki || setupOpts.GetVector {
169
170
171
172
173
174
175
176
177
178
179
			// Clone various things in multiple stages
			Spinner := spinner.New(spinner.CharSets[9], 100*time.Millisecond)
			Spinner.Prefix = "Performing step"
			Spinner.FinalMSG = Spinner.Prefix + "(done)\n"
			setupOpts.Options = exec.HandlerOptions{
				Spinner: Spinner,
			}

			mediawiki.CloneSetup(setupOpts)

			// Check that the needed things seem to have happened
Addshore's avatar
Addshore committed
180
			if setupOpts.GetMediaWiki && !mediawiki.MediaWikiIsPresent() {
181
				fmt.Println("Something went wrong cloning MediaWiki")
Addshore's avatar
Addshore committed
182
				os.Exit(1)
183
			}
Addshore's avatar
Addshore committed
184
			if setupOpts.GetVector && !mediawiki.VectorIsPresent() {
185
				fmt.Println("Something went wrong cloning Vector")
Addshore's avatar
Addshore committed
186
				os.Exit(1)
187
			}
188
189
		}
	},
190
191
}

192
/*DbType used by the install command*/
Addshore's avatar
Addshore committed
193
194
var DbType string

195
/*DbName used by the install command*/
Addshore's avatar
Addshore committed
196
var DbName string
197

Addshore's avatar
Addshore committed
198
var mwddMediawikiInstallCmd = &cobra.Command{
199
200
201
	Use:     "install",
	Short:   "Installs a new MediaWiki site using install.php",
	Aliases: []string{"i"},
Addshore's avatar
Addshore committed
202
	Run: func(cmd *cobra.Command, args []string) {
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
		mediawiki, _ := mediawiki.ForDirectory(mwdd.DefaultForUser().Env().Get("MEDIAWIKI_VOLUMES_CODE"))
		if !mediawiki.LocalSettingsIsPresent() {
			prompt := promptui.Prompt{
				IsConfirm: true,
				Label:     "No LocalSettings.php detected. Do you want to create the default mwdd file?",
			}
			_, err := prompt.Run()
			if err == nil {
				lsPath := mediawiki.Path("LocalSettings.php")

				f, err := os.Create(lsPath)
				if err != nil {
					fmt.Println(err)
					return
				}
218
				settingsStringToWrite := "<?php\n//require_once \"$IP/includes/PlatformSettings.php\";\nrequire_once '/mwdd/MwddSettings.php';\n"
Addshore's avatar
Addshore committed
219
				if mediawiki.VectorIsPresent() {
220
221
222
					settingsStringToWrite += "\nwfLoadSkin('Vector');\n"
				}
				_, err = f.WriteString(settingsStringToWrite)
223
224
225
226
227
228
229
230
231
232
233
234
235
236
				if err != nil {
					fmt.Println(err)
					f.Close()
					return
				}
				err = f.Close()
				if err != nil {
					fmt.Println(err)
					return
				}
			} else {
				fmt.Println("Can't install without the expected LocalSettings.php file")
				return
			}
237
		}
238

Addshore's avatar
Addshore committed
239
		if !mediawiki.LocalSettingsContains("/mwdd/MwddSettings.php") {
240
			fmt.Println("LocalSettings.php file exists, but doesn't look right (missing mwcli mwdd shim)")
Addshore's avatar
Addshore committed
241
			return
242
		}
Addshore's avatar
Addshore committed
243

244
		// TODO make sure of composer caches
Addshore's avatar
Addshore committed
245
		composerErr := mwdd.DefaultForUser().ExecNoOutput("mediawiki", []string{
246
			"php", "/var/www/html/w/maintenance/checkComposerLockUpToDate.php",
247
		},
Addshore's avatar
Addshore committed
248
249
250
			exec.HandlerOptions{}, User)
		if composerErr != nil {
			fmt.Println("Composer check failed:", composerErr)
251
252
253
254
255
256
257
258
259
			prompt := promptui.Prompt{
				IsConfirm: true,
				Label:     "Composer dependencies are not up to date, do you want to composer install?",
			}
			_, err := prompt.Run()
			if err == nil {
				mwdd.DefaultForUser().DockerExec(mwdd.DockerExecCommand{
					DockerComposeService: "mediawiki",
					// --ignore-platform-reqs is currently used as only PHP7.2 is provided and some things need higher
Addshore's avatar
Addshore committed
260
261
262
					Command: []string{"composer", "install", "--ignore-platform-reqs", "--no-interaction"},
					User:    User,
				})
263
264
265
266
267
			} else {
				fmt.Println("Can't install without up to date composer dependencies")
				os.Exit(1)
			}
		}
268

269
		// Fix some permissions
Addshore's avatar
Addshore committed
270
271
		mwdd.DefaultForUser().Exec("mediawiki", []string{"chown", "-R", "nobody", "/var/www/html/w/data"}, exec.HandlerOptions{}, "root")
		mwdd.DefaultForUser().Exec("mediawiki", []string{"chown", "-R", "nobody", "/var/log/mediawiki"}, exec.HandlerOptions{}, "root")
272

273
274
275
276
277
278
279
280
281
		// Copy current local settings "somewhere safe", incase someone needs to restore it
		currentTime := time.Now()
		currentTimeString := currentTime.Format("20060102150405")
		mwdd.DefaultForUser().Exec("mediawiki", []string{
			"cp",
			"/var/www/html/w/LocalSettings.php",
			"/var/www/html/w/LocalSettings.php.mwdd.bak." + currentTimeString,
		}, exec.HandlerOptions{}, User)

Addshore's avatar
Addshore committed
282
		// Move custom LocalSetting.php so the install doesn't overwrite it
Addshore's avatar
Addshore committed
283
		mwdd.DefaultForUser().Exec("mediawiki", []string{
Addshore's avatar
Addshore committed
284
285
			"mv",
			"/var/www/html/w/LocalSettings.php",
286
			"/var/www/html/w/LocalSettings.php.mwdd.tmp",
Addshore's avatar
Addshore committed
287
		}, exec.HandlerOptions{}, "root")
Addshore's avatar
Addshore committed
288

289
290
291
292
		var serverLink string = "http://" + DbName + ".mediawiki.mwdd.localhost:" + mwdd.DefaultForUser().Env().Get("PORT")
		const adminUser string = "admin"
		const adminPass string = "mwddpassword"

293
294
		// Do a DB type dependant install, writing the output LocalSettings.php to /tmp
		if DbType == "sqlite" {
Addshore's avatar
Addshore committed
295
			mwdd.DefaultForUser().Exec("mediawiki", []string{
296
297
				"php",
				"/var/www/html/w/maintenance/install.php",
298
				"--confpath", "/tmp",
299
				"--server", serverLink,
300
301
				"--dbtype", DbType,
				"--dbname", DbName,
302
				"--lang", "en",
303
				"--pass", adminPass,
304
				"docker-" + DbName,
305
				adminUser,
Addshore's avatar
Addshore committed
306
			}, exec.HandlerOptions{}, "nobody")
307
		}
308
		if DbType == "mysql" {
Addshore's avatar
Addshore committed
309
			mwdd.DefaultForUser().Exec("mediawiki", []string{
310
311
				"/wait-for-it.sh",
				"mysql:3306",
Addshore's avatar
Addshore committed
312
			}, exec.HandlerOptions{}, "nobody")
Addshore's avatar
Addshore committed
313
314
		}
		if DbType == "postgres" {
Addshore's avatar
Addshore committed
315
			mwdd.DefaultForUser().Exec("mediawiki", []string{
Addshore's avatar
Addshore committed
316
317
				"/wait-for-it.sh",
				"postgres:5432",
Addshore's avatar
Addshore committed
318
			}, exec.HandlerOptions{}, "nobody")
Addshore's avatar
Addshore committed
319
320
		}
		if DbType == "mysql" || DbType == "postgres" {
Addshore's avatar
Addshore committed
321
			mwdd.DefaultForUser().Exec("mediawiki", []string{
322
323
				"php",
				"/var/www/html/w/maintenance/install.php",
324
				"--confpath", "/tmp",
325
				"--server", serverLink,
326
				"--dbtype", DbType,
327
328
				"--dbuser", "root",
				"--dbpass", "toor",
329
				"--dbname", DbName,
Addshore's avatar
Addshore committed
330
				"--dbserver", DbType,
331
				"--lang", "en",
332
				"--pass", adminPass,
333
				"docker-" + DbName,
334
				adminUser,
Addshore's avatar
Addshore committed
335
			}, exec.HandlerOptions{}, "nobody")
336
		}
Addshore's avatar
Addshore committed
337
338

		// Move the custom one back
Addshore's avatar
Addshore committed
339
		mwdd.DefaultForUser().Exec("mediawiki", []string{
Addshore's avatar
Addshore committed
340
			"mv",
341
			"/var/www/html/w/LocalSettings.php.mwdd.tmp",
Addshore's avatar
Addshore committed
342
			"/var/www/html/w/LocalSettings.php",
Addshore's avatar
Addshore committed
343
		}, exec.HandlerOptions{}, "root")
Addshore's avatar
Addshore committed
344
345

		// Run update.php once too
Addshore's avatar
Addshore committed
346
		mwdd.DefaultForUser().Exec("mediawiki", []string{
Addshore's avatar
Addshore committed
347
348
			"php",
			"/var/www/html/w/maintenance/update.php",
349
			"--wiki", DbName,
Addshore's avatar
Addshore committed
350
			"--quick",
Addshore's avatar
Addshore committed
351
		}, exec.HandlerOptions{}, "nobody")
352
353
354
355
356
357
358
359

		fmt.Println("")
		fmt.Println("***************************************")
		fmt.Println("Installation successfull 🎉")
		fmt.Println("User: " + adminUser)
		fmt.Println("Pass: " + adminPass)
		fmt.Println("Link: " + serverLink)
		fmt.Println("***************************************")
Addshore's avatar
Addshore committed
360
361
362
363
	},
}

var mwddMediawikiComposerCmd = &cobra.Command{
Addshore's avatar
Addshore committed
364
365
366
	Use:     "composer",
	Short:   "Runs composer in a container in the context of MediaWiki",
	Example: "  composer info\n  composer install -- --ignore-platform-reqs",
Addshore's avatar
Addshore committed
367
	Run: func(cmd *cobra.Command, args []string) {
368
		mwdd.DefaultForUser().EnsureReady()
369
		mwdd.DefaultForUser().DockerExec(applyRelevantWorkingDirectory(mwdd.DockerExecCommand{
370
			DockerComposeService: "mediawiki",
Addshore's avatar
Addshore committed
371
372
			Command:              append([]string{"composer"}, args...),
			User:                 User,
373
		}))
Addshore's avatar
Addshore committed
374
375
376
	},
}

377
378
379
380
var mwddMediawikiCreateCmd = &cobra.Command{
	Use:   "create",
	Short: "Create the Mediawiki containers",
	Run: func(cmd *cobra.Command, args []string) {
Addshore's avatar
Addshore committed
381
382
		mwdd.DefaultForUser().EnsureReady()
		options := exec.HandlerOptions{
Addshore's avatar
Addshore committed
383
			Verbosity: Verbosity,
Addshore's avatar
Addshore committed
384
385
		}
		// TODO mediawiki should come from some default definition set?
Addshore's avatar
Addshore committed
386
		mwdd.DefaultForUser().UpDetached([]string{"mediawiki", "mediawiki-web"}, options)
Addshore's avatar
Addshore committed
387
388
		// TODO add functionality for writing to the hosts file...
		//mwdd.DefaultForUser().EnsureHostsFile()
389
390
391
392
393
394
395
	},
}

var mwddMediawikiDestroyCmd = &cobra.Command{
	Use:   "destroy",
	Short: "Destroy the Mediawiki containers",
	Run: func(cmd *cobra.Command, args []string) {
Addshore's avatar
Addshore committed
396
397
		mwdd.DefaultForUser().EnsureReady()
		options := exec.HandlerOptions{
Addshore's avatar
Addshore committed
398
			Verbosity: Verbosity,
Addshore's avatar
Addshore committed
399
		}
Addshore's avatar
Addshore committed
400
401
		mwdd.DefaultForUser().Rm([]string{"mediawiki", "mediawiki-web"}, options)
		mwdd.DefaultForUser().RmVolumes([]string{"mediawiki-data", "mediawiki-images", "mediawiki-logs", "mediawiki-dot-composer"}, options)
402
	},
403
404
405
406
407
408
}

var mwddMediawikiSuspendCmd = &cobra.Command{
	Use:   "suspend",
	Short: "Suspend the Mediawiki containers",
	Run: func(cmd *cobra.Command, args []string) {
409
410
		mwdd.DefaultForUser().EnsureReady()
		options := exec.HandlerOptions{
Addshore's avatar
Addshore committed
411
			Verbosity: Verbosity,
412
		}
Addshore's avatar
Addshore committed
413
		mwdd.DefaultForUser().Stop([]string{"mediawiki", "mediawiki-web"}, options)
414
415
416
417
418
419
420
	},
}

var mwddMediawikiResumeCmd = &cobra.Command{
	Use:   "resume",
	Short: "Resume the Mediawiki containers",
	Run: func(cmd *cobra.Command, args []string) {
421
422
		mwdd.DefaultForUser().EnsureReady()
		options := exec.HandlerOptions{
Addshore's avatar
Addshore committed
423
			Verbosity: Verbosity,
424
		}
Addshore's avatar
Addshore committed
425
		mwdd.DefaultForUser().Start([]string{"mediawiki", "mediawiki-web"}, options)
426
427
428
	},
}

Addshore's avatar
Addshore committed
429
430
431
432
433
var mwddMediawikiPhpunitCmd = &cobra.Command{
	Use:   "phpunit",
	Short: "Runs MediaWiki phpunit in the MediaWiki container",
	Run: func(cmd *cobra.Command, args []string) {
		mwdd.DefaultForUser().EnsureReady()
434
		mwdd.DefaultForUser().DockerExec(applyRelevantWorkingDirectory(mwdd.DockerExecCommand{
Addshore's avatar
Addshore committed
435
			DockerComposeService: "mediawiki",
Addshore's avatar
Addshore committed
436
437
			Command:              append([]string{"php", "/var/www/html/w/tests/phpunit/phpunit.php"}, args...),
			User:                 User,
438
		}))
Addshore's avatar
Addshore committed
439
440
441
	},
}

Addshore's avatar
Addshore committed
442
var mwddMediawikiExecCmd = &cobra.Command{
Addshore's avatar
Addshore committed
443
444
445
	Use:     "exec [flags] [command...]",
	Example: "  exec bash\n  exec -- bash --help\n  exec --user root bash\n  exec --user root -- bash --help",
	Short:   "Executes a command in the MediaWiki container",
Addshore's avatar
Addshore committed
446
447
448
449
	Run: func(cmd *cobra.Command, args []string) {
		mwdd.DefaultForUser().EnsureReady()
		mwdd.DefaultForUser().DockerExec(mwdd.DockerExecCommand{
			DockerComposeService: "mediawiki",
Addshore's avatar
Addshore committed
450
451
			Command:              args,
			User:                 User,
Addshore's avatar
Addshore committed
452
453
454
		})
	},
}
Addshore's avatar
Addshore committed
455

Addshore's avatar
Addshore committed
456
var applyRelevantWorkingDirectory = func(dockerExecCommand mwdd.DockerExecCommand) mwdd.DockerExecCommand {
457
458
459
	currentWorkingDirectory, _ := os.Getwd()
	mountedMwDirectory := mwdd.DefaultForUser().Env().Get("MEDIAWIKI_VOLUMES_CODE")
	// For paths inside the mediawiki path
Addshore's avatar
Addshore committed
460
	if strings.HasPrefix(currentWorkingDirectory, mountedMwDirectory) {
461
462
463
464
465
466
467
468
		dockerExecCommand.WorkingDir = strings.Replace(currentWorkingDirectory, mountedMwDirectory, "/var/www/html/w", 1)
	} else {
		fmt.Println("This command is not supported outside of the MediaWiki core directory: " + mountedMwDirectory)
		os.Exit(1)
	}
	return dockerExecCommand
}

469
470
471
472
473
474
func init() {
	mwddCmd.AddCommand(mwddMediawikiCmd)
	mwddMediawikiCmd.AddCommand(mwddMediawikiCreateCmd)
	mwddMediawikiCmd.AddCommand(mwddMediawikiDestroyCmd)
	mwddMediawikiCmd.AddCommand(mwddMediawikiSuspendCmd)
	mwddMediawikiCmd.AddCommand(mwddMediawikiResumeCmd)
Addshore's avatar
Addshore committed
475
	mwddMediawikiCmd.AddCommand(mwddMediawikiInstallCmd)
476
	mwddMediawikiInstallCmd.Flags().StringVarP(&DbName, "dbname", "", "default", "Name of the database to install (must be accepted by MediaWiki, stick to letters and numbers)")
Addshore's avatar
Addshore committed
477
	mwddMediawikiInstallCmd.Flags().StringVarP(&DbType, "dbtype", "", "sqlite", "Type of database to install (sqlite, mysql, postgres)")
Addshore's avatar
Addshore committed
478
	mwddMediawikiCmd.AddCommand(mwddMediawikiComposerCmd)
479
	mwddMediawikiComposerCmd.Flags().StringVarP(&User, "user", "u", mwdd.UserAndGroupForDockerExecution(), "User to run as, defaults to current OS user uid:gid")
Addshore's avatar
Addshore committed
480
	mwddMediawikiCmd.AddCommand(mwddMediawikiPhpunitCmd)
481
	mwddMediawikiPhpunitCmd.Flags().StringVarP(&User, "user", "u", mwdd.UserAndGroupForDockerExecution(), "User to run as, defaults to current OS user uid:gid")
Addshore's avatar
Addshore committed
482
	mwddMediawikiCmd.AddCommand(mwddMediawikiExecCmd)
483
484
	mwddMediawikiExecCmd.Flags().StringVarP(&User, "user", "u", mwdd.UserAndGroupForDockerExecution(), "User to run as, defaults to current OS user uid:gid")

485
}