├── LICENSE.txt ├── bin └── ngrok ├── cli ├── Valet │ ├── CommandLine.php │ ├── Configuration.php │ ├── Contracts │ │ ├── PackageManager.php │ │ └── ServiceManager.php │ ├── DnsMasq.php │ ├── Filesystem.php │ ├── Nginx.php │ ├── Ngrok.php │ ├── PackageManagers │ │ ├── Apt.php │ │ ├── Dnf.php │ │ ├── PackageKit.php │ │ ├── Pacman.php │ │ └── Yum.php │ ├── PhpFpm.php │ ├── Requirements.php │ ├── ServiceManagers │ │ ├── LinuxService.php │ │ └── Systemd.php │ ├── Site.php │ └── Valet.php ├── drivers │ ├── BasicValetDriver.php │ ├── BedrockValetDriver.php │ ├── Cake2ValetDriver.php │ ├── CakeValetDriver.php │ ├── Concrete5ValetDriver.php │ ├── ContaoValetDriver.php │ ├── CraftValetDriver.php │ ├── DrupalValetDriver.php │ ├── JigsawValetDriver.php │ ├── JoomlaValetDriver.php │ ├── KatanaValetDriver.php │ ├── KirbyValetDriver.php │ ├── LaravelValetDriver.php │ ├── Magento2ValetDriver.php │ ├── NeosValetDriver.php │ ├── SculpinValetDriver.php │ ├── StatamicV1ValetDriver.php │ ├── StatamicValetDriver.php │ ├── SymfonyValetDriver.php │ ├── Typo3ValetDriver.php │ ├── ValetDriver.php │ ├── WordPressValetDriver.php │ └── require.php ├── includes │ ├── compatibility.php │ ├── facades.php │ └── helpers.php ├── scripts │ ├── fetch-share-url.sh │ ├── install.sh │ └── update.sh ├── stubs │ ├── SampleValetDriver.php │ ├── dnsmasq.conf │ ├── dnsmasq_options │ ├── fastcgi_params │ ├── fpm.conf │ ├── init │ │ ├── systemd │ │ └── sysvinit │ ├── networkmanager.conf │ ├── nginx.conf │ ├── openssl.conf │ ├── resolved.conf │ ├── secure.valet.conf │ ├── valet-dns │ └── valet.conf ├── templates │ └── 404.html └── valet.php ├── composer.json ├── develop ├── readme.md ├── server.php └── valet /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /bin/ngrok: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jmarcher/valet-linux/51889000b7f5f6d87d13717d20d7d3844bebf91f/bin/ngrok -------------------------------------------------------------------------------- /cli/Valet/CommandLine.php: -------------------------------------------------------------------------------- 1 | runCommand($command.' > /dev/null 2>&1'); 19 | } 20 | 21 | /** 22 | * Simple global function to run commands. 23 | * 24 | * @param string $command 25 | * 26 | * @return void 27 | */ 28 | public function quietlyAsUser($command) 29 | { 30 | $this->quietly('sudo -u '.user().' '.$command.' > /dev/null 2>&1'); 31 | } 32 | 33 | /** 34 | * Pass the command to the command line and display the output. 35 | * 36 | * @param string $command 37 | * 38 | * @return void 39 | */ 40 | public function passthru($command) 41 | { 42 | passthru($command); 43 | } 44 | 45 | /** 46 | * Run the given command as the non-root user. 47 | * 48 | * @param string $command 49 | * @param callable $onError 50 | * 51 | * @return string 52 | */ 53 | public function run($command, callable $onError = null) 54 | { 55 | return $this->runCommand($command, $onError); 56 | } 57 | 58 | /** 59 | * Run the given command. 60 | * 61 | * @param string $command 62 | * @param callable $onError 63 | * 64 | * @return string 65 | */ 66 | public function runAsUser($command, callable $onError = null) 67 | { 68 | return $this->runCommand('sudo -u '.user().' '.$command, $onError); 69 | } 70 | 71 | /** 72 | * Run the given command. 73 | * 74 | * @param string $command 75 | * @param callable $onError 76 | * 77 | * @return string 78 | */ 79 | protected function runCommand($command, callable $onError = null) 80 | { 81 | $onError = $onError ?: function () { 82 | }; 83 | 84 | $process = new Process($command); 85 | 86 | $processOutput = ''; 87 | $process->setTimeout(null)->run(function ($type, $line) use (&$processOutput) { 88 | $processOutput .= $line; 89 | }); 90 | 91 | if ($process->getExitCode() > 0) { 92 | $onError($process->getExitCode(), $processOutput); 93 | } 94 | 95 | return $processOutput; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /cli/Valet/Configuration.php: -------------------------------------------------------------------------------- 1 | files = $files; 17 | } 18 | 19 | /** 20 | * Install the Valet configuration file. 21 | * 22 | * @return void 23 | */ 24 | public function install() 25 | { 26 | $this->createConfigurationDirectory(); 27 | $this->createDriversDirectory(); 28 | $this->createSitesDirectory(); 29 | $this->createExtensionsDirectory(); 30 | $this->createLogDirectory(); 31 | $this->createCertificatesDirectory(); 32 | $this->writeBaseConfiguration(); 33 | 34 | $this->files->chown($this->path(), user()); 35 | } 36 | 37 | /** 38 | * Uninstall the Valet configuration folder. 39 | * 40 | * @return void 41 | */ 42 | public function uninstall() 43 | { 44 | if ($this->files->isDir(VALET_HOME_PATH, user())) { 45 | $this->files->remove(VALET_HOME_PATH); 46 | } 47 | } 48 | 49 | /** 50 | * Create the Valet configuration directory. 51 | * 52 | * @return void 53 | */ 54 | public function createConfigurationDirectory() 55 | { 56 | $this->files->ensureDirExists(VALET_HOME_PATH, user()); 57 | } 58 | 59 | /** 60 | * Create the Valet drivers directory. 61 | * 62 | * @return void 63 | */ 64 | public function createDriversDirectory() 65 | { 66 | if ($this->files->isDir($driversDirectory = VALET_HOME_PATH.'/Drivers')) { 67 | return; 68 | } 69 | 70 | $this->files->mkdirAsUser($driversDirectory); 71 | 72 | $this->files->putAsUser( 73 | $driversDirectory.'/SampleValetDriver.php', 74 | $this->files->get(__DIR__.'/../stubs/SampleValetDriver.php') 75 | ); 76 | } 77 | 78 | /** 79 | * Create the Valet sites directory. 80 | * 81 | * @return void 82 | */ 83 | public function createSitesDirectory() 84 | { 85 | $this->files->ensureDirExists(VALET_HOME_PATH.'/Sites', user()); 86 | } 87 | 88 | /** 89 | * Create the directory for the Valet extensions. 90 | * 91 | * @return void 92 | */ 93 | public function createExtensionsDirectory() 94 | { 95 | $this->files->ensureDirExists(VALET_HOME_PATH.'/Extensions', user()); 96 | } 97 | 98 | /** 99 | * Create the directory for Nginx logs. 100 | * 101 | * @return void 102 | */ 103 | public function createLogDirectory() 104 | { 105 | $this->files->ensureDirExists(VALET_HOME_PATH.'/Log', user()); 106 | 107 | $this->files->touch(VALET_HOME_PATH.'/Log/nginx-error.log'); 108 | } 109 | 110 | /** 111 | * Create the directory for SSL certificates. 112 | * 113 | * @return void 114 | */ 115 | public function createCertificatesDirectory() 116 | { 117 | $this->files->ensureDirExists(VALET_HOME_PATH.'/Certificates', user()); 118 | } 119 | 120 | /** 121 | * Write the base, initial configuration for Valet. 122 | */ 123 | public function writeBaseConfiguration() 124 | { 125 | if (!$this->files->exists($this->path())) { 126 | $this->write(['domain' => 'test', 'paths' => [], 'port' => '80']); 127 | } 128 | } 129 | 130 | /** 131 | * Add the given path to the configuration. 132 | * 133 | * @param string $path 134 | * @param bool $prepend 135 | * 136 | * @return void 137 | */ 138 | public function addPath($path, $prepend = false) 139 | { 140 | $this->write(tap($this->read(), function (&$config) use ($path, $prepend) { 141 | $method = $prepend ? 'prepend' : 'push'; 142 | 143 | $config['paths'] = collect($config['paths'])->{$method}($path)->unique()->all(); 144 | })); 145 | } 146 | 147 | /** 148 | * Prepend the given path to the configuration. 149 | * 150 | * @param string $path 151 | * 152 | * @return void 153 | */ 154 | public function prependPath($path) 155 | { 156 | $this->addPath($path, true); 157 | } 158 | 159 | /** 160 | * Add the given path to the configuration. 161 | * 162 | * @param string $path 163 | * 164 | * @return void 165 | */ 166 | public function removePath($path) 167 | { 168 | $this->write(tap($this->read(), function (&$config) use ($path) { 169 | $config['paths'] = collect($config['paths'])->reject(function ($value) use ($path) { 170 | return $value === $path; 171 | })->values()->all(); 172 | })); 173 | } 174 | 175 | /** 176 | * Prune all non-existent paths from the configuration. 177 | * 178 | * @return void 179 | */ 180 | public function prune() 181 | { 182 | if (!$this->files->exists($this->path())) { 183 | return; 184 | } 185 | 186 | $this->write(tap($this->read(), function (&$config) { 187 | $config['paths'] = collect($config['paths'])->filter(function ($path) { 188 | return $this->files->isDir($path); 189 | })->values()->all(); 190 | })); 191 | } 192 | 193 | /** 194 | * Read the configuration file as JSON. 195 | * 196 | * @return array 197 | */ 198 | public function read() 199 | { 200 | return json_decode($this->files->get($this->path()), true); 201 | } 202 | 203 | /** 204 | * Get a configuration value. 205 | * 206 | * @param string $key 207 | * @param mixed $default 208 | * 209 | * @return mixed 210 | */ 211 | public function get($key, $default = null) 212 | { 213 | $config = $this->read(); 214 | 215 | return array_key_exists($key, $config) ? $config[$key] : $default; 216 | } 217 | 218 | /** 219 | * Update a specific key in the configuration file. 220 | * 221 | * @param string $key 222 | * @param mixed $value 223 | * 224 | * @return array 225 | */ 226 | public function updateKey($key, $value) 227 | { 228 | return tap($this->read(), function (&$config) use ($key, $value) { 229 | $config[$key] = $value; 230 | $this->write($config); 231 | }); 232 | } 233 | 234 | /** 235 | * Write the given configuration to disk. 236 | * 237 | * @param array $config 238 | * 239 | * @return void 240 | */ 241 | public function write(array $config) 242 | { 243 | $this->files->putAsUser($this->path(), json_encode( 244 | $config, 245 | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES 246 | ).PHP_EOL); 247 | } 248 | 249 | /** 250 | * Get the configuration file path. 251 | * 252 | * @return string 253 | */ 254 | public function path() 255 | { 256 | return VALET_HOME_PATH.'/config.json'; 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /cli/Valet/Contracts/PackageManager.php: -------------------------------------------------------------------------------- 1 | pm = $pm; 32 | $this->sm = $sm; 33 | $this->cli = $cli; 34 | $this->files = $files; 35 | $this->rclocal = '/etc/rc.local'; 36 | $this->resolvconf = '/etc/resolv.conf'; 37 | $this->dnsmasqconf = '/etc/dnsmasq.conf'; 38 | $this->configPath = '/etc/dnsmasq.d/valet'; 39 | $this->dnsmasqOpts = '/etc/dnsmasq.d/options'; 40 | $this->nmConfigPath = '/etc/NetworkManager/conf.d/valet.conf'; 41 | $this->resolvedConfigPath = '/etc/systemd/resolved.conf'; 42 | } 43 | 44 | /** 45 | * Install and configure DnsMasq. 46 | * 47 | * @param bool $lock Lock or Unlock the file 48 | * 49 | * @return void 50 | */ 51 | private function _lockResolvConf($lock = true) 52 | { 53 | $arg = $lock ? '+i' : '-i'; 54 | 55 | if (!$this->files->isLink($this->resolvconf)) { 56 | $this->cli->run( 57 | "chattr {$arg} {$this->resolvconf}", 58 | function ($code, $msg) { 59 | warning($msg); 60 | } 61 | ); 62 | } 63 | } 64 | 65 | /** 66 | * Enable nameserver merging. 67 | * 68 | * @return void 69 | */ 70 | private function _mergeDns() 71 | { 72 | $optDir = '/opt/valet-linux'; 73 | $script = $optDir.'/valet-dns'; 74 | 75 | $this->pm->ensureInstalled('inotify-tools'); 76 | $this->files->remove($optDir); 77 | $this->files->ensureDirExists($optDir); 78 | $this->files->put($script, $this->files->get(__DIR__.'/../stubs/valet-dns')); 79 | $this->cli->run("chmod +x $script"); 80 | $this->sm->installValetDns($this->files); 81 | 82 | if ($this->files->exists($this->rclocal)) { 83 | $this->files->restore($this->rclocal); 84 | } 85 | 86 | $this->files->backup($this->resolvconf); 87 | $this->files->unlink($this->resolvconf); 88 | $this->files->symlink($script, $this->resolvconf); 89 | 90 | return true; 91 | } 92 | 93 | /** 94 | * Install and configure DnsMasq. 95 | * 96 | * @param string $domain Domain TLD to use 97 | * 98 | * @return void 99 | */ 100 | public function install($domain = 'test') 101 | { 102 | $this->dnsmasqSetup(); 103 | $this->fixResolved(); 104 | $this->createCustomConfigFile($domain); 105 | $this->pm->nmRestart($this->sm); 106 | $this->sm->restart('dnsmasq'); 107 | $this->sm->start('valet-dns'); 108 | } 109 | 110 | /** 111 | * Append the custom DnsMasq configuration file to the main configuration file. 112 | * 113 | * @param string $domain Domain TLD to use 114 | * 115 | * @return void 116 | */ 117 | public function createCustomConfigFile($domain) 118 | { 119 | $this->files->putAsUser($this->configPath, 'address=/.'.$domain.'/127.0.0.1'.PHP_EOL); 120 | } 121 | 122 | /** 123 | * Fix systemd-resolved configuration. 124 | * 125 | * @return void 126 | */ 127 | public function fixResolved() 128 | { 129 | // $resolved = $this->resolvedConfigPath; 130 | 131 | // $this->files->backup($resolved); 132 | // $this->files->putAsUser($resolved, $this->files->get(__DIR__.'/../stubs/resolved.conf')); 133 | 134 | $this->sm->disable('systemd-resolved'); 135 | $this->sm->stop('systemd-resolved'); 136 | } 137 | 138 | /** 139 | * Setup dnsmasq with Network Manager. 140 | * 141 | * @return void 142 | */ 143 | public function dnsmasqSetup() 144 | { 145 | $this->pm->ensureInstalled('dnsmasq'); 146 | $this->sm->enable('dnsmasq'); 147 | 148 | $this->files->ensureDirExists('/etc/NetworkManager/conf.d'); 149 | $this->files->ensureDirExists('/etc/dnsmasq.d'); 150 | 151 | $this->files->uncommentLine('IGNORE_RESOLVCONF', '/etc/default/dnsmasq'); 152 | 153 | $this->_lockResolvConf(false); 154 | $this->_mergeDns(); 155 | 156 | $this->files->unlink('/etc/dnsmasq.d/network-manager'); 157 | $this->files->backup($this->dnsmasqconf); 158 | 159 | $this->files->putAsUser($this->dnsmasqconf, $this->files->get(__DIR__.'/../stubs/dnsmasq.conf')); 160 | $this->files->putAsUser($this->dnsmasqOpts, $this->files->get(__DIR__.'/../stubs/dnsmasq_options')); 161 | $this->files->putAsUser($this->nmConfigPath, $this->files->get(__DIR__.'/../stubs/networkmanager.conf')); 162 | } 163 | 164 | /** 165 | * Update the domain used by DnsMasq. 166 | * 167 | * @param string $oldDomain Old TLD 168 | * @param string $newDomain New TLD 169 | * 170 | * @return void 171 | */ 172 | public function updateDomain($oldDomain, $newDomain) 173 | { 174 | $this->createCustomConfigFile($newDomain); 175 | $this->sm->restart('dnsmasq'); 176 | } 177 | 178 | /** 179 | * Delete the DnsMasq config file. 180 | * 181 | * @return void 182 | */ 183 | public function uninstall() 184 | { 185 | $this->sm->stop('valet-dns'); 186 | $this->sm->disable('valet-dns'); 187 | 188 | $this->cli->passthru('rm -rf /opt/valet-linux'); 189 | $this->files->unlink($this->configPath); 190 | $this->files->unlink($this->dnsmasqOpts); 191 | $this->files->unlink($this->nmConfigPath); 192 | $this->files->restore($this->resolvedConfigPath); 193 | 194 | $this->_lockResolvConf(false); 195 | $this->files->restore($this->rclocal); 196 | $this->files->restore($this->resolvconf); 197 | $this->files->restore($this->dnsmasqconf); 198 | $this->files->commentLine('IGNORE_RESOLVCONF', '/etc/default/dnsmasq'); 199 | 200 | $this->pm->nmRestart($this->sm); 201 | $this->sm->restart('dnsmasq'); 202 | 203 | info('Valet DNS changes have been rolled back'); 204 | warning('If your system depended on systemd-resolved (like Ubuntu 17.04), please enable it manually'); 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /cli/Valet/Filesystem.php: -------------------------------------------------------------------------------- 1 | toIterator($files)); 28 | $files = array_reverse($files); 29 | foreach ($files as $file) { 30 | if (!file_exists($file) && !is_link($file)) { 31 | continue; 32 | } 33 | 34 | if (is_dir($file) && !is_link($file)) { 35 | $this->remove(new \FilesystemIterator($file)); 36 | 37 | if (true !== @rmdir($file)) { 38 | throw new \Exception(sprintf('Failed to remove directory "%s".', $file), 0, null, $file); 39 | } 40 | } else { 41 | // https://bugs.php.net/bug.php?id=52176 42 | if ('\\' === DIRECTORY_SEPARATOR && is_dir($file)) { 43 | if (true !== @rmdir($file)) { 44 | throw new \Exception(sprintf('Failed to remove file "%s".', $file), 0, null, $file); 45 | } 46 | } else { 47 | if (true !== @unlink($file)) { 48 | throw new \Exception(sprintf('Failed to remove file "%s".', $file), 0, null, $file); 49 | } 50 | } 51 | } 52 | } 53 | } 54 | 55 | /** 56 | * Determine if the given path is a directory. 57 | * 58 | * @param string $path 59 | * 60 | * @return bool 61 | */ 62 | public function isDir($path) 63 | { 64 | return is_dir($path); 65 | } 66 | 67 | /** 68 | * Create a directory. 69 | * 70 | * @param string $path 71 | * @param string|null $owner 72 | * @param int $mode 73 | * 74 | * @return void 75 | */ 76 | public function mkdir($path, $owner = null, $mode = 0755) 77 | { 78 | mkdir($path, $mode, true); 79 | 80 | if ($owner) { 81 | $this->chown($path, $owner); 82 | } 83 | } 84 | 85 | /** 86 | * Ensure that the given directory exists. 87 | * 88 | * @param string $path 89 | * @param string|null $owner 90 | * @param int $mode 91 | * 92 | * @return void 93 | */ 94 | public function ensureDirExists($path, $owner = null, $mode = 0755) 95 | { 96 | if (!$this->isDir($path)) { 97 | $this->mkdir($path, $owner, $mode); 98 | } 99 | } 100 | 101 | /** 102 | * Create a directory as the non-root user. 103 | * 104 | * @param string $path 105 | * @param int $mode 106 | * 107 | * @return void 108 | */ 109 | public function mkdirAsUser($path, $mode = 0755) 110 | { 111 | return $this->mkdir($path, user(), $mode); 112 | } 113 | 114 | /** 115 | * Touch the given path. 116 | * 117 | * @param string $path 118 | * @param string|null $owner 119 | * 120 | * @return string 121 | */ 122 | public function touch($path, $owner = null) 123 | { 124 | touch($path); 125 | 126 | if ($owner) { 127 | $this->chown($path, $owner); 128 | } 129 | 130 | return $path; 131 | } 132 | 133 | /** 134 | * Touch the given path as the non-root user. 135 | * 136 | * @param string $path 137 | * 138 | * @return void 139 | */ 140 | public function touchAsUser($path) 141 | { 142 | return $this->touch($path, user()); 143 | } 144 | 145 | /** 146 | * Determine if the given file exists. 147 | * 148 | * @param string $path 149 | * 150 | * @return bool 151 | */ 152 | public function exists($files) 153 | { 154 | foreach ($this->toIterator($files) as $file) { 155 | if (!file_exists($file)) { 156 | return false; 157 | } 158 | } 159 | 160 | return true; 161 | } 162 | 163 | /** 164 | * Read the contents of the given file. 165 | * 166 | * @param string $path 167 | * 168 | * @return string 169 | */ 170 | public function get($path) 171 | { 172 | return file_get_contents($path); 173 | } 174 | 175 | /** 176 | * Write to the given file. 177 | * 178 | * @param string $path 179 | * @param string $contents 180 | * @param string|null $owner 181 | * 182 | * @return string 183 | */ 184 | public function put($path, $contents, $owner = null) 185 | { 186 | $status = file_put_contents($path, $contents); 187 | 188 | if ($owner) { 189 | $this->chown($path, $owner); 190 | } 191 | 192 | return $status; 193 | } 194 | 195 | /** 196 | * Write to the given file as the non-root user. 197 | * 198 | * @param string $path 199 | * @param string $contents 200 | * 201 | * @return string 202 | */ 203 | public function putAsUser($path, $contents) 204 | { 205 | return $this->put($path, $contents, user()); 206 | } 207 | 208 | /** 209 | * Append the contents to the given file. 210 | * 211 | * @param string $path 212 | * @param string $contents 213 | * @param string|null $owner 214 | * 215 | * @return void 216 | */ 217 | public function append($path, $contents, $owner = null) 218 | { 219 | file_put_contents($path, $contents, FILE_APPEND); 220 | 221 | if ($owner) { 222 | $this->chown($path, $owner); 223 | } 224 | } 225 | 226 | /** 227 | * Append the contents to the given file as the non-root user. 228 | * 229 | * @param string $path 230 | * @param string $contents 231 | * 232 | * @return void 233 | */ 234 | public function appendAsUser($path, $contents) 235 | { 236 | $this->append($path, $contents, user()); 237 | } 238 | 239 | /** 240 | * Copy the given file to a new location. 241 | * 242 | * @param string $from 243 | * @param string $to 244 | * 245 | * @return void 246 | */ 247 | public function copy($from, $to) 248 | { 249 | copy($from, $to); 250 | } 251 | 252 | /** 253 | * Backup the given file. 254 | * 255 | * @param string $file 256 | * 257 | * @return bool 258 | */ 259 | public function backup($file) 260 | { 261 | $to = $file.'.bak'; 262 | 263 | if (!$this->exists($to)) { 264 | if ($this->exists($file)) { 265 | return rename($file, $to); 266 | } 267 | } 268 | 269 | return false; 270 | } 271 | 272 | /** 273 | * Restore a backed up file. 274 | * 275 | * @param string $file 276 | * 277 | * @return bool 278 | */ 279 | public function restore($file) 280 | { 281 | $from = $file.'.bak'; 282 | 283 | if ($this->exists($from)) { 284 | return rename($from, $file); 285 | } 286 | 287 | return false; 288 | } 289 | 290 | /** 291 | * Copy the given file to a new location for the non-root user. 292 | * 293 | * @param string $from 294 | * @param string $to 295 | * 296 | * @return void 297 | */ 298 | public function copyAsUser($from, $to) 299 | { 300 | copy($from, $to); 301 | 302 | $this->chown($to, user()); 303 | } 304 | 305 | /** 306 | * Create a symlink to the given target. 307 | * 308 | * @param string $target 309 | * @param string $link 310 | * 311 | * @return void 312 | */ 313 | public function symlink($target, $link) 314 | { 315 | if ($this->exists($link)) { 316 | $this->unlink($link); 317 | } 318 | 319 | symlink($target, $link); 320 | } 321 | 322 | /** 323 | * Create a symlink to the given target for the non-root user. 324 | * 325 | * This uses the command line as PHP can't change symlink permissions. 326 | * 327 | * @param string $target 328 | * @param string $link 329 | * 330 | * @return void 331 | */ 332 | public function symlinkAsUser($target, $link) 333 | { 334 | if ($this->exists($link)) { 335 | $this->unlink($link); 336 | } 337 | 338 | CommandLineFacade::runAsUser('ln -s '.escapeshellarg($target).' '.escapeshellarg($link)); 339 | } 340 | 341 | /** 342 | * Comment a line in a file. 343 | * 344 | * @param string $line 345 | * @param string $file 346 | * 347 | * @return void 348 | */ 349 | public function commentLine($line, $file) 350 | { 351 | if ($this->exists($file)) { 352 | $command = "sed -i '/{$line}/ s/^/# /' {$file}"; 353 | CommandLineFacade::run($command); 354 | } 355 | } 356 | 357 | /** 358 | * Uncomment a line in a file. 359 | * 360 | * @param string $line 361 | * @param string $file 362 | * 363 | * @return void 364 | */ 365 | public function uncommentLine($line, $file) 366 | { 367 | if ($this->exists($file)) { 368 | $command = "sed -i '/{$line}/ s/# *//' {$file}"; 369 | CommandLineFacade::run($command); 370 | } 371 | } 372 | 373 | /** 374 | * Delete the file at the given path. 375 | * 376 | * @param string $path 377 | * 378 | * @return void 379 | */ 380 | public function unlink($path) 381 | { 382 | if (file_exists($path) || is_link($path)) { 383 | @unlink($path); 384 | } 385 | } 386 | 387 | /** 388 | * Change the owner of the given path. 389 | * 390 | * @param string $path 391 | * @param string $user 392 | */ 393 | public function chown($path, $user) 394 | { 395 | chown($path, $user); 396 | } 397 | 398 | /** 399 | * Change the group of the given path. 400 | * 401 | * @param string $path 402 | * @param string $group 403 | */ 404 | public function chgrp($path, $group) 405 | { 406 | chgrp($path, $group); 407 | } 408 | 409 | /** 410 | * Resolve the given path. 411 | * 412 | * @param string $path 413 | * 414 | * @return string 415 | */ 416 | public function realpath($path) 417 | { 418 | return realpath($path); 419 | } 420 | 421 | /** 422 | * Determine if the given path is a symbolic link. 423 | * 424 | * @param string $path 425 | * 426 | * @return bool 427 | */ 428 | public function isLink($path) 429 | { 430 | return is_link($path); 431 | } 432 | 433 | /** 434 | * Resolve the given symbolic link. 435 | * 436 | * @param string $path 437 | * 438 | * @return string 439 | */ 440 | public function readLink($path) 441 | { 442 | $link = $path; 443 | 444 | while (is_link($link)) { 445 | $link = readlink($link); 446 | } 447 | 448 | return $link; 449 | } 450 | 451 | /** 452 | * Remove all of the broken symbolic links at the given path. 453 | * 454 | * @param string $path 455 | * 456 | * @return void 457 | */ 458 | public function removeBrokenLinksAt($path) 459 | { 460 | collect($this->scandir($path)) 461 | ->filter(function ($file) use ($path) { 462 | return $this->isBrokenLink($path.'/'.$file); 463 | }) 464 | ->each(function ($file) use ($path) { 465 | $this->unlink($path.'/'.$file); 466 | }); 467 | } 468 | 469 | /** 470 | * Determine if the given path is a broken symbolic link. 471 | * 472 | * @param string $path 473 | * 474 | * @return bool 475 | */ 476 | public function isBrokenLink($path) 477 | { 478 | return is_link($path) && !file_exists($path); 479 | } 480 | 481 | /** 482 | * Scan the given directory path. 483 | * 484 | * @param string $path 485 | * 486 | * @return array 487 | */ 488 | public function scandir($path) 489 | { 490 | return collect(scandir($path)) 491 | ->reject(function ($file) { 492 | return in_array($file, ['.', '..']); 493 | })->values()->all(); 494 | } 495 | } 496 | -------------------------------------------------------------------------------- /cli/Valet/Nginx.php: -------------------------------------------------------------------------------- 1 | cli = $cli; 35 | $this->pm = $pm; 36 | $this->sm = $sm; 37 | $this->site = $site; 38 | $this->files = $files; 39 | $this->configuration = $configuration; 40 | $this->nginx_conf = '/etc/nginx/nginx.conf'; 41 | $this->sites_available_conf = '/etc/nginx/sites-available/valet.conf'; 42 | $this->sites_enabled_conf = '/etc/nginx/sites-enabled/valet.conf'; 43 | } 44 | 45 | /** 46 | * Install the configuration files for Nginx. 47 | * 48 | * @return void 49 | */ 50 | public function install() 51 | { 52 | $this->pm->ensureInstalled('nginx'); 53 | $this->sm->enable('nginx'); 54 | $this->files->ensureDirExists('/etc/nginx/sites-available'); 55 | $this->files->ensureDirExists('/etc/nginx/sites-enabled'); 56 | 57 | $this->stop(); 58 | $this->installConfiguration(); 59 | $this->installServer(); 60 | $this->installNginxDirectory(); 61 | } 62 | 63 | /** 64 | * Install the Nginx configuration file. 65 | * 66 | * @return void 67 | */ 68 | public function installConfiguration() 69 | { 70 | $contents = $this->files->get(__DIR__.'/../stubs/nginx.conf'); 71 | $nginx = $this->nginx_conf; 72 | 73 | $pid_string = 'pid /run/nginx.pid'; 74 | $hasPIDoption = strpos($this->cli->run('cat /lib/systemd/system/nginx.service'), 'pid /'); 75 | 76 | if ($hasPIDoption) { 77 | $pid_string = '# pid /run/nginx.pid'; 78 | } 79 | 80 | $this->files->backup($nginx); 81 | 82 | $this->files->putAsUser( 83 | $nginx, 84 | str_array_replace([ 85 | 'VALET_USER' => user(), 86 | 'VALET_GROUP' => group(), 87 | 'VALET_HOME_PATH' => VALET_HOME_PATH, 88 | 'VALET_PID' => $pid_string, 89 | ], $contents) 90 | ); 91 | } 92 | 93 | /** 94 | * Install the Valet Nginx server configuration file. 95 | * 96 | * @return void 97 | */ 98 | public function installServer() 99 | { 100 | $this->files->putAsUser( 101 | $this->sites_available_conf, 102 | str_replace( 103 | ['VALET_HOME_PATH', 'VALET_SERVER_PATH', 'VALET_STATIC_PREFIX', 'VALET_PORT'], 104 | [VALET_HOME_PATH, VALET_SERVER_PATH, VALET_STATIC_PREFIX, $this->configuration->read()['port']], 105 | $this->files->get(__DIR__.'/../stubs/valet.conf') 106 | ) 107 | ); 108 | 109 | if ($this->files->exists('/etc/nginx/sites-enabled/default')) { 110 | $this->files->unlink('/etc/nginx/sites-enabled/default'); 111 | } 112 | 113 | $this->cli->run("ln -snf {$this->sites_available_conf} {$this->sites_enabled_conf}"); 114 | $this->files->backup('/etc/nginx/fastcgi_params'); 115 | 116 | $this->files->putAsUser( 117 | '/etc/nginx/fastcgi_params', 118 | $this->files->get(__DIR__.'/../stubs/fastcgi_params') 119 | ); 120 | } 121 | 122 | /** 123 | * Install the Nginx configuration directory to the ~/.valet directory. 124 | * 125 | * This directory contains all site-specific Nginx servers. 126 | * 127 | * @return void 128 | */ 129 | public function installNginxDirectory() 130 | { 131 | if (!$this->files->isDir($nginxDirectory = VALET_HOME_PATH.'/Nginx')) { 132 | $this->files->mkdirAsUser($nginxDirectory); 133 | } 134 | 135 | $this->files->putAsUser($nginxDirectory.'/.keep', "\n"); 136 | 137 | $this->rewriteSecureNginxFiles(); 138 | } 139 | 140 | /** 141 | * Update the port used by Nginx. 142 | * 143 | * @param string $newPort 144 | * 145 | * @return void 146 | */ 147 | public function updatePort($newPort) 148 | { 149 | $this->files->putAsUser( 150 | $this->sites_available_conf, 151 | str_replace( 152 | ['VALET_HOME_PATH', 'VALET_SERVER_PATH', 'VALET_PORT'], 153 | [VALET_HOME_PATH, VALET_SERVER_PATH, $newPort], 154 | $this->files->get(__DIR__.'/../stubs/valet.conf') 155 | ) 156 | ); 157 | } 158 | 159 | /** 160 | * Generate fresh Nginx servers for existing secure sites. 161 | * 162 | * @return void 163 | */ 164 | public function rewriteSecureNginxFiles() 165 | { 166 | $domain = $this->configuration->read()['domain']; 167 | 168 | $this->site->resecureForNewDomain($domain, $domain); 169 | } 170 | 171 | /** 172 | * Restart the Nginx service. 173 | * 174 | * @return void 175 | */ 176 | public function restart() 177 | { 178 | $this->sm->restart('nginx'); 179 | } 180 | 181 | /** 182 | * Stop the Nginx service. 183 | * 184 | * @return void 185 | */ 186 | public function stop() 187 | { 188 | $this->sm->stop('nginx'); 189 | } 190 | 191 | /** 192 | * Nginx service status. 193 | * 194 | * @return void 195 | */ 196 | public function status() 197 | { 198 | $this->sm->printStatus('nginx'); 199 | } 200 | 201 | /** 202 | * Prepare Nginx for uninstallation. 203 | * 204 | * @return void 205 | */ 206 | public function uninstall() 207 | { 208 | $this->stop(); 209 | $this->files->restore($this->nginx_conf); 210 | $this->files->restore('/etc/nginx/fastcgi_params'); 211 | $this->files->unlink($this->sites_enabled_conf); 212 | $this->files->unlink($this->sites_available_conf); 213 | 214 | if ($this->files->exists('/etc/nginx/sites-available/default')) { 215 | $this->files->symlink('/etc/nginx/sites-available/default', '/etc/nginx/sites-enabled/default'); 216 | } 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /cli/Valet/Ngrok.php: -------------------------------------------------------------------------------- 1 | tunnelsEndpoint)->send()->body; 21 | 22 | // If there are active tunnels on the Ngrok instance we will spin through them and 23 | // find the one responding on HTTP. Each tunnel has an HTTP and a HTTPS address 24 | // but for local testing purposes we just desire the plain HTTP URL endpoint. 25 | if (isset($body->tunnels) && count($body->tunnels) > 0) { 26 | return $this->findHttpTunnelUrl($body->tunnels); 27 | } else { 28 | throw new DomainException('Tunnel not established.'); 29 | } 30 | }, 250); 31 | } 32 | 33 | /** 34 | * Find the HTTP tunnel URL from the list of tunnels. 35 | * 36 | * @param array $tunnels 37 | * 38 | * @return string|null 39 | */ 40 | public function findHttpTunnelUrl(array $tunnels) 41 | { 42 | foreach ($tunnels as $tunnel) { 43 | if ($tunnel->proto === 'http') { 44 | return $tunnel->public_url; 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /cli/Valet/PackageManagers/Apt.php: -------------------------------------------------------------------------------- 1 | cli = $cli; 23 | } 24 | 25 | /** 26 | * Get array of installed packages. 27 | * 28 | * @param string $package 29 | * 30 | * @return array 31 | */ 32 | public function packages($package) 33 | { 34 | $query = "dpkg -l {$package} | grep '^ii' | sed 's/\s\+/ /g' | cut -d' ' -f2"; 35 | 36 | return explode(PHP_EOL, $this->cli->run($query)); 37 | } 38 | 39 | /** 40 | * Determine if the given package is installed. 41 | * 42 | * @param string $package 43 | * 44 | * @return bool 45 | */ 46 | public function installed($package) 47 | { 48 | return in_array($package, $this->packages($package)); 49 | } 50 | 51 | /** 52 | * Ensure that the given package is installed. 53 | * 54 | * @param string $package 55 | * 56 | * @return void 57 | */ 58 | public function ensureInstalled($package) 59 | { 60 | if (!$this->installed($package)) { 61 | $this->installOrFail($package); 62 | } 63 | } 64 | 65 | /** 66 | * Install the given package and throw an exception on failure. 67 | * 68 | * @param string $package 69 | * 70 | * @return void 71 | */ 72 | public function installOrFail($package) 73 | { 74 | output('['.$package.'] is not installed, installing it now via Apt... 🍻'); 75 | 76 | $this->cli->run(trim('apt-get install -y '.$package), function ($exitCode, $errorOutput) use ($package) { 77 | output($errorOutput); 78 | 79 | throw new DomainException('Apt was unable to install ['.$package.'].'); 80 | }); 81 | } 82 | 83 | /** 84 | * Configure package manager on valet install. 85 | * 86 | * @return void 87 | */ 88 | public function setup() 89 | { 90 | // Nothing to do 91 | } 92 | 93 | /** 94 | * Restart dnsmasq in Ubuntu. 95 | */ 96 | public function nmRestart($sm) 97 | { 98 | $sm->restart(['network-manager']); 99 | } 100 | 101 | /** 102 | * Determine if package manager is available on the system. 103 | * 104 | * @return bool 105 | */ 106 | public function isAvailable() 107 | { 108 | try { 109 | $output = $this->cli->run('which apt-get', function ($exitCode, $output) { 110 | throw new DomainException('Apt not available'); 111 | }); 112 | 113 | return $output != ''; 114 | } catch (DomainException $e) { 115 | return false; 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /cli/Valet/PackageManagers/Dnf.php: -------------------------------------------------------------------------------- 1 | cli = $cli; 23 | } 24 | 25 | /** 26 | * Determine if the given package is installed. 27 | * 28 | * @param string $package 29 | * 30 | * @return bool 31 | */ 32 | public function installed($package) 33 | { 34 | $query = "dnf list installed {$package} | grep {$package} | sed 's_ _\\t_g' | sed 's_\\._\\t_g' | cut -f 1"; 35 | 36 | $packages = explode(PHP_EOL, $this->cli->run($query)); 37 | 38 | return in_array($package, $packages); 39 | } 40 | 41 | /** 42 | * Ensure that the given package is installed. 43 | * 44 | * @param string $package 45 | * 46 | * @return void 47 | */ 48 | public function ensureInstalled($package) 49 | { 50 | if (!$this->installed($package)) { 51 | $this->installOrFail($package); 52 | } 53 | } 54 | 55 | /** 56 | * Install the given package and throw an exception on failure. 57 | * 58 | * @param string $package 59 | * 60 | * @return void 61 | */ 62 | public function installOrFail($package) 63 | { 64 | output('['.$package.'] is not installed, installing it now via Dnf... 🍻'); 65 | 66 | $this->cli->run(trim('dnf install -y '.$package), function ($exitCode, $errorOutput) use ($package) { 67 | output($errorOutput); 68 | 69 | throw new DomainException('Dnf was unable to install ['.$package.'].'); 70 | }); 71 | } 72 | 73 | /** 74 | * Configure package manager on valet install. 75 | * 76 | * @return void 77 | */ 78 | public function setup() 79 | { 80 | // Nothing to do 81 | } 82 | 83 | /** 84 | * Restart dnsmasq in Fedora. 85 | */ 86 | public function nmRestart($sm) 87 | { 88 | $sm->restart('NetworkManager'); 89 | } 90 | 91 | /** 92 | * Determine if package manager is available on the system. 93 | * 94 | * @return bool 95 | */ 96 | public function isAvailable() 97 | { 98 | try { 99 | $output = $this->cli->run('which dnf', function ($exitCode, $output) { 100 | throw new DomainException('Dnf not available'); 101 | }); 102 | 103 | return $output != ''; 104 | } catch (DomainException $e) { 105 | return false; 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /cli/Valet/PackageManagers/PackageKit.php: -------------------------------------------------------------------------------- 1 | cli = $cli; 23 | } 24 | 25 | /** 26 | * Get array of installed packages. 27 | * 28 | * @param string $package 29 | * 30 | * @return array 31 | */ 32 | public function packages($package) 33 | { 34 | $query = "pkcon search {$package} | grep '^In' | sed 's/\s\+/ /g' | cut -d' ' -f2 | sed 's/-[0-9].*//'"; 35 | 36 | return explode(PHP_EOL, $this->cli->run($query)); 37 | } 38 | 39 | /** 40 | * Determine if the given package is installed. 41 | * 42 | * @param string $package 43 | * 44 | * @return bool 45 | */ 46 | public function installed($package) 47 | { 48 | return in_array($package, $this->packages($package)); 49 | } 50 | 51 | /** 52 | * Ensure that the given package is installed. 53 | * 54 | * @param string $package 55 | * 56 | * @return void 57 | */ 58 | public function ensureInstalled($package) 59 | { 60 | if (!$this->installed($package)) { 61 | $this->installOrFail($package); 62 | } 63 | } 64 | 65 | /** 66 | * Install the given package and throw an exception on failure. 67 | * 68 | * @param string $package 69 | * 70 | * @return void 71 | */ 72 | public function installOrFail($package) 73 | { 74 | output('['.$package.'] is not installed, installing it now via PackageKit... 🍻'); 75 | 76 | $this->cli->run(trim('pkcon install -y '.$package), function ($exitCode, $errorOutput) use ($package) { 77 | output($errorOutput); 78 | 79 | throw new DomainException('PackageKit was unable to install ['.$package.'].'); 80 | }); 81 | } 82 | 83 | /** 84 | * Configure package manager on valet install. 85 | * 86 | * @return void 87 | */ 88 | public function setup() 89 | { 90 | // Nothing to do 91 | } 92 | 93 | /** 94 | * Restart dnsmasq in Ubuntu. 95 | */ 96 | public function nmRestart($sm) 97 | { 98 | $sm->restart(['network-manager']); 99 | 100 | $version = trim($this->cli->run('cat /etc/*release | grep DISTRIB_RELEASE | cut -d\= -f2')); 101 | 102 | if ($version === '17.04') { 103 | $sm->enable('systemd-resolved'); 104 | $sm->restart('systemd-resolved'); 105 | } 106 | } 107 | 108 | /** 109 | * Determine if package manager is available on the system. 110 | * 111 | * @return bool 112 | */ 113 | public function isAvailable() 114 | { 115 | try { 116 | $output = $this->cli->run('which pkcon', function ($exitCode, $output) { 117 | throw new DomainException('PackageKit not available'); 118 | }); 119 | 120 | return $output != ''; 121 | } catch (DomainException $e) { 122 | return false; 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /cli/Valet/PackageManagers/Pacman.php: -------------------------------------------------------------------------------- 1 | cli = $cli; 23 | } 24 | 25 | /** 26 | * Get array of installed packages. 27 | * 28 | * @param string $package 29 | * 30 | * @return array 31 | */ 32 | public function packages($package) 33 | { 34 | $query = "pacman -Qqs {$package}"; 35 | 36 | return explode(PHP_EOL, $this->cli->run($query)); 37 | } 38 | 39 | /** 40 | * Determine if the given package is installed. 41 | * 42 | * @param string $package 43 | * 44 | * @return bool 45 | */ 46 | public function installed($package) 47 | { 48 | return in_array($package, $this->packages($package)); 49 | } 50 | 51 | /** 52 | * Ensure that the given package is installed. 53 | * 54 | * @param string $package 55 | * 56 | * @return void 57 | */ 58 | public function ensureInstalled($package) 59 | { 60 | if (!$this->installed($package)) { 61 | $this->installOrFail($package); 62 | } 63 | } 64 | 65 | /** 66 | * Install the given package and throw an exception on failure. 67 | * 68 | * @param string $package 69 | * 70 | * @return void 71 | */ 72 | public function installOrFail($package) 73 | { 74 | output('['.$package.'] is not installed, installing it now via Pacman... 🍻'); 75 | 76 | $this->cli->run(trim('pacman --noconfirm --needed -S '.$package), function ($exitCode, $errorOutput) use ($package) { 77 | output($errorOutput); 78 | 79 | throw new DomainException('Pacman was unable to install ['.$package.'].'); 80 | }); 81 | } 82 | 83 | /** 84 | * Configure package manager on valet install. 85 | * 86 | * @return void 87 | */ 88 | public function setup() 89 | { 90 | // Nothing to do 91 | } 92 | 93 | /** 94 | * Restart dnsmasq in Ubuntu. 95 | */ 96 | public function nmRestart($sm) 97 | { 98 | $sm->restart('NetworkManager'); 99 | } 100 | 101 | /** 102 | * Determine if package manager is available on the system. 103 | * 104 | * @return bool 105 | */ 106 | public function isAvailable() 107 | { 108 | try { 109 | $output = $this->cli->run('which pacman', function ($exitCode, $output) { 110 | throw new DomainException('Pacman not available'); 111 | }); 112 | 113 | return $output != ''; 114 | } catch (DomainException $e) { 115 | return false; 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /cli/Valet/PackageManagers/Yum.php: -------------------------------------------------------------------------------- 1 | cli = $cli; 23 | } 24 | 25 | /** 26 | * Determine if the given package is installed. 27 | * 28 | * @param string $package 29 | * 30 | * @return bool 31 | */ 32 | public function installed($package) 33 | { 34 | $query = "yum list installed {$package} | grep {$package} | sed 's_ _\\t_g' | sed 's_\\._\\t_g' | cut -f 1"; 35 | 36 | $packages = explode(PHP_EOL, $this->cli->run($query)); 37 | 38 | return in_array($package, $packages); 39 | } 40 | 41 | /** 42 | * Ensure that the given package is installed. 43 | * 44 | * @param string $package 45 | * 46 | * @return void 47 | */ 48 | public function ensureInstalled($package) 49 | { 50 | if (!$this->installed($package)) { 51 | $this->installOrFail($package); 52 | } 53 | } 54 | 55 | /** 56 | * Install the given package and throw an exception on failure. 57 | * 58 | * @param string $package 59 | * 60 | * @return void 61 | */ 62 | public function installOrFail($package) 63 | { 64 | output('['.$package.'] is not installed, installing it now via Yum... 🍻'); 65 | 66 | $this->cli->run(trim('yum install -y '.$package), function ($exitCode, $errorOutput) use ($package) { 67 | output($errorOutput); 68 | 69 | throw new DomainException('Yum was unable to install ['.$package.'].'); 70 | }); 71 | } 72 | 73 | /** 74 | * Configure package manager on valet install. 75 | * 76 | * @return void 77 | */ 78 | public function setup() 79 | { 80 | // Nothing to do 81 | } 82 | 83 | /** 84 | * Restart dnsmasq in Fedora. 85 | */ 86 | public function nmRestart($sm) 87 | { 88 | $sm->restart('NetworkManager'); 89 | } 90 | 91 | /** 92 | * Determine if package manager is available on the system. 93 | * 94 | * @return bool 95 | */ 96 | public function isAvailable() 97 | { 98 | try { 99 | $output = $this->cli->run('which yum', function ($exitCode, $output) { 100 | throw new DomainException('Yum not available'); 101 | }); 102 | 103 | return $output != ''; 104 | } catch (DomainException $e) { 105 | return false; 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /cli/Valet/PhpFpm.php: -------------------------------------------------------------------------------- 1 | cli = $cli; 30 | $this->pm = $pm; 31 | $this->sm = $sm; 32 | $this->files = $files; 33 | $this->version = $this->getVersion(); 34 | } 35 | 36 | /** 37 | * Install and configure PHP FPM. 38 | * 39 | * @return void 40 | */ 41 | public function install() 42 | { 43 | if (!$this->pm->installed("php{$this->version}-fpm")) { 44 | $this->pm->ensureInstalled("php{$this->version}-fpm"); 45 | $this->sm->enable($this->fpmServiceName()); 46 | } 47 | 48 | $this->files->ensureDirExists('/var/log', user()); 49 | 50 | $this->installConfiguration(); 51 | 52 | $this->restart(); 53 | } 54 | 55 | /** 56 | * Uninstall PHP FPM valet config. 57 | * 58 | * @return void 59 | */ 60 | public function uninstall() 61 | { 62 | if ($this->files->exists($this->fpmConfigPath().'/valet.conf')) { 63 | $this->files->unlink($this->fpmConfigPath().'/valet.conf'); 64 | $this->stop(); 65 | } 66 | } 67 | 68 | /** 69 | * Update the PHP FPM configuration to use the current user. 70 | * 71 | * @return void 72 | */ 73 | public function installConfiguration() 74 | { 75 | $contents = $this->files->get(__DIR__.'/../stubs/fpm.conf'); 76 | 77 | $this->files->putAsUser( 78 | $this->fpmConfigPath().'/valet.conf', 79 | str_array_replace([ 80 | 'VALET_USER' => user(), 81 | 'VALET_GROUP' => group(), 82 | 'VALET_HOME_PATH' => VALET_HOME_PATH, 83 | ], $contents) 84 | ); 85 | } 86 | 87 | /** 88 | * Restart the PHP FPM process. 89 | * 90 | * @return void 91 | */ 92 | public function restart() 93 | { 94 | $this->sm->restart($this->fpmServiceName()); 95 | } 96 | 97 | /** 98 | * Stop the PHP FPM process. 99 | * 100 | * @return void 101 | */ 102 | public function stop() 103 | { 104 | $this->sm->stop($this->fpmServiceName()); 105 | } 106 | 107 | /** 108 | * PHP-FPM service status. 109 | * 110 | * @return void 111 | */ 112 | public function status() 113 | { 114 | $this->sm->printStatus($this->fpmServiceName()); 115 | } 116 | 117 | /** 118 | * Get installed PHP version. 119 | * 120 | * @return string 121 | */ 122 | public function getVersion() 123 | { 124 | return explode('php', basename($this->files->readLink('/usr/bin/php')))[1]; 125 | } 126 | 127 | /** 128 | * Determine php service name. 129 | * 130 | * @return string 131 | */ 132 | public function fpmServiceName() 133 | { 134 | $service = 'php'.$this->version.'-fpm'; 135 | $status = $this->sm->status($service); 136 | 137 | if (strpos($status, 'not-found') || strpos($status, 'not be found')) { 138 | return new DomainException('Unable to determine PHP service name.'); 139 | } 140 | 141 | return $service; 142 | } 143 | 144 | /** 145 | * Get the path to the FPM configuration file for the current PHP version. 146 | * 147 | * @return string 148 | */ 149 | public function fpmConfigPath() 150 | { 151 | return collect([ 152 | '/etc/php/'.$this->version.'/fpm/pool.d', // Ubuntu 153 | '/etc/php'.$this->version.'/fpm/pool.d', // Ubuntu 154 | '/etc/php-fpm.d', // Fedora 155 | '/etc/php/php-fpm.d', // Arch 156 | ])->first(function ($path) { 157 | return is_dir($path); 158 | }, function () { 159 | throw new DomainException('Unable to determine PHP-FPM configuration folder.'); 160 | }); 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /cli/Valet/Requirements.php: -------------------------------------------------------------------------------- 1 | cli = $cli; 20 | } 21 | 22 | /** 23 | * Determine if SELinux check should be skipped. 24 | * 25 | * @param bool $ignore 26 | * 27 | * @return $this 28 | */ 29 | public function setIgnoreSELinux($ignore = true) 30 | { 31 | $this->ignoreSELinux = $ignore; 32 | 33 | return $this; 34 | } 35 | 36 | /** 37 | * Run all checks and output warnings. 38 | */ 39 | public function check() 40 | { 41 | $this->homePathIsInsideRoot(); 42 | $this->seLinuxIsEnabled(); 43 | } 44 | 45 | /** 46 | * Verify if valet home is inside /root directory. 47 | * 48 | * This usually means the HOME parameters has not been 49 | * kept using sudo. 50 | */ 51 | public function homePathIsInsideRoot() 52 | { 53 | if (strpos(VALET_HOME_PATH, '/root/') === 0) { 54 | throw new RuntimeException('Valet home directory is inside /root'); 55 | } 56 | } 57 | 58 | /** 59 | * Verify is SELinux is enabled and in enforcing mode. 60 | */ 61 | public function seLinuxIsEnabled() 62 | { 63 | if ($this->ignoreSELinux) { 64 | return; 65 | } 66 | 67 | $output = $this->cli->run('sestatus'); 68 | 69 | if (preg_match('@SELinux status:(\s+)enabled@', $output) 70 | && preg_match('@Current mode:(\s+)enforcing@', $output) 71 | ) { 72 | throw new RuntimeException('SELinux is in enforcing mode'); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /cli/Valet/ServiceManagers/LinuxService.php: -------------------------------------------------------------------------------- 1 | cli = $cli; 21 | } 22 | 23 | /** 24 | * Start the given services. 25 | * 26 | * @param mixed $services Service name 27 | * 28 | * @return void 29 | */ 30 | public function start($services) 31 | { 32 | $services = is_array($services) ? $services : func_get_args(); 33 | 34 | foreach ($services as $service) { 35 | info("Starting $service..."); 36 | $this->cli->quietly('sudo service '.$this->getRealService($service).' start'); 37 | } 38 | } 39 | 40 | /** 41 | * Stop the given services. 42 | * 43 | * @param mixed $services Service name 44 | * 45 | * @return void 46 | */ 47 | public function stop($services) 48 | { 49 | $services = is_array($services) ? $services : func_get_args(); 50 | 51 | foreach ($services as $service) { 52 | info("Stopping $service..."); 53 | $this->cli->quietly('sudo service '.$this->getRealService($service).' stop'); 54 | } 55 | } 56 | 57 | /** 58 | * Restart the given services. 59 | * 60 | * @param mixed $services Service name 61 | * 62 | * @return void 63 | */ 64 | public function restart($services) 65 | { 66 | $services = is_array($services) ? $services : func_get_args(); 67 | 68 | foreach ($services as $service) { 69 | info("Restarting $service..."); 70 | $this->cli->quietly('sudo service '.$this->getRealService($service).' restart'); 71 | } 72 | } 73 | 74 | /** 75 | * Status of the given services. 76 | * 77 | * @param mixed $services Service name 78 | * 79 | * @return void 80 | */ 81 | public function printStatus($services) 82 | { 83 | $services = is_array($services) ? $services : func_get_args(); 84 | 85 | foreach ($services as $service) { 86 | if ($this->_hasSystemd()) { 87 | $status = $this->cli->run( 88 | 'systemctl status '.$this->getRealService($service).' | grep "Active:"' 89 | ); 90 | 91 | $running = strpos(trim($status), 'running'); 92 | 93 | if ($running) { 94 | return info(ucfirst($service).' is running...'); 95 | } else { 96 | return warning(ucfirst($service).' is stopped...'); 97 | } 98 | } 99 | 100 | return info($this->cli->run('service '.$this->getRealService($service))); 101 | } 102 | } 103 | 104 | /** 105 | * Status of the given services. 106 | * 107 | * @param mixed $service Service to get status from 108 | * 109 | * @return void 110 | */ 111 | public function status($service) 112 | { 113 | return $this->cli->run('service '.$this->getRealService($service).' status'); 114 | } 115 | 116 | /** 117 | * Check if service is disabled. 118 | * 119 | * @param mixed $service Service name 120 | * 121 | * @return bool 122 | */ 123 | public function disabled($service) 124 | { 125 | $service = $this->getRealService($service); 126 | 127 | return (strpos(trim($this->cli->run("systemctl is-enabled {$service}")), 'enabled')) === false; 128 | } 129 | 130 | /** 131 | * Disable services. 132 | * 133 | * @param mixed $services Service or services to disable 134 | * 135 | * @return void 136 | */ 137 | public function disable($services) 138 | { 139 | if ($this->_hasSystemd()) { 140 | $services = is_array($services) ? $services : func_get_args(); 141 | 142 | foreach ($services as $service) { 143 | try { 144 | $service = $this->getRealService($service); 145 | 146 | if (!$this->disabled($service)) { 147 | $this->cli->quietly('sudo systemctl disable '.$service); 148 | info(ucfirst($service).' has been disabled'); 149 | } 150 | 151 | info(ucfirst($service).' was already disabled'); 152 | } catch (DomainException $e) { 153 | warning(ucfirst($service).' not available.'); 154 | } 155 | } 156 | } else { 157 | $services = is_array($services) ? $services : func_get_args(); 158 | 159 | foreach ($services as $service) { 160 | try { 161 | $service = $this->getRealService($service); 162 | $this->cli->quietly("sudo chmod -x /etc/init.d/{$service}"); 163 | $this->cli->quietly("sudo update-rc.d $service defaults"); 164 | } catch (DomainException $e) { 165 | warning(ucfirst($service).' not available.'); 166 | } 167 | } 168 | } 169 | } 170 | 171 | /** 172 | * Enable services. 173 | * 174 | * @param mixed $services Service or services to enable 175 | * 176 | * @return void 177 | */ 178 | public function enable($services) 179 | { 180 | if ($this->_hasSystemd()) { 181 | $services = is_array($services) ? $services : func_get_args(); 182 | 183 | foreach ($services as $service) { 184 | try { 185 | $service = $this->getRealService($service); 186 | 187 | if ($this->disabled($service)) { 188 | $this->cli->quietly('sudo systemctl enable '.$service); 189 | info(ucfirst($service).' has been enabled'); 190 | 191 | return true; 192 | } 193 | 194 | info(ucfirst($service).' was already enabled'); 195 | 196 | return true; 197 | } catch (DomainException $e) { 198 | warning(ucfirst($service).' not available.'); 199 | 200 | return false; 201 | } 202 | } 203 | } else { 204 | $services = is_array($services) ? $services : func_get_args(); 205 | 206 | foreach ($services as $service) { 207 | try { 208 | $service = $this->getRealService($service); 209 | $this->cli->quietly("sudo update-rc.d $service defaults"); 210 | info(ucfirst($service).' has been enabled'); 211 | 212 | return true; 213 | } catch (DomainException $e) { 214 | warning(ucfirst($service).' not available.'); 215 | 216 | return false; 217 | } 218 | } 219 | } 220 | } 221 | 222 | /** 223 | * Determine if service manager is available on the system. 224 | * 225 | * @return bool 226 | */ 227 | public function isAvailable() 228 | { 229 | try { 230 | $output = $this->cli->run( 231 | 'which service', 232 | function ($exitCode, $output) { 233 | throw new DomainException('Service not available'); 234 | } 235 | ); 236 | 237 | return $output != ''; 238 | } catch (DomainException $e) { 239 | return false; 240 | } 241 | } 242 | 243 | /** 244 | * Determine real service name. 245 | * 246 | * @param mixed $service Service name 247 | * 248 | * @return string 249 | */ 250 | public function getRealService($service) 251 | { 252 | return collect($service)->first( 253 | function ($service) { 254 | return !strpos( 255 | $this->cli->run('service '.$service.' status'), 256 | 'not-found' 257 | ); 258 | }, 259 | function () { 260 | throw new DomainException('Unable to determine service name.'); 261 | } 262 | ); 263 | } 264 | 265 | /** 266 | * Determine if systemd is available on the system. 267 | * 268 | * @return bool 269 | */ 270 | private function _hasSystemd() 271 | { 272 | try { 273 | $this->cli->run( 274 | 'which systemctl', 275 | function ($exitCode, $output) { 276 | throw new DomainException('Systemd not available'); 277 | } 278 | ); 279 | 280 | return true; 281 | } catch (DomainException $e) { 282 | return false; 283 | } 284 | } 285 | 286 | /** 287 | * Install Valet DNS services. 288 | * 289 | * @param Filesystem $files Filesystem object 290 | * 291 | * @return void 292 | */ 293 | public function installValetDns($files) 294 | { 295 | info('Installing Valet DNS service...'); 296 | 297 | $servicePath = '/etc/init.d/valet-dns'; 298 | $serviceFile = __DIR__.'/../../stubs/init/sysvinit'; 299 | $hasSystemd = $this->_hasSystemd(); 300 | 301 | if ($hasSystemd) { 302 | $servicePath = '/etc/systemd/system/valet-dns.service'; 303 | $serviceFile = __DIR__.'/../../stubs/init/systemd'; 304 | } 305 | 306 | $files->put( 307 | $servicePath, 308 | $files->get($serviceFile) 309 | ); 310 | 311 | if (!$hasSystemd) { 312 | $this->cli->run("chmod +x $servicePath"); 313 | } 314 | 315 | $this->enable('valet-dns'); 316 | } 317 | } 318 | -------------------------------------------------------------------------------- /cli/Valet/ServiceManagers/Systemd.php: -------------------------------------------------------------------------------- 1 | cli = $cli; 21 | } 22 | 23 | /** 24 | * Start the given services. 25 | * 26 | * @param mixed $services Service name 27 | * 28 | * @return void 29 | */ 30 | public function start($services) 31 | { 32 | $services = is_array($services) ? $services : func_get_args(); 33 | 34 | foreach ($services as $service) { 35 | info("Starting $service..."); 36 | $this->cli->quietly('sudo systemctl start '.$this->getRealService($service)); 37 | } 38 | } 39 | 40 | /** 41 | * Stop the given services. 42 | * 43 | * @param mixed $services Service name 44 | * 45 | * @return void 46 | */ 47 | public function stop($services) 48 | { 49 | $services = is_array($services) ? $services : func_get_args(); 50 | 51 | foreach ($services as $service) { 52 | info("Stopping $service..."); 53 | $this->cli->quietly('sudo systemctl stop '.$this->getRealService($service)); 54 | } 55 | } 56 | 57 | /** 58 | * Restart the given services. 59 | * 60 | * @param mixed $services Service name 61 | * 62 | * @return void 63 | */ 64 | public function restart($services) 65 | { 66 | $services = is_array($services) ? $services : func_get_args(); 67 | 68 | foreach ($services as $service) { 69 | info("Restarting $service..."); 70 | $this->cli->quietly('sudo systemctl restart '.$this->getRealService($service)); 71 | } 72 | } 73 | 74 | /** 75 | * Status of the given services. 76 | * 77 | * @param mixed $services Service name 78 | * 79 | * @return void 80 | */ 81 | public function printStatus($services) 82 | { 83 | $services = is_array($services) ? $services : func_get_args(); 84 | 85 | foreach ($services as $service) { 86 | $status = $this->cli->run('systemctl status '.$this->getRealService($service).' | grep "Active:"'); 87 | $running = strpos(trim($status), 'running'); 88 | 89 | if ($running) { 90 | info(ucfirst($service).' is running...'); 91 | } else { 92 | warning(ucfirst($service).' is stopped...'); 93 | } 94 | } 95 | } 96 | 97 | /** 98 | * Status of the given services. 99 | * 100 | * @param mixed $service Service name 101 | * 102 | * @return void 103 | */ 104 | public function status($service) 105 | { 106 | return $this->cli->run('systemctl status '.$this->getRealService($service)); 107 | } 108 | 109 | /** 110 | * Check if service is disabled. 111 | * 112 | * @param mixed $service Service name 113 | * 114 | * @return void 115 | */ 116 | public function disabled($service) 117 | { 118 | $service = $this->getRealService($service); 119 | 120 | return (strpos(trim($this->cli->run("systemctl is-enabled {$service}")), 'enabled')) === false; 121 | } 122 | 123 | /** 124 | * Enable services. 125 | * 126 | * @param mixed $services Service name 127 | * 128 | * @return void 129 | */ 130 | public function enable($services) 131 | { 132 | $services = is_array($services) ? $services : func_get_args(); 133 | 134 | foreach ($services as $service) { 135 | try { 136 | $service = $this->getRealService($service); 137 | 138 | if ($this->disabled($service)) { 139 | $this->cli->quietly('sudo systemctl enable '.$service); 140 | info(ucfirst($service).' has been enabled'); 141 | 142 | return true; 143 | } 144 | 145 | info(ucfirst($service).' was already enabled'); 146 | 147 | return true; 148 | } catch (DomainException $e) { 149 | warning(ucfirst($service).' unavailable.'); 150 | 151 | return false; 152 | } 153 | } 154 | } 155 | 156 | /** 157 | * Disable services. 158 | * 159 | * @param mixed $services Service name 160 | * 161 | * @return void 162 | */ 163 | public function disable($services) 164 | { 165 | $services = is_array($services) ? $services : func_get_args(); 166 | 167 | foreach ($services as $service) { 168 | try { 169 | $service = $this->getRealService($service); 170 | 171 | if (!$this->disabled($service)) { 172 | $this->cli->quietly('sudo systemctl disable '.$service); 173 | info(ucfirst($service).' has been disabled'); 174 | 175 | return true; 176 | } 177 | 178 | info(ucfirst($service).' was already disabled'); 179 | 180 | return true; 181 | } catch (DomainException $e) { 182 | warning(ucfirst($service).' unavailable.'); 183 | 184 | return false; 185 | } 186 | } 187 | } 188 | 189 | /** 190 | * Determine if service manager is available on the system. 191 | * 192 | * @return bool 193 | */ 194 | public function isAvailable() 195 | { 196 | try { 197 | $output = $this->cli->run( 198 | 'which systemctl', 199 | function ($exitCode, $output) { 200 | throw new DomainException('Systemd not available'); 201 | } 202 | ); 203 | 204 | return $output != ''; 205 | } catch (DomainException $e) { 206 | return false; 207 | } 208 | } 209 | 210 | /** 211 | * Determine real service name. 212 | * 213 | * @param mixed $service Service name 214 | * 215 | * @return string 216 | */ 217 | public function getRealService($service) 218 | { 219 | return collect($service)->first( 220 | function ($service) { 221 | return strpos($this->cli->run("systemctl status {$service} | grep Loaded"), 'Loaded: loaded'); 222 | }, 223 | function () { 224 | throw new DomainException('Unable to determine service name.'); 225 | } 226 | ); 227 | } 228 | 229 | /** 230 | * Install Valet DNS services. 231 | * 232 | * @param Filesystem $files Filesystem object 233 | * 234 | * @return void 235 | */ 236 | public function installValetDns($files) 237 | { 238 | info('Installing Valet DNS service...'); 239 | 240 | $files->put( 241 | '/etc/systemd/system/valet-dns.service', 242 | $files->get(__DIR__.'/../../stubs/init/systemd') 243 | ); 244 | 245 | $this->enable('valet-dns'); 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /cli/Valet/Site.php: -------------------------------------------------------------------------------- 1 | cli = $cli; 21 | $this->files = $files; 22 | $this->config = $config; 23 | } 24 | 25 | /** 26 | * Get the real hostname for the given path, checking links. 27 | * 28 | * @param string $path 29 | * 30 | * @return string|null 31 | */ 32 | public function host($path) 33 | { 34 | foreach ($this->files->scandir($this->sitesPath()) as $link) { 35 | if ($resolved = realpath($this->sitesPath().'/'.$link) === $path) { 36 | return $link; 37 | } 38 | } 39 | 40 | return basename($path); 41 | } 42 | 43 | /** 44 | * Link the current working directory with the given name. 45 | * 46 | * @param string $target 47 | * @param string $link 48 | * 49 | * @return string 50 | */ 51 | public function link($target, $link) 52 | { 53 | $this->files->ensureDirExists( 54 | $linkPath = $this->sitesPath(), 55 | user() 56 | ); 57 | 58 | $this->config->prependPath($linkPath); 59 | 60 | $this->files->symlinkAsUser($target, $linkPath.'/'.$link); 61 | 62 | return $linkPath.'/'.$link; 63 | } 64 | 65 | /** 66 | * Pretty print out all links in Valet. 67 | * 68 | * @return \Illuminate\Support\Collection 69 | */ 70 | public function links() 71 | { 72 | $certsPath = VALET_HOME_PATH.'/Certificates'; 73 | 74 | $this->files->ensureDirExists($certsPath, user()); 75 | 76 | $certs = $this->getCertificates($certsPath); 77 | 78 | return $this->getLinks(VALET_HOME_PATH.'/Sites', $certs); 79 | } 80 | 81 | /** 82 | * Get all certificates from config folder. 83 | * 84 | * @param string $path 85 | * 86 | * @return \Illuminate\Support\Collection 87 | */ 88 | public function getCertificates($path) 89 | { 90 | return collect($this->files->scanDir($path))->filter(function ($value, $key) { 91 | return ends_with($value, '.crt'); 92 | })->map(function ($cert) { 93 | return substr($cert, 0, -9); 94 | })->flip(); 95 | } 96 | 97 | /** 98 | * Get list of links and present them formatted. 99 | * 100 | * @param string $path 101 | * @param \Illuminate\Support\Collection $certs 102 | * 103 | * @return \Illuminate\Support\Collection 104 | */ 105 | public function getLinks($path, $certs) 106 | { 107 | $config = $this->config->read(); 108 | 109 | $httpPort = $this->httpSuffix(); 110 | $httpsPort = $this->httpsSuffix(); 111 | 112 | return collect($this->files->scanDir($path))->mapWithKeys(function ($site) use ($path) { 113 | return [$site => $this->files->readLink($path.'/'.$site)]; 114 | })->map(function ($path, $site) use ($certs, $config, $httpPort, $httpsPort) { 115 | $secured = $certs->has($site); 116 | 117 | $url = ($secured ? 'https' : 'http').'://'.$site.'.'.$config['domain'].($secured ? $httpsPort : $httpPort); 118 | 119 | return [$site, $secured ? ' X' : '', $url, $path]; 120 | }); 121 | } 122 | 123 | /** 124 | * Return http port suffix. 125 | * 126 | * @return string 127 | */ 128 | public function httpSuffix() 129 | { 130 | $port = $this->config->get('port', 80); 131 | 132 | return ($port == 80) ? '' : ':'.$port; 133 | } 134 | 135 | /** 136 | * Return https port suffix. 137 | * 138 | * @return string 139 | */ 140 | public function httpsSuffix() 141 | { 142 | $port = $this->config->get('https_port', 443); 143 | 144 | return ($port == 443) ? '' : ':'.$port; 145 | } 146 | 147 | /** 148 | * Unlink the given symbolic link. 149 | * 150 | * @param string $name 151 | * 152 | * @return void 153 | */ 154 | public function unlink($name) 155 | { 156 | if ($this->files->exists($path = $this->sitesPath().'/'.$name)) { 157 | $this->files->unlink($path); 158 | } 159 | } 160 | 161 | /** 162 | * Remove all broken symbolic links. 163 | * 164 | * @return void 165 | */ 166 | public function pruneLinks() 167 | { 168 | $this->files->ensureDirExists($this->sitesPath(), user()); 169 | 170 | $this->files->removeBrokenLinksAt($this->sitesPath()); 171 | } 172 | 173 | /** 174 | * Resecure all currently secured sites with a fresh domain. 175 | * 176 | * @param string $oldDomain 177 | * @param string $domain 178 | * 179 | * @return void 180 | */ 181 | public function resecureForNewDomain($oldDomain, $domain) 182 | { 183 | if (!$this->files->exists($this->certificatesPath())) { 184 | return; 185 | } 186 | 187 | $secured = $this->secured(); 188 | 189 | foreach ($secured as $url) { 190 | $this->unsecure($url); 191 | } 192 | 193 | foreach ($secured as $url) { 194 | $this->secure(str_replace('.'.$oldDomain, '.'.$domain, $url)); 195 | } 196 | } 197 | 198 | /** 199 | * Get all of the URLs that are currently secured. 200 | * 201 | * @return \Illuminate\Support\Collection 202 | */ 203 | public function secured() 204 | { 205 | return collect($this->files->scandir($this->certificatesPath())) 206 | ->map(function ($file) { 207 | return str_replace(['.key', '.csr', '.crt', '.conf'], '', $file); 208 | })->unique()->values(); 209 | } 210 | 211 | /** 212 | * Secure the given host with TLS. 213 | * 214 | * @param string $url 215 | * 216 | * @return void 217 | */ 218 | public function secure($url) 219 | { 220 | $this->unsecure($url); 221 | 222 | $this->files->ensureDirExists($this->certificatesPath(), user()); 223 | 224 | $this->createCertificate($url); 225 | 226 | $this->createSecureNginxServer($url); 227 | } 228 | 229 | /** 230 | * Create and trust a certificate for the given URL. 231 | * 232 | * @param string $url 233 | * 234 | * @return void 235 | */ 236 | public function createCertificate($url) 237 | { 238 | $keyPath = $this->certificatesPath().'/'.$url.'.key'; 239 | $csrPath = $this->certificatesPath().'/'.$url.'.csr'; 240 | $crtPath = $this->certificatesPath().'/'.$url.'.crt'; 241 | $confPath = $this->certificatesPath().'/'.$url.'.conf'; 242 | 243 | $this->buildCertificateConf($confPath, $url); 244 | $this->createPrivateKey($keyPath); 245 | $this->createSigningRequest($url, $keyPath, $csrPath, $confPath); 246 | 247 | $this->cli->runAsUser(sprintf( 248 | 'openssl x509 -req -sha256 -days 365 -in %s -signkey %s -out %s -extensions v3_req -extfile %s', 249 | $csrPath, $keyPath, $crtPath, $confPath 250 | )); 251 | 252 | $this->trustCertificate($crtPath, $url); 253 | } 254 | 255 | /** 256 | * Create the private key for the TLS certificate. 257 | * 258 | * @param string $keyPath 259 | * 260 | * @return void 261 | */ 262 | public function createPrivateKey($keyPath) 263 | { 264 | $this->cli->runAsUser(sprintf('openssl genrsa -out %s 2048', $keyPath)); 265 | } 266 | 267 | /** 268 | * Create the signing request for the TLS certificate. 269 | * 270 | * @param string $keyPath 271 | * 272 | * @return void 273 | */ 274 | public function createSigningRequest($url, $keyPath, $csrPath, $confPath) 275 | { 276 | $this->cli->runAsUser(sprintf( 277 | 'openssl req -new -key %s -out %s -subj "/C=US/ST=MN/O=Valet/localityName=Valet/commonName=%s/organizationalUnitName=Valet/emailAddress=valet/" -config %s -passin pass:', 278 | $keyPath, $csrPath, $url, $confPath 279 | )); 280 | } 281 | 282 | /** 283 | * Build the SSL config for the given URL. 284 | * 285 | * @param string $url 286 | * 287 | * @return string 288 | */ 289 | public function buildCertificateConf($path, $url) 290 | { 291 | $config = str_replace('VALET_DOMAIN', $url, $this->files->get(__DIR__.'/../stubs/openssl.conf')); 292 | $this->files->putAsUser($path, $config); 293 | } 294 | 295 | /** 296 | * Trust the given certificate file in the Mac Keychain. 297 | * 298 | * @param string $crtPath 299 | * 300 | * @return void 301 | */ 302 | public function trustCertificate($crtPath, $url) 303 | { 304 | $this->cli->run(sprintf( 305 | 'certutil -d sql:$HOME/.pki/nssdb -A -t TC -n "%s" -i "%s"', $url, $crtPath 306 | )); 307 | 308 | $this->cli->run(sprintf( 309 | 'certutil -d $HOME/.mozilla/firefox/*.default -A -t TC -n "%s" -i "%s"', $url, $crtPath 310 | )); 311 | } 312 | 313 | /** 314 | * @param $url 315 | */ 316 | public function createSecureNginxServer($url) 317 | { 318 | $this->files->putAsUser( 319 | VALET_HOME_PATH.'/Nginx/'.$url, 320 | $this->buildSecureNginxServer($url) 321 | ); 322 | } 323 | 324 | /** 325 | * Build the TLS secured Nginx server for the given URL. 326 | * 327 | * @param string $url 328 | * 329 | * @return string 330 | */ 331 | public function buildSecureNginxServer($url) 332 | { 333 | $path = $this->certificatesPath(); 334 | 335 | return str_array_replace( 336 | [ 337 | 'VALET_HOME_PATH' => VALET_HOME_PATH, 338 | 'VALET_SERVER_PATH' => VALET_SERVER_PATH, 339 | 'VALET_STATIC_PREFIX' => VALET_STATIC_PREFIX, 340 | 'VALET_SITE' => $url, 341 | 'VALET_CERT' => $path.'/'.$url.'.crt', 342 | 'VALET_KEY' => $path.'/'.$url.'.key', 343 | 'VALET_HTTP_PORT' => $this->config->get('port', 80), 344 | 'VALET_HTTPS_PORT' => $this->config->get('https_port', 443), 345 | 'VALET_REDIRECT_PORT' => $this->httpsSuffix(), 346 | ], 347 | $this->files->get(__DIR__.'/../stubs/secure.valet.conf') 348 | ); 349 | } 350 | 351 | /** 352 | * Unsecure the given URL so that it will use HTTP again. 353 | * 354 | * @param string $url 355 | * 356 | * @return void 357 | */ 358 | public function unsecure($url) 359 | { 360 | if ($this->files->exists($this->certificatesPath().'/'.$url.'.crt')) { 361 | $this->files->unlink(VALET_HOME_PATH.'/Nginx/'.$url); 362 | 363 | $this->files->unlink($this->certificatesPath().'/'.$url.'.conf'); 364 | $this->files->unlink($this->certificatesPath().'/'.$url.'.key'); 365 | $this->files->unlink($this->certificatesPath().'/'.$url.'.csr'); 366 | $this->files->unlink($this->certificatesPath().'/'.$url.'.crt'); 367 | 368 | $this->cli->run(sprintf('certutil -d sql:$HOME/.pki/nssdb -D -n "%s"', $url)); 369 | $this->cli->run(sprintf('certutil -d $HOME/.mozilla/firefox/*.default -D -n "%s"', $url)); 370 | } 371 | } 372 | 373 | /** 374 | * Regenerate all secured file configurations. 375 | * 376 | * @return void 377 | */ 378 | public function regenerateSecuredSitesConfig() 379 | { 380 | $this->secured()->each(function ($url) { 381 | $this->createSecureNginxServer($url); 382 | }); 383 | } 384 | 385 | /** 386 | * Get the path to the linked Valet sites. 387 | * 388 | * @return string 389 | */ 390 | public function sitesPath() 391 | { 392 | return VALET_HOME_PATH.'/Sites'; 393 | } 394 | 395 | /** 396 | * Get the path to the Valet TLS certificates. 397 | * 398 | * @return string 399 | */ 400 | public function certificatesPath() 401 | { 402 | return VALET_HOME_PATH.'/Certificates'; 403 | } 404 | } 405 | -------------------------------------------------------------------------------- /cli/Valet/Valet.php: -------------------------------------------------------------------------------- 1 | cli = $cli; 35 | $this->files = $files; 36 | } 37 | 38 | /** 39 | * Symlink the Valet Bash script into the user's local bin. 40 | * 41 | * @return void 42 | */ 43 | public function symlinkToUsersBin() 44 | { 45 | $this->cli->run('ln -snf '.realpath(__DIR__.'/../../valet').' '.$this->valetBin); 46 | } 47 | 48 | /** 49 | * Unlink the Valet Bash script from the user's local bin 50 | * and the sudoers.d entry. 51 | * 52 | * @return void 53 | */ 54 | public function uninstall() 55 | { 56 | $this->files->unlink($this->valetBin); 57 | $this->files->unlink($this->sudoers); 58 | } 59 | 60 | /** 61 | * Get the paths to all of the Valet extensions. 62 | * 63 | * @return array 64 | */ 65 | public function extensions() 66 | { 67 | if (!$this->files->isDir(VALET_HOME_PATH.'/Extensions')) { 68 | return []; 69 | } 70 | 71 | return collect($this->files->scandir(VALET_HOME_PATH.'/Extensions')) 72 | ->reject(function ($file) { 73 | return is_dir($file); 74 | }) 75 | ->map(function ($file) { 76 | return VALET_HOME_PATH.'/Extensions/'.$file; 77 | }) 78 | ->values()->all(); 79 | } 80 | 81 | /** 82 | * Determine if this is the latest version of Valet. 83 | * 84 | * @param string $currentVersion 85 | * 86 | * @return bool 87 | */ 88 | public function onLatestVersion($currentVersion) 89 | { 90 | $response = \Httpful\Request::get($this->github)->send(); 91 | 92 | return version_compare($currentVersion, trim($response->body->tag_name), '>='); 93 | } 94 | 95 | /** 96 | * Determine current environment. 97 | * 98 | * @return void 99 | */ 100 | public function environmentSetup() 101 | { 102 | $this->packageManagerSetup(); 103 | $this->serviceManagerSetup(); 104 | } 105 | 106 | /** 107 | * Configure package manager. 108 | * 109 | * @return void 110 | */ 111 | public function packageManagerSetup() 112 | { 113 | Container::getInstance()->bind(PackageManager::class, $this->getAvailablePackageManager()); 114 | } 115 | 116 | /** 117 | * Determine the first available package manager. 118 | * 119 | * @return string 120 | */ 121 | public function getAvailablePackageManager() 122 | { 123 | return collect([ 124 | Apt::class, 125 | Dnf::class, 126 | Pacman::class, 127 | Yum::class, 128 | PackageKit::class, 129 | ])->first(function ($pm) { 130 | return resolve($pm)->isAvailable(); 131 | }, function () { 132 | throw new DomainException('No compatible package manager found.'); 133 | }); 134 | } 135 | 136 | /** 137 | * Configure service manager. 138 | * 139 | * @return void 140 | */ 141 | public function serviceManagerSetup() 142 | { 143 | Container::getInstance()->bind(ServiceManager::class, $this->getAvailableServiceManager()); 144 | } 145 | 146 | /** 147 | * Determine the first available service manager. 148 | * 149 | * @return string 150 | */ 151 | public function getAvailableServiceManager() 152 | { 153 | return collect([ 154 | LinuxService::class, 155 | Systemd::class, 156 | ])->first(function ($pm) { 157 | return resolve($pm)->isAvailable(); 158 | }, function () { 159 | throw new DomainException('No compatible service manager found.'); 160 | }); 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /cli/drivers/BasicValetDriver.php: -------------------------------------------------------------------------------- 1 | isActualFile($staticFilePath = $sitePath.$uri)) { 33 | return $staticFilePath; 34 | } 35 | 36 | return false; 37 | } 38 | 39 | /** 40 | * Get the fully resolved path to the application's front controller. 41 | * 42 | * @param string $sitePath 43 | * @param string $siteName 44 | * @param string $uri 45 | * 46 | * @return string 47 | */ 48 | public function frontControllerPath($sitePath, $siteName, $uri) 49 | { 50 | $dynamicCandidates = [ 51 | $this->asActualFile($sitePath, $uri), 52 | $this->asPhpIndexFileInDirectory($sitePath, $uri), 53 | $this->asHtmlIndexFileInDirectory($sitePath, $uri), 54 | ]; 55 | 56 | foreach ($dynamicCandidates as $candidate) { 57 | if ($this->isActualFile($candidate)) { 58 | $_SERVER['SCRIPT_FILENAME'] = $candidate; 59 | $_SERVER['SCRIPT_NAME'] = str_replace($sitePath, '', $candidate); 60 | $_SERVER['DOCUMENT_ROOT'] = $sitePath; 61 | 62 | return $candidate; 63 | } 64 | } 65 | 66 | $fixedCandidatesAndDocroots = [ 67 | $this->asRootPhpIndexFile($sitePath) => $sitePath, 68 | $this->asPublicPhpIndexFile($sitePath) => $sitePath.'/public', 69 | $this->asPublicHtmlIndexFile($sitePath) => $sitePath.'/public', 70 | ]; 71 | 72 | foreach ($fixedCandidatesAndDocroots as $candidate => $docroot) { 73 | if ($this->isActualFile($candidate)) { 74 | $_SERVER['SCRIPT_FILENAME'] = $candidate; 75 | $_SERVER['SCRIPT_NAME'] = '/index.php'; 76 | $_SERVER['DOCUMENT_ROOT'] = $docroot; 77 | 78 | return $candidate; 79 | } 80 | } 81 | } 82 | 83 | /** 84 | * Concatenate the site path and URI as a single file name. 85 | * 86 | * @param string $sitePath 87 | * @param string $uri 88 | * 89 | * @return string 90 | */ 91 | protected function asActualFile($sitePath, $uri) 92 | { 93 | return $sitePath.$uri; 94 | } 95 | 96 | /** 97 | * Format the site path and URI with a trailing "index.php". 98 | * 99 | * @param string $sitePath 100 | * @param string $uri 101 | * 102 | * @return string 103 | */ 104 | protected function asPhpIndexFileInDirectory($sitePath, $uri) 105 | { 106 | return $sitePath.rtrim($uri, '/').'/index.php'; 107 | } 108 | 109 | /** 110 | * Format the site path and URI with a trailing "index.html". 111 | * 112 | * @param string $sitePath 113 | * @param string $uri 114 | * 115 | * @return string 116 | */ 117 | protected function asHtmlIndexFileInDirectory($sitePath, $uri) 118 | { 119 | return $sitePath.rtrim($uri, '/').'/index.html'; 120 | } 121 | 122 | /** 123 | * Format the incoming site path as root "index.php" file path. 124 | * 125 | * @param string $sitePath 126 | * 127 | * @return string 128 | */ 129 | protected function asRootPhpIndexFile($sitePath) 130 | { 131 | return $sitePath.'/index.php'; 132 | } 133 | 134 | /** 135 | * Format the incoming site path as a "public/index.php" file path. 136 | * 137 | * @param string $sitePath 138 | * 139 | * @return string 140 | */ 141 | protected function asPublicPhpIndexFile($sitePath) 142 | { 143 | return $sitePath.'/public/index.php'; 144 | } 145 | 146 | /** 147 | * Format the incoming site path as a "public/index.php" file path. 148 | * 149 | * @param string $sitePath 150 | * 151 | * @return string 152 | */ 153 | protected function asPublicHtmlIndexFile($sitePath) 154 | { 155 | return $sitePath.'/public/index.html'; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /cli/drivers/BedrockValetDriver.php: -------------------------------------------------------------------------------- 1 | isActualFile($staticFilePath)) { 36 | return $staticFilePath; 37 | } 38 | 39 | return false; 40 | } 41 | 42 | /** 43 | * Get the fully resolved path to the application's front controller. 44 | * 45 | * @param string $sitePath 46 | * @param string $siteName 47 | * @param string $uri 48 | * 49 | * @return string 50 | */ 51 | public function frontControllerPath($sitePath, $siteName, $uri) 52 | { 53 | $_SERVER['PHP_SELF'] = $uri; 54 | 55 | if (strpos($uri, '/wp/') === 0) { 56 | return is_dir($sitePath.'/web'.$uri) 57 | ? $sitePath.'/web'.$this->forceTrailingSlash($uri).'/index.php' 58 | : $sitePath.'/web'.$uri; 59 | } 60 | 61 | return $sitePath.'/web/index.php'; 62 | } 63 | 64 | /** 65 | * Redirect to uri with trailing slash. 66 | * 67 | * @param string $uri 68 | * 69 | * @return string 70 | */ 71 | private function forceTrailingSlash($uri) 72 | { 73 | if (substr($uri, -1 * strlen('/wp/wp-admin')) == '/wp/wp-admin') { 74 | header('Location: '.$uri.'/'); 75 | die; 76 | } 77 | 78 | return $uri; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /cli/drivers/Cake2ValetDriver.php: -------------------------------------------------------------------------------- 1 | isActualFile($staticFilePath = $sitePath.'/webroot/'.$uri)) { 31 | return $staticFilePath; 32 | } 33 | 34 | return false; 35 | } 36 | 37 | /** 38 | * Get the fully resolved path to the application's front controller. 39 | * 40 | * @param string $sitePath 41 | * @param string $siteName 42 | * @param string $uri 43 | * 44 | * @return string 45 | */ 46 | public function frontControllerPath($sitePath, $siteName, $uri) 47 | { 48 | $_SERVER['DOCUMENT_ROOT'] = $sitePath.'/webroot'; 49 | $_SERVER['SCRIPT_FILENAME'] = $sitePath.'/webroot/index.php'; 50 | $_SERVER['SCRIPT_NAME'] = '/index.php'; 51 | $_SERVER['PHP_SELF'] = '/index.php'; 52 | 53 | return $sitePath.'/webroot/index.php'; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /cli/drivers/CakeValetDriver.php: -------------------------------------------------------------------------------- 1 | isActualFile($staticFilePath = $sitePath.'/webroot/'.$uri)) { 31 | return $staticFilePath; 32 | } 33 | 34 | return false; 35 | } 36 | 37 | /** 38 | * Get the fully resolved path to the application's front controller. 39 | * 40 | * @param string $sitePath 41 | * @param string $siteName 42 | * @param string $uri 43 | * 44 | * @return string 45 | */ 46 | public function frontControllerPath($sitePath, $siteName, $uri) 47 | { 48 | $_SERVER['DOCUMENT_ROOT'] = $sitePath.'/webroot'; 49 | $_SERVER['SCRIPT_FILENAME'] = $sitePath.'/webroot/index.php'; 50 | $_SERVER['SCRIPT_NAME'] = '/index.php'; 51 | $_SERVER['PHP_SELF'] = '/index.php'; 52 | 53 | return $sitePath.'/webroot/index.php'; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /cli/drivers/Concrete5ValetDriver.php: -------------------------------------------------------------------------------- 1 | isActualFile($staticFilePath = $sitePath.'/web'.$uri)) { 31 | return $staticFilePath; 32 | } 33 | 34 | return false; 35 | } 36 | 37 | /** 38 | * Get the fully resolved path to the application's front controller. 39 | * 40 | * @param string $sitePath 41 | * @param string $siteName 42 | * @param string $uri 43 | * 44 | * @return string 45 | */ 46 | public function frontControllerPath($sitePath, $siteName, $uri) 47 | { 48 | if ($uri === '/install.php') { 49 | return $sitePath.'/web/install.php'; 50 | } 51 | 52 | return $sitePath.'/web/app.php'; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /cli/drivers/CraftValetDriver.php: -------------------------------------------------------------------------------- 1 | frontControllerDirectory($sitePath); 43 | 44 | if ($this->isActualFile($staticFilePath = $sitePath.'/'.$frontControllerDirectory.$uri)) { 45 | return $staticFilePath; 46 | } 47 | 48 | return false; 49 | } 50 | 51 | /** 52 | * Get the fully resolved path to the application's front controller. 53 | * 54 | * @param string $sitePath 55 | * @param string $siteName 56 | * @param string $uri 57 | * 58 | * @return string 59 | */ 60 | public function frontControllerPath($sitePath, $siteName, $uri) 61 | { 62 | $frontControllerDirectory = $this->frontControllerDirectory($sitePath); 63 | 64 | // Default index path 65 | $indexPath = $sitePath.'/'.$frontControllerDirectory.'/index.php'; 66 | $scriptName = '/index.php'; 67 | 68 | // Check if the first URL segment matches any of the defined locales 69 | $locales = [ 70 | 'ar', 71 | 'ar_sa', 72 | 'bg', 73 | 'bg_bg', 74 | 'ca_es', 75 | 'cs', 76 | 'cy_gb', 77 | 'da', 78 | 'da_dk', 79 | 'de', 80 | 'de_at', 81 | 'de_ch', 82 | 'de_de', 83 | 'el', 84 | 'el_gr', 85 | 'en', 86 | 'en_as', 87 | 'en_au', 88 | 'en_bb', 89 | 'en_be', 90 | 'en_bm', 91 | 'en_bw', 92 | 'en_bz', 93 | 'en_ca', 94 | 'en_dsrt', 95 | 'en_dsrt_us', 96 | 'en_gb', 97 | 'en_gu', 98 | 'en_gy', 99 | 'en_hk', 100 | 'en_ie', 101 | 'en_in', 102 | 'en_jm', 103 | 'en_mh', 104 | 'en_mp', 105 | 'en_mt', 106 | 'en_mu', 107 | 'en_na', 108 | 'en_nz', 109 | 'en_ph', 110 | 'en_pk', 111 | 'en_sg', 112 | 'en_shaw', 113 | 'en_tt', 114 | 'en_um', 115 | 'en_us', 116 | 'en_us_posix', 117 | 'en_vi', 118 | 'en_za', 119 | 'en_zw', 120 | 'en_zz', 121 | 'es', 122 | 'es_cl', 123 | 'es_es', 124 | 'es_mx', 125 | 'es_us', 126 | 'es_ve', 127 | 'et', 128 | 'fi', 129 | 'fi_fi', 130 | 'fil', 131 | 'fr', 132 | 'fr_be', 133 | 'fr_ca', 134 | 'fr_ch', 135 | 'fr_fr', 136 | 'fr_ma', 137 | 'he', 138 | 'hr', 139 | 'hr_hr', 140 | 'hu', 141 | 'hu_hu', 142 | 'id', 143 | 'id_id', 144 | 'it', 145 | 'it_ch', 146 | 'it_it', 147 | 'ja', 148 | 'ja_jp', 149 | 'ko', 150 | 'ko_kr', 151 | 'lt', 152 | 'lv', 153 | 'ms', 154 | 'ms_my', 155 | 'nb', 156 | 'nb_no', 157 | 'nl', 158 | 'nl_be', 159 | 'nl_nl', 160 | 'nn', 161 | 'nn_no', 162 | 'no', 163 | 'pl', 164 | 'pl_pl', 165 | 'pt', 166 | 'pt_br', 167 | 'pt_pt', 168 | 'ro', 169 | 'ro_ro', 170 | 'ru', 171 | 'ru_ru', 172 | 'sk', 173 | 'sl', 174 | 'sr', 175 | 'sv', 176 | 'sv_se', 177 | 'th', 178 | 'th_th', 179 | 'tr', 180 | 'tr_tr', 181 | 'uk', 182 | 'vi', 183 | 'zh', 184 | 'zh_cn', 185 | 'zh_tw', 186 | ]; 187 | $parts = explode('/', $uri); 188 | 189 | if (count($parts) > 1 && in_array($parts[1], $locales)) { 190 | $indexPath = $sitePath.'/public/'.$parts[1].'/index.php'; 191 | $scriptName = '/'.$parts[1].'/index.php'; 192 | } 193 | 194 | $_SERVER['SCRIPT_FILENAME'] = $indexPath; 195 | $_SERVER['SERVER_NAME'] = $_SERVER['HTTP_HOST']; 196 | $_SERVER['SCRIPT_NAME'] = $scriptName; 197 | $_SERVER['PHP_SELF'] = $scriptName; 198 | $_SERVER['DOCUMENT_ROOT'] = $sitePath.'/'.$frontControllerDirectory; 199 | 200 | return $indexPath; 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /cli/drivers/DrupalValetDriver.php: -------------------------------------------------------------------------------- 1 | isActualFile($staticFilePath = $sitePath.$uri)) { 31 | return $staticFilePath; 32 | } 33 | 34 | return false; 35 | } 36 | 37 | /** 38 | * Get the fully resolved path to the application's front controller. 39 | * 40 | * @param string $sitePath 41 | * @param string $siteName 42 | * @param string $uri 43 | * 44 | * @return string 45 | */ 46 | public function frontControllerPath($sitePath, $siteName, $uri) 47 | { 48 | // Needed to force Kirby to use *.test to generate its URLs... 49 | $_SERVER['SERVER_NAME'] = $_SERVER['HTTP_HOST']; 50 | 51 | if (preg_match('/^\/panel/', $uri)) { 52 | $_SERVER['SCRIPT_NAME'] = '/panel/index.php'; 53 | 54 | return $sitePath.'/panel/index.php'; 55 | } 56 | 57 | if (file_exists($indexPath = $sitePath.'/index.php')) { 58 | $_SERVER['SCRIPT_NAME'] = '/index.php'; 59 | 60 | return $indexPath; 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /cli/drivers/LaravelValetDriver.php: -------------------------------------------------------------------------------- 1 | isActualFile($storagePath = $sitePath.'/storage/app/public'.$storageUri)) { 43 | return $storagePath; 44 | } 45 | 46 | return false; 47 | } 48 | 49 | /** 50 | * Get the fully resolved path to the application's front controller. 51 | * 52 | * @param string $sitePath 53 | * @param string $siteName 54 | * @param string $uri 55 | * 56 | * @return string 57 | */ 58 | public function frontControllerPath($sitePath, $siteName, $uri) 59 | { 60 | // Shortcut for getting the "local" hostname as the HTTP_HOST 61 | if (isset($_SERVER['HTTP_X_ORIGINAL_HOST'], $_SERVER['HTTP_X_FORWARDED_HOST'])) { 62 | $_SERVER['HTTP_HOST'] = $_SERVER['HTTP_X_FORWARDED_HOST']; 63 | } 64 | 65 | return $sitePath.'/public/index.php'; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /cli/drivers/Magento2ValetDriver.php: -------------------------------------------------------------------------------- 1 | checkMageMode($sitePath); 38 | 39 | $uri = $this->handleForVersions($uri); 40 | $route = parse_url(substr($uri, 1))['path']; 41 | 42 | $pub = ''; 43 | if ('developer' === $this->mageMode) { 44 | $pub = 'pub/'; 45 | } 46 | 47 | if (!$this->isPubDirectory($sitePath, $route, $pub)) { 48 | return false; 49 | } 50 | 51 | $magentoPackagePubDir = $sitePath; 52 | if ('developer' !== $this->mageMode) { 53 | $magentoPackagePubDir .= '/pub'; 54 | } 55 | 56 | $file = $magentoPackagePubDir.'/'.$route; 57 | 58 | if (file_exists($file)) { 59 | return $magentoPackagePubDir.$uri; 60 | } 61 | 62 | if (strpos($route, $pub.'static/') === 0) { 63 | $route = preg_replace('#'.$pub.'static/#', '', $route, 1); 64 | $_GET['resource'] = $route; 65 | include $magentoPackagePubDir.'/'.$pub.'static.php'; 66 | exit; 67 | } 68 | 69 | if (strpos($route, $pub.'media/') === 0) { 70 | include $magentoPackagePubDir.'/'.$pub.'get.php'; 71 | exit; 72 | } 73 | 74 | return false; 75 | } 76 | 77 | /** 78 | * Rewrite URLs that look like "versions12345/" to remove 79 | * the versions12345/ part. 80 | * 81 | * @param string $route 82 | */ 83 | private function handleForVersions($route) 84 | { 85 | return preg_replace('/version\d*\//', '', $route); 86 | } 87 | 88 | /** 89 | * Determine the current MAGE_MODE. 90 | * 91 | * @param string $sitePath 92 | */ 93 | private function checkMageMode($sitePath) 94 | { 95 | if (null !== $this->mageMode) { 96 | // We have already figure out mode, no need to check it again 97 | return; 98 | } 99 | if (!file_exists($sitePath.'/index.php')) { 100 | $this->mageMode = 'production'; // Can't use developer mode without index.php in project root 101 | return; 102 | } 103 | $mageConfig = []; 104 | if (file_exists($sitePath.'/app/etc/env.php')) { 105 | $mageConfig = require $sitePath.'/app/etc/env.php'; 106 | } 107 | if (array_key_exists('MAGE_MODE', $mageConfig)) { 108 | $this->mageMode = $mageConfig['MAGE_MODE']; 109 | } 110 | } 111 | 112 | /** 113 | * Checks to see if route is referencing any directory inside pub. This is a dynamic check so that if any new 114 | * directories are added to pub this driver will not need to be updated. 115 | * 116 | * @param string $sitePath 117 | * @param string $route 118 | * @param string $pub 119 | * 120 | * @return bool 121 | */ 122 | private function isPubDirectory($sitePath, $route, $pub = '') 123 | { 124 | $sitePath .= '/pub/'; 125 | $dirs = glob($sitePath.'*', GLOB_ONLYDIR); 126 | 127 | $dirs = str_replace($sitePath, '', $dirs); 128 | foreach ($dirs as $dir) { 129 | if (strpos($route, $pub.$dir.'/') === 0) { 130 | return true; 131 | } 132 | } 133 | 134 | return false; 135 | } 136 | 137 | /** 138 | * Get the fully resolved path to the application's front controller. 139 | * 140 | * @param string $sitePath 141 | * @param string $siteName 142 | * @param string $uri 143 | * 144 | * @return string 145 | */ 146 | public function frontControllerPath($sitePath, $siteName, $uri) 147 | { 148 | $this->checkMageMode($sitePath); 149 | 150 | if ('developer' === $this->mageMode) { 151 | return $sitePath.'/index.php'; 152 | } 153 | 154 | return $sitePath.'/pub/index.php'; 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /cli/drivers/NeosValetDriver.php: -------------------------------------------------------------------------------- 1 | isActualFile($staticFilePath = $sitePath.'/Web'.$uri)) { 31 | return $staticFilePath; 32 | } 33 | 34 | return false; 35 | } 36 | 37 | /** 38 | * Get the fully resolved path to the application's front controller. 39 | * 40 | * @param string $sitePath 41 | * @param string $siteName 42 | * @param string $uri 43 | * 44 | * @return string 45 | */ 46 | public function frontControllerPath($sitePath, $siteName, $uri) 47 | { 48 | putenv('FLOW_CONTEXT=Development'); 49 | putenv('FLOW_REWRITEURLS=1'); 50 | $_SERVER['SCRIPT_FILENAME'] = $sitePath.'/Web/index.php'; 51 | $_SERVER['SCRIPT_NAME'] = '/index.php'; 52 | 53 | return $sitePath.'/Web/index.php'; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /cli/drivers/SculpinValetDriver.php: -------------------------------------------------------------------------------- 1 | isActualFile($staticFilePath = $sitePath.$uri)) { 38 | return $staticFilePath; 39 | } 40 | 41 | return false; 42 | } 43 | 44 | /** 45 | * Get the fully resolved path to the application's front controller. 46 | * 47 | * @param string $sitePath 48 | * @param string $siteName 49 | * @param string $uri 50 | * 51 | * @return string 52 | */ 53 | public function frontControllerPath($sitePath, $siteName, $uri) 54 | { 55 | if (strpos($uri, '/admin.php') === 0) { 56 | $_SERVER['SCRIPT_NAME'] = '/admin.php'; 57 | 58 | return $sitePath.'/admin.php'; 59 | } 60 | 61 | if ($uri === '/admin') { 62 | $_SERVER['SCRIPT_NAME'] = '/admin/index.php'; 63 | 64 | return $sitePath.'/admin/index.php'; 65 | } 66 | 67 | $_SERVER['SCRIPT_NAME'] = '/index.php'; 68 | 69 | return $sitePath.'/index.php'; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /cli/drivers/StatamicValetDriver.php: -------------------------------------------------------------------------------- 1 | isActualFile($staticFilePath = $sitePath.$uri)) { 35 | return $staticFilePath; 36 | } elseif ($this->isActualFile($staticFilePath = $sitePath.'/public'.$uri)) { 37 | return $staticFilePath; 38 | } 39 | 40 | return false; 41 | } 42 | 43 | /** 44 | * Get the fully resolved path to the application's front controller. 45 | * 46 | * @param string $sitePath 47 | * @param string $siteName 48 | * @param string $uri 49 | * 50 | * @return string 51 | */ 52 | public function frontControllerPath($sitePath, $siteName, $uri) 53 | { 54 | if (file_exists($staticPath = $sitePath.'/static'.$uri.'/index.html')) { 55 | return $staticPath; 56 | } 57 | 58 | $_SERVER['SCRIPT_NAME'] = '/index.php'; 59 | 60 | if (strpos($_SERVER['REQUEST_URI'], '/index.php') === 0) { 61 | $_SERVER['REQUEST_URI'] = substr($_SERVER['REQUEST_URI'], 10); 62 | } 63 | 64 | if ($uri === '') { 65 | $uri = '/'; 66 | } 67 | 68 | if ($uri === '/installer.php') { 69 | return $sitePath.'/installer.php'; 70 | } 71 | 72 | if (file_exists($indexPath = $sitePath.'/index.php')) { 73 | return $indexPath; 74 | } 75 | 76 | if (file_exists($indexPath = $sitePath.'/public/index.php')) { 77 | return $indexPath; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /cli/drivers/SymfonyValetDriver.php: -------------------------------------------------------------------------------- 1 | isActualFile($staticFilePath = $sitePath.'/web/'.$uri)) { 33 | return $staticFilePath; 34 | } 35 | 36 | return false; 37 | } 38 | 39 | /** 40 | * Get the fully resolved path to the application's front controller. 41 | * 42 | * @param string $sitePath 43 | * @param string $siteName 44 | * @param string $uri 45 | * 46 | * @return string 47 | */ 48 | public function frontControllerPath($sitePath, $siteName, $uri) 49 | { 50 | if (file_exists($frontControllerPath = $sitePath.'/web/app_dev.php')) { 51 | return $frontControllerPath; 52 | } elseif (file_exists($frontControllerPath = $sitePath.'/web/app.php')) { 53 | return $frontControllerPath; 54 | } elseif (file_exists($frontControllerPath = $sitePath.'/web/index.php')) { 55 | return $frontControllerPath; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /cli/drivers/Typo3ValetDriver.php: -------------------------------------------------------------------------------- 1 | documentRoot.'/typo3'; 53 | 54 | return file_exists($typo3Dir) && is_dir($typo3Dir); 55 | } 56 | 57 | /** 58 | * Determine if the incoming request is for a static file. That is, it is 59 | * no PHP script file and the URI points to a valid file (no folder) on 60 | * the disk. Access to those static files will be authorized. 61 | * 62 | * @param string $sitePath 63 | * @param string $siteName 64 | * @param string $uri 65 | * 66 | * @return string|false 67 | */ 68 | public function isStaticFile($sitePath, $siteName, $uri) 69 | { 70 | // May the file contains a cache busting version string like filename.12345678.css 71 | // If that is the case, the file cannot be found on disk, so remove the version 72 | // identifier before retrying below. 73 | if (!$this->isActualFile($filePath = $sitePath.$this->documentRoot.$uri)) { 74 | $uri = preg_replace("@^(.+)\.(\d+)\.(js|css|png|jpg|gif|gzip)$@", '$1.$3', $uri); 75 | } 76 | 77 | // Now that any possible version string is cleared from the filename, the resulting 78 | // URI should be a valid file on disc. So assemble the absolut file name with the 79 | // same schema as above and if it exists, authorize access and return its path. 80 | if ($this->isActualFile($filePath = $sitePath.$this->documentRoot.$uri)) { 81 | return $this->isAccessAuthorized($uri) ? $filePath : false; 82 | } 83 | 84 | // This file cannot be found in the current project and thus cannot be served. 85 | return false; 86 | } 87 | 88 | /** 89 | * Determines if the given URI is blacklisted so that access is prevented. 90 | * 91 | * @param string $uri 92 | * 93 | * @return bool 94 | */ 95 | private function isAccessAuthorized($uri) 96 | { 97 | foreach ($this->forbiddenUriPatterns as $forbiddenUriPattern) { 98 | if (preg_match("@$forbiddenUriPattern@", $uri)) { 99 | return false; 100 | } 101 | } 102 | 103 | return true; 104 | } 105 | 106 | /** 107 | * Get the fully resolved path to the application's front controller. 108 | * This can be the currently requested PHP script, a folder that 109 | * contains an index.php or the global index.php otherwise. 110 | * 111 | * @param string $sitePath 112 | * @param string $siteName 113 | * @param string $uri 114 | * 115 | * @return string 116 | */ 117 | public function frontControllerPath($sitePath, $siteName, $uri) 118 | { 119 | // without modifying the URI, redirect if necessary 120 | $this->handleRedirectBackendShorthandUris($uri); 121 | 122 | // from now on, remove trailing / for convenience for all the following join operations 123 | $uri = rtrim($uri, '/'); 124 | 125 | // try to find the responsible script file for the requested folder / script URI 126 | if (file_exists($absoluteFilePath = $sitePath.$this->documentRoot.$uri)) { 127 | if (is_dir($absoluteFilePath)) { 128 | if (file_exists($absoluteFilePath.'/index.php')) { 129 | // this folder can be served by index.php 130 | return $this->serveScript($sitePath, $siteName, $uri.'/index.php'); 131 | } 132 | 133 | if (file_exists($absoluteFilePath.'/index.html')) { 134 | // this folder can be served by index.html 135 | return $absoluteFilePath.'/index.html'; 136 | } 137 | } elseif (pathinfo($absoluteFilePath, PATHINFO_EXTENSION) === 'php') { 138 | // this file can be served directly 139 | return $this->serveScript($sitePath, $siteName, $uri); 140 | } 141 | } 142 | 143 | // the global index.php will handle all other cases 144 | return $this->serveScript($sitePath, $siteName, '/index.php'); 145 | } 146 | 147 | /** 148 | * Direct access to installtool via domain.test/typo3/install/ will be redirected to 149 | * sysext install script. domain.test/typo3 will be redirected to /typo3/, because 150 | * the generated JavaScript URIs on the login screen would be broken on /typo3. 151 | * 152 | * @param string $uri 153 | */ 154 | private function handleRedirectBackendShorthandUris($uri) 155 | { 156 | if (rtrim($uri, '/') === '/typo3/install') { 157 | header('Location: /typo3/sysext/install/Start/Install.php'); 158 | die(); 159 | } 160 | 161 | if ($uri === '/typo3') { 162 | header('Location: /typo3/'); 163 | die(); 164 | } 165 | } 166 | 167 | /** 168 | * Configures the $_SERVER globals for serving the script at 169 | * the specified URI and returns it absolute file path. 170 | * 171 | * @param string $sitePath 172 | * @param string $siteName 173 | * @param string $uri 174 | * @param string $script 175 | * 176 | * @return string 177 | */ 178 | private function serveScript($sitePath, $siteName, $uri) 179 | { 180 | $docroot = $sitePath.$this->documentRoot; 181 | $abspath = $docroot.$uri; 182 | 183 | $_SERVER['SERVER_NAME'] = $siteName.'.test'; 184 | $_SERVER['DOCUMENT_ROOT'] = $docroot; 185 | $_SERVER['DOCUMENT_URI'] = $uri; 186 | $_SERVER['SCRIPT_FILENAME'] = $abspath; 187 | $_SERVER['SCRIPT_NAME'] = $uri; 188 | $_SERVER['PHP_SELF'] = $uri; 189 | 190 | return $abspath; 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /cli/drivers/ValetDriver.php: -------------------------------------------------------------------------------- 1 | serves($sitePath, $siteName, $driver->mutateUri($uri))) { 85 | return $driver; 86 | } 87 | } 88 | } 89 | 90 | /** 91 | * Get the custom driver class from the site path, if one exists. 92 | * 93 | * @param string $sitePath 94 | * 95 | * @return string 96 | */ 97 | public static function customSiteDriver($sitePath) 98 | { 99 | if (!file_exists($sitePath.'/LocalValetDriver.php')) { 100 | return; 101 | } 102 | 103 | require_once $sitePath.'/LocalValetDriver.php'; 104 | 105 | return 'LocalValetDriver'; 106 | } 107 | 108 | /** 109 | * Get all of the driver classes in a given path. 110 | * 111 | * @param string $path 112 | * 113 | * @return array 114 | */ 115 | public static function driversIn($path) 116 | { 117 | if (!is_dir($path)) { 118 | return []; 119 | } 120 | 121 | $drivers = []; 122 | 123 | foreach (scandir($path) as $file) { 124 | if ($file !== 'ValetDriver.php' && strpos($file, 'ValetDriver') !== false) { 125 | require_once $path.'/'.$file; 126 | 127 | $drivers[] = basename($file, '.php'); 128 | } 129 | } 130 | 131 | return $drivers; 132 | } 133 | 134 | /** 135 | * Mutate the incoming URI. 136 | * 137 | * @param string $uri 138 | * 139 | * @return string 140 | */ 141 | public function mutateUri($uri) 142 | { 143 | return $uri; 144 | } 145 | 146 | /** 147 | * Serve the static file at the given path. 148 | * 149 | * @param string $staticFilePath 150 | * @param string $sitePath 151 | * @param string $siteName 152 | * @param string $uri 153 | * 154 | * @return void 155 | */ 156 | public function serveStaticFile($staticFilePath, $sitePath, $siteName, $uri) 157 | { 158 | /* 159 | * Back story... 160 | * 161 | * PHP docs *claim* you can set default_mimetype = "" to disable the default 162 | * Content-Type header. This works in PHP 7+, but in PHP 5.* it sends an 163 | * *empty* Content-Type header, which is significantly different than 164 | * sending *no* Content-Type header. 165 | * 166 | * However, if you explicitly set a Content-Type header, then explicitly 167 | * remove that Content-Type header, PHP seems to not re-add the default. 168 | * 169 | * I have a hard time believing this is by design and not coincidence. 170 | * 171 | * Burn. it. all. 172 | */ 173 | header('Content-Type: text/html'); 174 | header_remove('Content-Type'); 175 | 176 | header('X-Accel-Redirect: /'.VALET_STATIC_PREFIX.$staticFilePath); 177 | } 178 | 179 | /** 180 | * Determine if the path is a file and not a directory. 181 | * 182 | * @param string $path 183 | * 184 | * @return bool 185 | */ 186 | protected function isActualFile($path) 187 | { 188 | return !is_dir($path) && file_exists($path); 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /cli/drivers/WordPressValetDriver.php: -------------------------------------------------------------------------------- 1 | forceTrailingSlash($uri) 36 | ); 37 | } 38 | 39 | /** 40 | * Redirect to uri with trailing slash. 41 | * 42 | * @param string $uri 43 | * 44 | * @return string 45 | */ 46 | private function forceTrailingSlash($uri) 47 | { 48 | if (substr($uri, -1 * strlen('/wp-admin')) == '/wp-admin') { 49 | header('Location: '.$uri.'/'); 50 | die; 51 | } 52 | 53 | return $uri; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /cli/drivers/require.php: -------------------------------------------------------------------------------- 1 | make(static::containerKey()); 28 | 29 | return call_user_func_array([$resolvedInstance, $method], $parameters); 30 | } 31 | } 32 | 33 | class Nginx extends Facade 34 | { 35 | } 36 | class PackageManager extends Facade 37 | { 38 | } 39 | class Apt extends Facade 40 | { 41 | } 42 | class Dnf extends Facade 43 | { 44 | } 45 | class Pacman extends Facade 46 | { 47 | } 48 | class ServiceManager extends Facade 49 | { 50 | } 51 | class LinuxService extends Facade 52 | { 53 | } 54 | class Systemd extends Facade 55 | { 56 | } 57 | class CommandLine extends Facade 58 | { 59 | } 60 | class Configuration extends Facade 61 | { 62 | } 63 | class DnsMasq extends Facade 64 | { 65 | } 66 | class Filesystem extends Facade 67 | { 68 | } 69 | class Ngrok extends Facade 70 | { 71 | } 72 | class PhpFpm extends Facade 73 | { 74 | } 75 | class Site extends Facade 76 | { 77 | } 78 | class Valet extends Facade 79 | { 80 | } 81 | class Requirements extends Facade 82 | { 83 | } 84 | -------------------------------------------------------------------------------- /cli/includes/helpers.php: -------------------------------------------------------------------------------- 1 | '.$output.''); 24 | } 25 | 26 | /** 27 | * Output the given text to the console. 28 | * 29 | * @param string $output 30 | * 31 | * @return void 32 | */ 33 | function warning($output) 34 | { 35 | output(''.$output.''); 36 | } 37 | 38 | /** 39 | * Output a table to the console. 40 | * 41 | * @param array $headers 42 | * @param array $rows 43 | * 44 | * @return void 45 | */ 46 | function table(array $headers = [], array $rows = []) 47 | { 48 | $table = new Table(new ConsoleOutput()); 49 | 50 | $table->setHeaders($headers)->setRows($rows); 51 | 52 | $table->render(); 53 | } 54 | 55 | /** 56 | * Output the given text to the console. 57 | * 58 | * @param string $output 59 | * 60 | * @return void 61 | */ 62 | function output($output) 63 | { 64 | if (isset($_ENV['APP_ENV']) && $_ENV['APP_ENV'] === 'testing') { 65 | return; 66 | } 67 | 68 | (new ConsoleOutput())->writeln($output); 69 | } 70 | 71 | if (!function_exists('resolve')) { 72 | /** 73 | * Resolve the given class from the container. 74 | * 75 | * @param string $class 76 | * 77 | * @return mixed 78 | */ 79 | function resolve($class) 80 | { 81 | return Container::getInstance()->make($class); 82 | } 83 | } 84 | 85 | /** 86 | * Swap the given class implementation in the container. 87 | * 88 | * @param string $class 89 | * @param mixed $instance 90 | * 91 | * @return void 92 | */ 93 | function swap($class, $instance) 94 | { 95 | Container::getInstance()->instance($class, $instance); 96 | } 97 | 98 | if (!function_exists('retry')) { 99 | /** 100 | * Retry the given function N times. 101 | * 102 | * @param int $retries 103 | * @param callable $retries 104 | * @param int $sleep 105 | * 106 | * @return mixed 107 | */ 108 | function retry($retries, $fn, $sleep = 0) 109 | { 110 | beginning: 111 | try { 112 | return $fn(); 113 | } catch (Exception $e) { 114 | if (!$retries) { 115 | throw $e; 116 | } 117 | 118 | $retries--; 119 | 120 | if ($sleep > 0) { 121 | usleep($sleep * 1000); 122 | } 123 | 124 | goto beginning; 125 | } 126 | } 127 | } 128 | 129 | /** 130 | * Verify that the script is currently running as "sudo". 131 | * 132 | * @return void 133 | */ 134 | function should_be_sudo() 135 | { 136 | if (!isset($_SERVER['SUDO_USER'])) { 137 | throw new Exception('This command must be run with sudo.'); 138 | } 139 | } 140 | 141 | if (!function_exists('tap')) { 142 | /** 143 | * Tap the given value. 144 | * 145 | * @param mixed $value 146 | * @param callable $callback 147 | * 148 | * @return mixed 149 | */ 150 | function tap($value, callable $callback) 151 | { 152 | $callback($value); 153 | 154 | return $value; 155 | } 156 | } 157 | 158 | if (!function_exists('ends_with')) { 159 | /** 160 | * Determine if a given string ends with a given substring. 161 | * 162 | * @param string $haystack 163 | * @param string|array $needles 164 | * 165 | * @return bool 166 | */ 167 | function ends_with($haystack, $needles) 168 | { 169 | foreach ((array) $needles as $needle) { 170 | if (substr($haystack, -strlen($needle)) === (string) $needle) { 171 | return true; 172 | } 173 | } 174 | 175 | return false; 176 | } 177 | } 178 | 179 | /** 180 | * Get the user. 181 | */ 182 | function user() 183 | { 184 | if (!isset($_SERVER['SUDO_USER'])) { 185 | return $_SERVER['USER']; 186 | } 187 | 188 | return $_SERVER['SUDO_USER']; 189 | } 190 | 191 | /** 192 | * Get the user's group. 193 | */ 194 | function group() 195 | { 196 | if (!isset($_SERVER['SUDO_USER'])) { 197 | return exec('id -gn '.$_SERVER['USER']); 198 | } 199 | 200 | return exec('id -gn '.$_SERVER['SUDO_USER']); 201 | } 202 | 203 | /** 204 | * Search and replace using associative array. 205 | * 206 | * @param array $searchAndReplace 207 | * @param string $subject 208 | * 209 | * @return string 210 | */ 211 | function str_array_replace($searchAndReplace, $subject) 212 | { 213 | return str_replace(array_keys($searchAndReplace), array_values($searchAndReplace), $subject); 214 | } 215 | -------------------------------------------------------------------------------- /cli/scripts/fetch-share-url.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 4 | 5 | php $DIR/../valet.php fetch-share-url | xsel -b 6 | -------------------------------------------------------------------------------- /cli/scripts/install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | apt-get install libnss3-tools jq xsel > /dev/null 2>&1 4 | 5 | # Install PHP 7.0 6 | if [[ ! $(apt-cache search php[5-7].[0-9]-cli) ]] 7 | then 8 | add-apt-repository -y ppa:ondrej/php && apt-get update 9 | fi 10 | 11 | $VERSION='7.0' 12 | # Install PHP $VERSION 13 | apt-get install -y "php$VERSION-cli php$VERSION-common php$VERSION-curl php$VERSION-json php$VERSION-mbstring php$VERSION-mcrypt php$VERSION-mysql php$VERSION-opcache php$VERSION-readline php$VERSION-xml php$VERSION-zip" 14 | 15 | # Install Composer to /usr/local/bin 16 | if [[ ! $(which -a composer) ]] 17 | then 18 | echo "Installing Composer..." 19 | /usr/bin/php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" 20 | /usr/bin/php -r "if (hash_file('SHA384', 'composer-setup.php') === '92102166af5abdb03f49ce52a40591073a7b859a86e8ff13338cf7db58a19f7844fbc0bb79b2773bf30791e935dbd938') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" > /dev/null 2>&1 21 | /usr/bin/php composer-setup.php > /dev/null 2>&1 22 | /usr/bin/php -r "unlink('composer-setup.php');" 23 | 24 | mv composer.phar /usr/local/bin/composer 25 | chmod +x /usr/local/bin/composer 26 | fi 27 | 28 | COMPOSER_PATH=$(which composer) 29 | 30 | # Download and unpack the latest Valet release 31 | rm -rf $HOME/.valet-cli 32 | mkdir $HOME/.valet-cli 33 | 34 | echo "Downloading Valet..." 35 | TARBALL=$(curl -s https://api.github.com/repos/cpriego/valet-ubuntu/releases/latest | jq ".tarball_url") 36 | TARBALL=$(echo $TARBALL | sed -e 's/^"//' -e 's/"$//') 37 | wget --max-redirect=10 $TARBALL -O $HOME/.valet-cli/valet.tar.gz > /dev/null 2>&1 38 | tar xvzf $HOME/.valet-cli/valet.tar.gz -C $HOME/.valet-cli --strip 1 > /dev/null 2>&1 39 | 40 | # Install Valet to /usr/local/bin 41 | ln -snf $HOME/.valet-cli/valet /usr/local/bin/valet 42 | chmod +x /usr/local/bin/valet 43 | 44 | # Install Valet's Composer dependencies 45 | echo "Installing Valet's Composer dependencies..." 46 | /usr/bin/php $COMPOSER_PATH install -d $HOME/.valet-cli > /dev/null 2>&1 47 | 48 | # Run the Valet server installation process 49 | /usr/local/bin/valet install 50 | -------------------------------------------------------------------------------- /cli/scripts/update.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | # Determine if the port config key exists, if not, create it 5 | function fix-config() { 6 | local CONFIG="$HOME/.valet/config.json" 7 | 8 | if [[ -f $CONFIG ]] 9 | then 10 | local PORT=$(jq -r ".port" "$CONFIG") 11 | 12 | if [[ "$PORT" = "null" ]] 13 | then 14 | echo "Fixing valet config file..." 15 | CONTENTS=$(jq '. + {port: "80"}' "$CONFIG") 16 | echo -n $CONTENTS >| "$CONFIG" 17 | fi 18 | fi 19 | } 20 | 21 | if [[ "$1" = "update" ]] 22 | then 23 | composer global update "cpriego/valet-linux" 24 | valet install 25 | fi 26 | 27 | fix-config 28 | -------------------------------------------------------------------------------- /cli/stubs/SampleValetDriver.php: -------------------------------------------------------------------------------- 1 | /dev/null 59 | 60 | cat "$DNSHEAD" | unique | tee "$DNSFILE" &>/dev/null 61 | cat "${FILES[@]}" | grep -i '^nameserver' | grep -v '127.0.0.1' | unique | tee -a "$DNSFILE" &>/dev/null 62 | 63 | symlinkResolv 64 | 65 | cat "${FILES[@]}" | grep -v '^nameserver' | grep -v '^#' | unique | tee "$VRESOLV" &>/dev/null 66 | echo 'nameserver 127.0.0.1' >> "$VRESOLV" 67 | 68 | # Add "search" and "domain" directives to /etc/resolv.conf 69 | # chattr -i "$RESOLV" && \ 70 | # cat "${FILES[@]}" | grep -v '^nameserver' | grep -v '^#' | unique | tee "$VRESOLV" &>/dev/null && \ 71 | # echo 'nameserver 127.0.0.1' >> "$VRESOLV" && \ 72 | # chattr +i "$RESOLV" 73 | } 74 | 75 | function getFiles() { 76 | FILES=() 77 | local TARRAY=() 78 | 79 | for DIR in "${DIRS[@]}"; do 80 | readarray -t TARRAY <<< "$(find ${DIR} -path ! -readable -prune -o -name 'resolv.conf' -print)" 81 | 82 | # Find nameserver files in the /run/resolvconf/interface folder (as they do not have a standard name) 83 | if [[ "$DIR" = "/run/resolvconf/interface" ]]; then 84 | readarray -t TARRAY <<< "$(find ${DIR} ! -readable -prune -o -type f -print)" 85 | fi 86 | 87 | FILES=(${FILES[@]} ${TARRAY[@]}) 88 | done 89 | } 90 | 91 | function watchDirs() { 92 | local WATCHERS=(${DIRS[@]} "$DNSHEAD") 93 | 94 | # Log which directories are being watched 95 | echo "Watching the following directories for changes:" >> "$LOGFILE" 96 | 97 | for DIR in "${DIRS[@]}"; do 98 | echo " - $DIR" >> "$LOGFILE" 99 | done 100 | 101 | # Watch directories for changes in files 102 | inotifywait -q -m -e modify -e create -e delete --format "%w%f" "${WATCHERS[@]}" | while read change; do 103 | updateNameservers 104 | done & 105 | } 106 | 107 | function main() { 108 | # Create dns file in case it does not exists 109 | touch "$DNSHEAD" 110 | touch "$DNSFILE" 111 | 112 | # Clear log file 113 | if [[ -f "$LOGFILE" ]]; then 114 | rm "$LOGFILE" 115 | fi 116 | 117 | touch "$LOGFILE" 118 | 119 | getDirs 120 | updateNameservers 121 | watchDirs 122 | } 123 | 124 | ################################################################################ 125 | 126 | function start { 127 | if [[ $(pgrep -f 'inotifywait -q -m -e modify') ]]; then 128 | echo -e "Valet DNS Watcher is already running..." 129 | else 130 | echo -e "Starting Valet DNS Watcher..." 131 | main 132 | sleep 2 && echo $(pgrep -f 'inotifywait -q -m -e modify') > "${WORKDIR}/watch.pid" 133 | echo -e "Valet DNS Watcher started succesfully." 134 | fi 135 | } 136 | 137 | function stop { 138 | echo -e "Stopping Valet DNS Watcher...\n" 139 | 140 | pkill -f "inotifywait -q -m -e modify" 141 | 142 | rm "$LOGFILE" && touch "$LOGFILE" 143 | 144 | if [[ ! $(pgrep -f 'inotifywait -q -m -e modify') ]]; then 145 | echo -e "\nValet DNS Watcher stopped succesfully." 146 | fi 147 | } 148 | 149 | function restart { 150 | echo -e "Stopping Valet DNS Watcher..." 151 | 152 | if [[ $(pgrep -f 'inotifywait -q -m -e modify') ]]; then 153 | pkill -f "inotifywait -q -m -e modify" 154 | fi 155 | 156 | echo -e "Starting Valet DNS Watcher..." 157 | 158 | main 159 | 160 | if [[ $(pgrep -f 'inotifywait -q -m -e modify') ]]; then 161 | echo -e "Valet DNS Watcher restarted succesfully." 162 | fi 163 | } 164 | 165 | function status { 166 | if [[ -f "$LOGFILE" ]]; then 167 | echo -e "Valet DNS service is running correctly.\n" 168 | cat '/opt/valet-linux/watch.log' 169 | else 170 | echo "Valet DNS service is not running." 171 | fi 172 | } 173 | 174 | case "$1" in 175 | start) 176 | start 177 | ;; 178 | stop) 179 | stop 180 | ;; 181 | restart) 182 | restart 183 | ;; 184 | status) 185 | status 186 | ;; 187 | *) 188 | echo "Usage: $0 {start|stop|restart|status}" 189 | esac 190 | 191 | exit 0 192 | -------------------------------------------------------------------------------- /cli/stubs/valet.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen VALET_PORT default_server; 3 | root /; 4 | charset utf-8; 5 | client_max_body_size 128M; 6 | 7 | location /VALET_STATIC_PREFIX/ { 8 | internal; 9 | alias /; 10 | try_files $uri $uri/; 11 | } 12 | 13 | location / { 14 | rewrite ^ VALET_SERVER_PATH last; 15 | } 16 | 17 | location = /favicon.ico { access_log off; log_not_found off; } 18 | location = /robots.txt { access_log off; log_not_found off; } 19 | 20 | access_log off; 21 | error_log VALET_HOME_PATH/Log/nginx-error.log; 22 | 23 | error_page 404 VALET_SERVER_PATH; 24 | 25 | location ~ \.php$ { 26 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 27 | fastcgi_pass unix:VALET_HOME_PATH/valet.sock; 28 | fastcgi_index VALET_SERVER_PATH; 29 | include fastcgi_params; 30 | fastcgi_param SCRIPT_FILENAME VALET_SERVER_PATH; 31 | } 32 | 33 | location ~ /\.ht { 34 | deny all; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /cli/templates/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Valet - Not Found 4 | 5 | 10 | 11 | 12 | 404 - Not Found 13 | 14 | 15 | -------------------------------------------------------------------------------- /cli/valet.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | command('install [--ignore-selinux]', function ($ignoreSELinux) { 34 | passthru(dirname(__FILE__).'/scripts/update.sh'); // Clean up cruft 35 | 36 | Requirements::setIgnoreSELinux($ignoreSELinux)->check(); 37 | Configuration::install(); 38 | Nginx::install(); 39 | PhpFpm::install(); 40 | DnsMasq::install(Configuration::read()['domain']); 41 | Nginx::restart(); 42 | Valet::symlinkToUsersBin(); 43 | 44 | output(PHP_EOL.'Valet installed successfully!'); 45 | })->descriptions('Install the Valet services', [ 46 | '--ignore-selinux' => 'Skip SELinux checks', 47 | ]); 48 | 49 | /* 50 | * Most commands are available only if valet is installed. 51 | */ 52 | if (is_dir(VALET_HOME_PATH)) { 53 | /* 54 | * Prune missing directories and symbolic links on every command. 55 | */ 56 | Configuration::prune(); 57 | Site::pruneLinks(); 58 | 59 | /* 60 | * Get or set the domain currently being used by Valet. 61 | */ 62 | $app->command('domain [domain]', function ($domain = null) { 63 | if ($domain === null) { 64 | return info(Configuration::read()['domain']); 65 | } 66 | 67 | DnsMasq::updateDomain( 68 | $oldDomain = Configuration::read()['domain'], $domain = trim($domain, '.') 69 | ); 70 | 71 | Configuration::updateKey('domain', $domain); 72 | Site::resecureForNewDomain($oldDomain, $domain); 73 | PhpFpm::restart(); 74 | Nginx::restart(); 75 | 76 | info('Your Valet domain has been updated to ['.$domain.'].'); 77 | })->descriptions('Get or set the domain used for Valet sites'); 78 | 79 | /* 80 | * Get or set the port number currently being used by Valet. 81 | */ 82 | $app->command('port [port] [--https]', function ($port, $https) { 83 | if ($port === null) { 84 | info('Current Nginx port (HTTP): '.Configuration::get('port', 80)); 85 | info('Current Nginx port (HTTPS): '.Configuration::get('https_port', 443)); 86 | 87 | return; 88 | } 89 | 90 | $port = trim($port); 91 | 92 | if ($https) { 93 | Configuration::updateKey('https_port', $port); 94 | } else { 95 | Nginx::updatePort($port); 96 | Configuration::updateKey('port', $port); 97 | } 98 | 99 | Site::regenerateSecuredSitesConfig(); 100 | 101 | Nginx::restart(); 102 | PhpFpm::restart(); 103 | 104 | $protocol = $https ? 'HTTPS' : 'HTTP'; 105 | info("Your Nginx {$protocol} port has been updated to [{$port}]."); 106 | })->descriptions('Get or set the port number used for Valet sites'); 107 | 108 | /* 109 | * Determine if the site is secured or not 110 | */ 111 | $app->command('secured [site]', function ($site) { 112 | if (Site::secured()->contains($site)) { 113 | info("{$site} is secured."); 114 | 115 | return 1; 116 | } 117 | 118 | info("{$site} is not secured."); 119 | 120 | return 0; 121 | })->descriptions('Determine if the site is secured or not'); 122 | 123 | /* 124 | * Add the current working directory to the paths configuration. 125 | */ 126 | $app->command('park [path]', function ($path = null) { 127 | Configuration::addPath($path ?: getcwd()); 128 | 129 | info(($path === null ? 'This' : "The [{$path}]")." directory has been added to Valet's paths."); 130 | })->descriptions('Register the current working (or specified) directory with Valet'); 131 | 132 | /* 133 | * Remove the current working directory from the paths configuration. 134 | */ 135 | $app->command('forget [path]', function ($path = null) { 136 | Configuration::removePath($path ?: getcwd()); 137 | 138 | info(($path === null ? 'This' : "The [{$path}]")." directory has been removed from Valet's paths."); 139 | })->descriptions('Remove the current working (or specified) directory from Valet\'s list of paths'); 140 | 141 | /* 142 | * Remove the current working directory to the paths configuration. 143 | */ 144 | $app->command('status', function () { 145 | PhpFpm::status(); 146 | Nginx::status(); 147 | })->descriptions('View Valet service status'); 148 | 149 | /* 150 | * Register a symbolic link with Valet. 151 | */ 152 | $app->command('link [name]', function ($name) { 153 | $linkPath = Site::link(getcwd(), $name = $name ?: basename(getcwd())); 154 | 155 | info('A ['.$name.'] symbolic link has been created in ['.$linkPath.'].'); 156 | })->descriptions('Link the current working directory to Valet'); 157 | 158 | /* 159 | * Display all of the registered symbolic links. 160 | */ 161 | $app->command('links', function () { 162 | $links = Site::links(); 163 | 164 | table(['Site', 'SSL', 'URL', 'Path'], $links->all()); 165 | })->descriptions('Display all of the registered Valet links'); 166 | 167 | /* 168 | * Unlink a link from the Valet links directory. 169 | */ 170 | $app->command('unlink [name]', function ($name) { 171 | Site::unlink($name = $name ?: basename(getcwd())); 172 | 173 | info('The ['.$name.'] symbolic link has been removed.'); 174 | })->descriptions('Remove the specified Valet link'); 175 | 176 | /* 177 | * Secure the given domain with a trusted TLS certificate. 178 | */ 179 | $app->command('secure [domain]', function ($domain = null) { 180 | $url = ($domain ?: Site::host(getcwd())).'.'.Configuration::read()['domain']; 181 | 182 | Site::secure($url); 183 | PhpFpm::restart(); 184 | Nginx::restart(); 185 | 186 | info('The ['.$url.'] site has been secured with a fresh TLS certificate.'); 187 | })->descriptions('Secure the given domain with a trusted TLS certificate'); 188 | 189 | /* 190 | * Stop serving the given domain over HTTPS and remove the trusted TLS certificate. 191 | */ 192 | $app->command('unsecure [domain]', function ($domain = null) { 193 | $url = ($domain ?: Site::host(getcwd())).'.'.Configuration::read()['domain']; 194 | 195 | Site::unsecure($url); 196 | PhpFpm::restart(); 197 | Nginx::restart(); 198 | 199 | info('The ['.$url.'] site will now serve traffic over HTTP.'); 200 | })->descriptions('Stop serving the given domain over HTTPS and remove the trusted TLS certificate'); 201 | 202 | /* 203 | * Determine which Valet driver the current directory is using. 204 | */ 205 | $app->command('which', function () { 206 | require __DIR__.'/drivers/require.php'; 207 | 208 | $driver = ValetDriver::assign(getcwd(), basename(getcwd()), '/'); 209 | 210 | if ($driver) { 211 | info('This site is served by ['.get_class($driver).'].'); 212 | } else { 213 | warning('Valet could not determine which driver to use for this site.'); 214 | } 215 | })->descriptions('Determine which Valet driver serves the current working directory'); 216 | 217 | /* 218 | * Display all of the registered paths. 219 | */ 220 | $app->command('paths', function () { 221 | $paths = Configuration::read()['paths']; 222 | 223 | if (count($paths) > 0) { 224 | info(json_encode($paths, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); 225 | } else { 226 | warning('No paths have been registered.'); 227 | } 228 | })->descriptions('Get all of the paths registered with Valet'); 229 | 230 | /* 231 | * Open the current directory in the browser. 232 | */ 233 | $app->command('open [domain]', function ($domain = null) { 234 | $url = 'http://'.($domain ?: Site::host(getcwd())).'.'.Configuration::read()['domain'].'/'; 235 | 236 | passthru('xdg-open '.escapeshellarg($url)); 237 | })->descriptions('Open the site for the current (or specified) directory in your browser'); 238 | 239 | /* 240 | * Generate a publicly accessible URL for your project. 241 | */ 242 | $app->command('share', function () { 243 | warning('It looks like you are running `cli/valet.php` directly, please use the `valet` script in the project root instead.'); 244 | })->descriptions('Generate a publicly accessible URL for your project'); 245 | 246 | /* 247 | * Echo the currently tunneled URL. 248 | */ 249 | $app->command('fetch-share-url', function () { 250 | output(Ngrok::currentTunnelUrl()); 251 | })->descriptions('Get the URL to the current Ngrok tunnel'); 252 | 253 | /* 254 | * Start the daemon services. 255 | */ 256 | $app->command('start', function () { 257 | PhpFpm::restart(); 258 | Nginx::restart(); 259 | 260 | info('Valet services have been started.'); 261 | })->descriptions('Start the Valet services'); 262 | 263 | /* 264 | * Restart the daemon services. 265 | */ 266 | $app->command('restart', function () { 267 | PhpFpm::restart(); 268 | Nginx::restart(); 269 | 270 | info('Valet services have been restarted.'); 271 | })->descriptions('Restart the Valet services'); 272 | 273 | /* 274 | * Stop the daemon services. 275 | */ 276 | $app->command('stop', function () { 277 | PhpFpm::stop(); 278 | Nginx::stop(); 279 | 280 | info('Valet services have been stopped.'); 281 | })->descriptions('Stop the Valet services'); 282 | 283 | /* 284 | * Uninstall Valet entirely. 285 | */ 286 | $app->command('uninstall', function () { 287 | Nginx::uninstall(); 288 | PhpFpm::uninstall(); 289 | DnsMasq::uninstall(); 290 | Configuration::uninstall(); 291 | Valet::uninstall(); 292 | 293 | info('Valet has been uninstalled.'); 294 | })->descriptions('Uninstall the Valet services'); 295 | 296 | /* 297 | * Determine if this is the latest release of Valet. 298 | */ 299 | $app->command('update', function () use ($version) { 300 | $script = dirname(__FILE__).'/scripts/update.sh'; 301 | 302 | if (Valet::onLatestVersion($version)) { 303 | info('You have the latest version of Valet Linux'); 304 | passthru($script); 305 | } else { 306 | warning('There is a new release of Valet Linux'); 307 | warning('Updating now...'); 308 | passthru($script.' update'); 309 | } 310 | })->descriptions('Update Valet Linux and clean up cruft'); 311 | } 312 | 313 | /* 314 | * Load all of the Valet extensions. 315 | */ 316 | foreach (Valet::extensions() as $extension) { 317 | include $extension; 318 | } 319 | 320 | /* 321 | * Run the application. 322 | */ 323 | $app->run(); 324 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jmarcher/valet-linux", 3 | "description": "A more enjoyable local development experience for Linux.", 4 | "keywords": ["laravel", "zonda", "wwdhhd", "ubuntu", "fedora", "arch", "linux", "valet"], 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Taylor Otwell", 9 | "email": "taylorotwell@gmail.com" 10 | }, 11 | { 12 | "name": "Adam Wathan", 13 | "email": "adam.wathan@gmail.com" 14 | }, 15 | { 16 | "name": "Carlos Priego", 17 | "email": "carlos.enrique.priego@gmail.com" 18 | }, 19 | { 20 | "name": "Leonardo Nodari", 21 | "email": "nodarileonardo@gmail.com" 22 | }, 23 | { 24 | "name": "Joaquin Marcher", 25 | "email": "joaquin@marcher.com.uy" 26 | } 27 | ], 28 | "autoload": { 29 | "files": [ 30 | "cli/includes/compatibility.php", 31 | "cli/includes/facades.php", 32 | "cli/includes/helpers.php" 33 | ], 34 | "psr-4": { 35 | "Valet\\": "cli/Valet/" 36 | } 37 | }, 38 | "autoload-dev": { 39 | "psr-4": { 40 | "Valet\\Tests\\": "tests/" 41 | } 42 | }, 43 | "require": { 44 | "php": ">=5.6", 45 | "illuminate/container": "~5.3", 46 | "mnapoli/silly": "~1.1", 47 | "symfony/process": "~2.7|~3.0|~4.0", 48 | "nategood/httpful": "~0.2", 49 | "tightenco/collect": "~5.3" 50 | }, 51 | "require-dev": { 52 | "mockery/mockery": "~0.9.4", 53 | "phpunit/phpunit": "~5.5" 54 | }, 55 | "scripts": { 56 | "post-install-cmd": [ 57 | ], 58 | "post-update-cmd": [ 59 | ] 60 | }, 61 | "bin": [ 62 | "valet" 63 | ] 64 | } 65 | -------------------------------------------------------------------------------- /develop: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | VAGRANT_FOLDER="./tests/Acceptance/Vagrant" 4 | 5 | if [ $# -gt 0 ]; then 6 | 7 | # Spin up given development environment 8 | if [ "$1" == "up" ]; then 9 | cd "$VAGRANT_FOLDER/$2" && \ 10 | shift 2 && \ 11 | VALET_ENVIRONMENT=development vagrant up "$@" 12 | 13 | # SSH into the given development environment 14 | elif [ "$1" == "ssh" ]; then 15 | cd "$VAGRANT_FOLDER/$2" && \ 16 | shift 2 && \ 17 | vagrant ssh "$@" 18 | 19 | # Switch off given development environment 20 | elif [ "$1" == "down" ]; then 21 | cd "$VAGRANT_FOLDER/$2" && \ 22 | shift 2 && \ 23 | vagrant halt "$@" 24 | 25 | # Destroy given development environment 26 | elif [ "$1" == "destroy" ]; then 27 | cd "$VAGRANT_FOLDER/$2" && \ 28 | shift 2 && \ 29 | vagrant destroy "$@" 30 | 31 | # Destroy all development environments 32 | elif [ "$1" == "destroy-all" ]; then 33 | for DIRECTORY in `find $VAGRANT_FOLDER -maxdepth 1 -type d`; do 34 | if [[ "$DIRECTORY" != $VAGRANT_FOLDER ]]; then 35 | ./develop destroy ${DIRECTORY##*/} -f 36 | fi 37 | done 38 | 39 | # Run Acceptance tests against given environment 40 | elif [ "$1" == "test" ]; then 41 | ./develop up $2 && \ 42 | ./develop ssh $2 --command "bash ~/cpriego-valet-linux/tests/Acceptance/vagrant.sh" 43 | 44 | # Run Acceptance tests against ALL environments 45 | elif [ "$1" == "test-all" ]; then 46 | for DIRECTORY in `find $VAGRANT_FOLDER -maxdepth 1 -type d`; do 47 | if [[ "$DIRECTORY" != $VAGRANT_FOLDER ]]; then 48 | echo -e "\033[44m ${DIRECTORY##*/} \033[0m" 49 | 50 | ./develop test ${DIRECTORY##*/} && \ 51 | ./develop down ${DIRECTORY##*/} 52 | fi 53 | done 54 | 55 | # Run Acceptance tests against ALL environments in parallel 56 | elif [ "$1" == "test-all-parallel" ]; then 57 | for DIRECTORY in `find $VAGRANT_FOLDER -maxdepth 1 -type d`; do 58 | if [[ "$DIRECTORY" != $VAGRANT_FOLDER ]]; then 59 | gnome-terminal --tab \ 60 | --command="bash -c \"./develop test ${DIRECTORY##*/} && ./develop down ${DIRECTORY##*/}; read\"" 61 | fi 62 | done 63 | 64 | fi 65 | 66 | else 67 | # Display usage 68 | echo -e "Usage: ./develop [action] [arguments]\n" 69 | echo -e "Available actions:" 70 | 71 | # ./develop up {OS_NAME} 72 | echo -e "\tup {OS_NAME}" 73 | echo -e "\t\tSpin up a development environment using vagrant and SSH into it." 74 | echo -e -n "\t\tAvailable OSes: " 75 | for DIRECTORY in `find $VAGRANT_FOLDER -maxdepth 1 -type d`; do 76 | if [[ "$DIRECTORY" != $VAGRANT_FOLDER ]]; then 77 | echo -e -n "${DIRECTORY##*/} " 78 | fi 79 | done 80 | echo 81 | 82 | # ./develop ssh {OS_NAME} 83 | echo -e "\tssh {OS_NAME}" 84 | echo -e "\t\tSSH into the given development environment." 85 | 86 | # ./develop down {OS_NAME} 87 | echo -e "\tdown {OS_NAME}" 88 | echo -e "\t\tSwitch off given development environment. This does NOT destroy the box." 89 | 90 | # ./develop destroy {OS_NAME} 91 | echo -e "\tdestroy {OS_NAME}" 92 | echo -e "\t\tDestroy given development environment." 93 | 94 | # ./develop destroy-all 95 | echo -e "\tdestroy-all" 96 | echo -e "\t\tDestroy all development environments." 97 | 98 | # ./develop test {OS_NAME} 99 | echo -e "\ttest {OS_NAME}" 100 | echo -e "\t\tRun Acceptance tests against a given OS." 101 | 102 | # ./develop test-all 103 | echo -e "\ttest-all" 104 | echo -e "\t\tRun Acceptance tests against ALL OSes." 105 | 106 | # ./develop test-all-parallel 107 | echo -e "\ttest-all-parallel" 108 | echo -e "\t\tRun Acceptance tests against ALL OSes in parellel. Requires gnome-terminal." 109 | 110 | fi 111 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | Build Status 5 | Total Downloads 6 | Latest Stable Version 7 | Latest Unstable Version 8 | License 9 |

10 | 11 | ## Introduction 12 | 13 | Valet *Linux* is a Laravel development environment for Linux minimalists. No Vagrant, no `/etc/hosts` file. You can even share your sites publicly using local tunnels. _Yeah, we like it too._ 14 | 15 | Valet *Linux* configures your system to always run Nginx in the background when your machine starts. Then, using [DnsMasq](https://en.wikipedia.org/wiki/Dnsmasq), Valet proxies all requests on the `*.test` domain to point to sites installed on your local machine. 16 | 17 | In other words, a blazing fast Laravel development environment that uses roughly 7mb of RAM. Valet *Linux* isn't a complete replacement for Vagrant or Homestead, but provides a great alternative if you want flexible basics, prefer extreme speed, or are working on a machine with a limited amount of RAM. 18 | 19 | ## Official Documentation 20 | 21 | Documentation for Valet can be found on the [Valet Linux website](https://cpriego.github.io/valet-linux/). 22 | 23 | ## License 24 | 25 | Laravel Valet is open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT) 26 | -------------------------------------------------------------------------------- /server.php: -------------------------------------------------------------------------------- 1 | 6) { 30 | $domain = implode('.', array_reverse(array_slice(array_reverse($domainPart), 6))); 31 | } 32 | } 33 | 34 | return $domain; 35 | } 36 | 37 | /** 38 | * Convert absolute path to slug. 39 | * 40 | * @param string $path 41 | * 42 | * @return string Slug version of last folder name 43 | */ 44 | function valet_path_to_slug($path) 45 | { 46 | $replace = [ 47 | '<' => '', '>' => '', ''' => '', '&' => '', 48 | '"' => '', 'À' => 'A', 'Á' => 'A', 'Â' => 'A', 'Ã' => 'A', 'Ä'=> 'Ae', 49 | 'Ä' => 'A', 'Å' => 'A', 'Ā' => 'A', 'Ą' => 'A', 'Ă' => 'A', 'Æ' => 'Ae', 50 | 'Ç' => 'C', 'Ć' => 'C', 'Č' => 'C', 'Ĉ' => 'C', 'Ċ' => 'C', 'Ď' => 'D', 'Đ' => 'D', 51 | 'Ð' => 'D', 'È' => 'E', 'É' => 'E', 'Ê' => 'E', 'Ë' => 'E', 'Ē' => 'E', 52 | 'Ę' => 'E', 'Ě' => 'E', 'Ĕ' => 'E', 'Ė' => 'E', 'Ĝ' => 'G', 'Ğ' => 'G', 53 | 'Ġ' => 'G', 'Ģ' => 'G', 'Ĥ' => 'H', 'Ħ' => 'H', 'Ì' => 'I', 'Í' => 'I', 54 | 'Î' => 'I', 'Ï' => 'I', 'Ī' => 'I', 'Ĩ' => 'I', 'Ĭ' => 'I', 'Į' => 'I', 55 | 'İ' => 'I', 'IJ' => 'IJ', 'Ĵ' => 'J', 'Ķ' => 'K', 'Ł' => 'K', 'Ľ' => 'K', 56 | 'Ĺ' => 'K', 'Ļ' => 'K', 'Ŀ' => 'K', 'Ñ' => 'N', 'Ń' => 'N', 'Ň' => 'N', 57 | 'Ņ' => 'N', 'Ŋ' => 'N', 'Ò' => 'O', 'Ó' => 'O', 'Ô' => 'O', 'Õ' => 'O', 58 | 'Ö' => 'Oe', 'Ö' => 'Oe', 'Ø' => 'O', 'Ō' => 'O', 'Ő' => 'O', 'Ŏ' => 'O', 59 | 'Œ' => 'OE', 'Ŕ' => 'R', 'Ř' => 'R', 'Ŗ' => 'R', 'Ś' => 'S', 'Š' => 'S', 60 | 'Ş' => 'S', 'Ŝ' => 'S', 'Ș' => 'S', 'Ť' => 'T', 'Ţ' => 'T', 'Ŧ' => 'T', 61 | 'Ț' => 'T', 'Ù' => 'U', 'Ú' => 'U', 'Û' => 'U', 'Ü' => 'Ue', 'Ū' => 'U', 62 | 'Ü' => 'Ue', 'Ů' => 'U', 'Ű' => 'U', 'Ŭ' => 'U', 'Ũ' => 'U', 'Ų' => 'U', 63 | 'Ŵ' => 'W', 'Ý' => 'Y', 'Ŷ' => 'Y', 'Ÿ' => 'Y', 'Ź' => 'Z', 'Ž' => 'Z', 64 | 'Ż' => 'Z', 'Þ' => 'T', 'à' => 'a', 'á' => 'a', 'â' => 'a', 'ã' => 'a', 65 | 'ä' => 'ae', 'ä' => 'ae', 'å' => 'a', 'ā' => 'a', 'ą' => 'a', 'ă' => 'a', 66 | 'æ' => 'ae', 'ç' => 'c', 'ć' => 'c', 'č' => 'c', 'ĉ' => 'c', 'ċ' => 'c', 67 | 'ď' => 'd', 'đ' => 'd', 'ð' => 'd', 'è' => 'e', 'é' => 'e', 'ê' => 'e', 68 | 'ë' => 'e', 'ē' => 'e', 'ę' => 'e', 'ě' => 'e', 'ĕ' => 'e', 'ė' => 'e', 69 | 'ƒ' => 'f', 'ĝ' => 'g', 'ğ' => 'g', 'ġ' => 'g', 'ģ' => 'g', 'ĥ' => 'h', 70 | 'ħ' => 'h', 'ì' => 'i', 'í' => 'i', 'î' => 'i', 'ï' => 'i', 'ī' => 'i', 71 | 'ĩ' => 'i', 'ĭ' => 'i', 'į' => 'i', 'ı' => 'i', 'ij' => 'ij', 'ĵ' => 'j', 72 | 'ķ' => 'k', 'ĸ' => 'k', 'ł' => 'l', 'ľ' => 'l', 'ĺ' => 'l', 'ļ' => 'l', 73 | 'ŀ' => 'l', 'ñ' => 'n', 'ń' => 'n', 'ň' => 'n', 'ņ' => 'n', 'ʼn' => 'n', 74 | 'ŋ' => 'n', 'ò' => 'o', 'ó' => 'o', 'ô' => 'o', 'õ' => 'o', 'ö' => 'oe', 75 | 'ö' => 'oe', 'ø' => 'o', 'ō' => 'o', 'ő' => 'o', 'ŏ' => 'o', 'œ' => 'oe', 76 | 'ŕ' => 'r', 'ř' => 'r', 'ŗ' => 'r', 'š' => 's', 'ù' => 'u', 'ú' => 'u', 77 | 'û' => 'u', 'ü' => 'ue', 'ū' => 'u', 'ü' => 'ue', 'ů' => 'u', 'ű' => 'u', 78 | 'ŭ' => 'u', 'ũ' => 'u', 'ų' => 'u', 'ŵ' => 'w', 'ý' => 'y', 'ÿ' => 'y', 79 | 'ŷ' => 'y', 'ž' => 'z', 'ż' => 'z', 'ź' => 'z', 'þ' => 't', 'ß' => 'ss', 80 | 'ſ' => 'ss', 'ый' => 'iy', 'А' => 'A', 'Б' => 'B', 'В' => 'V', 'Г' => 'G', 81 | 'Д' => 'D', 'Е' => 'E', 'Ё' => 'YO', 'Ж' => 'ZH', 'З' => 'Z', 'И' => 'I', 82 | 'Й' => 'Y', 'К' => 'K', 'Л' => 'L', 'М' => 'M', 'Н' => 'N', 'О' => 'O', 83 | 'П' => 'P', 'Р' => 'R', 'С' => 'S', 'Т' => 'T', 'У' => 'U', 'Ф' => 'F', 84 | 'Х' => 'H', 'Ц' => 'C', 'Ч' => 'CH', 'Ш' => 'SH', 'Щ' => 'SCH', 'Ъ' => '', 85 | 'Ы' => 'Y', 'Ь' => '', 'Э' => 'E', 'Ю' => 'YU', 'Я' => 'YA', 'а' => 'a', 86 | 'б' => 'b', 'в' => 'v', 'г' => 'g', 'д' => 'd', 'е' => 'e', 'ё' => 'yo', 87 | 'ж' => 'zh', 'з' => 'z', 'и' => 'i', 'й' => 'y', 'к' => 'k', 'л' => 'l', 88 | 'м' => 'm', 'н' => 'n', 'о' => 'o', 'п' => 'p', 'р' => 'r', 'с' => 's', 89 | 'т' => 't', 'у' => 'u', 'ф' => 'f', 'х' => 'h', 'ц' => 'c', 'ч' => 'ch', 90 | 'ш' => 'sh', 'щ' => 'sch', 'ъ' => '', 'ы' => 'y', 'ь' => '', 'э' => 'e', 91 | 'ю' => 'yu', 'я' => 'ya', 92 | ]; 93 | 94 | // make a human readable string 95 | $slug = strtr(basename($path), $replace); 96 | 97 | // replace non letter or digits by - 98 | $slug = preg_replace('~[^\\pL\d.]+~u', '-', $slug); 99 | 100 | // trim 101 | $slug = trim($slug, '-'); 102 | 103 | // remove unwanted characters 104 | $slug = preg_replace('~[^-\w.]+~', '', $slug); 105 | 106 | return strtolower($slug); 107 | } 108 | 109 | /** 110 | * Load the Valet configuration. 111 | */ 112 | $valetConfig = json_decode( 113 | file_get_contents(VALET_HOME_PATH.'/config.json'), true 114 | ); 115 | 116 | /** 117 | * Parse the URI and site / host for the incoming request. 118 | */ 119 | $uri = urldecode( 120 | explode('?', $_SERVER['REQUEST_URI'])[0] 121 | ); 122 | 123 | $siteName = basename( 124 | // Filter host to support xip.io feature 125 | valet_support_xip_io(explode(':', strtolower($_SERVER['HTTP_HOST']))[0]), 126 | '.'.$valetConfig['domain'] 127 | ); 128 | 129 | if (strpos($siteName, 'www.') === 0) { 130 | $siteName = substr($siteName, 4); 131 | } 132 | 133 | /** 134 | * Determine the fully qualified path to the site. 135 | */ 136 | $valetSitePath = null; 137 | 138 | foreach ($valetConfig['paths'] as $path) { 139 | $domain = ($pos = strrpos($siteName, '.')) !== false 140 | ? substr($siteName, $pos + 1) 141 | : null; 142 | 143 | foreach (glob($path.'/*', GLOB_ONLYDIR) as $dirPath) { 144 | $slug = valet_path_to_slug($dirPath); 145 | 146 | if ($slug == $siteName || $slug == $domain) { 147 | $valetSitePath = $dirPath; 148 | 149 | break 2; 150 | } 151 | } 152 | } 153 | 154 | if (is_null($valetSitePath)) { 155 | show_valet_404(); 156 | } 157 | 158 | /** 159 | * Find the appropriate Valet driver for the request. 160 | */ 161 | $valetDriver = null; 162 | 163 | require __DIR__.'/cli/drivers/require.php'; 164 | 165 | $valetDriver = ValetDriver::assign($valetSitePath, $siteName, $uri); 166 | 167 | if (!$valetDriver) { 168 | show_valet_404(); 169 | } 170 | 171 | /* 172 | * Overwrite the HTTP host for Ngrok. 173 | */ 174 | if (isset($_SERVER['HTTP_X_ORIGINAL_HOST'])) { 175 | $_SERVER['HTTP_HOST'] = $_SERVER['HTTP_X_ORIGINAL_HOST']; 176 | } 177 | 178 | /** 179 | * Allow driver to mutate incoming URL. 180 | */ 181 | $uri = $valetDriver->mutateUri($uri); 182 | 183 | /** 184 | * Determine if the incoming request is for a static file. 185 | */ 186 | $isPhpFile = pathinfo($uri, PATHINFO_EXTENSION) === 'php'; 187 | 188 | if ($uri !== '/' && !$isPhpFile && $staticFilePath = $valetDriver->isStaticFile($valetSitePath, $siteName, $uri)) { 189 | return $valetDriver->serveStaticFile($staticFilePath, $valetSitePath, $siteName, $uri); 190 | } 191 | 192 | /** 193 | * Attempt to dispatch to a front controller. 194 | */ 195 | $frontControllerPath = $valetDriver->frontControllerPath( 196 | $valetSitePath, $siteName, $uri 197 | ); 198 | 199 | if (!$frontControllerPath) { 200 | show_valet_404(); 201 | } 202 | 203 | chdir(dirname($frontControllerPath)); 204 | 205 | require $frontControllerPath; 206 | -------------------------------------------------------------------------------- /valet: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | SOURCE="${BASH_SOURCE[0]}" 6 | SUDOCMDS="uninstall install start restart stop unsecure secure" 7 | HOMEPATH=$HOME 8 | 9 | function check_dependencies() { 10 | local RED='\033[1;31m' 11 | local NC='\033[0m' 12 | local msg='' 13 | 14 | for cmd in "jq" "xsel" "certutil"; do 15 | local str='' 16 | 17 | if ! [[ -x "$(command -v $cmd)" ]]; then 18 | printf -v str " - %s\n" "$cmd" 19 | local msg+="$str" 20 | fi 21 | done 22 | 23 | if [[ $msg != '' ]]; then 24 | printf "${RED}You have missing Valet dependiencies:\n" 25 | printf "$msg" 26 | printf "\nPlease refer to https://cpriego.github.io/valet-linux/requirements on how to install them.${NC}\n" 27 | exit 1 28 | fi 29 | } 30 | 31 | # If the current source is a symbolic link, we need to resolve it to an 32 | # actual directory name. We'll use PHP to do this easier than we can 33 | # do it in pure Bash. So, we'll call into PHP CLI here to resolve. 34 | if [[ -L $SOURCE ]] 35 | then 36 | DIR=$(php -r "echo dirname(realpath('$SOURCE'));") 37 | else 38 | DIR="$( cd "$( dirname "$SOURCE" )" && pwd )" 39 | fi 40 | 41 | # If we are in the global Composer "bin" directory, we need to bump our 42 | # current directory up two, so that we will correctly proxy into the 43 | # Valet CLI script which is written in PHP. Will use PHP to do it. 44 | if [ ! -f "$DIR/cli/valet.php" ] 45 | then 46 | DIR=$(php -r "echo realpath('$DIR/../laravel/valet');") 47 | fi 48 | 49 | # If the command is one of the commands that requires "sudo" privileges 50 | # then we'll proxy the incoming CLI request using sudo, which allows 51 | # us to never require the end users to manually specify each sudo. 52 | if [[ -n $1 && $SUDOCMDS =~ $1 ]] 53 | then 54 | check_dependencies 55 | 56 | if [[ "$EUID" -ne 0 ]] 57 | then 58 | sudo env HOME=$HOMEPATH $SOURCE "$@" 59 | exit 0 60 | fi 61 | fi 62 | 63 | if [[ -n $2 && "domain port" =~ $1 ]] 64 | then 65 | if [[ "$EUID" -ne 0 ]] 66 | then 67 | sudo env HOME=$HOMEPATH $SOURCE "$@" 68 | exit 0 69 | fi 70 | fi 71 | 72 | # If the command is the "share" command we will need to resolve out any 73 | # symbolic links for the site. Before starting Ngrok, we will fire a 74 | # process to retrieve the live Ngrok tunnel URL in the background. 75 | if [[ "$1" = "share" ]] 76 | then 77 | check_dependencies 78 | 79 | HOST="${PWD##*/}" 80 | DOMAIN=$(php "$DIR/cli/valet.php" domain) 81 | PORT=$(cat "$HOME/.valet/config.json" | jq -r ".port") 82 | 83 | for linkname in ~/.valet/Sites/*; do 84 | if [[ "$(readlink $linkname)" = "$PWD" ]] 85 | then 86 | HOST="${linkname##*/}" 87 | fi 88 | done 89 | 90 | php "$DIR/cli/valet.php" secured "$HOST.$DOMAIN" &> /dev/null || ( 91 | printf "\033[1;31mSecured sites can not be shared.\033[0m\n" 92 | exit 1 93 | ) 94 | 95 | # Fetch Ngrok URL In Background... 96 | bash "$DIR/cli/scripts/fetch-share-url.sh" & 97 | sudo -u $USER "$DIR/bin/ngrok" http "$HOST.$DOMAIN:$PORT" -host-header=rewrite ${*:2} 98 | exit 99 | 100 | # Finally, for every other command we will just proxy into the PHP tool 101 | # and let it handle the request. These are commands which can be run 102 | # without sudo and don't require taking over terminals like Ngrok. 103 | else 104 | php "$DIR/cli/valet.php" "$@" 105 | fi 106 | --------------------------------------------------------------------------------