Commit 1f910e34 authored by Jeena Huneidi's avatar Jeena Huneidi Committed by Addshore
Browse files

Add dev alias, replace docker with mwdd

Creates an alias called dev for the default development environment.
This will be useful when other environments are added to the cli.
Replaces the docker command with mwdd to remove duplicate functionality.

Also update the test to be mwdd / docker focused
but this needs to be commented out for now...

Change-Id: Ibb2798f438f9372289694f0bb4ce5dbb087d18dc
parent f63a1329
......@@ -7,10 +7,9 @@ Take a look at the user facing docs https://www.mediawiki.org/wiki/Cli
## Docker
There are currently 2 subcommands:
There is currently 1 subcommand:
- `docker` allows interacting with MediaWiki core's docker-compose development environment. (See `mw help docker`)
- `mwdd` allows interacting with a new version of the MediaWiki-docker-dev development environment. (See `mw help mwdd`)
- `docker` allows interacting with a new version of the MediaWiki-docker-dev development environment. (See `mw help docker`)
## Contributing
......
......@@ -17,8 +17,6 @@ limitations under the License.
package cmd
import (
"fmt"
"gerrit.wikimedia.org/r/mediawiki/tools/cli/internal/config"
"github.com/spf13/cobra"
)
......@@ -29,14 +27,14 @@ var configCmd = &cobra.Command{
}
var configShowCmd = &cobra.Command{
Use: "show",
Short: "Shows the raw config",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(config.LoadFromDisk())
},
Use: "show",
Short: "Shows the raw config",
Run: func(cmd *cobra.Command, args []string) {
config.LoadFromDisk().PrettyPrint()
},
}
func init() {
rootCmd.AddCommand(configCmd)
configCmd.AddCommand(configShowCmd)
configCmd.AddCommand(configShowCmd)
}
/*Package cmd is used for command line.
Copyright © 2020 Kosta Harlan <kosta@kostaharlan.net>
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 (
"bytes"
"fmt"
"log"
"os"
"strings"
"time"
"github.com/manifoldco/promptui"
"github.com/briandowns/spinner"
"github.com/spf13/cobra"
"gerrit.wikimedia.org/r/mediawiki/tools/cli/internal/docker"
"gerrit.wikimedia.org/r/mediawiki/tools/cli/internal/exec"
"gerrit.wikimedia.org/r/mediawiki/tools/cli/internal/mediawiki"
)
var dockerCmd = &cobra.Command{
Use: "docker",
Short: "The MediaWiki-Docker development environment",
RunE: nil,
}
func mediawikiOrFatal() mediawiki.MediaWiki {
MediaWiki, err := mediawiki.ForCurrentWorkingDirectory()
if err != nil {
log.Fatal("❌ Please run this command within the root of the MediaWiki core repository.")
os.Exit(1)
}
return MediaWiki
}
var dockerStartCmd = &cobra.Command{
Use: "start",
Short: "Start the development environment",
Run: func(cmd *cobra.Command, args []string) {
Spinner := spinner.New(spinner.CharSets[9], 100*time.Millisecond)
Spinner.Prefix = "Starting the development environment "
Spinner.FinalMSG = Spinner.Prefix + "(done)\n"
options := exec.HandlerOptions{
Spinner: Spinner,
Verbosity: Verbosity,
HandleError: handlePortError,
}
exec.RunCommand(options, docker.ComposeCommand("up", "-d"))
MediaWiki := mediawikiOrFatal()
if isLinuxHost() {
fileCreated, err := docker.EnsureDockerComposeUserOverrideExists()
if fileCreated {
fmt.Println("Creating docker-compose.override.yml for correct user ID and group ID mapping from host to container")
}
if err != nil {
log.Fatal(err)
}
}
MediaWiki.EnsureCacheDirectory()
if docker.MediaWikiComposerDependenciesNeedInstallation(exec.HandlerOptions{Verbosity: Verbosity}) {
fmt.Println("MediaWiki has some external dependencies that need to be installed")
installComposer := func() {
Spinner := spinner.New(spinner.CharSets[9], 100*time.Millisecond)
Spinner.Prefix = "Installing Composer dependencies (this may take a few minutes) "
Spinner.FinalMSG = Spinner.Prefix + "(done)\n"
options := exec.HandlerOptions{
Spinner: Spinner,
Verbosity: Verbosity,
}
docker.MediaWikiComposerUpdate(options)
}
if NonInteractive {
installComposer()
} else {
prompt := promptui.Prompt{
IsConfirm: true,
Label: "Install dependencies now",
}
_, err := prompt.Run()
if err == nil {
installComposer()
}
}
}
if !MediaWiki.VectorIsPresent() {
downloadVector := func() {
Spinner := spinner.New(spinner.CharSets[9], 100*time.Millisecond)
Spinner.Prefix = "Downloading Vector "
Spinner.FinalMSG = Spinner.Prefix + "(done)\n"
options := exec.HandlerOptions{
Spinner: Spinner,
Verbosity: Verbosity,
HandleError: func(stderr bytes.Buffer, err error) {
if err != nil {
log.Fatal(err)
}
},
}
MediaWiki.GitCloneVector(options)
}
if NonInteractive {
downloadVector()
} else {
prompt := promptui.Prompt{
IsConfirm: true,
Label: "Download and use the Vector skin",
}
_, err := prompt.Run()
if err == nil {
downloadVector()
}
}
}
if !MediaWiki.LocalSettingsIsPresent() {
installMediawiki := func() {
Spinner := spinner.New(spinner.CharSets[9], 100*time.Millisecond)
Spinner.Prefix = "Installing "
Spinner.FinalMSG = Spinner.Prefix + "(done)\n"
options := exec.HandlerOptions{
Spinner: Spinner,
Verbosity: Verbosity,
}
docker.MediaWikiInstall(options)
}
if NonInteractive {
installMediawiki()
} else {
prompt := promptui.Prompt{
IsConfirm: true,
Label: "Install MediaWiki database tables and create LocalSettings.php",
}
_, err := prompt.Run()
if err == nil {
installMediawiki()
}
}
}
printSuccess()
},
}
var dockerExecCmd = &cobra.Command{
Use: "exec [service] [command] [args]",
Short: "Run a command in the specified container",
Args: cobra.MinimumNArgs(2),
PreRun: func(cmd *cobra.Command, args []string) {
mediawiki.CheckIfInCoreDirectory()
},
Run: func(cmd *cobra.Command, args []string) {
options := exec.HandlerOptions{
Verbosity: Verbosity,
}
if Detach {
args = append([]string{"-d"}, args...)
}
if Privileged {
args = append([]string{"--privileged"}, args...)
}
if User != "" {
args = append([]string{"-u", User}, args...)
}
if Index != "" {
args = append([]string{fmt.Sprintf("--index=%v", Index)}, args...)
}
for _, keyvar := range Env {
args = append([]string{fmt.Sprintf("-e %v", keyvar)}, args...)
}
if Workdir != "" {
args = append([]string{fmt.Sprintf("-w %v", Workdir)}, args...)
}
if NoTTY {
args = append([]string{"-T"}, args...)
exec.RunCommand(options, docker.ComposeCommand("exec", args...))
} else {
exec.RunTTYCommand(options, docker.ComposeCommand("exec", args...))
}
},
}
var dockerDestroyCmd = &cobra.Command{
Use: "destroy [service...]",
Short: "destroys the development environment or specified containers",
Run: func(cmd *cobra.Command, args []string) {
MediaWiki := mediawikiOrFatal()
options := exec.HandlerOptions{
Verbosity: Verbosity,
}
runArgs := append([]string{"-sfv"}, args...)
exec.RunTTYCommand(options, docker.ComposeCommand("rm", runArgs...))
if len(args) == 0 || contains(args, "mediawiki") {
MediaWiki.RenameLocalSettings()
MediaWiki.DeleteCache()
MediaWiki.DeleteVendor()
}
},
}
func contains(slice []string, s string) bool {
for _, i := range slice {
if s == i {
return true
}
}
return false
}
var dockerStopCmd = &cobra.Command{
Use: "stop",
Short: "Stop development environment",
PreRun: func(cmd *cobra.Command, args []string) {
mediawiki.CheckIfInCoreDirectory()
},
Run: func(cmd *cobra.Command, args []string) {
Spinner := spinner.New(spinner.CharSets[9], 100*time.Millisecond)
Spinner.Prefix = "Stopping development environment "
Spinner.FinalMSG = Spinner.Prefix + "(done)\n"
options := exec.HandlerOptions{
Spinner: Spinner,
Verbosity: Verbosity,
}
exec.RunCommand(options, docker.ComposeCommand("stop"))
},
}
var dockerStatusCmd = &cobra.Command{
Use: "status",
Short: "List development environment status",
PreRun: func(cmd *cobra.Command, args []string) {
mediawiki.CheckIfInCoreDirectory()
},
Run: func(cmd *cobra.Command, args []string) {
options := exec.HandlerOptions{
Verbosity: Verbosity,
}
exec.RunCommand(options, docker.ComposeCommand("ps"))
},
}
func printSuccess() {
options := exec.HandlerOptions{
Verbosity: Verbosity,
HandleStdout: func(stdout bytes.Buffer) {
// Replace 0.0.0.0 in the output with localhost
fmt.Printf("Success! View MediaWiki-Docker at http://%s",
strings.Replace(stdout.String(), "0.0.0.0", "localhost", 1))
},
}
exec.RunCommand(options, docker.ComposeCommand("port", "mediawiki", "8080"))
}
func handlePortError(stderr bytes.Buffer, err error) {
stdoutStderr := stderr.Bytes()
portError := strings.Index(string(stdoutStderr), " failed: port is already allocated")
if portError > 0 {
// TODO: This assumes a port that is four characters long.
log.Fatalf("Port %s is already allocated! \n\nPlease override the port via MW_DOCKER_PORT in the .env file\nYou can use the 'docker env' command to do this\nSee `mw docker env --help` for more information.",
string(stdoutStderr[portError-4:])[0:4])
} else if err != nil && stderr.String() != "" {
fmt.Printf("\n%s\n%s\n", "STDERR:", stderr.String())
}
}
func isLinuxHost() bool {
unameCommand := exec.Command("uname")
stdout, err := unameCommand.CombinedOutput()
if err != nil {
log.Fatal(err)
}
return string(stdout) == "Linux\n"
}
func init() {
dockerCmd.PersistentFlags().IntVarP(&Verbosity, "verbosity", "v", 1, "verbosity level (1-2)")
rootCmd.AddCommand(dockerCmd)
dockerStartCmd.Flags().BoolVarP(&NonInteractive, "acceptPrompts", "y", false, "Answer yes to all prompts")
dockerExecCmd.Flags().BoolVarP(&Detach, "detach", "d", false, "Detached mode: Run command in the background.")
dockerExecCmd.Flags().BoolVarP(&Privileged, "privileged", "p", false, "Give extended privileges to the process.")
dockerExecCmd.Flags().StringVarP(&User, "user", "u", "", "Run the command as this user.")
dockerExecCmd.Flags().BoolVarP(&NoTTY, "TTY", "T", false, "Disable pseudo-tty allocation. By default a TTY is allocated")
dockerExecCmd.Flags().StringVarP(&Index, "index", "i", "", "Index of the container if there are multiple instances of a service [default: 1]")
dockerExecCmd.Flags().StringSliceVarP(&Env, "env", "e", []string{}, "Set environment variables. Can be used multiple times")
dockerExecCmd.Flags().StringVarP(&Workdir, "workdir", "w", "", "Path to workdir directory for this command.")
dockerCmd.AddCommand(dockerStartCmd)
dockerCmd.AddCommand(dockerStopCmd)
dockerCmd.AddCommand(dockerStatusCmd)
dockerCmd.AddCommand(dockerDestroyCmd)
dockerCmd.AddCommand(dockerExecCmd)
}
/*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 (
"gerrit.wikimedia.org/r/mediawiki/tools/cli/internal/cmd"
)
var dockerEnvCmd = cmd.Env("Provides subcommands for interacting with development environment variables");
var dockerEnvDeleteCmd = cmd.EnvDelete(func()string{return mediawikiOrFatal().Directory()});
var dockerEnvSetCmd = cmd.EnvSet(func()string{return mediawikiOrFatal().Directory()});
var dockerEnvGetCmd = cmd.EnvGet(func()string{return mediawikiOrFatal().Directory()});
var dockerEnvListCmd = cmd.EnvList(func()string{return mediawikiOrFatal().Directory()});
var dockerEnvWhereCmd = cmd.EnvWhere(func()string{return mediawikiOrFatal().Directory()});
func init() {
dockerCmd.AddCommand(dockerEnvCmd)
dockerEnvCmd.AddCommand(dockerEnvWhereCmd)
dockerEnvCmd.AddCommand(dockerEnvSetCmd)
dockerEnvCmd.AddCommand(dockerEnvGetCmd)
dockerEnvCmd.AddCommand(dockerEnvListCmd)
dockerEnvCmd.AddCommand(dockerEnvDeleteCmd)
}
......@@ -30,15 +30,15 @@ import (
)
var mwddCmd = &cobra.Command{
Use: "mwdd",
Use: "docker",
Short: "The MediaWiki-Docker-Dev like development environment",
RunE: nil,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
mwdd := mwdd.DefaultForUser()
mwdd.EnsureReady()
if(mwdd.Env().Missing("PORT")){
if mwdd.Env().Missing("PORT") {
prompt := promptui.Prompt{
Label: "What port would you like to use for your development environment?",
Label: "What port would you like to use for your development environment?",
// TODO suggest a port that is definitely available for listening on
Default: "8080",
Validate: func(input string) error {
......@@ -53,7 +53,7 @@ var mwddCmd = &cobra.Command{
}
value, err := prompt.Run()
if err == nil {
mwdd.Env().Set("PORT",value)
mwdd.Env().Set("PORT", value)
} else {
fmt.Println("Can't continue without a port")
os.Exit(1)
......@@ -75,9 +75,9 @@ var mwddDestroyCmd = &cobra.Command{
Short: "Destroy the Default containers",
Run: func(cmd *cobra.Command, args []string) {
options := exec.HandlerOptions{
Verbosity: Verbosity,
Verbosity: Verbosity,
}
mwdd.DefaultForUser().DownWithVolumesAndOrphans( options )
mwdd.DefaultForUser().DownWithVolumesAndOrphans(options)
},
}
......@@ -86,9 +86,9 @@ var mwddSuspendCmd = &cobra.Command{
Short: "Suspend the Default containers",
Run: func(cmd *cobra.Command, args []string) {
options := exec.HandlerOptions{
Verbosity: Verbosity,
Verbosity: Verbosity,
}
mwdd.DefaultForUser().Stop( []string{}, options )
mwdd.DefaultForUser().Stop([]string{}, options)
},
}
......@@ -97,10 +97,10 @@ var mwddResumeCmd = &cobra.Command{
Short: "Resume the Default containers",
Run: func(cmd *cobra.Command, args []string) {
options := exec.HandlerOptions{
Verbosity: Verbosity,
Verbosity: Verbosity,
}
fmt.Println("Any services that you have not already created will show as 'failed'" )
mwdd.DefaultForUser().Start( []string{}, options )
fmt.Println("Any services that you have not already created will show as 'failed'")
mwdd.DefaultForUser().Start([]string{}, options)
},
}
......
......@@ -21,6 +21,7 @@ import (
"fmt"
"os"
"gerrit.wikimedia.org/r/mediawiki/tools/cli/internal/config"
"gerrit.wikimedia.org/r/mediawiki/tools/cli/internal/updater"
"github.com/spf13/cobra"
)
......@@ -56,14 +57,19 @@ var Workdir string
// GitCommit holds short commit hash of source tree
var GitCommit string
// GitBranch holds current branch name the code is built off
var GitBranch string
// GitState shows whether there are uncommitted changes
var GitState string
// GitSummary holds output of git describe --tags --dirty --always
var GitSummary string
// BuildDate holds RFC3339 formatted UTC date (build time)
var BuildDate string
// Version holds contents of ./VERSION file, if exists, or the value passed via the -version option
var Version string
......@@ -72,6 +78,16 @@ var rootCmd = &cobra.Command{
Short: "Developer utilities for working with MediaWiki",
}
func wizardDevMode() {
c := config.LoadFromDisk()
fmt.Println("\nYou need to choose a development environment mode in order to continue:")
fmt.Println(" - '" + config.ConfigDevModeMwdd + "' will provide advanced CLI tooling around a new mediawiki-docker-dev inspired development environment.")
fmt.Println("\nAs the only environment available currently, it will be set as your default dev environment (alias 'dev')")
c.DevMode = config.ConfigDevModeMwdd
c.WriteToDisk()
}
/*Execute the root command*/
func Execute(GitCommitIn string, GitBranchIn string, GitStateIn string, GitSummaryIn string, BuildDateIn string, VersionIn string) {
GitCommit = GitCommitIn
......@@ -82,7 +98,7 @@ func Execute(GitCommitIn string, GitBranchIn string, GitStateIn string, GitSumma
Version = VersionIn
canUpdate, release := updater.CanUpdateDaily(Version, GitSummary, false)
if(canUpdate){
if canUpdate {
colorReset := "\033[0m"
colorYellow := "\033[33m"
colorWhite := "\033[37m"
......@@ -93,6 +109,19 @@ func Execute(GitCommitIn string, GitBranchIn string, GitStateIn string, GitSumma
)
}
// check for dev alias
c := config.LoadFromDisk()
if c.DevMode != config.ConfigDevModeMwdd {
wizardDevMode()
c = config.LoadFromDisk()
}
// mwdd mode
if c.DevMode == config.ConfigDevModeMwdd {
mwddCmd.Aliases = []string{"dev"}
mwddCmd.Short += "\t(alias: dev)"
}
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
......
......@@ -27,8 +27,12 @@ import (
"strings"
)
/*ConfigDevModeMwdd ...*/
const ConfigDevModeMwdd string = "docker"
/*Config representation of a cli config*/
type Config struct {
DevMode string `json:"dev_mode"`
}
func configPath() string {
......@@ -62,17 +66,17 @@ func ensureExists() {
}
/*LoadFromDisk loads the config.json from disk*/
func LoadFromDisk() (Config) {
func LoadFromDisk() Config {
ensureExists()
var config Config
configFile, err := os.Open(configPath())
defer configFile.Close()
if err != nil {
fmt.Println(err.Error())
}
jsonParser := json.NewDecoder(configFile)
jsonParser.Decode(&config)
return config
var config Config
configFile, err := os.Open(configPath())
defer configFile.Close()
if err != nil {
fmt.Println(err.Error())
}
jsonParser := json.NewDecoder(configFile)
jsonParser.Decode(&config)
return config
}
/*WriteToDisk writers the config to disk*/
......@@ -87,3 +91,12 @@ func (c Config) WriteToDisk() {
jsonEncoder.Encode(c)
w.Flush()
}
/*PrettyPrint writers the config to disk*/
func (c Config) PrettyPrint() {
empJSON, err := json.MarshalIndent(c, "", " ")
if err != nil {
log.Fatalf(err.Error())
}
fmt.Printf("%s\n", string(empJSON))
}
......@@ -27,7 +27,6 @@ then
popd
fi
title() {
echo
echo "test: $1..."
......@@ -40,8 +39,6 @@ assert() {
test "$actual" = "$expected"
}
# TODO add mwdd commands
#build the cli executable
title "Build mwcli"
{
......@@ -49,68 +46,92 @@ title "Build mwcli"
mv $BASE/bin/cli $BASE/bin/mw