github.com/ddev/ddev@v1.23.2-0.20240519125000-d824ffe36ff3/pkg/ddevapp/craftcms.go (about) 1 package ddevapp 2 3 import ( 4 "fmt" 5 "os" 6 "path/filepath" 7 "regexp" 8 9 "github.com/ddev/ddev/pkg/archive" 10 "github.com/ddev/ddev/pkg/fileutil" 11 "github.com/ddev/ddev/pkg/nodeps" 12 "github.com/ddev/ddev/pkg/util" 13 ) 14 15 // isCraftCmsApp returns true if the app is of type craftcms 16 func isCraftCmsApp(app *DdevApp) bool { 17 return fileutil.FileExists(filepath.Join(app.AppRoot, app.ComposerRoot, "craft")) 18 } 19 20 // craftCmsImportFilesAction defines the workflow for importing project files. 21 func craftCmsImportFilesAction(app *DdevApp, uploadDir, importPath, extPath string) error { 22 destPath := app.calculateHostUploadDirFullPath(uploadDir) 23 24 // parent of destination dir should exist 25 if !fileutil.FileExists(filepath.Dir(destPath)) { 26 return fmt.Errorf("unable to import to %s: parent directory does not exist", destPath) 27 } 28 29 // parent of destination dir should be writable. 30 if err := os.Chmod(filepath.Dir(destPath), 0755); err != nil { 31 return err 32 } 33 34 // If the destination path exists, remove it as was warned 35 if fileutil.FileExists(destPath) { 36 if err := os.RemoveAll(destPath); err != nil { 37 return fmt.Errorf("failed to cleanup %s before import: %v", destPath, err) 38 } 39 } 40 41 if isTar(importPath) { 42 if err := archive.Untar(importPath, destPath, extPath); err != nil { 43 return fmt.Errorf("failed to extract provided archive: %v", err) 44 } 45 46 return nil 47 } 48 49 if isZip(importPath) { 50 if err := archive.Unzip(importPath, destPath, extPath); err != nil { 51 return fmt.Errorf("failed to extract provided archive: %v", err) 52 } 53 54 return nil 55 } 56 57 //nolint: revive 58 if err := fileutil.CopyDir(importPath, destPath); err != nil { 59 return err 60 } 61 62 return nil 63 } 64 65 // Set up the .env file for ddev 66 func craftCmsPostStartAction(app *DdevApp) error { 67 // If settings management is disabled, do nothing 68 if app.DisableSettingsManagement { 69 return nil 70 } 71 72 // Check version is v4 or higher or warn user about app type mismatch. 73 if !isCraftCms4orHigher(app) { 74 util.Warning("It looks like the installed Craft CMS is lower than version 4 where it's recommended to use project type `php` or disable settings management with `ddev config --disable-settings-management`") 75 if !util.Confirm("Would you like to stop here, not do the automatic configuration and change project type?") { 76 return nil 77 } 78 } 79 80 // If the .env file doesn't exist, try to create it by copying .env.example to .env 81 envFilePath := filepath.Join(app.AppRoot, app.ComposerRoot, ".env") 82 if !fileutil.FileExists(envFilePath) { 83 var exampleEnvFilePaths = []string{".env.example", ".env.example.dev"} 84 for _, envFileName := range exampleEnvFilePaths { 85 exampleEnvFilePath := filepath.Join(app.AppRoot, app.ComposerRoot, envFileName) 86 if fileutil.FileExists(exampleEnvFilePath) { 87 util.Success(fmt.Sprintf("Copied %s to .env", envFileName)) 88 err := fileutil.CopyFile(exampleEnvFilePath, envFilePath) 89 if err != nil { 90 util.Error(fmt.Sprintf("Error copying %s to .env", exampleEnvFilePath)) 91 92 return err 93 } 94 } 95 } 96 } 97 // If the .env file *still* doesn't exist, return early 98 if !fileutil.FileExists(envFilePath) { 99 return nil 100 } 101 // Read in the .env file 102 envMap, envText, err := ReadProjectEnvFile(envFilePath) 103 if err != nil && !os.IsNotExist(err) { 104 return fmt.Errorf("unable to read .env file: %v", err) 105 } 106 107 port := "3306" 108 driver := "mysql" 109 if app.Database.Type == nodeps.Postgres { 110 driver = "pgsql" 111 port = "5432" 112 } 113 114 // If they have older version of .env with DB_DRIVER, DB_SERVER etc, use those 115 if _, ok := envMap["DB_SERVER"]; ok { 116 // TODO: Remove, was never an official standard of Craft CMS. 117 envMap = map[string]string{ 118 "DB_DRIVER": driver, 119 "DB_SERVER": "db", 120 "DB_PORT": port, 121 "DB_DATABASE": "db", 122 "DB_USER": "db", 123 "DB_PASSWORD": "db", 124 "MAILPIT_SMTP_HOSTNAME": "127.0.0.1", 125 "MAILPIT_SMTP_PORT": "1025", 126 "PRIMARY_SITE_URL": app.GetPrimaryURL(), 127 } 128 } else { 129 // Otherwise use the current CRAFT_DB_SERVER etc. 130 envMap = map[string]string{ 131 "CRAFT_DB_DRIVER": driver, 132 "CRAFT_DB_SERVER": "db", 133 "CRAFT_DB_PORT": port, 134 "CRAFT_DB_DATABASE": "db", 135 "CRAFT_DB_USER": "db", 136 "CRAFT_DB_PASSWORD": "db", 137 "CRAFT_WEB_ROOT": app.GetAbsDocroot(true), 138 "MAILPIT_SMTP_HOSTNAME": "127.0.0.1", 139 "MAILPIT_SMTP_PORT": "1025", 140 "PRIMARY_SITE_URL": app.GetPrimaryURL(), 141 } 142 } 143 144 err = WriteProjectEnvFile(envFilePath, envMap, envText) 145 if err != nil { 146 return err 147 } 148 149 return nil 150 } 151 152 func craftCmsConfigOverrideAction(app *DdevApp) error { 153 app.Database = DatabaseDesc{nodeps.MySQL, nodeps.MySQL80} 154 return nil 155 } 156 157 // isCraftCms4orHigher returns true if the Craft CMS version is 4 or higher. The 158 // proper detection will fail if the vendor folder location is changed in the 159 // composer.json. 160 // The detection is based on a change starting with 4.0.0-RC1 where deprecated 161 // constants were removed in src/Craft.php see 162 // https://github.com/craftcms/cms/commit/1660ff90a3a69cec425271d47ade66523a4bd44e#diff-21e22a30e7c48265a4dcedc1b1c8b9372eca5d3fdeff6d72c7d9c6b671365c56 163 func isCraftCms4orHigher(app *DdevApp) bool { 164 craftFilePath := filepath.Join(app.GetComposerRoot(false, false), "vendor", "craftcms", "cms", "src", "Craft.php") 165 if !fileutil.FileExists(craftFilePath) { 166 // Sources are not installed, assuming v4 or higher. 167 return true 168 } 169 170 craftFileContent, err := fileutil.ReadFileIntoString(craftFilePath) 171 if err != nil { 172 util.Warning("unable to read file `%s` in project `%s`: %v", craftFilePath, app.Name, err) 173 174 return true 175 } 176 177 return !regexp.MustCompile(`const\s+Personal\s*=\s*0`).MatchString(craftFileContent) 178 }