-
-
Notifications
You must be signed in to change notification settings - Fork 580
/
composer-create.go
330 lines (280 loc) · 9.71 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
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
package cmd
import (
"fmt"
"os"
"path"
"path/filepath"
"runtime"
"strings"
"github.com/ddev/ddev/pkg/composer"
"github.com/ddev/ddev/pkg/ddevapp"
"github.com/ddev/ddev/pkg/fileutil"
"github.com/ddev/ddev/pkg/nodeps"
"github.com/ddev/ddev/pkg/output"
"github.com/ddev/ddev/pkg/util"
"github.com/mattn/go-isatty"
"github.com/spf13/cobra"
)
// ComposerCreateCmd handles ddev composer create
var ComposerCreateCmd = &cobra.Command{
DisableFlagParsing: true,
Use: "create [args] [flags]",
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 Composer root directory after install.`,
Example: `ddev composer create drupal/recommended-project
ddev composer create -y 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
ddev composer create --prefer-dist --no-interaction --no-dev psr/log
ddev composer create --preserve-flags --no-interaction psr/log
`,
ValidArgsFunction: getComposerCompletionFunc(true),
Run: func(cmd *cobra.Command, _ []string) {
preserveFlags, _ := cmd.Flags().GetBool("preserve-flags")
// 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:]
osargs = nodeps.RemoveItemFromSlice(osargs, "--yes")
osargs = nodeps.RemoveItemFromSlice(osargs, "-y")
osargs = nodeps.RemoveItemFromSlice(osargs, "--preserve-flags")
}
app, err := ddevapp.GetActiveApp("")
if err != nil {
util.Failed(err.Error())
}
// Ensure project is running
status, _ := app.SiteStatus()
if status != ddevapp.SiteRunning {
err = app.Start()
if err != nil {
util.Failed("failed to start app %s to run create-project: %v", app.Name, err)
}
}
composerRoot := app.GetComposerRoot(false, false)
err = os.MkdirAll(composerRoot, 0755)
if err != nil {
util.Failed("Failed to create composerRoot: %v", err)
}
appRoot := app.GetAbsAppRoot(false)
skipDirs := []string{".ddev", ".git", ".tarballs"}
composerCreateAllowedPaths, _ := app.GetComposerCreateAllowedPaths()
err = filepath.Walk(appRoot,
func(walkPath string, walkInfo os.FileInfo, err error) error {
if walkPath == appRoot {
return nil
}
checkPath := app.GetRelativeDirectory(walkPath)
if walkInfo.IsDir() && nodeps.ArrayContainsString(skipDirs, checkPath) {
return filepath.SkipDir
}
if !nodeps.ArrayContainsString(composerCreateAllowedPaths, checkPath) {
return fmt.Errorf("'%s' is not allowed to be present. composer create needs to be run on a clean/empty project with only the following paths: %v - please clean up the project before using 'ddev composer create'", filepath.Join(appRoot, checkPath), composerCreateAllowedPaths)
}
if err != nil {
return err
}
return nil
})
if err != nil {
util.Failed("Failed to create project: %v", err)
}
// Define a randomly named temp directory for install target
tmpDir := util.RandString(6)
containerInstallPath := path.Join("/tmp", tmpDir)
// Add some args to avoid troubles while cloning as long as
// --preserve-flags is not set.
createArgs := osargs
if !preserveFlags && !nodeps.ArrayContainsString(createArgs, "--no-plugins") {
createArgs = append(createArgs, "--no-plugins")
}
if !preserveFlags && !nodeps.ArrayContainsString(createArgs, "--no-scripts") {
createArgs = append(createArgs, "--no-scripts")
}
// Remember if --no-install was provided by the user
noInstallPresent := nodeps.ArrayContainsString(createArgs, "--no-install")
if !noInstallPresent {
// Add the --no-install option by default to avoid issues with
// rsyncing many files afterwards to the project root.
createArgs = append(createArgs, "--no-install")
}
// Build container Composer command
composerCmd := []string{
"composer",
"create-project",
}
composerCmd = append(composerCmd, createArgs...)
composerCmd = append(composerCmd, containerInstallPath)
output.UserOut.Printf("Executing Composer command: %v\n", composerCmd)
stdout, stderr, err := app.Exec(&ddevapp.ExecOpts{
Service: "web",
RawCmd: composerCmd,
Dir: "/var/www/html",
Tty: isatty.IsTerminal(os.Stdin.Fd()),
})
if err != nil {
util.Failed("failed to create project: %v\nstderr=%v", err, stderr)
}
if len(stdout) > 0 {
output.UserOut.Println(stdout)
}
if len(stderr) > 0 {
output.UserErr.Println(stderr)
}
output.UserOut.Printf("Moving install to Composer root")
rsyncArgs := "-rltgopD" // Same as -a
if runtime.GOOS == "windows" {
rsyncArgs = "-rlD" // on windows can't do perms, owner, group, times
}
_, _, err = app.Exec(&ddevapp.ExecOpts{
Service: "web",
Cmd: fmt.Sprintf(`rsync %s "%s/" "%s/"`, rsyncArgs, containerInstallPath, app.GetComposerRoot(true, false)),
Dir: "/var/www/html",
})
if err != nil {
util.Failed("failed to create project: %v", err)
}
composerManifest, _ := composer.NewManifest(path.Join(composerRoot, "composer.json"))
if !preserveFlags && composerManifest != nil && composerManifest.HasPostRootPackageInstallScript() {
// Try to run post-root-package-install.
composerCmd = []string{
"composer",
"run-script",
"post-root-package-install",
}
output.UserOut.Printf("Executing composer command: %v\n", composerCmd)
stdout, stderr, _ = app.Exec(&ddevapp.ExecOpts{
Service: "web",
Dir: app.GetComposerRoot(true, false),
RawCmd: composerCmd,
Tty: isatty.IsTerminal(os.Stdin.Fd()),
})
if len(stdout) > 0 {
output.UserOut.Println(stdout)
}
if len(stderr) > 0 {
output.UserErr.Println(stderr)
}
}
// Do a spare restart, which will create any needed settings files
// and also restart mutagen
err = app.Restart()
if err != nil {
util.Warning("failed to restart project after composer create: %v", err)
}
// If --no-install was not provided by the user, call composer install
// now to finish the installation in the project root folder.
if !noInstallPresent {
composerCmd = []string{
"composer",
"install",
}
// Apply args supported by install
supportedArgs := []string{
"--prefer-source",
"--prefer-dist",
"--prefer-install",
"--no-dev",
"--no-progress",
"--ignore-platform-req",
"--ignore-platform-reqs",
"-q",
"--quiet",
"--ansi",
"--no-ansi",
"-n",
"--no-interaction",
"--profile",
"--no-plugins",
"--no-scripts",
"-d",
"--working-dir",
"--no-cache",
"-v",
"-vv",
"-vvv",
"--verbose",
}
for _, osarg := range osargs {
for _, supportedArg := range supportedArgs {
if strings.HasPrefix(osarg, supportedArg) {
composerCmd = append(composerCmd, osarg)
}
}
}
// Run install command.
output.UserOut.Printf("Executing Composer command: %v\n", composerCmd)
stdout, stderr, err = app.Exec(&ddevapp.ExecOpts{
Service: "web",
RawCmd: composerCmd,
Dir: app.GetComposerRoot(true, false),
Tty: isatty.IsTerminal(os.Stdin.Fd()),
})
if err != nil {
util.Failed("failed to install project: %v\nstderr=%v", err, stderr)
}
if len(stdout) > 0 {
output.UserOut.Println(stdout)
}
if len(stderr) > 0 {
output.UserErr.Println(stderr)
}
// Reload composer.json if it has changed in the meantime.
composerManifest, _ := composer.NewManifest(path.Join(composerRoot, "composer.json"))
if !preserveFlags && composerManifest != nil && composerManifest.HasPostCreateProjectCmdScript() {
// Try to run post-create-project-cmd.
composerCmd = []string{
"composer",
"run-script",
"post-create-project-cmd",
}
output.UserOut.Printf("Executing composer command: %v\n", composerCmd)
stdout, stderr, _ = app.Exec(&ddevapp.ExecOpts{
Service: "web",
Dir: app.GetComposerRoot(true, false),
RawCmd: composerCmd,
Tty: isatty.IsTerminal(os.Stdin.Fd()),
})
if len(stdout) > 0 {
output.UserOut.Println(stdout)
}
if len(stderr) > 0 {
output.UserErr.Println(stderr)
}
}
}
// Do a spare restart, which will create any needed settings files
// and also restart Mutagen
err = app.Restart()
if err != nil {
util.Warning("Failed to restart project after composer create: %v", err)
}
util.Success("\nddev composer create was successful.\nConsider using `ddev config --update` to autodetect configuration for your project")
if runtime.GOOS == "windows" {
fileutil.ReplaceSimulatedLinks(app.AppRoot)
}
},
}
// ComposerCreateProjectCmd sends people to the right thing
// when they try ddev composer create-project
var ComposerCreateProjectCmd = &cobra.Command{
Use: "create-project",
Short: "Unsupported, use `ddev composer create` instead",
DisableFlagParsing: true,
Hidden: true,
Run: func(_ *cobra.Command, _ []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() {
ComposerCreateCmd.Flags().BoolP("yes", "y", false, "Yes - skip confirmation prompt")
ComposerCreateCmd.Flags().Bool("preserve-flags", false, "Do not append `--no-plugins` and `--no-scripts` flags")
ComposerCmd.AddCommand(ComposerCreateProjectCmd)
ComposerCmd.AddCommand(ComposerCreateCmd)
}