-
-
Notifications
You must be signed in to change notification settings - Fork 580
/
composer-create.go
177 lines (154 loc) · 5.62 KB
/
composer-create.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
package cmd
import (
"fmt"
"github.com/drud/ddev/pkg/nodeps"
"github.com/mattn/go-isatty"
"os"
"path"
"path/filepath"
"runtime"
"strings"
"github.com/drud/ddev/pkg/fileutil"
"github.com/drud/ddev/pkg/ddevapp"
"github.com/drud/ddev/pkg/output"
"github.com/drud/ddev/pkg/util"
"github.com/spf13/cobra"
)
var ComposerCreateCmd = &cobra.Command{
Use: "create [args] [flags]",
FParseErrWhitelist: cobra.FParseErrWhitelist{
UnknownFlags: true,
},
Short: "Executes 'composer create-project' within the web container with the arguments and flags provided",
Long: `Directs basic invocations of 'composer create-project' within the context of the
web container. Projects will be installed to a temporary directory and moved to
the project root directory after installation. Any existing files in the
project root will be deleted when creating a project.`,
Example: `ddev composer create drupal/recommended-project
ddev composer create "typo3/cms-base-distribution:^10"
ddev composer create drupal/recommended-project --no-install
ddev composer create --repository=https://repo.magento.com/ magento/project-community-edition`,
Run: func(cmd *cobra.Command, args []string) {
// We only want to pass all flags and args to composer
// cobra does not seem to allow direct access to everything predictably
osargs := []string{}
if len(os.Args) > 3 {
osargs = os.Args[3:]
}
app, err := ddevapp.GetActiveApp("")
if err != nil {
util.Failed(err.Error())
}
// Ensure project is running
if app.SiteStatus() != ddevapp.SiteRunning {
err = app.Start()
if err != nil {
util.Failed("Failed to start app %s to run create-project: %v", app.Name, err)
}
}
// Make the user confirm that existing contents will be deleted
util.Warning("Warning: ALL EXISTING CONTENT of the project root (%s) will be deleted", app.AppRoot)
if !util.Confirm("Would you like to continue?") {
util.Failed("create-project cancelled")
}
// Remove any contents of project root
util.Warning("Removing any existing files in project root")
objs, err := fileutil.ListFilesInDir(app.AppRoot)
if err != nil {
util.Failed("Failed to create project: %v", err)
}
for _, o := range objs {
// Preserve .ddev/
if o == ".ddev" {
continue
}
if err = os.RemoveAll(filepath.Join(app.AppRoot, o)); err != nil {
util.Failed("Failed to create project: %v", err)
}
}
// Define a randomly named temp directory for install target
tmpDir := fmt.Sprintf(".tmp_ddev_composer_create_%s", util.RandString(6))
containerInstallPath := path.Join("/var/www/html", tmpDir)
hostInstallPath := filepath.Join(app.AppRoot, tmpDir)
// Build container composer command
composerCmd := []string{
"composer",
"create-project",
}
composerCmd = append(composerCmd, osargs...)
composerCmd = append(composerCmd, containerInstallPath)
composerCmdString := strings.TrimSpace(strings.Join(composerCmd, " "))
output.UserOut.Printf("Executing composer command: %s\n", composerCmdString)
stdout, stderr, err := app.Exec(&ddevapp.ExecOpts{
Service: "web",
Cmd: composerCmdString,
Dir: "/var/www/html",
Tty: isatty.IsTerminal(os.Stdin.Fd()),
})
if err != nil {
util.Failed("Failed to create project:%v, stderr=%v", err, stderr)
}
if len(stdout) > 0 {
fmt.Println(strings.TrimSpace(stdout))
}
output.UserOut.Printf("Moving installation to project root")
// Windows has serious problems with performance.
// If not NFSMountEnabled,
// we will move the contents of the temp installation
// using host-side manipulation, but can't do that with a cached filesystem.
if runtime.GOOS == "windows" && !(app.NFSMountEnabled || app.NFSMountEnabledGlobal) {
// If traditional windows mount
err = filepath.Walk(hostInstallPath, func(path string, info os.FileInfo, err error) error {
// Skip the initial tmp install directory
if path == hostInstallPath {
return nil
}
elements := strings.Split(path, tmpDir)
newPath := filepath.Join(elements...)
// Dirs must be created, not renamed
if info.IsDir() {
if err := os.MkdirAll(newPath, info.Mode()); err != nil {
return fmt.Errorf("unable to move %s to %s: %v", path, newPath, err)
}
return nil
}
// Rename files to to a path excluding the tmpDir
if err := os.Rename(path, newPath); err != nil {
return fmt.Errorf("unable to move %s to %s: %v", path, newPath, err)
}
return nil
})
} else {
// All other cases than Windows, we can move the contents easily and quickly inside the container.
_, _, err = app.Exec(&ddevapp.ExecOpts{
Service: "web",
Cmd: fmt.Sprintf("shopt -s dotglob && mv %s/* /var/www/html && rmdir %s", containerInstallPath, containerInstallPath),
Dir: "/var/www/html",
})
}
// This err check picks up either of the above: The filepath.Walk and the mv
if err != nil {
util.Failed("Failed to create project: %v", err)
}
if runtime.GOOS == "windows" && !nodeps.IsDockerToolbox() {
fileutil.ReplaceSimulatedLinks(app.AppRoot)
}
// Do a spare start, which will create any needed settings files
err = app.Start()
if err != nil {
util.Failed("Failed to start project after composer create: %v", err)
}
},
}
var ComposerCreateProjectCmd = &cobra.Command{
Use: "create-project",
Run: func(cmd *cobra.Command, args []string) {
util.Failed(`'ddev composer create-project' is unsupported. Please use 'ddev composer create'
for basic project creation or 'ddev ssh' into the web container and execute
'composer create-project' directly.`)
},
}
func init() {
ComposerCmd.AddCommand(ComposerCreateProjectCmd)
ComposerCmd.AddCommand(ComposerCreateCmd)
}