[
  {
    "path": ".editorconfig",
    "content": "root = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\ninsert_final_newline = true\nindent_style = space\nindent_size = 4\ntrim_trailing_whitespace = true\n\n[*.md]\ntrim_trailing_whitespace = false\n\n[*.yml]\nindent_style = space\nindent_size = 2\n"
  },
  {
    "path": ".gitattributes",
    "content": "* text=auto\n.travis.yml export-ignore\n"
  },
  {
    "path": ".github/workflows/run-tests.yml",
    "content": "name: Run Tests\n\non:\n  push:\n    branches: [ main ]\n  pull_request:\n\njobs:\n  tests:\n    strategy:\n      matrix:\n        os: [Ubuntu, macOS]\n        php: [8.1, 8.2]\n\n        include:\n        - os: Ubuntu\n          os-version: ubuntu-latest\n\n        - os: macOS\n          os-version: macos-latest\n\n    name: ${{ matrix.os }} - PHP ${{ matrix.php }}\n\n    runs-on: ${{ matrix.os-version }}\n\n    steps:\n    - name: Checkout code\n      uses: actions/checkout@v1\n\n    - name: Setup PHP\n      uses: shivammathur/setup-php@v2\n      with:\n          php-version: ${{ matrix.php }}\n          extensions: posix, dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick\n          coverage: none\n\n    - name: Install dependencies\n      run: composer update --prefer-stable --prefer-dist --no-interaction --no-suggest\n\n    - name: Run PHP tests\n      run: vendor/bin/phpunit\n"
  },
  {
    "path": ".gitignore",
    "content": "/vendor\n.phpunit.result.cache\n/storage/framework/\n.php_cs.cache\n"
  },
  {
    "path": ".phpcs.xml.dist",
    "content": "<?xml version=\"1.0\"?>\n<ruleset>\n   <file>app</file>\n   <file>config</file>\n   <file>tests</file>\n\n   <rule ref=\"Tighten\"/>\n</ruleset>\n"
  },
  {
    "path": "LICENSE.TXT",
    "content": "The MIT License (MIT)\n\nCopyright (c) <Matt Stauffer>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "app/Actions/AbortsCommands.php",
    "content": "<?php\n\nnamespace App\\Actions;\n\nuse App\\LamboException;\n\ntrait AbortsCommands\n{\n    public function abortIf(bool $abort, string $message, $process = null)\n    {\n        if ($abort) {\n            if ($process) {\n                throw new LamboException(\"{$message}\\nFailed to run: '{$process->getCommandLine()}'.\");\n            }\n            throw new LamboException(\"{$message}\");\n        }\n    }\n}\n"
  },
  {
    "path": "app/Actions/Concerns/InteractsWithComposer.php",
    "content": "<?php\n\nnamespace App\\Actions\\Concerns;\n\nuse App\\Actions\\AbortsCommands;\nuse App\\Shell;\n\ntrait InteractsWithComposer\n{\n    use AbortsCommands;\n\n    protected function composerRequire(string $package, bool $forDev = true): void\n    {\n        $command = $this->getComposerRequireCommand($package, $forDev);\n        $composerProcess = app(Shell::class)->execInProject($command);\n        $this->abortIf(! $composerProcess->isSuccessful(), 'Composer package installation failed.', $composerProcess);\n    }\n\n    protected function getComposerRequireCommand(string $package, bool $forDev): string\n    {\n        return sprintf(\n            'composer require %s%s%s',\n            $package,\n            $forDev ? ' --dev' : '',\n            config('lambo.store.with_output') ? '' : ' --quiet'\n        );\n    }\n}\n"
  },
  {
    "path": "app/Actions/Concerns/InteractsWithGitHub.php",
    "content": "<?php\n\nnamespace App\\Actions\\Concerns;\n\nuse App\\Configuration\\LamboConfiguration;\nuse App\\LamboException;\n\ntrait InteractsWithGitHub\n{\n    protected static function shouldCreateRepository(): bool\n    {\n        return static::gitHubInitializationRequested() && static::gitHubToolingInstalled();\n    }\n\n    protected static function gitHubInitializationRequested(): bool\n    {\n        return config('lambo.store.' . LamboConfiguration::INITIALIZE_GITHUB) === true;\n    }\n\n    protected static function getDescription(): string\n    {\n        $description = config('lambo.store.' . LamboConfiguration::GITHUB_DESCRIPTION);\n\n        if (is_null($description)) {\n            return '';\n        }\n\n        return sprintf(' --description=\"%s\"', $description);\n    }\n\n    protected static function getHomepage(): string\n    {\n        $homepage = config('lambo.store.' . LamboConfiguration::GITHUB_HOMEPAGE);\n\n        if (is_null($homepage)) {\n            return '';\n        }\n\n        return sprintf(' --homepage=\"%s\"', $homepage);\n    }\n\n    /**\n     * @throws LamboException\n     */\n    protected static function getGitHubCreateCommand(): string\n    {\n        if (static::ghInstalled()) {\n            return sprintf(\n                'gh repo create%s --confirm %s%s%s',\n                static::getRepositoryName(),\n                config('lambo.store.github_public') ? ' --public' : ' --private',\n                static::getDescription(),\n                static::getHomepage(),\n            );\n        }\n\n        if (static::hubInstalled()) {\n            return sprintf(\n                'hub create %s%s%s%s',\n                config('lambo.store.github_public') ? '' : '--private ',\n                static::getDescription(),\n                static::getHomepage(),\n                static::getRepositoryName()\n            );\n        }\n\n        throw new LamboException(\"Missing tool. Expected one of 'gh' or 'hub' to be installed but none found.\");\n    }\n\n    protected static function getRepositoryName(): string\n    {\n        $name = config('lambo.store.project_name');\n        $organization = config('lambo.store.' . LamboConfiguration::GITHUB_ORGANIZATION);\n\n        return $organization ? \" {$organization}/{$name}\" : \" {$name}\";\n    }\n\n    protected static function ghInstalled(): bool\n    {\n        return config('lambo.store.tools.gh') === true;\n    }\n\n    protected static function hubInstalled(): bool\n    {\n        return config('lambo.store.tools.hub') === true;\n    }\n\n    protected static function gitHubToolingInstalled(): bool\n    {\n        return static::ghInstalled() || static::hubInstalled();\n    }\n}\n"
  },
  {
    "path": "app/Actions/Concerns/InteractsWithNpm.php",
    "content": "<?php\n\nnamespace App\\Actions\\Concerns;\n\nuse App\\Actions\\AbortsCommands;\nuse App\\Shell;\n\ntrait InteractsWithNpm\n{\n    use AbortsCommands;\n\n    /**\n     * @throws \\App\\LamboException\n     */\n    protected function installAndCompileNodeDependencies(): void\n    {\n        $this->installNodeDependencies();\n        $this->compileNodeDependencies();\n    }\n\n    /**\n     * @throws \\App\\LamboException\n     */\n    public function installNodeDependencies(): void\n    {\n        $process = app(Shell::class)->execInProject('npm install' . (config('lambo.store.with_output') ? '' : ' --silent'));\n        $this->abortIf(! $process->isSuccessful(), 'Installation of npm dependencies did not complete successfully', $process);\n    }\n\n    /**\n     * @throws \\App\\LamboException\n     */\n    protected function compileNodeDependencies(): void\n    {\n        $process = app(Shell::class)->execInProject('npm run build' . (config('lambo.store.with_output') ? '' : ' --silent'));\n        $this->abortIf(! $process->isSuccessful(), 'Compilation of project assets did not complete successfully', $process);\n    }\n}\n"
  },
  {
    "path": "app/Actions/CreateDatabase.php",
    "content": "<?php\n\nnamespace App\\Actions;\n\nuse App\\ConsoleWriter;\nuse App\\Shell;\nuse App\\Tools\\Database;\nuse PDOException;\n\nclass CreateDatabase\n{\n    use AbortsCommands;\n\n    protected $shell;\n    protected $database;\n    protected $consoleWriter;\n\n    public function __construct(Shell $shell, Database $database, ConsoleWriter $consoleWriter)\n    {\n        $this->shell = $shell;\n        $this->database = $database;\n        $this->consoleWriter = $consoleWriter;\n    }\n\n    public function __invoke()\n    {\n        if (! config('lambo.store.create_database')) {\n            return;\n        }\n\n        $this->consoleWriter->logStep('Creating database');\n\n        $db_name = config('lambo.store.database_name');\n\n        try {\n            $databaseCreated = $this->database\n                ->fillFromLamboStore(config('lambo.store'))\n                ->create($db_name);\n\n            if (! $databaseCreated) {\n                $this->consoleWriter->warn($this->failureToCreateError($db_name));\n                return;\n            }\n        } catch (PDOException $e) {\n            $this->consoleWriter->warn($e->getMessage());\n            $this->consoleWriter->warn($this->failureToCreateError($db_name));\n            return;\n        }\n\n        $this->consoleWriter->success(\"Created a new database '{$db_name}'\");\n    }\n\n    protected function failureToCreateError(string $db_name): string\n    {\n        return sprintf(\n            \"Failed to create database '%s' using credentials <fg=yellow>mysql://%s:****@%s:%s</>\\nYou will need to create the database manually.\",\n            $db_name,\n            config('lambo.store.database_username'),\n            config('lambo.store.database_host'),\n            config('lambo.store.database_port')\n        );\n    }\n}\n"
  },
  {
    "path": "app/Actions/CustomizeDotEnv.php",
    "content": "<?php\n\nnamespace App\\Actions;\n\nuse App\\ConsoleWriter;\nuse Illuminate\\Support\\Arr;\nuse Illuminate\\Support\\Facades\\File;\n\nclass CustomizeDotEnv\n{\n    protected $consoleWriter;\n\n    public function __construct(ConsoleWriter $consoleWriter)\n    {\n        $this->consoleWriter = $consoleWriter;\n    }\n\n    public function __invoke()\n    {\n        $this->consoleWriter->logStep('Customizing .env and .env.example');\n\n        $filePath = config('lambo.store.project_path') . '/.env.example';\n\n        $output = $this->customize(File::get($filePath));\n\n        File::put($filePath, $output);\n        File::put(str_replace('.env.example', '.env', $filePath), $output);\n\n        $this->consoleWriter->success('.env files configured.');\n    }\n\n    public function customize($contents): string\n    {\n        return collect(explode(\"\\n\", $contents))->transform(function ($item) {\n            $parts = explode('=', $item, 2);\n\n            // Line doesn't contain an equal sign (=); return without modification\n            if (count($parts) < 2) {\n                return $item;\n            }\n\n            [$envKey, $envVal] = $parts;\n\n            $replace = $this->value($envKey, $envVal);\n\n            return \"{$envKey}={$replace}\";\n        })->implode(\"\\n\");\n    }\n\n    public function value($key, $fallback)\n    {\n        $replacements = [\n            'APP_NAME' => config('lambo.store.project_name'),\n            'APP_URL' => config('lambo.store.project_url'),\n            'DB_PORT' => config('lambo.store.database_port'),\n            'DB_DATABASE' => config('lambo.store.database_name'),\n            'DB_USERNAME' => config('lambo.store.database_username'),\n            'DB_PASSWORD' => config('lambo.store.database_password'),\n        ];\n\n        return Arr::get($replacements, $key, $fallback);\n    }\n}\n"
  },
  {
    "path": "app/Actions/DisplayHelpScreen.php",
    "content": "<?php\n\nnamespace App\\Actions;\n\nuse App\\Options;\n\nclass DisplayHelpScreen\n{\n    public function __invoke()\n    {\n        app('console-writer')->text(\"\\n <comment>Usage:</comment>{$this->createCliStringForCommandUsage()}\");\n        app('console-writer')->text(\"\\n <comment>Options (lambo new myApplication):</comment>{$this->createCliStringForOptionDescriptions()}\");\n    }\n\n    public function createCliStringForOptionDescriptions(): string\n    {\n        return collect((new Options())->all())->reduce(function ($carry, $option) {\n            $flag = isset($option['short'])\n                ? '-' . $option['short'] . ', --' . $option['long']\n                : '    --' . $option['long'];\n\n            $flag .= isset($option['param_description'])\n                ? \"={$option['param_description']}\"\n                : '';\n\n            return $carry . sprintf(\"\\n   <info>%-33s</info>%s\", $flag, $option['cli_description']);\n        });\n    }\n\n    private function createCliStringForCommandUsage(): string\n    {\n        return collect([\n            [\n                'usage' => 'lambo edit-config [--editor=<editor>]',\n                'description' => 'Create or edit your <comment>~/.lambo/config</comment> file',\n            ],\n            [\n                'usage' => 'lambo edit-after [--editor=<editor>]',\n                'description' => 'Create or edit your <comment>~/.lambo/after</comment> file',\n            ],\n            [\n                'usage' => 'lambo new myApplication [options]',\n                'description' => 'Scaffold a new Laravel application',\n            ],\n        ])->reduce(function ($carry, $command) {\n                return $carry . sprintf(\"\\n   <info>%-40s</info> %s\", $command['usage'], $command['description']);\n        });\n    }\n}\n"
  },
  {
    "path": "app/Actions/DisplayLamboWelcome.php",
    "content": "<?php\n\nnamespace App\\Actions;\n\nclass DisplayLamboWelcome\n{\n    protected $lamboLogo = '\n     __                    __               :version:\n    / /   ____ _____ ___  / /_  ____\n   / /   / __ `/ __ `__ \\/ __ \\/ __ \\\n  / /___/ /_/ / / / / / / /_/ / /_/ /\n /_____/\\__,_/_/ /_/ /_/_.___/\\____/';\n\n    protected $welcomeText = \"\n<info>Lambo:</info> Super-powered <comment>'laravel new'</comment> for Laravel and Valet.\";\n\n    public function __construct()\n    {\n        $this->lamboLogo = str_replace(':version:', config('app.version'), $this->lamboLogo);\n    }\n\n    public function __invoke()\n    {\n        foreach (explode(\"\\n\", $this->lamboLogo) as $line) {\n            // Extra space on the end fixes an issue with console when it ends with backslash\n            app('console-writer')->text(\"<info>{$line} </info>\");\n        }\n\n        foreach (explode(\"\\n\", $this->welcomeText) as $line) {\n            // Extra space on the end fixes an issue with console when it ends with backslash\n            app('console-writer')->text(\"{$line} \");\n        }\n    }\n}\n"
  },
  {
    "path": "app/Actions/EditConfigFile.php",
    "content": "<?php\n\nnamespace App\\Actions;\n\nuse App\\Shell;\nuse Illuminate\\Support\\Facades\\File;\n\nclass EditConfigFile\n{\n    use AbortsCommands;\n\n    public $shell;\n\n    public function __construct(Shell $shell)\n    {\n        $this->shell = $shell;\n    }\n\n    public function __invoke(string $fileName)\n    {\n        $configDir = config('home_dir') . '/.lambo';\n        $configFilePath = $configDir . '/' . $fileName;\n\n        if (! File::isDirectory($configDir)) {\n            app('console-writer')->note(\"Configuration directory '{$configDir}' does not exist, creating it now...\");\n            $this->abortIf(! File::makeDirectory($configDir), \"I could not create the directory: {$configDir}.\");\n        }\n\n        if (! File::isFile($configFilePath)) {\n            app('console-writer')->note(\"File '{$configFilePath}' does not exist, creating it now...\");\n            $this->abortIf(! File::put($configFilePath, File::get(base_path(\"stubs/{$fileName}\"))), \"I could not create the configuration file: {$configFilePath}.\");\n        }\n\n        $process = $this->shell->withTTY()->execIn($configDir, config('lambo.store.editor') . \" {$fileName}\");\n\n        $this->abortIf(! $process->isSuccessful(), \"I could not open {$configFilePath} for editing.\", $process);\n    }\n}\n"
  },
  {
    "path": "app/Actions/GenerateAppKey.php",
    "content": "<?php\n\nnamespace App\\Actions;\n\nuse App\\ConsoleWriter;\nuse App\\Shell;\n\nclass GenerateAppKey\n{\n    use AbortsCommands;\n\n    protected $shell;\n    protected $consoleWriter;\n\n    public function __construct(Shell $shell, ConsoleWriter $consoleWriter)\n    {\n        $this->shell = $shell;\n        $this->consoleWriter = $consoleWriter;\n    }\n\n    public function __invoke()\n    {\n        $this->consoleWriter->logStep('Setting APP_KEY in .env');\n\n        $process = $this->shell->execInProject(\"php artisan key:generate{$this->withQuiet()}\");\n\n        $this->abortIf(! $process->isSuccessful(), 'Failed to generated APP_KEY successfully', $process);\n\n        $this->consoleWriter->success('APP_KEY has been set.');\n    }\n\n    private function withQuiet()\n    {\n        return config('lambo.store.with_output') ? '' : ' --quiet';\n    }\n}\n"
  },
  {
    "path": "app/Actions/InitializeGitHubRepository.php",
    "content": "<?php\n\nnamespace App\\Actions;\n\nuse App\\Actions\\Concerns\\InteractsWithGitHub;\nuse App\\ConsoleWriter;\nuse App\\Shell;\n\nclass InitializeGitHubRepository\n{\n    use AbortsCommands;\n    use InteractsWithGitHub;\n\n    public const WARNING_FAILED_TO_CREATE_REPOSITORY = 'Failed to create new GitHub repository';\n\n    protected $shell;\n    protected $consoleWriter;\n\n    public function __construct(Shell $shell, ConsoleWriter $consoleWriter)\n    {\n        $this->shell = $shell;\n        $this->consoleWriter = $consoleWriter;\n    }\n\n    public function __invoke()\n    {\n        if (! static::gitHubInitializationRequested()) {\n            return;\n        }\n\n        $this->consoleWriter->logStep('Initializing GitHub repository');\n\n        $process = $this->shell->execInProject(static::getGitHubCreateCommand());\n\n        if (! $process->isSuccessful()) {\n            $this->consoleWriter->warn(self::WARNING_FAILED_TO_CREATE_REPOSITORY);\n            $this->consoleWriter->warnCommandFailed($process->getCommandLine());\n            $this->consoleWriter->showOutputErrors($process->getErrorOutput());\n            config(['lambo.store.push_to_github' => false]);\n\n            return;\n        }\n        config(['lambo.store.push_to_github' => true]);\n\n        $this->consoleWriter->success('Successfully created new GitHub repository');\n    }\n}\n"
  },
  {
    "path": "app/Actions/InitializeGitRepository.php",
    "content": "<?php\n\nnamespace App\\Actions;\n\nuse App\\ConsoleWriter;\nuse App\\Shell;\n\nclass InitializeGitRepository\n{\n    use AbortsCommands;\n\n    protected $shell;\n    protected $consoleWriter;\n\n    public function __construct(Shell $shell, ConsoleWriter $consoleWriter)\n    {\n        $this->shell = $shell;\n        $this->consoleWriter = $consoleWriter;\n    }\n\n    public function __invoke()\n    {\n        $this->consoleWriter->logStep('Initializing git repository');\n\n        $this->exec(sprintf(\n            'git init%s',\n            config('lambo.store.with_output') ? '' : ' --quiet',\n        ));\n\n        $this->exec('git add .');\n\n        $this->exec(sprintf(\n            \"git commit%s -m '%s'\",\n            config('lambo.store.with_output') ? '' : ' --quiet',\n            config('lambo.store.commit_message')\n        ));\n\n        $this->consoleWriter->success('New git repository initialized.');\n    }\n\n    public function exec($command)\n    {\n        $process = $this->shell->execInProject($command);\n        $this->abortIf(! $process->isSuccessful(), 'Initialization of git repository did not complete successfully.', $process);\n    }\n}\n"
  },
  {
    "path": "app/Actions/InstallBreeze.php",
    "content": "<?php\n\nnamespace App\\Actions;\n\nuse App\\Actions\\Concerns\\InteractsWithComposer;\nuse App\\Actions\\Concerns\\InteractsWithNpm;\nuse App\\ConsoleWriter;\nuse App\\LamboException;\nuse App\\Shell;\n\nclass InstallBreeze\n{\n    use AbortsCommands;\n    use InteractsWithComposer;\n    use InteractsWithNpm;\n\n    public const VALID_STACKS = [\n        'Blade' => 'blade',\n        'Vue' => 'vue',\n        'React' => 'react',\n    ];\n\n    private $shell;\n    private $consoleWriter;\n\n    public function __construct(Shell $shell, ConsoleWriter $consoleWriter)\n    {\n        $this->shell = $shell;\n        $this->consoleWriter = $consoleWriter;\n    }\n\n    public function __invoke()\n    {\n        if (($stack = config('lambo.store.breeze')) === false) {\n            return;\n        }\n\n        if (! in_array($stack, array_values(self::VALID_STACKS), true)) {\n            throw new LamboException(\"'{$stack}' is not a valid Breeze configuration.\");\n        }\n\n        $this->consoleWriter->logStep('Installing Laravel Breeze starter kit');\n\n        $this->composerRequire('laravel/breeze');\n        $this->installBreeze($stack);\n        $this->installAndCompileNodeDependencies();\n\n        $this->consoleWriter->success('Successfully installed Laravel Breeze.');\n    }\n\n    protected function installBreeze($stack): void\n    {\n        $process = $this->shell->execInProject(sprintf(\n            'php artisan breeze:install%s%s',\n            $stack === 'blade' ? '' : \" {$stack}\",\n            config('lambo.store.with_output') ? '' : ' --quiet'\n        ));\n        $this->abortIf(! $process->isSuccessful(), 'Failed to install Laravel Breeze.', $process);\n    }\n}\n"
  },
  {
    "path": "app/Actions/InstallJetstream.php",
    "content": "<?php\n\nnamespace App\\Actions;\n\nuse App\\Actions\\Concerns\\InteractsWithComposer;\nuse App\\Actions\\Concerns\\InteractsWithNpm;\nuse App\\ConsoleWriter;\nuse App\\LamboException;\nuse App\\Shell;\n\nclass InstallJetstream\n{\n    use AbortsCommands;\n    use InteractsWithComposer;\n    use InteractsWithNpm;\n\n    public const VALID_CONFIGURATIONS = [\n        'inertia',\n        'livewire',\n        'inertia,teams',\n        'livewire,teams',\n        'teams,inertia',\n        'teams,livewire'\n    ];\n\n    public const VALID_STACKS = [\n        'Inertia' => 'inertia',\n        'Livewire' => 'livewire',\n    ];\n\n    private ConsoleWriter $consoleWriter;\n    private Shell $shell;\n\n    public function __construct(Shell $shell, ConsoleWriter $consoleWriter)\n    {\n        $this->shell = $shell;\n        $this->consoleWriter = $consoleWriter;\n    }\n\n    /**\n     * @throws LamboException\n     */\n    public function __invoke(): void\n    {\n        if (($stack = config('lambo.store.jetstream')) === false) {\n            return;\n        }\n\n        if (! in_array($stack, self::VALID_CONFIGURATIONS, true)) {\n            throw new LamboException(\"'{$stack}' is not a valid Jetstream configuration.\");\n        }\n\n        $this->consoleWriter->logStep('Installing Laravel Jetstream starter kit');\n\n        $this->composerRequire('laravel/jetstream');\n        $this->installJetstream($stack);\n        $this->installAndCompileNodeDependencies();\n\n        $this->consoleWriter->success('Successfully installed Laravel Jetstream.');\n    }\n\n    /**\n     * @throws LamboException\n     */\n    protected function installJetstream(string $stack): void\n    {\n        $configuration = explode(',', $stack);\n        $installJetstreamProcess = $this->shell->execInProject(sprintf(\n            'php artisan jetstream:install %s%s%s',\n            in_array('livewire', $configuration) ? 'livewire' : 'inertia',\n            in_array('teams', $configuration) ? ' --teams' : '',\n            config('lambo.store.with_output') ? '' : ' --quiet'\n        ));\n        $this->abortIf(! $installJetstreamProcess->isSuccessful(), 'Failed to install Laravel Jetstream.', $installJetstreamProcess);\n    }\n}\n"
  },
  {
    "path": "app/Actions/InstallLaravel.php",
    "content": "<?php\n\nnamespace App\\Actions;\n\nuse App\\ConsoleWriter;\nuse App\\Shell;\n\nclass InstallLaravel\n{\n    use AbortsCommands;\n\n    protected $shell;\n    protected $consoleWriter;\n\n    public function __construct(Shell $shell, ConsoleWriter $consoleWriter)\n    {\n        $this->shell = $shell;\n        $this->consoleWriter = $consoleWriter;\n    }\n\n    public function __invoke()\n    {\n        $this->consoleWriter->logStep('Creating the new Laravel project');\n\n        $process = $this->shell->execInRoot(sprintf(\n            'composer create-project laravel/laravel %s%s --remove-vcs --prefer-dist %s',\n            config('lambo.store.project_name'),\n            config('lambo.store.dev') ? ' dev-master' : '',\n            config('lambo.store.with_output') ? '' : '--quiet'\n        ));\n\n        $this->abortIf(! $process->isSuccessful(), 'The laravel installer did not complete successfully.', $process);\n\n        $this->consoleWriter->success(sprintf(\n            \"A new application '%s' has been created from the %s branch.\",\n            config('lambo.store.project_name'),\n            config('lambo.store.dev') ? 'develop' : 'release'\n        ));\n    }\n}\n"
  },
  {
    "path": "app/Actions/InstallNpmDependencies.php",
    "content": "<?php\n\nnamespace App\\Actions;\n\nuse App\\ConsoleWriter;\nuse App\\Shell;\n\nclass InstallNpmDependencies\n{\n    use AbortsCommands;\n\n    protected $shell;\n    protected $consoleWriter;\n\n    public function __construct(Shell $shell, ConsoleWriter $consoleWriter)\n    {\n        $this->shell = $shell;\n        $this->consoleWriter = $consoleWriter;\n    }\n\n    public function __invoke()\n    {\n        $this->consoleWriter->logStep('Installing node dependencies');\n\n        $process = $this->shell->execInProject(\"npm install{$this->withQuiet()}\");\n        $this->abortIf(! $process->isSuccessful(), 'Installation of npm dependencies did not complete successfully', $process);\n\n        $this->consoleWriter->newLine();\n        $this->consoleWriter->success('Npm dependencies installed.');\n    }\n\n    public function withQuiet()\n    {\n        return config('lambo.store.with_output') ? '' : ' --silent';\n    }\n}\n"
  },
  {
    "path": "app/Actions/MigrateDatabase.php",
    "content": "<?php\n\nnamespace App\\Actions;\n\nuse App\\ConsoleWriter;\nuse App\\Shell;\nuse App\\Tools\\Database;\nuse PDOException;\n\nclass MigrateDatabase\n{\n    use AbortsCommands;\n\n    protected $shell;\n    protected $database;\n    protected $consoleWriter;\n\n    public function __construct(Shell $shell, Database $database, ConsoleWriter $consoleWriter)\n    {\n        $this->shell = $shell;\n        $this->database = $database;\n        $this->consoleWriter = $consoleWriter;\n    }\n\n    public function __invoke()\n    {\n        if (! config('lambo.store.migrate_database')) {\n            return;\n        }\n\n        $this->consoleWriter->logStep('Running database migrations');\n\n        try {\n            $this->database\n                ->fillFromLamboStore(config('lambo.store'))\n                ->ensureExists(config('lambo.store.database_name'));\n\n            $process = $this->shell->execInProject(\"php artisan migrate{$this->withQuiet()}\");\n\n            return $process->isSuccessful()\n                ? $this->consoleWriter->success('Database migrated')\n                : $this->consoleWriter->warn(\"Failed to run {$process->getCommandLine()}\");\n        } catch (PDOException $e) {\n            $this->consoleWriter->warn($e->getMessage());\n            return $this->consoleWriter->warn($this->failureMigrateError());\n        }\n    }\n\n    protected function failureMigrateError(): string\n    {\n        return sprintf(\n            \"Skipping database migration using credentials <fg=yellow>mysql://%s:****@%s:%s</>\\nYou will need to run the database migrations manually.\",\n            config('lambo.store.database_username'),\n            config('lambo.store.database_host'),\n            config('lambo.store.database_port')\n        );\n    }\n\n    private function withQuiet()\n    {\n        return config('lambo.store.with_output') ? '' : ' --quiet';\n    }\n}\n"
  },
  {
    "path": "app/Actions/OpenInBrowser.php",
    "content": "<?php\n\nnamespace App\\Actions;\n\nuse App\\Environment;\nuse App\\Shell;\n\nclass OpenInBrowser\n{\n    use AbortsCommands;\n\n    protected $shell;\n    protected $consoleWriter;\n    protected $environment;\n\n    public function __construct(Shell $shell, Environment $environment)\n    {\n        $this->shell = $shell;\n        $this->environment = $environment;\n    }\n\n    public function __invoke()\n    {\n        if (config('lambo.store.no_browser')) {\n            return;\n        }\n\n        app('console-writer')->logStep('Opening in Browser');\n\n        if ($this->environment->isMac() && $this->browser()) {\n            $this->shell->execInProject(sprintf(\n                'open -a \"%s\" \"%s\"',\n                $this->browser(),\n                config('lambo.store.project_url')\n            ));\n\n            return;\n        }\n\n        $this->shell->execInProject('valet open');\n    }\n\n    public function browser()\n    {\n        return config('lambo.store.browser');\n    }\n}\n"
  },
  {
    "path": "app/Actions/OpenInEditor.php",
    "content": "<?php\n\nnamespace App\\Actions;\n\nuse App\\ConsoleWriter;\nuse App\\Shell;\n\nclass OpenInEditor\n{\n    use AbortsCommands;\n\n    protected $shell;\n    protected $consoleWriter;\n\n    public function __construct(Shell $shell, ConsoleWriter $consoleWriter)\n    {\n        $this->shell = $shell;\n        $this->consoleWriter = $consoleWriter;\n    }\n\n    public function __invoke()\n    {\n        $this->consoleWriter->logStep('Opening In Editor');\n\n        $process = $this->shell->withTTY()->execInProject(sprintf('%s .', config('lambo.store.editor')));\n        $this->abortIf(! $process->isSuccessful(), sprintf('Failed to open editor %s', config('lambo.store.editor')), $process);\n\n        $this->consoleWriter->success('Opening your project in ' . config('lambo.store.editor'));\n    }\n}\n"
  },
  {
    "path": "app/Actions/PushToGitHub.php",
    "content": "<?php\n\nnamespace App\\Actions;\n\nuse App\\ConsoleWriter;\nuse App\\Shell;\n\nclass PushToGitHub\n{\n    public const WARNING_FAILED_TO_PUSH = 'Failed to push project to GitHub.';\n    public const WARNING_UNABLE_TO_GET_BRANCH_NAME = self::WARNING_FAILED_TO_PUSH . ' Unable to determine git branch name.';\n\n    protected $shell;\n    protected $consoleWriter;\n\n    public function __construct(Shell $shell, ConsoleWriter $consoleWriter)\n    {\n        $this->shell = $shell;\n        $this->consoleWriter = $consoleWriter;\n    }\n\n    public function __invoke()\n    {\n        if (! (config('lambo.store.push_to_github'))) {\n            return;\n        }\n\n        $this->consoleWriter->logStep('Pushing new project to GitHub');\n\n        $branchNameProcess = $this->shell->execInProject('git rev-parse --abbrev-ref HEAD');\n        if (! $branchNameProcess->isSuccessful()) {\n            $this->consoleWriter->warn(self::WARNING_UNABLE_TO_GET_BRANCH_NAME);\n            $this->consoleWriter->warn(\"Failed to run {$branchNameProcess->getCommandLine()}\");\n            $this->consoleWriter->showOutputErrors($branchNameProcess->getErrorOutput());\n            return;\n        }\n\n        $process = $this->shell->execInProject(\"git push -u origin {$branchNameProcess->getOutput()}\");\n        if (! $process->isSuccessful()) {\n            $this->consoleWriter->warn(self::WARNING_FAILED_TO_PUSH);\n            $this->consoleWriter->warn(\"Failed to run {$process->getCommandLine()}\");\n            $this->consoleWriter->showOutputErrors($process->getErrorOutput());\n            return;\n        }\n\n        $this->consoleWriter->success('Successfully pushed project to GitHub');\n    }\n}\n"
  },
  {
    "path": "app/Actions/RunAfterScript.php",
    "content": "<?php\n\nnamespace App\\Actions;\n\nuse App\\ConsoleWriter;\nuse App\\Shell;\nuse Illuminate\\Support\\Facades\\File;\n\nclass RunAfterScript\n{\n    use AbortsCommands;\n\n    protected $shell;\n    protected $consoleWriter;\n\n    public function __construct(Shell $shell, ConsoleWriter $consoleWriter)\n    {\n        $this->shell = $shell;\n        $this->consoleWriter = $consoleWriter;\n    }\n\n    public function __invoke()\n    {\n        $afterScriptPath = config('home_dir') . '/.lambo/after';\n        if (! File::isFile($afterScriptPath)) {\n            return;\n        }\n\n        $this->consoleWriter->logStep('Running after script');\n\n        $this->shell->withTTY();\n        $process = $this->shell->execInProject(sprintf(\n            'env PROJECTPATH=%s sh %s',\n            config('lambo.store.project_path'),\n            $afterScriptPath\n        ));\n        $this->abortIf(! $process->isSuccessful(), 'After file did not complete successfully', $process);\n\n        $this->consoleWriter->success('After script has completed.');\n    }\n}\n"
  },
  {
    "path": "app/Actions/UpgradeSavedConfiguration.php",
    "content": "<?php\n\nnamespace App\\Actions;\n\nuse Carbon\\Carbon;\nuse Illuminate\\Support\\Facades\\File;\nuse Illuminate\\Support\\Str;\n\nclass UpgradeSavedConfiguration\n{\n    // IMPORTANT NOTE: Every time we make *any* changes to configuration, we need\n    // to increment this configurationVersion so that users get upgraded config\n    private $configurationVersion = 1;\n    private $configDir;\n    private $configFilePath;\n    private $lastVersionUpdateFilePath;\n    private $commented = [];\n    private $removedConfigurationKeys = [\n        'NODE',\n        'MIX',\n        'AUTH',\n        'FRONTEND',\n    ];\n    private $newConfiguration = [\n        'MIGRATE_DATABASE' => [\n            'commented' => false,\n            'default' => 'false',\n            'description' => [\n                'Run the standard Laravel database migrations.',\n                'Possible values:',\n                '  true, 1, \"yes\" or \"on\"',\n                '  false (default), 0, \"no\" or \"off\"',\n            ],\n        ],\n        'DB_HOST' => [\n            'commented' => true,\n            'default' => '127.0.0.1',\n            'description' => [\n                'The database host. Defaults to 127.0.0.1.',\n            ],\n        ],\n        'DB_PORT' => [\n            'commented' => true,\n            'default' => '',\n            'description' => [\n                'The database port. Defaults to 3306.',\n            ],\n        ],\n        'DB_NAME' => [\n            'commented' => true,\n            'default' => '',\n            'description' => [\n                'The database name. Defaults to the project name.',\n            ],\n        ],\n    ];\n\n    public function __construct()\n    {\n        $this->configDir = config('home_dir') . '/.lambo';\n        $this->configFilePath = \"{$this->configDir}/config\";\n        $this->lastVersionUpdateFilePath = \"{$this->configDir}/.last_version_update\";\n    }\n\n    public function __invoke(): bool\n    {\n        if (! $this->shouldUpgrade()) {\n            return false;\n        }\n\n        $savedConfiguration = File::get($this->configFilePath);\n        File::move($this->configFilePath, $this->configFilePath . '.' . Carbon::now()->toDateTimeLocalString());\n\n        File::put($this->configFilePath, $this->upgrade($savedConfiguration, $this->removedConfigurationKeys, $this->newConfiguration));\n\n        File::delete($this->lastVersionUpdateFilePath);\n        File::put($this->lastVersionUpdateFilePath, $this->configurationVersion);\n\n        return true;\n    }\n\n    public function upgrade(string $savedConfiguration, array $removedConfigurationKeys, array $newConfiguration = []): string\n    {\n        return implode(PHP_EOL, [\n            $this->commentRemovedConfiguration($savedConfiguration, $removedConfigurationKeys),\n            \"\\n# ------------------------------------------------------------------------------\",\n            '# ' . Carbon::now(config('app.timezone'))->format('j-M-Y g:i a') . ' (auto-generated by Lambo):',\n            '# ------------------------------------------------------------------------------',\n            $this->summarizeComments(),\n            $this->addNewConfiguration($newConfiguration),\n        ]);\n    }\n\n    private function shouldUpgrade(): bool\n    {\n        if (! File::isFile($this->configFilePath)) {\n            return false;\n        }\n\n        $localVersion = File::isFile($this->lastVersionUpdateFilePath)\n            ? (int)File::get($this->lastVersionUpdateFilePath)\n            : 0;\n\n        if ($localVersion < $this->configurationVersion) {\n            return true;\n        }\n\n        return false;\n    }\n\n    private function commentRemovedConfiguration(string $savedConfiguration, array $oldConfigurationKeys): string\n    {\n        return collect(explode(\"\\n\", $savedConfiguration))->transform(function ($item) use ($oldConfigurationKeys) {\n            $matched = collect($oldConfigurationKeys)->reduce(function ($carry, $oldKey) use ($item) {\n                return $carry || Str::of($item)->startsWith($oldKey);\n            }, false);\n            if ($matched) {\n                $this->commented[] = $item;\n                return \"#{$item}\";\n            }\n            return $item;\n        })->implode(\"\\n\");\n    }\n\n    private function summarizeComments(): string\n    {\n        if (count($this->commented) < 1) {\n            return '';\n        }\n\n        return implode(PHP_EOL, [\n            '# Lambo has commented out the following configuration items as they',\n            '# are no-longer used. You may safely remove them:',\n            collect($this->commented)->reduce(function ($carry, $item) {\n                return \"{$carry}#   {$item}\\n\";\n            }, '')\n        ]);\n    }\n\n    private function addNewConfiguration(array $newConfiguration): string\n    {\n        if (count($newConfiguration) < 1) {\n            return '';\n        }\n\n        return collect(array_keys($newConfiguration))->reduce(function ($carry, $key) use ($newConfiguration) {\n            $description = collect($newConfiguration[$key]['description'])->reduce(function ($carry, $item) {\n                return \"{$carry}# {$item}\\n\";\n            }, '');\n\n            $configurationItem = sprintf(\n                \"%s%s=%s\\n\\n\",\n                $newConfiguration[$key]['commented'] ? '#' : '',\n                $key,\n                $newConfiguration[$key]['default']\n            );\n\n            return \"{$carry}{$description}{$configurationItem}\";\n        }, \"# Lambo has introduced new configuration options. They have been added here\\n# with sensible defaults; however, you should review them.\\n#\\n\");\n    }\n}\n"
  },
  {
    "path": "app/Actions/ValetLink.php",
    "content": "<?php\n\nnamespace App\\Actions;\n\nuse App\\ConsoleWriter;\nuse App\\Shell;\n\nclass ValetLink\n{\n    use AbortsCommands;\n\n    protected $shell;\n\n    private $consoleWriter;\n\n    public function __construct(Shell $shell, ConsoleWriter $consoleWriter)\n    {\n        $this->shell = $shell;\n        $this->consoleWriter = $consoleWriter;\n    }\n\n    public function __invoke()\n    {\n        if (! config('lambo.store.valet_link')) {\n            return;\n        }\n\n        $this->consoleWriter->logStep('Running valet link');\n\n        $process = $this->shell->execInProject('valet link');\n        $this->abortIf(! $process->isSuccessful(), 'valet link did not complete successfully', $process);\n\n        $this->consoleWriter->success('valet link successful');\n    }\n}\n"
  },
  {
    "path": "app/Actions/ValetSecure.php",
    "content": "<?php\n\nnamespace App\\Actions;\n\nuse App\\ConsoleWriter;\nuse App\\Shell;\n\nclass ValetSecure\n{\n    use AbortsCommands;\n\n    protected $shell;\n    protected $consoleWriter;\n\n    public function __construct(Shell $shell, ConsoleWriter $consoleWriter)\n    {\n        $this->shell = $shell;\n        $this->consoleWriter = $consoleWriter;\n    }\n\n    public function __invoke()\n    {\n        if (! config('lambo.store.valet_secure')) {\n            return;\n        }\n\n        $this->consoleWriter->logStep('Running valet secure');\n\n        $process = $this->shell->execInProject('valet secure');\n        $this->abortIf(! $process->isSuccessful(), 'valet secure did not complete successfully', $process);\n\n        $this->consoleWriter->success('valet secure successful');\n    }\n}\n"
  },
  {
    "path": "app/Actions/ValidateGitHubConfiguration.php",
    "content": "<?php\n\nnamespace App\\Actions;\n\nuse App\\Actions\\Concerns\\InteractsWithGitHub;\nuse App\\Configuration\\LamboConfiguration;\nuse App\\ConsoleWriter;\nuse App\\Shell;\n\nclass ValidateGitHubConfiguration\n{\n    use InteractsWithGitHub;\n\n    public const WARNING_UNABLE_TO_CREATE_REPOSITORY = 'Unable to create a new GitHub repository';\n    public const INSTRUCTIONS_GITHUB_TOOLING_MISSING = [\n        'For Lambo to initialize a new repository on GitHub it requires either the',\n        'official GitHub command line tool or the unofficial hub tool, but neither',\n        'are installed. You can find more information about each tool by visiting:',\n        '    - <fg=blue;options=underscore>https://github.com/cli/cli#installation</>',\n        '    - <fg=blue;options=underscore>https://github.com/github/hub</>',\n    ];\n    public const INSTRUCTIONS_GH_NOT_AUTHENTICATED = [\n        'You are not logged into GitHub. Please run <comment>gh auth login</comment>.',\n        'For more information, please visit, <fg=blue;options=underscore>https://cli.github.com/manual/gh_auth_login</>',\n    ];\n    public const QUESTION_SHOULD_CONTINUE = 'Would you like Lambo to continue without GitHub repository creation?';\n    public const SELECTED_GITHUB_TOOL_MESSAGE_PATTERN = \"Using the '%s' command for GitHub configuration.\";\n\n    private $consoleWriter;\n    private $shell;\n\n    public function __construct(Shell $shell, ConsoleWriter $consoleWriter)\n    {\n        $this->consoleWriter = $consoleWriter;\n        $this->shell = $shell;\n    }\n\n    public function __invoke()\n    {\n        if (! static::gitHubInitializationRequested()) {\n            config(['lambo.store.' . LamboConfiguration::INITIALIZE_GITHUB => false]);\n            return;\n        }\n\n        if (! static::gitHubToolingInstalled()) {\n            $this->consoleWriter->warn(self::WARNING_UNABLE_TO_CREATE_REPOSITORY);\n            $this->consoleWriter->text(self::INSTRUCTIONS_GITHUB_TOOLING_MISSING);\n            $this->askToContinueWithoutGitHubSetup();\n            return;\n        }\n\n        if (static::hubInstalled()) {\n            $this->consoleWriter->note(sprintf(self::SELECTED_GITHUB_TOOL_MESSAGE_PATTERN, 'hub'));\n            return;\n        }\n\n        if (! $this->ghAuthenticated()) {\n            $this->consoleWriter->warn(self::WARNING_UNABLE_TO_CREATE_REPOSITORY);\n            $this->consoleWriter->text(self::INSTRUCTIONS_GH_NOT_AUTHENTICATED);\n            $this->askToContinueWithoutGitHubSetup();\n            return;\n        }\n\n        $this->consoleWriter->note(sprintf(self::SELECTED_GITHUB_TOOL_MESSAGE_PATTERN, 'gh'));\n    }\n\n    private function askToContinueWithoutGitHubSetup()\n    {\n        config(['lambo.store.' . LamboConfiguration::INITIALIZE_GITHUB => false]);\n\n        if (! app('console')->confirm(self::QUESTION_SHOULD_CONTINUE)) {\n            exit;\n        }\n    }\n\n    private function ghAuthenticated(): bool\n    {\n        $process = $this->shell->execQuietly('gh auth status');\n\n        return $process->isSuccessful();\n    }\n}\n"
  },
  {
    "path": "app/Actions/VerifyDependencies.php",
    "content": "<?php\n\nnamespace App\\Actions;\n\nuse App\\ConsoleWriter;\nuse Symfony\\Component\\Process\\ExecutableFinder;\n\nclass VerifyDependencies\n{\n    use AbortsCommands;\n\n    protected $finder;\n    protected $consoleWriter;\n\n    private $dependencies = [\n        [\n            'command' => 'composer',\n            'label' => 'Composer',\n            'instructions_url' => 'https://getcomposer.org',\n        ],\n        [\n            'command' => 'valet',\n            'label' => 'Laravel valet',\n            'instructions_url' => 'https://laravel.com/docs/valet',\n        ],\n        [\n            'command' => 'git',\n            'label' => 'Git version control',\n            'instructions_url' => 'https://git-scm.com',\n        ],\n    ];\n\n    private $optionalDependencies = [\n        [\n            'command' => 'hub',\n            'label' => 'Unofficial GitHub command line tool',\n            'instructions_url' => 'https://github.com/github/hub',\n        ],\n        [\n            'command' => 'gh',\n            'label' => 'Official GitHub command line tool',\n            'instructions_url' => 'https://cli.github.com/',\n        ],\n    ];\n\n    public function __construct(ExecutableFinder $finder, ConsoleWriter $consoleWriter)\n    {\n        $this->finder = $finder;\n        $this->consoleWriter = $consoleWriter;\n    }\n\n    public function __invoke()\n    {\n        $this->consoleWriter->logStep('Verifying dependencies');\n\n        $this->consoleWriter->text('Optional dependencies');\n        $this->consoleWriter->newLine();\n\n        foreach ($this->optionalDependencies as $optionalDependency) {\n            list($command, $label, $instructionsUrl) = array_values($optionalDependency);\n\n            if (($installedDependency = $this->finder->find($command)) === null) {\n                $this->consoleWriter->note(\"{$label}, an optional dependency, is missing. You can find installation instructions at:\\n        <fg=blue;href={$instructionsUrl}>{$instructionsUrl}</>\");\n                config([\"lambo.store.tools.{$command}\" => false]);\n            } else {\n                $this->consoleWriter->success(\"{$label} found at:\\n        <fg=blue>{$installedDependency}</>\");\n                config([\"lambo.store.tools.{$command}\" => true]);\n            }\n        }\n\n        $this->consoleWriter->newLine();\n        $this->consoleWriter->text('Required dependencies');\n        $this->consoleWriter->newLine();\n        $this->abortIf(\n            collect($this->dependencies)->reduce(function ($carry, $dependency) {\n                list($command, $label, $instructionsUrl) = array_values($dependency);\n                if (($installedDependency = $this->finder->find($command)) === null) {\n                    $this->consoleWriter->warn(\"{$label} is missing. You can find installation instructions at:\\n        <fg=blue;href={$instructionsUrl}>{$instructionsUrl}</>\");\n                    return true;\n                }\n                $this->consoleWriter->success(\"{$label} found at:\\n        <fg=blue>{$installedDependency}</>\");\n                return $carry ?? false;\n            }),\n            'Please install missing dependencies and try again.'\n        );\n    }\n}\n"
  },
  {
    "path": "app/Actions/VerifyPathAvailable.php",
    "content": "<?php\n\nnamespace App\\Actions;\n\nuse App\\LamboException;\nuse Illuminate\\Support\\Facades\\File;\n\nclass VerifyPathAvailable\n{\n    use AbortsCommands;\n\n    private $consoleWriter;\n\n    public function __invoke()\n    {\n        app('console-writer')->logStep('Verifying path availability');\n\n        $rootPath = config('lambo.store.root_path');\n\n        if (! File::isDirectory($rootPath)) {\n            throw new LamboException(\"{$rootPath} is not a directory.\");\n        }\n\n        $projectPath = config('lambo.store.project_path');\n\n        if (empty($projectPath)) {\n            throw new LamboException(\"Configuration 'lambo.store.project_path' cannot be null or an empty string.\");\n        }\n\n        if (File::isDirectory($projectPath)) {\n            if (! config('lambo.store.force_create')) {\n                throw new LamboException(\"{$projectPath} is already a directory.\");\n            }\n\n            if (! File::deleteDirectory($projectPath)) {\n                throw new LamboException(\"{$projectPath} is already a directory and, although the force option was specified, deletion failed.\");\n            }\n        }\n\n        app('console-writer')->success(\"Directory '{$projectPath}' is available.\");\n    }\n}\n"
  },
  {
    "path": "app/Commands/.gitkeep",
    "content": ""
  },
  {
    "path": "app/Commands/Debug.php",
    "content": "<?php\n\nnamespace App\\Commands;\n\nuse App\\ConsoleWriter;\nuse Carbon\\Carbon;\nuse Dotenv\\Dotenv;\nuse Illuminate\\Support\\Arr;\nuse Illuminate\\Support\\Facades\\File;\nuse IntlTimeZone;\n\ntrait Debug\n{\n    protected function arrayToTable(array $data, array $filter = null, string $keyPrefix = '', array $headers = null): void\n    {\n        if (empty($data)) {\n            return;\n        }\n\n        $rows = collect($data)\n            ->filter(function ($value, $key) use ($filter) {\n                return is_null($filter) || in_array($key, $filter);\n            })\n            ->map(function ($value, $key) use ($keyPrefix) {\n                $type = gettype($value);\n\n                if (is_string($value)) {\n                    $value =  empty($value)\n                        ? ConsoleWriter::formatString('\"\"', ConsoleWriter::BLUE)\n                        : ConsoleWriter::formatString($value, ConsoleWriter::BLUE);\n                }\n\n                if (is_integer($value)) {\n                    $value =  ConsoleWriter::formatString($value, ConsoleWriter::MAGENTA);\n                }\n\n                if (is_bool($value)) {\n                    $value =  $value\n                        ? ConsoleWriter::formatString('true', ConsoleWriter::GREEN)\n                        : ConsoleWriter::formatString('false', ConsoleWriter::RED);\n                }\n\n                if (is_null($value)) {\n                    $value = '';\n                }\n\n                return [$keyPrefix . $key, \"({$type})\", $value];\n            })->values()->toArray();\n\n        $this->consoleWriter->table($headers ?: ['key', 'type', 'value'], $rows);\n    }\n\n    protected function debugReport(): void\n    {\n        $this->consoleWriter->panel('Debug', 'Start', 'fg=black;bg=white');\n\n        $this->consoleWriter->sectionTitle('Computed configuration');\n        $this->consoleWriter->text([\n            'The following is the configuration lambo has computed by merging:',\n        ]);\n        $this->consoleWriter->listing([\n            'command line parameters',\n            'saved configuration',\n            'shell environment variables.',\n        ]);\n\n        $this->configToTable();\n\n        $this->consoleWriter->sectionTitle('Pre-flight Configuration');\n\n        $this->consoleWriter->newLine();\n        $this->consoleWriter->text('Raw commmand line:');\n        $this->arrayToTable(\n            $_SERVER['argv'],\n        );\n\n        $this->consoleWriter->text('Command line arguments:');\n        $this->arrayToTable($this->arguments());\n\n        $this->consoleWriter->text('Command line options:');\n        $this->arrayToTable(\n            $this->options(),\n            [\n                'editor',\n                'path',\n                'message',\n                'github',\n                'gh-public',\n                'gh-description',\n                'gh-homepage',\n                'gh-org',\n                'breeze',\n                'jetstream',\n                'browser',\n                'dbhost',\n                'dbport',\n                'dbname',\n                'dbuser',\n                'dbpassword',\n                'create-db',\n                'migrate-db',\n                'force',\n                'link',\n                'secure',\n                'quiet',\n                'dev',\n                'full',\n                'projectName',\n            ],\n            '--'\n        );\n\n        $this->consoleWriter->text('Saved configuration:');\n\n        $savedConfig = [];\n        if (File::isFile(config('home_dir') . '/.lambo/config')) {\n            $savedConfig = Dotenv::createMutable(config('home_dir') . '/.lambo', 'config')->load();\n        }\n        $this->arrayToTable(\n            $savedConfig,\n            [\n                'CODEEDITOR',\n                'MESSAGE',\n                'PROJECTPATH',\n                'BROWSER',\n                'FRONTEND',\n                'DB_PORT',\n                'DB_NAME',\n                'DB_USERNAME',\n                'DB_PASSWORD',\n                'CREATE_DATABASE',\n                'LINK',\n                'SECURE',\n                'QUIET',\n                'WITH_OUTPUT',\n                'DEVELOP',\n                'FULL',\n                'WITH_TEAMS',\n            ]\n        );\n\n        $this->consoleWriter->text('Shell environment variables:');\n        $this->arrayToTable($_SERVER, ['EDITOR'], '$');\n\n        $this->logTimezoneData();\n\n        $this->consoleWriter->panel('Debug', 'End', 'fg=black;bg=white');\n    }\n\n    protected function configToTable(): void\n    {\n        $config = Arr::prepend(config('lambo.store'), config('home_dir'), 'home_dir');\n        $this->arrayToTable($this->dotFlatten('lambo.store', $config));\n    }\n\n    private function dotFlatten($prefix, $array): array\n    {\n        $result = [];\n        foreach ($array as $key => $value) {\n            if (is_array($value)) {\n                $result = array_merge($result, $this->dotFlatten($prefix . '.' . $key, $value));\n            } else {\n                $result[$prefix . '.' . $key] = $value;\n            }\n        }\n        return $result;\n    }\n\n    protected function logTimezoneData()\n    {\n        $this->consoleWriter->sectionTitle('Timezone configuration');\n        $this->consoleWriter->newLine();\n        $this->consoleWriter->text('System settings');\n        $this->arrayToTable([\n            'OS Config (\"/etc/localtime\")' => exec('/bin/ls -l /etc/localtime|/usr/bin/cut -d\"/\" -f8-'),\n            \"ini_get('date.timezone')\" => ini_get('date.timezone') ?: 'Not configured',\n            'IntlTimeZone::createDefault()' => IntlTimeZone::createDefault()->getID(),\n            'date_default_timezone_get()' => date_default_timezone_get(),\n            'config->get(\"app.timezone\")' => config()->get('app.timezone'),\n        ]);\n\n        $this->consoleWriter->text('Carbon');\n        $this->arrayToTable([\n            // UTC, GMT, Atlantic/Azores\n            'Carbon (Timezone identifier)' => Carbon::now()->format('e'),\n\n            // 1 if Daylight Saving Time, 0 otherwise.\n            'Carbon (Daylight savings)' => (bool)Carbon::now()->format('I'),\n\n            // Difference to Greenwich time (GMT)\n            'Carbon (Difference to GMT w/O)' => Carbon::now()->format('O'), // +0200'Carbon (Difference to GMT w/P)' => Carbon::now()->format('P'), // +02:00\n\n            // Examples: EST, MDT\n            'Carbon (tz abbreviation)' => Carbon::now()->format('T'),\n        ]);\n    }\n}\n"
  },
  {
    "path": "app/Commands/EditAfter.php",
    "content": "<?php\n\nnamespace App\\Commands;\n\nuse App\\Actions\\EditConfigFile;\nuse App\\Configuration\\CommandLineConfiguration;\nuse App\\Configuration\\LamboConfiguration;\nuse App\\Configuration\\SavedConfiguration;\nuse App\\Configuration\\SetConfig;\nuse App\\Configuration\\ShellConfiguration;\nuse App\\LamboException;\n\nclass EditAfter extends LamboCommand\n{\n    protected $signature = 'edit-after {--editor= : Open the config file in the specified <info>EDITOR</info> or the system default if none is specified.}';\n\n    protected $description = 'Edit Config File. A new config file is created if one does not already exist.';\n\n    public function handle()\n    {\n        app()->bind('console', function () {\n            return $this;\n        });\n\n        $commandLineConfiguration = new CommandLineConfiguration([\n            'editor' => LamboConfiguration::EDITOR,\n        ]);\n\n        $savedConfiguration = new SavedConfiguration([\n            'CODEEDITOR' => LamboConfiguration::EDITOR,\n        ]);\n\n        $shellConfiguration = new ShellConfiguration([\n            'EDITOR' => LamboConfiguration::EDITOR,\n        ]);\n\n        (new SetConfig(\n            $commandLineConfiguration,\n            $savedConfiguration,\n            $shellConfiguration,\n            app('console-writer'),\n            $this->input\n        ))([\n            LamboConfiguration::EDITOR => 'nano',\n        ]);\n\n        try {\n            app(EditConfigFile::class)('after');\n        } catch (LamboException $e) {\n            app('console-writer')->exception($e->getMessage());\n        }\n    }\n}\n"
  },
  {
    "path": "app/Commands/EditConfig.php",
    "content": "<?php\n\nnamespace App\\Commands;\n\nuse App\\Actions\\EditConfigFile;\nuse App\\Configuration\\CommandLineConfiguration;\nuse App\\Configuration\\LamboConfiguration;\nuse App\\Configuration\\SavedConfiguration;\nuse App\\Configuration\\SetConfig;\nuse App\\Configuration\\ShellConfiguration;\nuse App\\LamboException;\n\nclass EditConfig extends LamboCommand\n{\n    protected $signature = 'edit-config {--editor= : Open the config file in the specified <info>EDITOR</info> or the system default if none is specified.}';\n\n    protected $description = 'Edit Config File. A new config file is created if one does not already exist.';\n\n    public function handle()\n    {\n        app()->bind('console', function () {\n            return $this;\n        });\n\n        $commandLineConfiguration = new CommandLineConfiguration([\n            'editor' => LamboConfiguration::EDITOR,\n        ]);\n\n        $savedConfiguration = new SavedConfiguration([\n            'CODEEDITOR' => LamboConfiguration::EDITOR,\n        ]);\n\n        $shellConfiguration = new ShellConfiguration([\n            'EDITOR' => LamboConfiguration::EDITOR,\n        ]);\n\n        (new SetConfig(\n            $commandLineConfiguration,\n            $savedConfiguration,\n            $shellConfiguration,\n            app('console-writer'),\n            $this->input\n        ))([\n            LamboConfiguration::EDITOR => 'nano',\n        ]);\n\n        try {\n            app(EditConfigFile::class)('config');\n        } catch (LamboException $e) {\n            app('console-writer')->exception($e->getMessage());\n        }\n    }\n}\n"
  },
  {
    "path": "app/Commands/HelpCommand.php",
    "content": "<?php\n\nnamespace App\\Commands;\n\nuse App\\Actions\\DisplayHelpScreen;\nuse App\\Actions\\DisplayLamboWelcome;\n\nclass HelpCommand extends LamboCommand\n{\n    protected $signature = 'help-screen';\n    protected $description = 'Show help';\n\n    public function handle()\n    {\n        app(DisplayLamboWelcome::class)();\n        app(DisplayHelpScreen::class)();\n    }\n}\n"
  },
  {
    "path": "app/Commands/LamboCommand.php",
    "content": "<?php\n\nnamespace App\\Commands;\n\nuse App\\ConsoleWriter;\nuse LaravelZero\\Framework\\Commands\\Command;\nuse Symfony\\Component\\Console\\Input\\InputInterface;\nuse Symfony\\Component\\Console\\Output\\OutputInterface;\n\nabstract class LamboCommand extends Command\n{\n    public function run(InputInterface $input, OutputInterface $output): int\n    {\n        app()->singleton(ConsoleWriter::class, function () use ($input, $output) {\n            return new ConsoleWriter($input, $output);\n        });\n\n        app()->alias(ConsoleWriter::class, 'console-writer');\n\n        return parent::run($input, $output);\n    }\n}\n"
  },
  {
    "path": "app/Commands/NewCommand.php",
    "content": "<?php\n\nnamespace App\\Commands;\n\nuse App\\Actions\\CreateDatabase;\nuse App\\Actions\\CustomizeDotEnv;\nuse App\\Actions\\DisplayHelpScreen;\nuse App\\Actions\\DisplayLamboWelcome;\nuse App\\Actions\\EditConfigFile;\nuse App\\Actions\\GenerateAppKey;\nuse App\\Actions\\InitializeGitHubRepository;\nuse App\\Actions\\InitializeGitRepository;\nuse App\\Actions\\InstallBreeze;\nuse App\\Actions\\InstallJetstream;\nuse App\\Actions\\InstallLaravel;\nuse App\\Actions\\MigrateDatabase;\nuse App\\Actions\\OpenInBrowser;\nuse App\\Actions\\OpenInEditor;\nuse App\\Actions\\PushToGitHub;\nuse App\\Actions\\RunAfterScript;\nuse App\\Actions\\UpgradeSavedConfiguration;\nuse App\\Actions\\ValetLink;\nuse App\\Actions\\ValetSecure;\nuse App\\Actions\\ValidateGitHubConfiguration;\nuse App\\Actions\\VerifyDependencies;\nuse App\\Actions\\VerifyPathAvailable;\nuse App\\Configuration\\CommandLineConfiguration;\nuse App\\Configuration\\LamboConfiguration;\nuse App\\Configuration\\SavedConfiguration;\nuse App\\Configuration\\SetConfig;\nuse App\\Configuration\\ShellConfiguration;\nuse App\\ConsoleWriter;\nuse App\\LamboException;\nuse App\\Options;\n\nclass NewCommand extends LamboCommand\n{\n    use Debug;\n\n    protected $signature;\n    protected $description = 'Creates a fresh Laravel application';\n    protected $consoleWriter;\n\n    public function __construct()\n    {\n        $this->signature = $this->buildSignature();\n\n        parent::__construct();\n\n        app()->bind('console', function () {\n            return $this;\n        });\n    }\n\n    public function buildSignature()\n    {\n        return collect((new Options())->all())->reduce(\n            function ($carry, $option) {\n                return $carry . $this->buildSignatureOption($option);\n            },\n            \"new\\n{projectName? : Name of the Laravel project}\"\n        );\n    }\n\n    public function buildSignatureOption($option): string\n    {\n        $commandlineOption = isset($option['short']) ? ($option['short'] . '|' . $option['long']) : $option['long'];\n\n        if (isset($option['param_description'])) {\n            $commandlineOption .= '=' . ($option['default'] ?? '');\n        }\n\n        return \"\\n{--{$commandlineOption} : {$option['cli_description']}}\";\n    }\n\n    public function handle()\n    {\n        app(DisplayLamboWelcome::class)();\n\n        if (! $this->argument('projectName')) {\n            app(DisplayHelpScreen::class)();\n            exit;\n        }\n\n        $this->setConsoleWriter();\n        $this->setConfig();\n\n        if (app(UpgradeSavedConfiguration::class)()) {\n            $this->consoleWriter->newLine();\n            $this->consoleWriter->note('Your Lambo configuration (~/.lambo/config) has been updated.');\n            $this->consoleWriter->note('Please review the changes then run lambo again.');\n            if ($this->confirm(sprintf('Review the changes now in %s?', config('lambo.store.editor')))) {\n                app(EditConfigFile::class)('config');\n            }\n            return;\n        }\n\n        $this->consoleWriter->sectionTitle(\"Creating a new Laravel app '{$this->argument('projectName')}'\");\n\n        try {\n            app(VerifyDependencies::class)();\n            app(ValidateGitHubConfiguration::class)();\n            app(VerifyPathAvailable::class)();\n            app(InstallLaravel::class)();\n            app(CustomizeDotEnv::class)();\n            app(GenerateAppKey::class)();\n            app(InstallBreeze::class)();\n            app(InstallJetstream::class)();\n            app(CreateDatabase::class)();\n            app(MigrateDatabase::class)();\n            app(InitializeGitRepository::class)();\n            app(RunAfterScript::class)();\n            app(InitializeGitHubRepository::class)();\n            app(PushToGitHub::class)();\n            app(ValetLink::class)();\n            app(ValetSecure::class)();\n            app(OpenInEditor::class)();\n            app(OpenInBrowser::class)();\n        } catch (LamboException $e) {\n            $this->consoleWriter->exception($e->getMessage());\n            exit;\n        }\n\n        $this->consoleWriter->newLine();\n        $this->consoleWriter->text([\n            '<fg=green>Done, happy coding!</>',\n            'Lambo is brought to you by the lovely folks at <fg=blue;href=https://tighten.co/>Tighten</>.',\n        ]);\n        $this->consoleWriter->newLine();\n    }\n\n    protected function setConsoleWriter()\n    {\n        $this->consoleWriter = app(ConsoleWriter::class);\n    }\n\n    private function setConfig(): void\n    {\n        config(['lambo.store' => []]); // @todo remove if debug code is removed.\n\n        $commandLineConfiguration = new CommandLineConfiguration([\n            'editor' => LamboConfiguration::EDITOR,\n            'message' => LamboConfiguration::COMMIT_MESSAGE,\n            'path' => LamboConfiguration::ROOT_PATH,\n            'browser' => LamboConfiguration::BROWSER,\n            'frontend' => LamboConfiguration::FRONTEND_FRAMEWORK,\n            'dbhost' => LamboConfiguration::DATABASE_HOST,\n            'dbport' => LamboConfiguration::DATABASE_PORT,\n            'dbname' => LamboConfiguration::DATABASE_NAME,\n            'dbuser' => LamboConfiguration::DATABASE_USERNAME,\n            'dbpassword' => LamboConfiguration::DATABASE_PASSWORD,\n            'create-db' => LamboConfiguration::CREATE_DATABASE,\n            'force' => LamboConfiguration::FORCE_CREATE,\n            'migrate-db' => LamboConfiguration::MIGRATE_DATABASE,\n            'link' => LamboConfiguration::VALET_LINK,\n            'secure' => LamboConfiguration::VALET_SECURE,\n            'with-output' => LamboConfiguration::WITH_OUTPUT,\n            'dev' => LamboConfiguration::USE_DEVELOP_BRANCH,\n            'full' => LamboConfiguration::FULL,\n            'github' => LamboConfiguration::INITIALIZE_GITHUB,\n            'gh-public' => LamboConfiguration::GITHUB_PUBLIC,\n            'gh-description' => LamboConfiguration::GITHUB_DESCRIPTION,\n            'gh-homepage' => LamboConfiguration::GITHUB_HOMEPAGE,\n            'gh-org' => LamboConfiguration::GITHUB_ORGANIZATION,\n            'projectName' => LamboConfiguration::PROJECT_NAME,\n            'breeze' => LamboConfiguration::BREEZE,\n            'jetstream' => LamboConfiguration::JETSTREAM,\n        ]);\n\n        $savedConfiguration = new SavedConfiguration([\n            'PROJECTPATH' => LamboConfiguration::ROOT_PATH,\n            'MESSAGE' => LamboConfiguration::COMMIT_MESSAGE,\n            'DEVELOP' => LamboConfiguration::USE_DEVELOP_BRANCH,\n            'CODEEDITOR' => LamboConfiguration::EDITOR,\n            'BROWSER' => LamboConfiguration::BROWSER,\n            'DB_HOST' => LamboConfiguration::DATABASE_HOST,\n            'DB_PORT' => LamboConfiguration::DATABASE_PORT,\n            'DB_NAME' => LamboConfiguration::DATABASE_NAME,\n            'DB_USERNAME' => LamboConfiguration::DATABASE_USERNAME,\n            'DB_PASSWORD' => LamboConfiguration::DATABASE_PASSWORD,\n            'CREATE_DATABASE' => LamboConfiguration::CREATE_DATABASE,\n            'MIGRATE_DATABASE' => LamboConfiguration::MIGRATE_DATABASE,\n            'LINK' => LamboConfiguration::VALET_LINK,\n            'SECURE' => LamboConfiguration::VALET_SECURE,\n        ]);\n\n        $shellConfiguration = new ShellConfiguration([\n            'EDITOR' => LamboConfiguration::EDITOR,\n        ]);\n\n        (new SetConfig(\n            $commandLineConfiguration,\n            $savedConfiguration,\n            $shellConfiguration,\n            $this->consoleWriter,\n            $this->input\n        ))([\n            LamboConfiguration::COMMAND => self::class,\n            LamboConfiguration::EDITOR => 'nano',\n            LamboConfiguration::COMMIT_MESSAGE => 'Initial commit',\n            LamboConfiguration::ROOT_PATH => getcwd(),\n            LamboConfiguration::BROWSER => null,\n            LamboConfiguration::DATABASE_HOST => '127.0.0.1',\n            LamboConfiguration::DATABASE_PORT => 3306,\n            LamboConfiguration::DATABASE_NAME => $this->argument('projectName'),\n            LamboConfiguration::DATABASE_USERNAME => 'root',\n            LamboConfiguration::DATABASE_PASSWORD => '',\n            LamboConfiguration::CREATE_DATABASE => false,\n            LamboConfiguration::FORCE_CREATE => false,\n            LamboConfiguration::MIGRATE_DATABASE => false,\n            LamboConfiguration::VALET_LINK => false,\n            LamboConfiguration::VALET_SECURE => false,\n            LamboConfiguration::WITH_OUTPUT => false,\n            LamboConfiguration::USE_DEVELOP_BRANCH => false,\n            LamboConfiguration::FULL => false,\n            LamboConfiguration::INITIALIZE_GITHUB => false,\n            LamboConfiguration::GITHUB_PUBLIC => false,\n            LamboConfiguration::PROJECT_NAME => null,\n            LamboConfiguration::GITHUB_DESCRIPTION => null,\n            LamboConfiguration::GITHUB_HOMEPAGE => null,\n            LamboConfiguration::GITHUB_ORGANIZATION => null,\n            LamboConfiguration::BREEZE => false,\n            LamboConfiguration::JETSTREAM => false,\n            LamboConfiguration::TLD => null,\n        ]);\n\n        if ($this->consoleWriter->isDebug()) {\n            $this->debugReport();\n        }\n    }\n}\n"
  },
  {
    "path": "app/Configuration/CommandLineConfiguration.php",
    "content": "<?php\n\nnamespace App\\Configuration;\n\nclass CommandLineConfiguration extends LamboConfiguration\n{\n    protected function getSettings(): array\n    {\n        $commandLineConfiguration = app('console')->options();\n\n        foreach (app('console')->arguments() as $key => $value) {\n            $commandLineConfiguration[$key] = $value;\n        }\n\n        return $commandLineConfiguration;\n    }\n}\n"
  },
  {
    "path": "app/Configuration/LamboConfiguration.php",
    "content": "<?php\n\nnamespace App\\Configuration;\n\nuse Illuminate\\Support\\Str;\n\nabstract class LamboConfiguration\n{\n    public const EDITOR = 'editor';\n    public const PROJECT_NAME = 'project_name';\n    public const ROOT_PATH = 'root_path';\n    public const WITH_OUTPUT = 'with_output';\n    public const USE_DEVELOP_BRANCH = 'dev';\n    public const CREATE_DATABASE = 'create_database';\n    public const FORCE_CREATE = 'force_create';\n    public const MIGRATE_DATABASE = 'migrate_database';\n    public const DATABASE_HOST = 'database_host';\n    public const DATABASE_PORT = 'database_port';\n    public const DATABASE_NAME = 'database_name';\n    public const DATABASE_USERNAME = 'database_username';\n    public const DATABASE_PASSWORD = 'database_password';\n    public const FRONTEND_FRAMEWORK = 'frontend';\n    public const FULL = 'full';\n    public const TLD = 'tld';\n    public const COMMIT_MESSAGE = 'commit_message';\n    public const VALET_LINK = 'valet_link';\n    public const VALET_SECURE = 'valet_secure';\n    public const BROWSER = 'browser';\n    public const TEAMS = 'teams';\n    public const INITIALIZE_GITHUB = 'initialize_github';\n    public const GITHUB_PUBLIC = 'github_public';\n    public const GITHUB_DESCRIPTION = 'github_description';\n    public const GITHUB_HOMEPAGE = 'github_homepage';\n    public const GITHUB_ORGANIZATION = 'github_organization';\n    public const COMMAND = 'command';\n    public const BREEZE = 'breeze';\n    public const JETSTREAM = 'jetstream';\n\n    public function __construct(array $keyMap)\n    {\n        $settings = $this->getSettings();\n\n        collect($keyMap)->each(function ($item, $key) use ($settings) {\n            $this->$item = $this->get($key, $settings);\n        });\n    }\n\n    abstract protected function getSettings(): array;\n\n    protected function get(string $key, array $array)\n    {\n        if (array_key_exists($key, $array)) {\n            if ($array[$key] === '') {\n                return null;\n            }\n\n            if (in_array(Str::lower($array[$key]), ['1', 'true', 'on', 'yes'])) {\n                return true;\n            }\n\n            if (in_array(Str::lower($array[$key]), ['0', 'false', 'off', 'no'])) {\n                return false;\n            }\n\n            return $array[$key];\n        }\n\n        return null;\n    }\n\n    public function __get($name)\n    {\n        return null;\n    }\n}\n"
  },
  {
    "path": "app/Configuration/SavedConfiguration.php",
    "content": "<?php\n\nnamespace App\\Configuration;\n\nuse Dotenv\\Dotenv;\nuse Illuminate\\Support\\Facades\\File;\n\nclass SavedConfiguration extends LamboConfiguration\n{\n    protected function getSettings(): array\n    {\n        $configurationPath = config('home_dir') . '/' . config('config_dir', '.lambo');\n        $configurationFile = config('config_file', 'config');\n\n        if (! File::exists(\"{$configurationPath}/{$configurationFile}\")) {\n            return [];\n        }\n\n        return Dotenv::createMutable($configurationPath, $configurationFile)->load();\n    }\n}\n"
  },
  {
    "path": "app/Configuration/SetConfig.php",
    "content": "<?php\n\nnamespace App\\Configuration;\n\nuse App\\Actions\\InstallBreeze;\nuse App\\Actions\\InstallJetstream;\nuse App\\Commands\\Debug;\nuse App\\Commands\\NewCommand;\nuse App\\ConsoleWriter;\nuse App\\LamboException;\nuse Illuminate\\Support\\Arr;\nuse Illuminate\\Support\\Facades\\File;\nuse Illuminate\\Support\\Str;\nuse Symfony\\Component\\Console\\Input\\InputInterface;\nuse Symfony\\Component\\Console\\Style\\SymfonyStyle;\n\nclass SetConfig\n{\n    use Debug;\n\n    protected $consoleWriter;\n    protected $fullFlags = [\n        LamboConfiguration::CREATE_DATABASE,\n        LamboConfiguration::MIGRATE_DATABASE,\n        LamboConfiguration::VALET_LINK,\n        LamboConfiguration::VALET_SECURE,\n    ];\n    protected $options;\n\n    private $commandLineConfiguration;\n    private $savedConfiguration;\n    private $shellConfiguration;\n    private $commandLineInput;\n\n    public function __construct(\n        CommandLineConfiguration $commandLineConfiguration,\n        SavedConfiguration $savedConfiguration,\n        ShellConfiguration $shellConfiguration,\n        ConsoleWriter $consoleWriter,\n        InputInterface $commandLineOptions\n    ) {\n        $this->commandLineConfiguration = $commandLineConfiguration;\n        $this->savedConfiguration = $savedConfiguration;\n        $this->shellConfiguration = $shellConfiguration;\n        $this->consoleWriter = $consoleWriter;\n\n        $this->commandLineInput = array_filter($commandLineOptions->getOptions(), function ($value, $key) use ($commandLineOptions) {\n            return $commandLineOptions->hasParameterOption(\"--{$key}\");\n        }, ARRAY_FILTER_USE_BOTH);\n    }\n\n    public function __invoke($defaultConfiguration)\n    {\n        foreach ($defaultConfiguration as $configurationKey => $default) {\n            $methodName = 'get' . Str::of($configurationKey)->studly();\n            if (method_exists($this, $methodName)) {\n                config([\"lambo.store.{$configurationKey}\" => $this->$methodName($configurationKey, $default)]);\n            } else {\n                config([\"lambo.store.{$configurationKey}\" => $this->get($configurationKey, $default)]);\n            }\n        }\n\n        // If we're in the \"new\" command, generate a few config items which\n        // require others to be set above first.\n        if (config('lambo.store.command') === NewCommand::class) {\n            $projectPath = config('lambo.store.root_path') . '/' . config('lambo.store.project_name');\n            config(['lambo.store.project_path' => $projectPath]);\n            config(['lambo.store.project_url' => $this->getProjectURL()]);\n        }\n\n        if (config('lambo.store.full')) {\n            foreach ($this->fullFlags as $fullFlag) {\n                config([\"lambo.store.{$fullFlag}\" => true]);\n            }\n        }\n    }\n\n    private function get(string $configurationKey, $default)\n    {\n        if (isset($this->commandLineConfiguration->$configurationKey)) {\n            return $this->commandLineConfiguration->$configurationKey;\n        }\n\n        if (isset($this->savedConfiguration->$configurationKey)) {\n            return $this->savedConfiguration->$configurationKey;\n        }\n\n        if (isset($this->shellConfiguration->$configurationKey)) {\n            return $this->shellConfiguration->$configurationKey;\n        }\n\n        return $default;\n    }\n\n    private function getTld(): string\n    {\n        $valetConfig = config('home_dir') . '/.config/valet/config.json';\n        $legacyValetConfig = config('home_dir') . '/.valet/config.json';\n\n        if (File::isFile($valetConfig)) {\n            return json_decode(File::get($valetConfig))->tld;\n        }\n\n        if (File::isFile($legacyValetConfig)) {\n            return json_decode(File::get($legacyValetConfig))->domain;\n        }\n\n        throw new LamboException(\n            implode(PHP_EOL, [\n                'Unable to find valet domain (tld) configuration.',\n                'No Valet configuration located at either of the following locations:',\n                \"- {$valetConfig}\",\n                \"- {$legacyValetConfig}\",\n            ])\n        );\n    }\n\n    private function getRootPath(string $key, $default)\n    {\n        $configuredKeyValue = $this->get($key, $default);\n\n        return ($configuredKeyValue === $default)\n            ? $default\n            : str_replace('~', config('home_dir'), $configuredKeyValue);\n    }\n\n    private function getDatabaseName(string $key, $default)\n    {\n        return str_replace('-', '_', $this->get($key, $default));\n    }\n\n    private function getProjectURL(): string\n    {\n        return sprintf(\n            'http%s://%s.%s',\n            config('lambo.store.valet_secure') ? 's' : '',\n            config('lambo.store.project_name'),\n            config('lambo.store.tld')\n        );\n    }\n\n    private function getMigrateDatabase(string $key, $default)\n    {\n        if ($this->commandLineConfiguration->inertia || $this->commandLineConfiguration->livewire) {\n            return true;\n        }\n\n        return $this->get($key, $default);\n    }\n\n    private function getWithOutput(string $key, $default): bool\n    {\n        if ($this->consoleWriter->getVerbosity() > SymfonyStyle::VERBOSITY_NORMAL) {\n            return true;\n        }\n\n        return $this->get($key, $default);\n    }\n\n    private function getBreeze(string $key, $default)\n    {\n        $this->ensureOnlyOneStarterKitSelected();\n\n        if (! Arr::has($this->commandLineInput, 'breeze')) {\n            return false;\n        }\n\n        config(['lambo.store.jetstream' => false]);\n\n        return in_array($this->commandLineInput['breeze'], InstallBreeze::VALID_STACKS)\n            ? $this->commandLineInput['breeze']\n            : $this->configureBreezeStack();\n    }\n\n    private function getJetstream(string $key, $default)\n    {\n        $this->ensureOnlyOneStarterKitSelected();\n\n        if (! Arr::has($this->commandLineInput, 'jetstream')) {\n            return false;\n        }\n\n        config(['lambo.store.breeze' => false]);\n\n        return in_array($this->commandLineInput['jetstream'], InstallJetstream::VALID_CONFIGURATIONS)\n            ? $this->commandLineInput['jetstream']\n            : $this->configureJetstreamStack();\n    }\n\n    private function configureBreezeStack(): string\n    {\n        $this->consoleWriter->note(\"Laravel Breeze does not provide a <fg=yellow>'{$this->commandLineInput['breeze']}'</> front-end.\");\n        $choice = $this->consoleWriter->choice('Please choose one of the following', array_keys(InstallBreeze::VALID_STACKS));\n        $this->consoleWriter->ok(\"Using Laravel Breeze with a {$choice} front-end.\");\n\n        return Str::lower($choice);\n    }\n\n    private function configureJetstreamStack(): string\n    {\n        $this->consoleWriter->note(\"<fg=yellow>'{$this->commandLineInput['jetstream']}'</> is not a valid Laravel Jetstream configuration.\");\n        $stack = $this->consoleWriter->choice('Please choose a front-end', array_keys(InstallJetstream::VALID_STACKS));\n        $teams = $this->consoleWriter->confirm('Would you like to use teams?');\n        $this->consoleWriter->ok(sprintf('Using %s%s.', $stack, $teams ? ' and teams' : ' without teams'));\n\n        return InstallJetstream::VALID_STACKS[$stack] . ($teams ? ',teams' : '');\n    }\n\n    private function ensureOnlyOneStarterKitSelected(): void\n    {\n        if (Arr::has($this->commandLineInput, ['breeze', 'jetstream'])) {\n            $this->consoleWriter->newLine();\n            $this->consoleWriter->note('Only one starter-kit may be configured.');\n\n            $choice = $this->consoleWriter->choice('Please choose a starter-kit:', [\n                'None',\n                'Laravel Breeze',\n                'Laravel Jetstream',\n            ], 0);\n\n            switch ($choice) {\n                case 'Laravel Breeze':\n                    unset($this->commandLineConfiguration->jetstream);\n                    Arr::forget($this->commandLineInput, 'jetstream');\n                    $this->consoleWriter->ok('Using Laravel Breeze');\n                    break;\n                case 'Laravel Jetstream':\n                    Arr::forget($this->commandLineInput, 'breeze');\n                    unset($this->commandLineConfiguration->breeze);\n                    $this->consoleWriter->ok('Using Laravel Jetstream');\n                    break;\n                case 'None':\n                    Arr::forget($this->commandLineInput, 'jetstream');\n                    unset($this->commandLineConfiguration->jetstream);\n                    Arr::forget($this->commandLineInput, 'breeze');\n                    unset($this->commandLineConfiguration->breeze);\n                    $this->consoleWriter->ok('Skipping starter-kit installation.');\n                    break;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/Configuration/ShellConfiguration.php",
    "content": "<?php\n\nnamespace App\\Configuration;\n\nclass ShellConfiguration extends LamboConfiguration\n{\n    protected function getSettings(): array\n    {\n        return $_SERVER;\n    }\n}\n"
  },
  {
    "path": "app/ConsoleWriter.php",
    "content": "<?php\n\nnamespace App;\n\nuse Illuminate\\Console\\OutputStyle;\nuse Symfony\\Component\\Process\\Process;\n\nclass ConsoleWriter extends OutputStyle\n{\n    public const BLUE = 'fg=blue';\n    public const GREEN = 'fg=green';\n    public const RED = 'fg=red';\n    public const MAGENTA = 'fg=magenta';\n\n    public static function formatString(string $string, string $format): string\n    {\n        return \"<{$format}>{$string}</>\";\n    }\n\n    public function panel(string $prefix, string $message, string $style)\n    {\n        parent::block($message, $prefix, $style, ' ', true, false);\n    }\n\n    public function sectionTitle($sectionTitle)\n    {\n        $this->newLine();\n        $this->text([\n            \"<fg=yellow;bg=default>{$sectionTitle}</>\",\n            '<fg=yellow;bg=default>' . str_repeat('#', strlen($sectionTitle)) . '</>',\n        ]);\n    }\n\n    public function logStep($message)\n    {\n        parent::block($message, null, 'fg=yellow;bg=default', ' // ', false, false);\n    }\n\n    public function exec(string $command)\n    {\n        $this->labeledLine('EXEC', $command, 'bg=blue;fg=black');\n    }\n\n    public function success($message, $label = 'PASS'): void\n    {\n        $this->labeledLine($label, $message, 'fg=black;bg=green');\n    }\n\n    public function ok($message): void\n    {\n        $this->success($message, ' OK ');\n    }\n\n    public function note($message, $label = 'NOTE'): void\n    {\n        $this->labeledLine($label, $message, 'fg=black;bg=yellow');\n    }\n\n    public function warn($message, $label = 'WARN'): void\n    {\n        $this->labeledLine($label, \"<fg=red;bg=default>{$message}</>\", 'fg=black;bg=red');\n    }\n\n    public function warnCommandFailed($command): void\n    {\n        $this->warn(\"Failed to run {$command}\");\n    }\n\n    public function showOutputErrors(string $errors)\n    {\n        parent::text([\n            '<fg=red;bg=default>--------------------------------------------------------------------------------',\n            str_replace(PHP_EOL, PHP_EOL . ' ', trim($errors)),\n            '--------------------------------------------------------------------------------</>',\n        ]);\n    }\n\n    public function showOutput(string $errors)\n    {\n        parent::text([\n            '--------------------------------------------------------------------------------',\n            str_replace(PHP_EOL, PHP_EOL . ' ', trim($errors)),\n            '--------------------------------------------------------------------------------',\n        ]);\n    }\n\n    public function exception($message)\n    {\n        parent::block($message, null, 'fg=black;bg=red', ' ', true, false);\n    }\n\n    public function text($message)\n    {\n        parent::text($message);\n    }\n\n    public function listing(array $items): void\n    {\n        parent::newLine();\n        $text = collect($items)->map(function ($dependency) {\n            return '  - ' . $dependency;\n        })->toArray();\n        parent::text($text);\n        parent::newLine();\n    }\n\n    public function table(array $columnHeadings, array $rowData)\n    {\n        parent::table($columnHeadings, $rowData);\n    }\n\n    public function consoleOutput(string $line, $type)\n    {\n        if (config('lambo.store.with_output')) {\n            ($type === Process::ERR)\n                ? $this->labeledLine('!️', '┃ ' . $line, 'fg=yellow')\n                : $this->labeledLine('✓︎', '┃ ' . $line, 'fg=green;');\n        }\n    }\n\n    public function labeledLine(string $label, string $message, string $labelFormat = 'fg=default;bg=default', int $indentColumns = 0): void\n    {\n        $indent = str_repeat(' ', $indentColumns);\n        $this->isDecorated()\n            ? parent::text(\"{$indent}<{$labelFormat}> {$label} </> {$message}\")\n            : parent::text(\"{$indent}[ {$label} ] {$message}\");\n    }\n}\n"
  },
  {
    "path": "app/Environment.php",
    "content": "<?php\n\nnamespace App;\n\nclass Environment\n{\n    public static function isMac(): bool\n    {\n        return PHP_OS === 'Darwin';\n    }\n}\n"
  },
  {
    "path": "app/Helpers/GetTimezone.php",
    "content": "<?php\n\nnamespace App\\Helpers;\n\nuse IntlTimeZone;\n\nclass GetTimezone\n{\n    public function __invoke(): string\n    {\n        if ($timezone = ini_get('date.timezone')) {\n            return $timezone;\n        }\n\n        return IntlTimeZone::createDefault()->getID();\n    }\n}\n"
  },
  {
    "path": "app/LamboException.php",
    "content": "<?php\n\nnamespace App;\n\nuse Exception;\n\nclass LamboException extends Exception\n{\n}\n"
  },
  {
    "path": "app/LogsToConsole.php",
    "content": "<?php\n\nnamespace App;\n\ntrait LogsToConsole\n{\n    public function alert(string $message)\n    {\n        app('console')->alert($message);\n    }\n\n    public function warn(string $message)\n    {\n        app('console')->warn($message);\n    }\n\n    public function error(string $message)\n    {\n        app('console')->error($message);\n    }\n\n    public function line(string $message)\n    {\n        app('console')->line($message);\n    }\n\n    public function info(string $message)\n    {\n        app('console')->info($message);\n    }\n}\n"
  },
  {
    "path": "app/Options.php",
    "content": "<?php\n\nnamespace App;\n\nclass Options\n{\n    protected $options = [\n        /** Parameters first, then flags */\n        [\n            'short' => 'e',\n            'long' => 'editor',\n            'param_description' => 'EDITOR',\n            'cli_description' => \"Specify an editor to run <info>'EDITOR .'</info> with after\",\n        ],\n        [\n            'short' => 'p',\n            'long' => 'path',\n            'param_description' => 'PATH',\n            'cli_description' => 'Customize the path in which the new project will be created',\n        ],\n        [\n            'short' => 'm',\n            'long' => 'message',\n            'param_description' => 'MESSAGE',\n            'cli_description' => 'Customize the initial commit message (wrap with quotes!)',\n        ],\n        [\n            'short' => 'g',\n            'long' => 'github',\n            'cli_description' => 'Initialize a new private GitHub repository',\n        ],\n        [\n            'long' => 'gh-public',\n            'cli_description' => 'Make the new GitHub repository public',\n        ],\n        [\n            'long' => 'gh-description',\n            'param_description' => 'DESCRIPTION',\n            'cli_description' => 'Initialize the new GitHub repository with the provided <info>DESCRIPTION</info>',\n        ],\n        [\n            'long' => 'gh-homepage',\n            'param_description' => 'URL',\n            'cli_description' => 'Initialize the new GitHub repository with the provided homepage <info>URL</info>',\n        ],\n        [\n            'long' => 'gh-org',\n            'param_description' => 'ORG',\n            'cli_description' => 'Initialize the new GitHub repository for <info>ORG</info>/project',\n        ],\n        [\n            'long' => 'breeze',\n            'param_description' => 'STACK',\n            'cli_description' => 'Use the Laravel Breeze starter kit. <info>STACK</info> may be either <info>blade</info>, <info>vue</info> or <info>react</info>.',\n        ],\n        [\n            'long' => 'jetstream',\n            'param_description' => 'STACK[,teams]',\n            'cli_description' => 'Use the Laravel Jetstream starter kit. <info>STACK</info> may be either <info>inertia</info> or <info>livewire</info>.' ,\n        ],\n        [\n            'short' => 'b',\n            'long' => 'browser',\n            'param_description' => 'BROWSER',\n            'cli_description' => 'Open the site in the specified <info>BROWSER</info>. E.g. <info>firefox</info>',\n        ],\n        [\n            'long' => 'dbhost',\n            'param_description' => 'HOST',\n            'cli_description' => 'Specify the database <info>HOST</info>',\n        ],\n        [\n            'long' => 'dbport',\n            'param_description' => 'PORT',\n            'cli_description' => 'Specify the database <info>PORT</info>',\n        ],\n        [\n            'long' => 'dbname',\n            'param_description' => 'NAME',\n            'cli_description' => 'Specify the database <info>NAME</info>',\n        ],\n        [\n            'long' => 'dbuser',\n            'param_description' => 'USERNAME',\n            'cli_description' => 'Specify the database <info>USERNAME</info>',\n        ],\n        [\n            'long' => 'dbpassword',\n            'param_description' => 'PASSWORD',\n            'cli_description' => 'Specify the database <info>PASSWORD</info>',\n        ],\n        [\n            'long' => 'create-db',\n            'cli_description' => 'Create a new MySQL database',\n        ],\n        [\n            'short' => 'f',\n            'long' => 'force',\n            'cli_description' => 'Force install even if the directory already exists',\n        ],\n        [\n            'long' => 'migrate-db',\n            'cli_description' => 'Run database migrations',\n        ],\n        [\n            'short' => 'l',\n            'long' => 'link',\n            'cli_description' => 'Create a Valet link to the project directory',\n        ],\n        [\n            'short' => 's',\n            'long' => 'secure',\n            'cli_description' => 'Generate and use an HTTPS cert with Valet',\n        ],\n        [\n            'short' => 'd',\n            'long' => 'dev',\n            'cli_description' => 'Install Laravel using the develop branch',\n        ],\n        [\n            'long' => 'full',\n            'cli_description' => 'Shortcut of --create-db --migrate-db --link --secure',\n        ],\n        [\n            'short' => 'q',\n            'long' => 'quiet',\n            'cli_description' => 'Do not output to the console (except for user input)',\n        ],\n    ];\n\n    public function all(): array\n    {\n        return $this->options;\n    }\n}\n"
  },
  {
    "path": "app/Providers/AppServiceProvider.php",
    "content": "<?php\n\nnamespace App\\Providers;\n\nuse Illuminate\\Support\\ServiceProvider;\n\nclass AppServiceProvider extends ServiceProvider\n{\n    /**\n     * Bootstrap any application services.\n     *\n     * @return void\n     */\n    public function boot()\n    {\n        if (function_exists('posix_getuid')) {\n            // Mac or Linux\n            $path = posix_getpwuid(posix_getuid())['dir'];\n        } else {\n            // Windows\n            $path = exec('echo %USERPROFILE%');\n        }\n\n        config()->set([\n            'home_dir' => $path,\n        ]);\n    }\n\n    /**\n     * Register any application services.\n     *\n     * @return void\n     */\n    public function register()\n    {\n        //\n    }\n}\n"
  },
  {
    "path": "app/Shell.php",
    "content": "<?php\n\nnamespace App;\n\nuse Illuminate\\Contracts\\Config\\Repository;\nuse Symfony\\Component\\Process\\Process;\n\nclass Shell\n{\n    protected $rootPath;\n    protected $projectPath;\n    protected $consoleWriter;\n\n    private $useTTY = false;\n\n    public function __construct(Repository $config, ConsoleWriter $consoleWriter)\n    {\n        $this->rootPath = $config->get('lambo.store.root_path');\n        $this->projectPath = $config->get('lambo.store.project_path');\n        $this->consoleWriter = $consoleWriter;\n    }\n\n    public function execInRoot($command)\n    {\n        return $this->exec(\"cd {$this->rootPath} && $command\");\n    }\n\n    public function execInProject($command)\n    {\n        return $this->exec(\"cd {$this->projectPath} && $command\");\n    }\n\n    public function execIn(string $directory, string $command)\n    {\n        return $this->exec(\"cd {$directory} && $command\");\n    }\n\n    public function exec(string $command)\n    {\n        $this->consoleWriter->exec($command);\n\n        $process = Process::fromShellCommandline($command)\n            ->setTty($this->useTTY)\n            ->setTimeout(null)\n            ->enableOutput();\n        $process->run(function ($type, $buffer) {\n            if (empty(trim($buffer)) || $buffer === PHP_EOL) {\n                return;\n            }\n\n            foreach (explode(PHP_EOL, trim($buffer)) as $line) {\n                $this->consoleWriter->consoleOutput($line, $type);\n            }\n        });\n        $this->useTTY = false;\n\n        return $process;\n    }\n\n    public function execQuietly(string $command)\n    {\n        $process = Process::fromShellCommandline($command)\n            ->setTimeout(null)\n            ->enableOutput();\n\n        $process->run();\n\n        return $process;\n    }\n\n    public function withTTY()\n    {\n        $this->useTTY = true;\n        return $this;\n    }\n}\n"
  },
  {
    "path": "app/Tools/Database.php",
    "content": "<?php\n\nnamespace App\\Tools;\n\nuse PDO;\n\nclass Database\n{\n    private $dsn;\n    private $username;\n    private $password;\n\n    public function fill(string $type, string $host, $port, string $username, $password): self\n    {\n        $this->dsn = \"{$type}:host={$host};port={$port}\";\n        $this->username = $username;\n        $this->password = $password;\n\n        return $this;\n    }\n\n    public function fillFromUrl(string $url): self\n    {\n        $url = parse_url($url);\n\n        return $this->fill($url['scheme'], $url['host'], $url['port'], $url['user'], $url['pass']);\n    }\n\n    public function fillFromLamboStore(array $store): self\n    {\n        return $this->fill(\n            $type = 'mysql',\n            $host = $store['database_host'],\n            $port = $store['database_port'],\n            $username = $store['database_username'],\n            $password = $store['database_password']\n        );\n    }\n\n    public function ensureExists(string $databaseName = null)\n    {\n        $dsn = is_null($databaseName)\n            ? $this->dsn\n            : \"{$this->dsn};dbname={$databaseName}\";\n\n        new PDO($dsn, $this->username, $this->password);\n    }\n\n    public function create(string $databaseName)\n    {\n        $connection = new PDO($this->dsn, $this->username, $this->password);\n        return $connection->exec(\"CREATE DATABASE IF NOT EXISTS {$databaseName};\") === 1;\n    }\n}\n"
  },
  {
    "path": "bin/testLambo.sh",
    "content": "#!/usr/bin/env bash\n\n# ------------------------------------------------------------------------------\n# THIS SCRIPT WILL ONLY WORK ON *NIX SYSTEMS. SORRY WINDOWS USERS.\n#\n# Manual testing script that speeds things up by copying a \"template\"\n# installation of Laravel rather than running the Laravel Installer every time.\n# Useful when running lambo over and over during testing.\n#\n# NOTE: on first run since boot the script will install a fresh version of the\n# Laravel template.\n#\n# 1. Comment out the following in app/Commands/NewCommand:\n#    app(VerifyPathAvailable::class)()\n#    app(InstallLaravel::class)()\n#\n# 2. run this script passing any regular lambo flags and/or options while\n#    omitting new, Example:\n#\n#   /path/to/testLambo.sh my-project [other lambo flags]\n# ------------------------------------------------------------------------------\n\nDEBUG=false\n\nif [ \"$#\" -lt \"1\" ]; then\n  echo \"usage: $0 name [regular lambo parameters]\"\n  exit\nfi\n\nSTART_DIR=$(pwd)\ncd $(dirname $0)\nSCRIPT_PATH=$(pwd)\n\nNAME=$1\nshift\n\nTEST_DIR=\"/tmp/lambo\"\nTEMPLATE_NAME=\"template\"\nPROJECT_PATH=\"$TEST_DIR/$NAME\"\nTEMPLATE_PATH=\"$TEST_DIR/$TEMPLATE_NAME\"\n\nif [ \"$DEBUG\" = true ]; then\n  echo \" [INFO] Start directory: $START_DIR\"\n  echo \" [INFO] Script path: $SCRIPT_PATH\"\n  echo \" [INFO] Test directory: $TEST_DIR\"\n  echo \" [INFO] Project name: $NAME\"\n  echo \" [INFO] Project path: $PROJECT_PATH\"\n  echo \" [INFO] Template name: $TEMPLATE_NAME\"\n  echo \" [INFO] Template path: $TEMPLATE_PATH\"\nfi\n\n# Create test directory\nif [ ! -d \"$TEST_DIR\" ]; then\n  echo \"*[WARN] Test directory '$TEST_DIR' does not exist, creating it now…\"\n  mkdir $TEST_DIR\nelse\n  echo \" [INFO] Using test directory '$TEST_DIR'\"\nfi\n\n# Create template Laravel installation\nif [ ! -d \"$TEMPLATE_PATH\" ]; then\n  echo \"*[WARN] Laravel template '$TEMPLATE_PATH' does not exist, creating it now…\"\n  cd $TEST_DIR\n  composer create-project laravel/laravel $TEMPLATE_NAME --remove-vcs --prefer-dist --quiet\n  cd $START_DIR\n  echo \" [INFO] Created template '$TEMPLATE_PATH'\"\nelse\n  echo \" [INFO] Using template '$TEMPLATE_PATH'\"\nfi\n\n# remove previous run.\nif [ -f \"$TEST_DIR/.last-run\" ]; then\n  last_run=$(cat $TEST_DIR/.last-run)\n  rm -rf $last_run\n  echo \" [INFO] Deleted previous run '$last_run'\"\nfi\n\ncp -r $TEMPLATE_PATH $PROJECT_PATH\necho \" [INFO] Copied laravel template '$TEMPLATE_PATH' to '$PROJECT_PATH'\"\n\ncd $TEST_DIR\necho $PROJECT_PATH > .last-run\n\ncd $SCRIPT_PATH\necho \" [INFO] Running Lambo…\"\necho\necho \"lambo new $NAME --path $TEST_DIR $*\"\n../lambo new $NAME --path $TEST_DIR $*\n"
  },
  {
    "path": "bootstrap/app.php",
    "content": "<?php\n\n/*\n|--------------------------------------------------------------------------\n| Create The Application\n|--------------------------------------------------------------------------\n|\n| The first thing we will do is create a new Laravel application instance\n| which serves as the \"glue\" for all the components of Laravel, and is\n| the IoC container for the system binding all of the various parts.\n|\n*/\n\n$app = new LaravelZero\\Framework\\Application(\n    dirname(__DIR__)\n);\n\n/*\n|--------------------------------------------------------------------------\n| Bind Important Interfaces\n|--------------------------------------------------------------------------\n|\n| Next, we need to bind some important interfaces into the container so\n| we will be able to resolve them when needed. The kernels serve the\n| incoming requests to this application from both the web and CLI.\n|\n*/\n\n$app->singleton(\n    Illuminate\\Contracts\\Console\\Kernel::class,\n    LaravelZero\\Framework\\Kernel::class\n);\n\n$app->singleton(\n    Illuminate\\Contracts\\Debug\\ExceptionHandler::class,\n    Illuminate\\Foundation\\Exceptions\\Handler::class\n);\n\n/*\n|--------------------------------------------------------------------------\n| Return The Application\n|--------------------------------------------------------------------------\n|\n| This script returns the application instance. The instance is given to\n| the calling script so we can separate the building of the instances\n| from the actual running of the application and sending responses.\n|\n*/\n\nreturn $app;\n"
  },
  {
    "path": "box.json",
    "content": "{\n    \"chmod\": \"0755\",\n    \"directories\": [\n        \"app\",\n        \"bootstrap\",\n        \"config\",\n        \"stubs\",\n        \"vendor\"\n    ],\n    \"files\": [\n        \"composer.json\"\n    ],\n    \"exclude-dev-files\": false,\n    \"exclude-composer-files\": false,\n    \"compression\": \"GZ\",\n    \"compactors\": [\n        \"KevinGH\\\\Box\\\\Compactor\\\\Php\",\n        \"KevinGH\\\\Box\\\\Compactor\\\\Json\"\n    ]\n}\n"
  },
  {
    "path": "composer.json",
    "content": "{\n    \"name\": \"tightenco/lambo\",\n    \"description\": \"Super-powered 'laravel new' with Laravel and Valet.\",\n    \"keywords\": [\n        \"laravel\",\n        \"zonda\",\n        \"wwdhhd\",\n        \"lambo\"\n    ],\n    \"type\": \"project\",\n    \"license\": \"MIT\",\n    \"authors\": [\n        {\n            \"name\": \"Matt Stauffer\",\n            \"email\": \"matt@tighten.co\"\n        },\n        {\n            \"name\": \"Jon Sugar\"\n        }\n    ],\n    \"require\": {\n        \"php\": \"^8.0\",\n        \"ext-json\": \"*\",\n        \"ext-pdo\": \"*\",\n        \"ext-intl\": \"*\"\n    },\n    \"require-dev\": {\n        \"laravel-zero/framework\": \"^10.0\",\n        \"mockery/mockery\": \"^1.0\",\n        \"pestphp/pest\": \"^2.5\",\n        \"spatie/laravel-ray\": \"^1.17\",\n        \"tightenco/duster\": \"^1.0\"\n    },\n    \"autoload\": {\n        \"psr-4\": {\n            \"App\\\\\": \"app/\"\n        }\n    },\n    \"autoload-dev\": {\n        \"psr-4\": {\n            \"Tests\\\\\": \"tests/\"\n        }\n    },\n    \"config\": {\n        \"preferred-install\": \"dist\",\n        \"sort-packages\": true,\n        \"optimize-autoloader\": true,\n        \"allow-plugins\": {\n            \"dealerdirect/phpcodesniffer-composer-installer\": true,\n            \"pestphp/pest-plugin\": true\n        }\n    },\n    \"minimum-stability\": \"dev\",\n    \"prefer-stable\": true,\n    \"bin\": [\"builds/lambo\"]\n}\n"
  },
  {
    "path": "config/app.php",
    "content": "<?php\n\nuse App\\Helpers\\GetTimezone;\n\nreturn [\n\n    /*\n    |--------------------------------------------------------------------------\n    | Application Name\n    |--------------------------------------------------------------------------\n    |\n    | This value is the name of your application. This value is used when the\n    | framework needs to place the application's name in a notification or\n    | any other location as required by the application or its packages.\n    |\n    */\n\n    'name' => 'Lambo',\n\n    /*\n    |--------------------------------------------------------------------------\n    | Application Version\n    |--------------------------------------------------------------------------\n    |\n    | This value determines the \"version\" your application is currently running\n    | in. You may want to follow the \"Semantic Versioning\" - Given a version\n    | number MAJOR.MINOR.PATCH when an update happens: https://semver.org.\n    |\n    */\n\n    'version' => app('git.version'),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Application Environment\n    |--------------------------------------------------------------------------\n    |\n    | This value determines the \"environment\" your application is currently\n    | running in. This may determine how you prefer to configure various\n    | services your application utilizes. Should be true in production.\n    |\n    */\n\n    'env' => 'development',\n\n    /*\n    |--------------------------------------------------------------------------\n    | Timezone\n    |--------------------------------------------------------------------------\n    |\n    | This value determines the timezone that Lambo is currently running in.\n    | It will be the value of \"date.timezone\" in php.ini or the value set\n    | by your operating system. If php cannot determine the value set\n    | by your operating system, then UTC will be used. You may set\n    | this value manually to use a timezone of your choosing.\n    */\n    'timezone' => app(GetTimezone::class)(),\n\n    /*\n    |--------------------------------------------------------------------------\n    | Autoloaded Service Providers\n    |--------------------------------------------------------------------------\n    |\n    | The service providers listed here will be automatically loaded on the\n    | request to your application. Feel free to add your own services to\n    | this array to grant expanded functionality to your applications.\n    |\n    */\n\n    'providers' => [\n        App\\Providers\\AppServiceProvider::class,\n    ],\n\n];\n"
  },
  {
    "path": "config/commands.php",
    "content": "<?php\n\nreturn [\n\n    /*\n    |--------------------------------------------------------------------------\n    | Default Command\n    |--------------------------------------------------------------------------\n    |\n    | Laravel Zero will always run the command specified below when no command name is\n    | provided. Consider update the default command for single command applications.\n    | You cannot pass arguments to the default command because they are ignored.\n    |\n    */\n\n    'default' => App\\Commands\\HelpCommand::class,\n\n    /*\n    |--------------------------------------------------------------------------\n    | Commands Paths\n    |--------------------------------------------------------------------------\n    |\n    | This value determines the \"paths\" that should be loaded by the console's\n    | kernel. Foreach \"path\" present on the array provided below the kernel\n    | will extract all \"Illuminate\\Console\\Command\" based class commands.\n    |\n    */\n\n    'paths' => [app_path('Commands')],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Added Commands\n    |--------------------------------------------------------------------------\n    |\n    | You may want to include a single command class without having to load an\n    | entire folder. Here you can specify which commands should be added to\n    | your list of commands. The console's kernel will try to load them.\n    |\n    */\n\n    'add' => [\n        // ..\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Hidden Commands\n    |--------------------------------------------------------------------------\n    |\n    | Your application commands will always be visible on the application list\n    | of commands. But you can still make them \"hidden\" specifying an array\n    | of commands below. All \"hidden\" commands can still be run/executed.\n    |\n    */\n\n    'hidden' => [\n        NunoMaduro\\LaravelConsoleSummary\\SummaryCommand::class,\n        Symfony\\Component\\Console\\Command\\HelpCommand::class,\n        Illuminate\\Console\\Scheduling\\ScheduleRunCommand::class,\n        Illuminate\\Console\\Scheduling\\ScheduleFinishCommand::class,\n        Illuminate\\Foundation\\Console\\VendorPublishCommand::class,\n    ],\n\n    /*\n    |--------------------------------------------------------------------------\n    | Removed Commands\n    |--------------------------------------------------------------------------\n    |\n    | Do you have a service provider that loads a list of commands that\n    | you don't need? No problem. Laravel Zero allows you to specify\n    | below a list of commands that you don't to see in your app.\n    |\n    */\n\n    'remove' => [\n        Symfony\\Component\\Console\\Command\\HelpCommand::class,\n    ],\n\n];\n"
  },
  {
    "path": "lambo",
    "content": "#!/usr/bin/env php\n<?php\n\ndefine('LARAVEL_START', microtime(true));\n\n/*\n|--------------------------------------------------------------------------\n| Register The Auto Loader\n|--------------------------------------------------------------------------\n|\n| Composer provides a convenient, automatically generated class loader\n| for our application. We just need to utilize it! We'll require it\n| into the script here so that we do not have to worry about the\n| loading of any our classes \"manually\". Feels great to relax.\n|\n*/\n\n$autoloader = require file_exists(__DIR__ . '/vendor/autoload.php') ? __DIR__ . '/vendor/autoload.php' : __DIR__ . '/../../autoload.php';\n\n$app = require_once __DIR__ . '/bootstrap/app.php';\n\n/*\n|--------------------------------------------------------------------------\n| Run The Artisan Application\n|--------------------------------------------------------------------------\n|\n| When we run the console application, the current CLI command will be\n| executed in this console and the response sent back to a terminal\n| or another output device for the developers. Here goes nothing!\n|\n*/\n\n$kernel = $app->make(Illuminate\\Contracts\\Console\\Kernel::class);\n\n$app->bind('Symfony\\Component\\Console\\Output\\ConsoleOutput', function () {\n    return new Symfony\\Component\\Console\\Output\\ConsoleOutput;\n});\n\n\n$status = $kernel->handle(\n    $input = new Symfony\\Component\\Console\\Input\\ArgvInput,\n    $app->make('Symfony\\Component\\Console\\Output\\ConsoleOutput')\n);\n\n/*\n|--------------------------------------------------------------------------\n| Shutdown The Application\n|--------------------------------------------------------------------------\n|\n| Once Artisan has finished running, we will fire off the shutdown events\n| so that any final work may be done by the application before we shut\n| down the process. This is the last thing to happen to the request.\n|\n*/\n\n$kernel->terminate($input, $status);\n\nexit($status);\n"
  },
  {
    "path": "phpunit.xml.dist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<phpunit xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:noNamespaceSchemaLocation=\"./vendor/phpunit/phpunit/phpunit.xsd\"\n         bootstrap=\"vendor/autoload.php\"\n         colors=\"true\"\n>\n    <testsuites>\n        <testsuite name=\"Feature\">\n            <directory suffix=\"Test.php\">./tests/Feature</directory>\n        </testsuite>\n        <testsuite name=\"Unit\">\n            <directory suffix=\"Test.php\">./tests/Unit</directory>\n        </testsuite>\n    </testsuites>\n    <source>\n        <include>\n            <directory suffix=\".php\">./app</directory>\n        </include>\n    </source>\n</phpunit>"
  },
  {
    "path": "readme.md",
    "content": "![Lambo logo](https://raw.githubusercontent.com/tighten/lambo/main/lambo-banner.png)\n\n[![Run tests](https://github.com/tighten/takeout/workflows/Run%20tests/badge.svg?branch=main)](https://github.com/tighten/lambo/actions?query=workflow%3A%22Run+Tests%22)\n\n**Super-powered `laravel new` for Laravel and Valet**\n\nLambo is a command-line tool that replaces the Laravel installer and wraps up the most common tasks you might take when creating a Laravel app: opening it in your editor and your browser, initialize a git repository, tweak your `.env` and `.env.example`, and more.\n\n\n# Requirements\n\n- PHP 7.3+\n- (optional, but beneficial) [Laravel Valet](https://laravel.com/docs/valet)\n\n# Installation\n\n```bash\ncomposer global require tightenco/lambo:^3.0\n```\n\n# Upgrading\n\n```bash\ncomposer global update tightenco/lambo\n```\n\n# Usage\n\nMake sure `~/.composer/vendor/bin` is in your terminal's path.\n\n```bash\ncd ~/Sites\nlambo new myNextProject\n```\n\n# What exactly does it do?\n\n- `laravel new $PROJECTNAME`\n- Initialize a git repo, add all the files, and, after some changes below, make a commit with the text \"Initial commit.\"\n- Replace the `.env` (and `.env.example`) database credentials with the default macOS MySQL credentials: database of `$PROJECTNAME`, user `root`, and empty password\n- Replace the `.env` (and `.env.example`) `APP_URL` with `$PROJECTNAME.$YOURVALETTLD`\n- Generate an app key\n- Open the project in your favorite editor\n- Open `$PROJECTNAME.$YOURVALETTLD` in your browser\n\n> Note: If your `$PROJECTNAME` has dashes (`-`) in it, they will be replaced with underscores (`_`) in the database name.\n\nThere are also a few optional behaviors based on the parameters you pass (or define in your config file), including creating a database, migrating, installing Jetstream, running Valet Link and/or Secure, and running a custom bash script of your definition after the fact.\n\n# Customizing Lambo\n\nWhile the default actions Lambo provides are great, most users will want to customize at least a few of the steps. Thankfully, Lambo is built to be customized!\n\nThere are three ways to customize your usage of Lambo: command-line arguments, a config file, and an \"after\" file.\n\nMost users will want to set their preferred configuration options once and then never think about it again. That's best solved by creating a config file.\n\nBut if you find yourself needing to change the way you interact with Lambo on a project-by-project basis, you may also want to use the command-line parameters to customize Lambo when you're using it.\n\n## Creating a config file\n\nYou can create a config file at `~/.lambo/config` rather than pass the same arguments each time you create a new project.\n\nThe following command creates the file, if it doesn't exist, and edits it:\n\n```bash\nlambo edit-config\n```\n\nThe config file contains the configuration parameters you can customize, and will be read on every usage of Lambo.\n\n## Creating an \"after\" file\n\nYou can also create an after file at `~/.lambo/after` to run additional commands after you create a new project.\n\nThe following command creates the file, if it doesn't exist, and edits it:\n\n```bash\nlambo edit-after\n```\n\nThe after file is interpreted as a bash script, so you can include any commands here, such as installing additional composer dependencies...\n\n```bash\n# Install additional composer dependencies as you would from the command line.\necho \"Installing Composer Dependencies\"\ncomposer require tightenco/mailthief tightenco/quicksand\n```\n\n...or copying additional files to your new project.\n\n```bash\n# To copy standard files to new lambo project place them in ~/.lambo/includes directory.\necho \"Copying Include Files\"\ncp -R ~/.lambo/includes/ $PROJECTPATH\n```\n\nYou also have access to variables from your config file such as `$PROJECTPATH` and `$CODEEDITOR`.\n\n## Using command-line parameters\n\nAny command-line parameters passed in will override Lambo's defaults and your config settings. See a [full list of the parameters you can pass in](#parameters).\n\n# Lambo Commands\n\n- `help` or `help-screen` show the help screen\n\n<a id=\"config-files\"></a>\n- `edit-config` edits your config file (and creates one if it doesn't exist)\n\n  ```bash\n  lambo edit-config\n  ```\n\n- `edit-after` edits your \"after\" file (and creates one if it doesn't exist)\n\n  ```bash\n  lambo edit-after\n  ```\n\n\n<a id=\"parameters\"></a>\n# Configurable parameters\n\nYou can optionally pass one or more of these parameters every time you use Lambo. If you find yourself wanting to configure any of these settings every time you run Lambo, that's a perfect use for the [config files](#config-files).\n\n- `-e` or `--editor` to define your editor command. Whatever is passed here will be run as `$EDITOR .` after creating the project.\n\n  ```bash\n  # runs \"subl .\" in the project directory after creating the project\n  lambo new superApplication --editor=subl\n  ```\n\n- `-p` or `--path` to specify where to install the application.\n\n  ```bash\n  lambo new superApplication --path=~/Sites\n  ```\n\n- `-m` or `--message` to set the first Git commit message.\n\n  ```bash\n  lambo new superApplication --message=\"This lambo runs fast!\"\n  ```\n\n- `-f` or `--force` to force install even if the directory already exists \n\n  ```bash\n  # Creates a new Laravel application after deleting ~/Sites/superApplication  \n  lambo new superApplication --force\n  ```\n  \n- `-d` or `--dev` to choose the `develop` branch instead of `master`, getting the beta install.\n\n  ```bash\n  lambo new superApplication --dev\n  ```\n\n- `-b` or `--browser` to define which browser you want to open the project in.\n\n  ```bash\n  lambo new superApplication --browser=\"/Applications/Google Chrome Canary.app\"\n  ```\n\n- `-l` or `--link` to create a Valet link to the project directory.\n\n  ```bash\n  lambo new superApplication --link\n  ```\n\n- `-s` or `--secure` to secure the Valet site using https.\n\n  ```bash\n  lambo new superApplication --secure\n  ```\n\n- `--create-db` to create a new MySQL database which has the same name as your project.\n  This requires `mysql` command to be available on your system.\n\n  ```bash\n  lambo new superApplication --create-db\n  ```\n\n- `--migrate-db` to migrate your database.\n\n  ```bash\n  lambo new superApplication --migrate-db\n  ```\n\n- `--dbuser` to specify the database username.\n\n  ```bash\n  lambo new superApplication --dbuser=USER\n  ```\n\n- `--dbpassword` specify the database password.\n\n  ```bash\n  lambo new superApplication --dbpassword=SECRET\n  ```\n\n- `--dbhost` specify the database host.\n\n  ```bash\n  lambo new superApplication --dbhost=127.0.0.1\n  ```\n\n- `--breeze=STACK` to use the Laravel Breeze starter kit. `STACK` may be either `blade`, `vue` or `react`.\n\n  ```bash\n  lambo new superApplication --breeze=blade\n  lambo new superApplication --breeze=vue\n  lambo new superApplication --breeze=react\n  ```\n\n- `--jetstream=STACK[,teams]` to use the Laravel Jetstream starter kit. `STACK` may be either `inertia` or `livewire`.\n\n  ```bash\n  lambo new superApplication --jetstream=inertia\n  lambo new superApplication --jetstream=inertia,teams\n  lambo new superApplication --jetstream=livewire\n  lambo new superApplication --jetstream=livewire,teams\n  ```\n  \n- `--full` to use `--create-db`, `--migrate-db`, `--link`, and `-secure`.\n\n  ```bash\n  lambo new superApplication --full\n\n**GitHub Repository Creation**\n\n**Important:** To create new repositories Lambo requires one of the following tools to be installed:\n- the official [GitHub command line tool](https://github.com/cli/cli#installation)\n- the [hub command line tool](https://github.com/github/hub#installation)\n \nLambo will give you the option to continue without GitHub repository creation if neither tool is installed.\n\n- `-g` or `--github` to  Initialize a new private GitHub repository and push your new project to it.\n\n```bash\n# Repository created at https://github.com/<your_github_username>/superApplication\nlambo new superApplication --github\n```\n\n- Use `--gh-public` with `--github` to make the new GitHub repository public.\n\n```bash\nlambo new superApplication --github --gh-public\n```\n\n- Use `--gh-description` with `--github` to initialize the new GitHub repository with a description.\n\n```bash\nlambo new superApplication --github --gh-description='My super application'\n```\n- Use `--gh-homepage` with `--github` to initialize the new GitHub repository with a homepage url. \n\n```bash\nlambo new superApplication --github --gh-homepage=https://example.com\n```\n- Use `--gh-org` with `--github` to initialize the new GitHub repository with a specified organization.\n\n```bash\n# Repository created at https://github.com/acme/superApplication\nlambo new superApplication --github --gh-org=acme\n```\n\n-----\n\n# For contributors:\n\n## Process for release\n\nIf you're working with us and are assigned to push a release, here's the easiest process:\n\n1. Visit the [Lambo Releases page](https://github.com/tighten/lambo/releases); figure out what your next tag will be (increase the third number if it's a patch or fix; increase the second number if it's adding features)\n2. On your local machine, pull down the latest version of `main` (`git checkout main && git pull`)\n3. Build for the version you're targeting (`./lambo app:build`)\n4. Run the build once to make sure it works (`./builds/lambo`)\n5. Commit your build and push it up\n6. [Draft a new release](https://github.com/tighten/lambo/releases/new) with both the tag version and release title of your tag (e.g. `v1.5.1`)\n7. Set the body to be a bullet-point list with simple descriptions for each of the PRs merged, as well as the PR link in parentheses at the end. For example:\n\n    `- Add a superpower (#92)`\n8. Hit `Publish release`\n9. Profit\n\n## Notes for future development\n\n- All new configuration keys must be added to the `$newConfiguration` property in `UpgradeSavedConfiguration`\n- All removed or deprecated configuration keys must be added to the `$removedConfigurationKeys` property in `UpgradeSavedConfiguration`\n- Any time configuration keys are changed, the `$configurationVersion` property in `UpgradeSavedConfiguration` needs to be incremented\n"
  },
  {
    "path": "stubs/after",
    "content": "#!/usr/bin/env bash\n\n# Install additional composer dependencies as you would from the command line.\n# echo \"\n# Installing Composer Dependencies\n# \"\n# composer require tightenco/mailthief tightenco/quicksand\n\n# To copy standard files to new lambo project place them in ~/.lambo/includes directory.\n# echo \"\n# Copying Include Files\n# \"\n# cp -R ~/.lambo/includes/ $PROJECTPATH\n\n# To add a git commit after given modifications\n# echo \"\n# Committing after modifications to Git\n# \"\n# git add .\n# git commit -am \"Initialize Composer dependencies and additional files.\"\n"
  },
  {
    "path": "stubs/config",
    "content": "PROJECTPATH=\nMESSAGE=\"Initial commit.\"\nDEVELOP=false\nCODEEDITOR=\nBROWSER=\nLINK=false\nSECURE=false\nCREATE_DATABASE=false\nMIGRATE_DATABASE=false\nDB_HOST=127.0.0.1\nDB_PORT=\nDB_NAME=\nDB_USERNAME=root\nDB_PASSWORD=\n\n"
  },
  {
    "path": "tests/CreatesApplication.php",
    "content": "<?php\n\nnamespace Tests;\n\nuse Illuminate\\Contracts\\Console\\Kernel;\n\ntrait CreatesApplication\n{\n    /**\n     * Creates the application.\n     *\n     * @return \\Illuminate\\Foundation\\Application\n     */\n    public function createApplication()\n    {\n        $app = require __DIR__ . '/../bootstrap/app.php';\n\n        $app->make(Kernel::class)->bootstrap();\n\n        return $app;\n    }\n}\n"
  },
  {
    "path": "tests/Feature/CreateDatabaseTest.php",
    "content": "<?php\n\nnamespace Tests\\Feature;\n\nuse App\\Actions\\CreateDatabase;\nuse App\\Tools\\Database;\nuse Tests\\TestCase;\n\nclass CreateDatabaseTest extends TestCase\n{\n    private $database;\n\n    public function setUp(): void\n    {\n        parent::setUp();\n\n        $this->database = $this->mock(Database::class);\n    }\n\n    /** @test */\n    function it_creates_a_mysql_database()\n    {\n        $fakeStore = [\n            'create_database' => true,\n            'database_host' => 'example.test',\n            'database_port' => 3306,\n            'database_username' => 'user',\n            'database_password' => 'password',\n            'database_name' => 'foo',\n        ];\n\n        $this->database->shouldReceive('fillFromLamboStore')\n            ->with($fakeStore)\n            ->once()\n            ->andReturnSelf();\n\n        $this->database->shouldReceive('create')\n            ->once()\n            ->globally()\n            ->andReturnTrue()\n            ->ordered();\n\n        config(['lambo.store' => $fakeStore]);\n\n        app(CreateDatabase::class)();\n    }\n\n    /** @test */\n    function it_skips_database_creation()\n    {\n        $spy = $this->spy(Database::class);\n\n        config(['lambo.store.create_database' => false]);\n\n        config(['lambo.store.database_host' => 'example.test']);\n        config(['lambo.store.database_port' => 3306]);\n        config(['lambo.store.database_username' => 'user']);\n        config(['lambo.store.database_password' => 'password']);\n        config(['lambo.store.database_name' => 'foo']);\n\n        app(CreateDatabase::class)();\n\n        $spy->shouldNotHaveReceived('find');\n        $spy->shouldNotHaveReceived('createSchema');\n    }\n}\n"
  },
  {
    "path": "tests/Feature/CustomizeDotEnvTest.php",
    "content": "<?php\n\nnamespace Tests\\Feature;\n\nuse App\\Actions\\CustomizeDotEnv;\nuse Illuminate\\Support\\Facades\\File;\nuse Tests\\TestCase;\n\nclass CustomizeDotEnvTest extends TestCase\n{\n    /** @test */\n    function it_saves_the_customized_dot_env_files()\n    {\n        config(['lambo.store.project_name' => 'my-project']);\n        config(['lambo.store.database_name' => 'my_project']);\n        config(['lambo.store.project_url' => 'http://my-project.example.com']);\n        config(['lambo.store.database_username' => 'username']);\n        config(['lambo.store.database_password' => 'password']);\n        config(['lambo.store.project_path' => '/some/project/path']);\n\n        $originalDotEnv = File::get(base_path('tests/Feature/Fixtures/.env.original'));\n        $customizedDotEnv = File::get(base_path('tests/Feature/Fixtures/.env.customized'));\n\n        File::shouldReceive('get')\n            ->once()->with('/some/project/path/.env.example')\n            ->andReturn($originalDotEnv);\n\n        File::shouldReceive('put')\n            ->with('/some/project/path/.env.example', $customizedDotEnv);\n\n        File::shouldReceive('put')\n            ->with('/some/project/path/.env', $customizedDotEnv);\n\n        app(CustomizeDotEnv::class)();\n    }\n\n    /** @test */\n    function it_replaces_static_strings()\n    {\n        config()->set('lambo.store.database_username', 'root');\n\n        $customizeDotEnv = app(CustomizeDotEnv::class);\n        $contents = 'DB_USERNAME=previous';\n        $contents = $customizeDotEnv->customize($contents);\n        $this->assertEquals('DB_USERNAME=root', $contents);\n    }\n\n    /** @test */\n    function un_targeted_lines_are_unchanged()\n    {\n        config()->set('lambo.store.database_username', 'root');\n\n        $customizeDotEnv = app(CustomizeDotEnv::class);\n        $contents = \"DB_USERNAME=previous\\nDONT_TOUCH_ME=cant_touch_me\";\n        $contents = $customizeDotEnv->customize($contents);\n        $this->assertEquals(\"DB_USERNAME=root\\nDONT_TOUCH_ME=cant_touch_me\", $contents);\n    }\n\n    /** @test */\n    function lines_with_no_equals_are_unchanged()\n    {\n        $customizeDotEnv = app(CustomizeDotEnv::class);\n        $contents = \"SOME_VALUE=previous\\nABCDEFGNOEQUALS\";\n        $contents = $customizeDotEnv->customize($contents);\n        $this->assertEquals(\"SOME_VALUE=previous\\nABCDEFGNOEQUALS\", $contents);\n    }\n\n    /** @test */\n    function line_breaks_remain()\n    {\n        $customizeDotEnv = app(CustomizeDotEnv::class);\n        $contents = \"A=B\\n\\nC=D\";\n        $contents = $customizeDotEnv->customize($contents);\n        $this->assertEquals(\"A=B\\n\\nC=D\", $contents);\n    }\n}\n"
  },
  {
    "path": "tests/Feature/EditConfigFileTest.php",
    "content": "<?php\n\nnamespace Tests\\Feature;\n\nuse App\\Actions\\EditConfigFile;\nuse App\\LamboException;\nuse Illuminate\\Support\\Facades\\File;\nuse Tests\\Feature\\Fakes\\FakeProcess;\nuse Tests\\TestCase;\n\nclass EditConfigFileTest extends TestCase\n{\n    private $fileName;\n    private $configDirectory;\n    private $configFilePath;\n    private $fileTemplate;\n    public function setUp(): void\n    {\n        parent::setUp();\n\n        $this->fileName = 'my-config-file-name';\n        $homeDirectory = '/my/home/directory';\n        $this->configDirectory = \"{$homeDirectory}/.lambo\";\n        $this->configFilePath = \"{$this->configDirectory}/{$this->fileName}\";\n\n        config(['home_dir' => $homeDirectory]);\n        config(['lambo.store.editor' => 'vim']);\n\n        $this->fileTemplate = 'my-config-file-template';\n    }\n\n    /** @test */\n    function it_creates_the_config_directory_and_file_then_opens_the_file_for_editing()\n    {\n        $when = $and = $then = $this;\n\n        $when->the_config_directory_does_not_exist();\n        $and->the_config_directory_is_created();\n\n        $and->the_config_file_does_not_exist();\n        $and->the_config_file_is_created();\n\n        $then->the_config_file_is_opened_for_editing();\n\n        app(EditConfigFile::class)($this->fileName);\n    }\n\n    /** @test */\n    function it_creates_a_config_file_then_opens_the_file_for_editing()\n    {\n        $this->configDirectoryExists();\n        $this->configFileExists(false);\n        $this->successfullyCreateConfigFile();\n        $this->successfullyOpenInEditor();\n\n        app(EditConfigFile::class)($this->fileName);\n    }\n\n    /** @test */\n    function it_opens_a_config_file_for_editing()\n    {\n        $this->configDirectoryExists();\n        $this->configFileExists();\n        $this->successfullyOpenInEditor();\n\n        app(EditConfigFile::class)($this->fileName);\n    }\n\n    /** @test */\n    function it_throws_an_exception_if_the_configured_editor_fails_to_open()\n    {\n        $this->configDirectoryExists();\n        $this->configFileExists();\n        $this->successfullyOpenInEditor(false);\n\n        $this->expectException(LamboException::class);\n\n        app(EditConfigFile::class)($this->fileName);\n    }\n\n    /** @test */\n    function failing_to_create_the_configuration_directory_throws_an_exception()\n    {\n        $this->configDirectoryExists(false);\n        $this->successfullyCreateConfigDirectory(false);\n\n        $this->expectException(LamboException::class);\n\n        app(EditConfigFile::class)($this->fileName);\n    }\n\n    /** @test */\n    public function failing_to_create_the_configuration_file_throws_an_exception()\n    {\n        $this->configDirectoryExists();\n        $this->configFileExists(false);\n        $this->successfullyCreateConfigFile(false);\n\n        $this->expectException(LamboException::class);\n\n        app(EditConfigFile::class)($this->fileName);\n    }\n\n    private function configDirectoryExists(bool $exists = true): void\n    {\n        File::shouldReceive('isDirectory')\n            ->with($this->configDirectory)\n            ->once()\n            ->andReturn($exists)\n            ->globally()\n            ->ordered();\n    }\n\n    private function successfullyCreateConfigDirectory(bool $success = true): void\n    {\n        File::shouldReceive('makeDirectory')\n            ->with($this->configDirectory)\n            ->once()\n            ->andReturn($success)\n            ->globally()\n            ->ordered();\n    }\n\n    private function configFileExists(bool $success = true): void\n    {\n        File::shouldReceive('isFile')\n            ->with($this->configFilePath)\n            ->once()\n            ->andReturn($success)\n            ->globally()\n            ->ordered();\n    }\n\n    private function successfullyCreateConfigFile(bool $success = true): void\n    {\n        File::shouldReceive('get')->with(base_path(\"stubs/{$this->fileName }\"))\n            ->once()\n            ->andReturn($this->fileTemplate)\n            ->globally()\n            ->ordered();\n\n        File::shouldReceive('put')\n            ->with($this->configFilePath, $this->fileTemplate)\n            ->once()\n            ->andReturn($success)\n            ->globally()\n            ->ordered();\n    }\n\n    private function successfullyOpenInEditor(bool $success = true)\n    {\n        $this->shell->shouldReceive('withTTY')\n            ->once()\n            ->globally()\n            ->andReturnSelf()\n            ->ordered();\n\n        $command = \"vim {$this->fileName}\";\n        $expectation = $this->shell->shouldReceive('execIn')\n            ->with($this->configDirectory, $command)\n            ->once();\n\n        if ($success) {\n            $expectation->andReturn(FakeProcess::success());\n        } else {\n            $expectation->andReturn(FakeProcess::fail($command));\n        }\n\n        return $expectation->globally()->ordered();\n    }\n\n    private function the_config_directory_does_not_exist()\n    {\n        File::shouldReceive('isDirectory')\n            ->with($this->configDirectory)\n            ->once()\n            ->andReturn(false)\n            ->globally()\n            ->ordered();\n    }\n\n    private function the_config_directory_is_created()\n    {\n        File::shouldReceive('makeDirectory')\n            ->with($this->configDirectory)\n            ->once()\n            ->andReturn(true)\n            ->globally()\n            ->ordered();\n    }\n\n    private function the_config_file_does_not_exist()\n    {\n        File::shouldReceive('isFile')\n            ->with($this->configFilePath)\n            ->once()\n            ->andReturn(false)\n            ->globally()\n            ->ordered();\n    }\n\n    private function the_config_file_is_created()\n    {\n        File::shouldReceive('get')->with(base_path(\"stubs/{$this->fileName }\"))\n            ->once()\n            ->andReturn($this->fileTemplate)\n            ->globally()\n            ->ordered();\n\n        File::shouldReceive('put')\n            ->with($this->configFilePath, $this->fileTemplate)\n            ->once()\n            ->andReturn(true)\n            ->globally()\n            ->ordered();\n    }\n\n    private function the_config_file_is_opened_for_editing()\n    {\n        $this->shell->shouldReceive('withTTY')\n            ->once()\n            ->globally()\n            ->andReturnSelf()\n            ->ordered();\n\n        $this->shell->shouldReceive('execIn')\n            ->with($this->configDirectory, \"vim {$this->fileName}\")\n            ->once()\n            ->andReturn(FakeProcess::success())\n            ->globally()\n            ->ordered();\n    }\n}\n"
  },
  {
    "path": "tests/Feature/Fakes/FakeProcess.php",
    "content": "<?php\n\nnamespace Tests\\Feature\\Fakes;\n\nclass FakeProcess\n{\n    public $isSuccessful;\n    public $failedCommand;\n\n    private $output;\n    private $errorOutput;\n\n    public function __construct(bool $isSuccessful, string $failedCommand = '')\n    {\n        $this->isSuccessful = $isSuccessful;\n        $this->failedCommand = $failedCommand;\n    }\n\n    public static function success(): FakeProcess\n    {\n        return new self(true);\n    }\n\n    public static function fail(string $failedCommand): FakeProcess\n    {\n        return new self(false, $failedCommand);\n    }\n\n    public function isSuccessful(): bool\n    {\n        return $this->isSuccessful;\n    }\n\n    public function getCommandLine(): string\n    {\n        return $this->failedCommand;\n    }\n\n    public function withOutput(string $output): FakeProcess\n    {\n        $this->output = $output;\n        return $this;\n    }\n\n    public function withErrorOutput(string $errorOutput): FakeProcess\n    {\n        $this->errorOutput = $errorOutput;\n        return $this;\n    }\n\n    public function getOutput()\n    {\n        return $this->output;\n    }\n\n    public function getErrorOutput()\n    {\n        return $this->errorOutput;\n    }\n\n    public function getExitCode()\n    {\n        return $this->isSuccessful ? 0 : 1;\n    }\n}\n"
  },
  {
    "path": "tests/Feature/Fixtures/.lambo/commented_configuration",
    "content": "CODEEDITOR=vim\n#QUIET=true\n#AUTH=false\n#NODE=\n\n# ------------------------------------------------------------------------------\n# 1-APR-2020 5:00 am (auto-generated by Lambo):\n# Lambo has commented out the configuration items QUIET, AUTH and NODE;\n# they are no longer used, and you may safely remove them.\n# ------------------------------------------------------------------------------\n"
  },
  {
    "path": "tests/Feature/Fixtures/.lambo/config",
    "content": "CONFIGURATION_OPTION=foo\n#MISSING_CONFIGURATION_OPTION=foo\nCONFIGURATION_OPTION_NO_VALUE\nCONFIGURATION_OPTION_EMPTY_VALUE=\n\nBOOLEAN_OPTION_1=1\nBOOLEAN_OPTION_TRUE=true\nBOOLEAN_OPTION_ON=on\nBOOLEAN_OPTION_YES=yes\nBOOLEAN_OPTION_0=0\nBOOLEAN_OPTION_FALSE=false\nBOOLEAN_OPTION_OFF=off\nBOOLEAN_OPTION_NO=no\n"
  },
  {
    "path": "tests/Feature/Fixtures/.lambo/old_configuration",
    "content": "CODEEDITOR=vim\nQUIET=true\nAUTH=false\nNODE=\n"
  },
  {
    "path": "tests/Feature/Fixtures/composer-with-laravel-jetstream.json",
    "content": "{\n    \"require\": {\n        \"laravel/jetstream\": \"^1.0\"\n    }\n}\n"
  },
  {
    "path": "tests/Feature/Fixtures/composer-without-laravel-jetstream.json",
    "content": "{\n    \"require\": {\n    }\n}\n"
  },
  {
    "path": "tests/Feature/Fixtures/composer.json",
    "content": "{\n    \"require\": {\n        \"laravel/jetstream\": \"^1.0\"\n    }\n}\n"
  },
  {
    "path": "tests/Feature/Fixtures/package-silent.json",
    "content": "{\"scripts\":{\"development\":\"cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js\"}}\n"
  },
  {
    "path": "tests/Feature/Fixtures/package.json",
    "content": "{\n  \"scripts\": {\n    \"development\": \"cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js\"\n  }\n}\n"
  },
  {
    "path": "tests/Feature/GenerateAppKeyTest.php",
    "content": "<?php\n\nnamespace Tests\\Feature;\n\nuse App\\Actions\\GenerateAppKey;\nuse App\\LamboException;\nuse Tests\\Feature\\Fakes\\FakeProcess;\nuse Tests\\TestCase;\n\nclass GenerateAppKeyTest extends TestCase\n{\n    /** @test */\n    function it_generates_a_new_app_key()\n    {\n        $this->shell->shouldReceive('execInProject')\n            ->with('php artisan key:generate --quiet')\n            ->once()\n            ->andReturn(FakeProcess::success());\n\n        app(GenerateAppKey::class)();\n    }\n\n    /** @test */\n    function it_throws_an_exception_if_new_app_key_generation_fails()\n    {\n        $this->shell->shouldReceive('execInProject')\n            ->with('php artisan key:generate --quiet')\n            ->once()\n            ->andReturn(FakeProcess::fail('php artisan key:generate'));\n\n        $this->expectException(LamboException::class);\n\n        app(GenerateAppKey::class)();\n    }\n}\n"
  },
  {
    "path": "tests/Feature/InitializeGitHubRepositoryTest.php",
    "content": "<?php\n\nnamespace Tests\\Feature;\n\nuse App\\Actions\\Concerns\\InteractsWithGitHub;\nuse App\\Actions\\InitializeGitHubRepository;\nuse App\\Configuration\\LamboConfiguration;\nuse App\\ConsoleWriter;\nuse App\\LamboException;\nuse Tests\\Feature\\Fakes\\FakeProcess;\nuse Tests\\TestCase;\n\n/**\n * @group git-and-github\n */\nclass InitializeGitHubRepositoryTest extends TestCase\n{\n    use InteractsWithGitHub;\n\n    protected $toolConfigurations = [\n        ['gh' => true, 'hub' => true],\n        ['gh' => true, 'hub' => false],\n        ['gh' => false, 'hub' => true],\n        ['gh' => false, 'hub' => false],\n    ];\n\n    protected $gitHubConfigurations = [\n        [\n            LamboConfiguration::GITHUB_PUBLIC => false,\n            LamboConfiguration::GITHUB_DESCRIPTION => null,\n            LamboConfiguration::GITHUB_HOMEPAGE => null,\n            LamboConfiguration::GITHUB_ORGANIZATION => null,\n        ],\n        [\n            LamboConfiguration::GITHUB_PUBLIC => false,\n            LamboConfiguration::GITHUB_DESCRIPTION => null,\n            LamboConfiguration::GITHUB_HOMEPAGE => null,\n            LamboConfiguration::GITHUB_ORGANIZATION => 'org',\n        ],\n        [\n            LamboConfiguration::GITHUB_PUBLIC => false,\n            LamboConfiguration::GITHUB_DESCRIPTION => null,\n            LamboConfiguration::GITHUB_HOMEPAGE => 'https://example.com',\n            LamboConfiguration::GITHUB_ORGANIZATION => null,\n        ],\n        [\n            LamboConfiguration::GITHUB_PUBLIC => false,\n            LamboConfiguration::GITHUB_DESCRIPTION => null,\n            LamboConfiguration::GITHUB_HOMEPAGE => 'https://example.com',\n            LamboConfiguration::GITHUB_ORGANIZATION => 'org',\n        ],\n        [\n            LamboConfiguration::GITHUB_PUBLIC => false,\n            LamboConfiguration::GITHUB_DESCRIPTION => 'My awesome project',\n            LamboConfiguration::GITHUB_HOMEPAGE => null,\n            LamboConfiguration::GITHUB_ORGANIZATION => null,\n        ],\n        [\n            LamboConfiguration::GITHUB_PUBLIC => false,\n            LamboConfiguration::GITHUB_DESCRIPTION => 'My awesome project',\n            LamboConfiguration::GITHUB_HOMEPAGE => null,\n            LamboConfiguration::GITHUB_ORGANIZATION => 'org',\n        ],\n        [\n            LamboConfiguration::GITHUB_PUBLIC => false,\n            LamboConfiguration::GITHUB_DESCRIPTION => 'My awesome project',\n            LamboConfiguration::GITHUB_HOMEPAGE => 'https://example.com',\n            LamboConfiguration::GITHUB_ORGANIZATION => null,\n        ],\n        [\n            LamboConfiguration::GITHUB_PUBLIC => false,\n            LamboConfiguration::GITHUB_DESCRIPTION => 'My awesome project',\n            LamboConfiguration::GITHUB_HOMEPAGE => 'https://example.com',\n            LamboConfiguration::GITHUB_ORGANIZATION => 'org',\n        ],\n        [\n            LamboConfiguration::GITHUB_PUBLIC => true,\n            LamboConfiguration::GITHUB_DESCRIPTION => null,\n            LamboConfiguration::GITHUB_HOMEPAGE => null,\n            LamboConfiguration::GITHUB_ORGANIZATION => null,\n        ],\n        [\n            LamboConfiguration::GITHUB_PUBLIC => true,\n            LamboConfiguration::GITHUB_DESCRIPTION => null,\n            LamboConfiguration::GITHUB_HOMEPAGE => null,\n            LamboConfiguration::GITHUB_ORGANIZATION => 'org',\n        ],\n        [\n            LamboConfiguration::GITHUB_PUBLIC => true,\n            LamboConfiguration::GITHUB_DESCRIPTION => null,\n            LamboConfiguration::GITHUB_HOMEPAGE => 'https://example.com',\n            LamboConfiguration::GITHUB_ORGANIZATION => null,\n        ],\n        [\n            LamboConfiguration::GITHUB_PUBLIC => true,\n            LamboConfiguration::GITHUB_DESCRIPTION => null,\n            LamboConfiguration::GITHUB_HOMEPAGE => 'https://example.com',\n            LamboConfiguration::GITHUB_ORGANIZATION => 'org',\n        ],\n        [\n            LamboConfiguration::GITHUB_PUBLIC => true,\n            LamboConfiguration::GITHUB_DESCRIPTION => 'My awesome project',\n            LamboConfiguration::GITHUB_HOMEPAGE => null,\n            LamboConfiguration::GITHUB_ORGANIZATION => null,\n        ],\n        [\n            LamboConfiguration::GITHUB_PUBLIC => true,\n            LamboConfiguration::GITHUB_DESCRIPTION => 'My awesome project',\n            LamboConfiguration::GITHUB_HOMEPAGE => null,\n            LamboConfiguration::GITHUB_ORGANIZATION => 'org',\n        ],\n        [\n            LamboConfiguration::GITHUB_PUBLIC => true,\n            LamboConfiguration::GITHUB_DESCRIPTION => 'My awesome project',\n            LamboConfiguration::GITHUB_HOMEPAGE => 'https://example.com',\n            LamboConfiguration::GITHUB_ORGANIZATION => null,\n        ],\n        [\n            LamboConfiguration::GITHUB_PUBLIC => true,\n            LamboConfiguration::GITHUB_DESCRIPTION => 'My awesome project',\n            LamboConfiguration::GITHUB_HOMEPAGE => 'https://example.com',\n            LamboConfiguration::GITHUB_ORGANIZATION => 'org',\n        ],\n    ];\n\n    /** @test */\n    function it_manages_new_repository_initialization()\n    {\n        foreach ([true, false] as $initializeGitHub) {\n            foreach ($this->toolConfigurations as $toolConfiguration) {\n                foreach ($this->gitHubConfigurations as $gitHubConfiguration) {\n                    config(['lambo.store.project_name' => 'name']);\n                    config(['lambo.store.' . LamboConfiguration::INITIALIZE_GITHUB => $initializeGitHub]);\n                    config(['lambo.store.push_to_github' => false]);\n                    config(['lambo.store.tools' => $toolConfiguration]);\n                    config(['lambo.store' => array_merge(config('lambo.store'), $gitHubConfiguration)]);\n\n                    if ($this->shouldCreateRepository()) {\n                        $this->shell->shouldReceive('execInProject', [$this->getGitHubCreateCommand()])\n                            ->andReturn(FakeProcess::success());\n                    }\n\n                    if (! $this->gitHubToolingInstalled()) {\n                        $this->expectException(LamboException::class);\n                    }\n\n                    app(InitializeGitHubRepository::class)();\n\n                    if ($this->shouldCreateRepository()) {\n                        $this->assertTrue(config('lambo.store.push_to_github'));\n                    }\n                }\n            }\n        }\n    }\n\n    /** @test */\n    function it_warns_the_user_if_repository_creation_fails()\n    {\n        $consoleWriter = $this->mock(ConsoleWriter::class);\n        $consoleWriter->shouldReceive('logStep');\n\n        config(['lambo.store.project_name' => 'name']);\n        config(['lambo.store.' . LamboConfiguration::INITIALIZE_GITHUB => true]);\n        config(['lambo.store.push_to_github' => false]);\n        config(['lambo.store.tools.gh' => true]);\n\n        $failedCommandOutput = 'Failed command output';\n\n        $this->shell->shouldReceive('execInProject')\n            ->with($this->getGitHubCreateCommand())\n            ->once()\n            ->andReturn(FakeProcess::fail($this->getGitHubCreateCommand())->withErrorOutput($failedCommandOutput));\n\n        $consoleWriter->shouldReceive('warn')\n            ->with(InitializeGitHubRepository::WARNING_FAILED_TO_CREATE_REPOSITORY)\n            ->globally()\n            ->ordered();\n\n        $consoleWriter->shouldReceive('warnCommandFailed')\n            ->with($this->getGitHubCreateCommand())\n            ->globally()\n            ->ordered();\n\n        $consoleWriter->shouldReceive('showOutputErrors')\n            ->with($failedCommandOutput)\n            ->globally()\n            ->ordered();\n\n        app(InitializeGitHubRepository::class)();\n    }\n}\n"
  },
  {
    "path": "tests/Feature/InitializeGitRepositoryTest.php",
    "content": "<?php\n\nnamespace Tests\\Feature;\n\nuse App\\Actions\\InitializeGitRepository;\nuse App\\LamboException;\nuse Tests\\Feature\\Fakes\\FakeProcess;\nuse Tests\\TestCase;\n\nclass InitializeGitRepositoryTest extends TestCase\n{\n    /** @test */\n    function it_initializes_git()\n    {\n        config(['lambo.store.commit_message' => 'Initial commit']);\n\n        $this->shell->shouldReceive('execInProject')\n            ->with('git init --quiet')\n            ->once()\n            ->andReturn(FakeProcess::success());\n\n        $this->shell->shouldReceive('execInProject')\n            ->with('git add .')\n            ->once()\n            ->andReturn(FakeProcess::success());\n\n        $this->shell->shouldReceive('execInProject')\n            ->with(\"git commit --quiet -m 'Initial commit'\")\n            ->once()\n            ->andReturn(FakeProcess::success());\n\n        app(InitializeGitRepository::class)();\n    }\n\n    /** @test */\n    function it_throws_an_exception_if_git_init_fails()\n    {\n        $this->shell->shouldReceive('execInProject')\n            ->with('git init --quiet')\n            ->once()\n            ->andReturn(FakeProcess::fail('git init'));\n\n        $this->expectException(LamboException::class);\n\n        app(InitializeGitRepository::class)();\n    }\n\n    /** @test */\n    function it_throws_an_exception_if_git_add_fails()\n    {\n        $this->shell->shouldReceive('execInProject')\n            ->with('git init --quiet')\n            ->once()\n            ->andReturn(FakeProcess::success());\n\n        $this->shell->shouldReceive('execInProject')\n            ->with('git add .')\n            ->once()\n            ->andReturn(FakeProcess::fail('git add .'));\n\n        $this->expectException(LamboException::class);\n\n        app(InitializeGitRepository::class)();\n    }\n\n    /** @test */\n    function it_throws_an_exception_if_git_commit_fails()\n    {\n        config(['lambo.store.commit_message' => 'Initial commit']);\n\n        $command = 'git init --quiet';\n        $this->shell->shouldReceive('execInProject')\n            ->with($command)\n            ->once()\n            ->andReturn(FakeProcess::success());\n\n        $this->shell->shouldReceive('execInProject')\n            ->with('git add .')\n            ->once()\n            ->andReturn(FakeProcess::success());\n\n        $this->shell->shouldReceive('execInProject')\n            ->with(\"git commit --quiet -m 'Initial commit'\")\n            ->once()\n            ->andReturn(FakeProcess::fail('git commit -m \"Initial commit\"'));\n\n        $this->expectException(LamboException::class);\n\n        app(InitializeGitRepository::class)();\n    }\n\n    /** @test */\n    function it_removes_the_quiet_flag_when_show_output_is_enabled()\n    {\n        config(['lambo.store.commit_message' => 'Initial commit']);\n        config(['lambo.store.with_output' => true]);\n\n        $this->shell->shouldReceive('execInProject')\n            ->with('git init')\n            ->once()\n            ->andReturn(FakeProcess::success());\n\n        $this->shell->shouldReceive('execInProject')\n            ->with('git add .')\n            ->once()\n            ->andReturn(FakeProcess::success());\n\n        $this->shell->shouldReceive('execInProject')\n            ->with(\"git commit -m 'Initial commit'\")\n            ->once()\n            ->andReturn(FakeProcess::success());\n\n        app(InitializeGitRepository::class)();\n    }\n}\n"
  },
  {
    "path": "tests/Feature/InstallBreezeTest.php",
    "content": "<?php\n\nnamespace Tests\\Feature;\n\nuse App\\Actions\\InstallBreeze;\nuse App\\LamboException;\nuse App\\Shell;\nuse Tests\\TestCase;\n\n/**\n * @group front-end-scaffolding\n */\nclass InstallBreezeTest extends TestCase\n{\n    /**\n     * @test\n     * @throws LamboException\n     */\n    function it_installs_laravel_breeze()\n    {\n        foreach ([false, true] as $withOutput) {\n            foreach (InstallBreeze::VALID_STACKS as $stack) {\n                config(['lambo.store.breeze' => $stack]);\n                config(['lambo.store.with_output' => $withOutput]);\n\n                if ($this->isVerbose()) {\n                    $this->logUseCase($stack, $withOutput);\n                }\n\n                $this->shouldExecInProject($this->getComposerCommand($withOutput));\n                $this->shouldExecInProject($this->getBreezeInstallCommand($stack, $withOutput));\n                $this->shouldExecInProject($this->getNpmInstallCommand($withOutput));\n                $this->shouldExecInProject($this->getCompileAssetsCommand($withOutput));\n\n                app(InstallBreeze::class)();\n\n                if ($this->isDebug()) {\n                    $this->toSTDOUT(\"\\n ✔ PASS\\n\");\n                }\n            }\n        }\n    }\n\n    /**\n     * @test\n     * @throws LamboException\n     */\n    function it_skips_breeze_installation()\n    {\n        $this->spy(Shell::class);\n        config(['lambo.store.breeze' => false]);\n\n        app(InstallBreeze::class)();\n\n        $this->shell->shouldNotHaveReceived('execInProject');\n    }\n\n    /** @test */\n    function it_throws_a_lambo_exception_if_an_invalid_breeze_stack_is_requested()\n    {\n        config(['lambo.store.breeze' => null]);\n        $this->expectException(LamboException::class);\n        app(InstallBreeze::class)();\n\n        config(['lambo.store.breeze' => '']);\n        $this->expectException(LamboException::class);\n        app(InstallBreeze::class)();\n\n        config(['lambo.store.breeze' => 'invalid']);\n        $this->expectException(LamboException::class);\n        app(InstallBreeze::class)();\n\n        config(['lambo.store.breeze' => true]);\n        $this->expectException(LamboException::class);\n        app(InstallBreeze::class)();\n    }\n\n    /** @test */\n    function it_throws_a_lambo_exception_if_composer_installation_fails()\n    {\n        config(['lambo.store.breeze' => 'blade']);\n\n        $this->shouldExecInProjectAndFail('composer require laravel/breeze --dev --quiet');\n        $this->expectException(LamboException::class);\n\n        app(InstallBreeze::class)();\n    }\n\n    /** @test */\n    function it_throws_a_lambo_exception_if_breeze_installation_fails()\n    {\n        config(['lambo.store.breeze' => 'react']);\n\n        $this->shouldExecInProject('composer require laravel/breeze --dev --quiet');\n        $this->shouldExecInProjectAndFail('php artisan breeze:install react --quiet');\n\n        $this->expectException(LamboException::class);\n\n        app(InstallBreeze::class)();\n    }\n\n    private function getComposerCommand(bool $withOutput = false): string\n    {\n        return 'composer require laravel/breeze --dev' . ($withOutput ? '' : ' --quiet');\n    }\n\n    private function getBreezeInstallCommand(string $stack, $showOutput): string\n    {\n        return sprintf(\n            'php artisan breeze:install%s%s',\n            $stack === 'blade' ? '' : \" {$stack}\",\n            $showOutput ? '' : ' --quiet'\n        );\n    }\n\n    private function getNpmInstallCommand($showOutput): string\n    {\n        return 'npm install' . ($showOutput ? '' : ' --silent');\n    }\n\n    private function getCompileAssetsCommand($withOutput): string\n    {\n        return 'npm run build' . ($withOutput ? '' : ' --silent');\n    }\n\n    private function logUseCase(string $stack, $showOutput): void\n    {\n        $showOutputStr = $showOutput ? 'true' : 'false';\n\n        $this->toSTDOUT(\"────────────────────────────\\n\");\n        $this->toSTDOUT(implode(PHP_EOL, [\n            sprintf('   lambo new <project> %s--breeze=%s', $showOutput ? '-v(vv) ' : '', $stack),\n        ]), ' USE CASE');\n        $this->toSTDOUT(implode(PHP_EOL, [\n            \"   1. {$this->getComposerCommand($showOutput)}\",\n            \"   2. {$this->getBreezeInstallCommand($stack, $showOutput)}\",\n            \"   3. {$this->getNpmInstallCommand($showOutput)}\",\n            \"   4. {$this->getCompileAssetsCommand($showOutput)}\",\n        ]), ' COMMAND EXECUTION ORDER');\n        $this->toSTDOUT(implode(PHP_EOL, [\n            \"   \\$stack : {$stack}\",\n            \"   \\$showOutput : {$showOutputStr}\",\n            '   config(lambo.store.breeze) : ' . config('lambo.store.breeze'),\n            '   config(lambo.store.show_output) : ' . (config('lambo.store.show_output') ? 'true' : 'false'),\n        ]), ' TEST ITERATION CONTEXT');\n    }\n}\n"
  },
  {
    "path": "tests/Feature/InstallJetstreamTest.php",
    "content": "<?php\n\nnamespace Tests\\Feature;\n\nuse App\\Actions\\InstallJetstream;\nuse App\\LamboException;\nuse App\\Shell;\nuse Tests\\TestCase;\n\n/**\n * @group front-end-scaffolding\n */\nclass InstallJetstreamTest extends TestCase\n{\n    /**\n     * @test\n     * @throws LamboException\n     */\n    function it_installs_laravel_jetstream()\n    {\n        foreach ([false, true] as $withOutput) {\n            foreach ([false, true] as $useTeams) {\n                foreach (InstallJetstream::VALID_STACKS as $stack) {\n                    config(['lambo.store.jetstream' => $stack]);\n                    config(['lambo.store.with_output' => $withOutput]);\n\n                    if ($this->isDebug()) {\n                        $this->logUseCase($stack, $useTeams, $withOutput);\n                    }\n\n                    $this->shouldExecInProject($this->getComposerCommand($withOutput));\n                    $this->shouldExecInProject($this->getJetstreamInstallCommand($stack, $withOutput));\n                    $this->shouldExecInProject($this->getNpmInstallCommand($withOutput));\n                    $this->shouldExecInProject($this->getCompileAssetsCommand($withOutput));\n\n                    app(InstallJetstream::class)();\n\n                    if ($this->isDebug()) {\n                        $this->toSTDOUT(\"\\n ✔ PASS\\n\");\n                    }\n                }\n            }\n        }\n    }\n\n    /**\n     * @test\n     * @throws LamboException\n     */\n    function it_skips_jetstream_installation()\n    {\n        $this->spy(Shell::class);\n        config(['lambo.store.jetstream' => false]);\n\n        app(InstallJetstream::class)();\n\n        $this->shell->shouldNotHaveReceived('execInProject');\n    }\n\n    /** @test */\n    function it_throws_a_lambo_exception_if_an_invalid_jetstream_stack_is_requested()\n    {\n        config(['lambo.store.jetstream' => null]);\n        $this->expectException(LamboException::class);\n        app(InstallJetstream::class)();\n\n        config(['lambo.store.jetstream' => '']);\n        $this->expectException(LamboException::class);\n        app(InstallJetstream::class)();\n\n        config(['lambo.store.jetstream' => 'invalid']);\n        $this->expectException(LamboException::class);\n        app(InstallJetstream::class)();\n\n        config(['lambo.store.jetstream' => true]);\n        $this->expectException(LamboException::class);\n        app(InstallJetstream::class)();\n    }\n\n    /** @test */\n    function it_throws_a_lambo_exception_if_composer_installation_fails()\n    {\n        config(['lambo.store.jetstream' => 'inertia']);\n\n        $this->shouldExecInProjectAndFail('composer require laravel/jetstream --dev --quiet');\n        $this->expectException(LamboException::class);\n\n        app(InstallJetstream::class)();\n    }\n\n    /** @test */\n    function it_throws_a_lambo_exception_if_jetstream_installation_fails()\n    {\n        config(['lambo.store.jetstream' => 'inertia']);\n\n        $this->shouldExecInProject('composer require laravel/jetstream --dev --quiet');\n        $this->shouldExecInProjectAndFail('php artisan jetstream:install inertia --quiet');\n        $this->expectException(LamboException::class);\n\n        app(InstallJetstream::class)();\n    }\n\n    private function getJetstreamInstallCommand(string $stack, $showOutput): string\n    {\n        return \"php artisan jetstream:install {$stack}\" . ($showOutput ? '' : ' --quiet');\n    }\n\n    private function getNpmInstallCommand($showOutput): string\n    {\n        return 'npm install' . ($showOutput ? '' : ' --silent');\n    }\n\n    private function getComposerCommand(bool $withOutput = false): string\n    {\n        return 'composer require laravel/jetstream --dev' . ($withOutput ? '' : ' --quiet');\n    }\n\n    private function getCompileAssetsCommand($withOutput): string\n    {\n        return 'npm run build' . ($withOutput ? '' : ' --silent');\n    }\n\n    private function logUseCase(string $stack, $useTeams, $showOutput): void\n    {\n        $useTeamsStr = $useTeams ? 'true' : 'false';\n        $showOutputStr = $showOutput ? 'true' : 'false';\n\n        $configStack = config('lambo.store.jetstream');\n        $configShowOutputStr = config('lambo.store.show_output') ? 'true' : 'false';\n\n        $this->toSTDOUT(\"────────────────────────────\\n\");\n        $this->toSTDOUT(sprintf(\n            \" USE CASE\\n   lambo new <project> %s--jetstream=%s%s\",\n            $showOutput ? '-v[vv] ' : '',\n            $stack,\n            $useTeams ? ',teams' : ''\n        ));\n        $this->toSTDOUT(implode(PHP_EOL, [\n            \"   1. {$this->getComposerCommand($showOutput)}\",\n            \"   2. {$this->getJetstreamInstallCommand($stack, $showOutput)}\",\n            \"   3. {$this->getNpmInstallCommand($showOutput)}\",\n            \"   4. {$this->getCompileAssetsCommand($showOutput)}\",\n        ]), ' COMMAND EXECUTION ORDER');\n        $this->toSTDOUT(implode(PHP_EOL, [\n            \"   \\$stack : {$stack}\",\n            \"   \\$useTeams : {$useTeamsStr}\",\n            \"   \\$showOutput : {$showOutputStr}\",\n            \"   config(lambo.store.jetstream) : {$configStack}\",\n            \"   config(lambo.store.show_output) : {$configShowOutputStr}\",\n        ]), ' TEST ITERATION CONTEXT');\n    }\n}\n"
  },
  {
    "path": "tests/Feature/InstallLaravelTest.php",
    "content": "<?php\n\nnamespace Tests\\Feature;\n\nuse App\\Actions\\InstallLaravel;\nuse App\\LamboException;\nuse Tests\\Feature\\Fakes\\FakeProcess;\nuse Tests\\TestCase;\n\nclass InstallLaravelTest extends TestCase\n{\n    /** @test */\n    function it_installs_laravel()\n    {\n        collect([\n            ['lambo.store.dev' => false, 'lambo.store.with_output' => false],\n            ['lambo.store.dev' => false, 'lambo.store.with_output' => true],\n            ['lambo.store.dev' => true, 'lambo.store.with_output' => false],\n            ['lambo.store.dev' => true, 'lambo.store.with_output' => true],\n        ])->each(function ($options) {\n            config(['lambo.store.project_name' => 'my-project']);\n            config(['lambo.store.dev' => $options['lambo.store.dev']]);\n            config(['lambo.store.with_output' => $options['lambo.store.with_output']]);\n            $this->shell->shouldReceive('execInRoot')\n                ->with(sprintf(\n                    'composer create-project laravel/laravel %s%s --remove-vcs --prefer-dist %s',\n                    config('lambo.store.project_name'),\n                    config('lambo.store.dev') ? ' dev-master' : '',\n                    config('lambo.store.with_output') ? '' : '--quiet'\n                ))\n                ->once()\n                ->andReturn(FakeProcess::success());\n\n            app(InstallLaravel::class)();\n        });\n    }\n\n    /** @test */\n    function it_throws_an_exception_if_laravel_fails_to_install()\n    {\n        config(['lambo.store.project_name' => 'my-project']);\n        config(['lambo.store.dev' => false]);\n        config(['lambo.store.with_output' => false]);\n\n        $this->shell->shouldReceive('execInRoot')\n            ->andReturn(FakeProcess::fail('failed command'));\n\n        $this->expectException(LamboException::class);\n\n        app(InstallLaravel::class)();\n    }\n}\n"
  },
  {
    "path": "tests/Feature/InstallNpmDependenciesTest.php",
    "content": "<?php\n\nnamespace Tests\\Feature;\n\nuse App\\Actions\\InstallNpmDependencies;\nuse App\\LamboException;\nuse Tests\\Feature\\Fakes\\FakeProcess;\nuse Tests\\TestCase;\n\nclass InstallNpmDependenciesTest extends TestCase\n{\n    /** @test */\n    function it_installs_npm_dependencies()\n    {\n        config(['lambo.store.with_output' => false]);\n\n        $this->shell->shouldReceive('execInProject')\n            ->with('npm install --silent')\n            ->once()\n            ->andReturn(FakeProcess::success());\n\n        app(InstallNpmDependencies::class)();\n    }\n\n    /** @test */\n    function it_installs_npm_dependencies_and_shows_console_output()\n    {\n        config(['lambo.store.with_output' => true]);\n\n        $this->shell->shouldReceive('execInProject')\n            ->with('npm install')\n            ->once()\n            ->andReturn(FakeProcess::success());\n\n        app(InstallNpmDependencies::class)();\n    }\n\n    /** @test */\n    function it_throws_an_exception_if_npm_install_fails()\n    {\n        config(['lambo.store.with_output' => false]);\n\n        $this->shell->shouldReceive('execInProject')\n            ->with('npm install --silent')\n            ->once()\n            ->andReturn(FakeProcess::fail('npm install --silent'));\n\n        $this->expectException(LamboException::class);\n\n        app(InstallNpmDependencies::class)();\n    }\n}\n"
  },
  {
    "path": "tests/Feature/LamboTestEnvironment.php",
    "content": "<?php\n\nnamespace Tests\\Feature;\n\nuse Illuminate\\Support\\Facades\\File;\n\ntrait LamboTestEnvironment\n{\n    protected function withValetTld($tld = 'test'): void\n    {\n        $valetConfig = config('home_dir') . '/.config/valet/config.json';\n\n        File::shouldReceive('isFile')\n            ->with($valetConfig)\n            ->andReturnTrue();\n\n        File::shouldReceive('get')\n            ->with($valetConfig)\n            ->andReturn(sprintf('{\"tld\": \"%s\"}', $tld));\n    }\n}\n"
  },
  {
    "path": "tests/Feature/MigrateDatabaseTest.php",
    "content": "<?php\n\nnamespace Tests\\Feature;\n\nuse App\\Actions\\MigrateDatabase;\nuse App\\Shell;\nuse App\\Tools\\Database;\nuse Tests\\Feature\\Fakes\\FakeProcess;\nuse Tests\\TestCase;\n\nclass MigrateDatabaseTest extends TestCase\n{\n    private $database;\n\n    public function setUp(): void\n    {\n        parent::setUp();\n        $this->database = $this->mock(Database::class);\n    }\n\n    /** @test */\n    function it_migrates_the_database()\n    {\n        $fakeStore = [\n            'migrate_database' => true,\n            'database_host' => 'example.test',\n            'database_port' => 3306,\n            'database_username' => 'user',\n            'database_password' => 'password',\n            'database_name' => 'foo',\n        ];\n\n        config(['lambo.store' => $fakeStore]);\n\n        $this->database->shouldReceive('fillFromLamboStore')\n            ->with($fakeStore)\n            ->once()\n            ->andReturnSelf();\n\n        $this->database->shouldReceive('ensureExists')\n            ->once()\n            ->andReturnTrue();\n\n        $this->shell->shouldReceive('execInProject')\n            ->with('php artisan migrate --quiet')\n            ->once()\n            ->andReturn(FakeProcess::success());\n\n        app(MigrateDatabase::class)();\n    }\n\n    /** @test */\n    function failed_migrations_do_not_halt_execution()\n    {\n        $fakeStore = [\n            'migrate_database' => true,\n            'database_host' => 'example.test',\n            'database_port' => 3306,\n            'database_username' => 'user',\n            'database_password' => 'password',\n            'database_name' => 'foo',\n        ];\n\n        config(['lambo.store' => $fakeStore]);\n\n        $this->database->shouldReceive('fillFromLamboStore')\n            ->with($fakeStore)\n            ->once()\n            ->andReturnSelf();\n\n        $this->database->shouldReceive('ensureExists')\n            ->once()\n            ->andReturnTrue();\n\n        $this->shell->shouldReceive('execInProject')\n            ->with('php artisan migrate --quiet')\n            ->once()\n            ->andReturn(FakeProcess::fail('php artisan migrate --quiet'));\n\n        app(MigrateDatabase::class)();\n    }\n\n    /** @test */\n    function it_skips_migrations()\n    {\n        $databaseSpy = $this->spy(Database::class);\n        $shellSpy = $this->spy(Shell::class);\n\n        // Mock the Database->url() so that if it is called it\n        // returns properly.\n        $databaseSpy->shouldReceive('url')->andReturnSelf();\n\n        config(['lambo.store.migrate_database' => false]);\n\n        config(['lambo.store.database_host' => 'example.test']);\n        config(['lambo.store.database_port' => 3306]);\n        config(['lambo.store.database_username' => 'user']);\n        config(['lambo.store.database_password' => 'password']);\n        config(['lambo.store.database_name' => 'foo']);\n\n        app(MigrateDatabase::class)();\n\n        $databaseSpy->shouldNotHaveReceived('fillFromLamboStore');\n        $databaseSpy->shouldNotHaveReceived('ensureExists');\n        $shellSpy->shouldNotHaveReceived('execInProject');\n    }\n}\n"
  },
  {
    "path": "tests/Feature/OpenInBrowserTest.php",
    "content": "<?php\n\nnamespace Tests\\Feature;\n\nuse App\\Actions\\OpenInBrowser;\nuse App\\Environment;\nuse App\\Shell;\nuse Tests\\TestCase;\n\nclass OpenInBrowserTest extends TestCase\n{\n    private $environment;\n\n    public function setUp(): void\n    {\n        parent::setUp();\n        $this->environment = $this->mock(Environment::class);\n    }\n\n    /** @test */\n    function it_uses_the_open_command_on_mac_when_a_browser_is_specified()\n    {\n        config(['lambo.store.browser' => '/Applications/my/browser.app']);\n        config(['lambo.store.project_url' => 'http://my-project.test']);\n\n        $this->environment->shouldReceive('isMac')\n            ->once()\n            ->andReturn(true);\n\n        $this->shell->shouldReceive('execInProject')\n            ->once()\n            ->with('open -a \"/Applications/my/browser.app\" \"http://my-project.test\"');\n\n        app(OpenInBrowser::class)();\n    }\n\n    /** @test */\n    function it_uses_valet_open_on_mac_when_no_browser_is_specified()\n    {\n        $this->assertEmpty(config('lambo.store.browser'));\n\n        $this->environment->shouldReceive('isMac')\n            ->once()\n            ->andReturn(true);\n\n        $this->shell->shouldReceive('execInProject')\n            ->once()\n            ->with('valet open');\n\n        app(OpenInBrowser::class)();\n    }\n\n    /** @test */\n    function it_uses_valet_open_when_not_running_on_mac()\n    {\n        $this->environment->shouldReceive('isMac')\n            ->once()\n            ->andReturn(false);\n\n        $this->shell->shouldReceive('execInProject')\n            ->once()\n            ->with('valet open');\n\n        app(OpenInBrowser::class)();\n    }\n\n    /** @test */\n    function it_ignores_the_specified_browser_when_not_running_on_mac()\n    {\n        config(['lambo.store.browser' => '/path/to/a/browser']);\n        config(['lambo.store.project_url' => 'http://my-project.test']);\n\n        $this->environment->shouldReceive('isMac')\n            ->once()\n            ->andReturn(false);\n\n        $this->shell->shouldReceive('execInProject')\n            ->once()\n            ->with('valet open');\n\n        app(OpenInBrowser::class)();\n    }\n\n    /** @test */\n    function it_skips_opening_the_site()\n    {\n        $shell = $this->spy(Shell::class);\n\n        config(['lambo.store.no_browser' => false]);\n\n        app(OpenInBrowser::class);\n\n        $shell->shouldNotHaveReceived('execInProject');\n    }\n}\n"
  },
  {
    "path": "tests/Feature/OpenInEditorTest.php",
    "content": "<?php\n\nnamespace Tests\\Feature;\n\nuse App\\Actions\\OpenInEditor;\nuse App\\LamboException;\nuse Tests\\Feature\\Fakes\\FakeProcess;\nuse Tests\\TestCase;\n\nclass OpenInEditorTest extends TestCase\n{\n    /** @test */\n    function it_opens_the_project_folder_in_the_specified_editor()\n    {\n        config(['lambo.store.editor' => 'my-editor']);\n\n        $this->shell->shouldReceive('withTTY')\n            ->once()\n            ->andReturnSelf();\n\n        $this->shell->shouldReceive('execInProject')\n            ->with('my-editor .')\n            ->once()\n            ->andReturn(FakeProcess::success());\n\n        app(OpenInEditor::class)();\n    }\n\n    /** @test */\n    function it_throws_an_exception_if_it_fails_to_open_the_editor()\n    {\n        config(['lambo.store.editor' => 'my-editor']);\n\n        $this->shell->shouldReceive('withTTY')\n            ->once()\n            ->andReturnSelf();\n\n        $this->shell->shouldReceive('execInProject')\n            ->with('my-editor .')\n            ->once()\n            ->andReturn(FakeProcess::fail('my-editor .'));\n\n        $this->expectException(LamboException::class);\n\n        app(OpenInEditor::class)();\n    }\n}\n"
  },
  {
    "path": "tests/Feature/PushToGitHubTest.php",
    "content": "<?php\n\nnamespace Tests\\Feature;\n\nuse App\\Actions\\PushToGitHub;\nuse App\\ConsoleWriter;\nuse App\\Shell;\nuse Tests\\Feature\\Fakes\\FakeProcess;\nuse Tests\\TestCase;\n\n/**\n * @group git-and-github\n */\nclass PushToGitHubTest extends TestCase\n{\n    private $consoleWriter;\n\n    /** @test */\n    function it_pushes_to_github()\n    {\n        config(['lambo.store.push_to_github' => true]);\n\n        $this->shell->shouldReceive('execInProject')\n            ->with('git rev-parse --abbrev-ref HEAD')\n            ->once()\n            ->andReturn(FakeProcess::success()->withOutput('main'))\n            ->globally()\n            ->ordered();\n\n        $this->shell->shouldReceive('execInProject')\n            ->with('git push -u origin main')\n            ->once()\n            ->andReturn(FakeProcess::success());\n\n        app(PushToGitHub::class)();\n    }\n\n    /** @test */\n    function it_logs_a_warning_if_branch_name_cannot_be_determined()\n    {\n        $this->consoleWriter = $this->mock(ConsoleWriter::class);\n\n        config(['lambo.store.push_to_github' => true]);\n\n        $this->shouldLogStep('Pushing new project to GitHub');\n\n        $getBranchNameCommand = 'git rev-parse --abbrev-ref HEAD';\n        $errorMessage = 'Oops, something went wrong.';\n        $failedBranchNameProcess = FakeProcess::fail($getBranchNameCommand)\n            ->withErrorOutput($errorMessage);\n\n        $this->shell->shouldReceive('execInProject')\n            ->with($getBranchNameCommand)\n            ->once()\n            ->andReturn($failedBranchNameProcess)\n            ->globally()\n            ->ordered();\n\n        $this->shouldLogWarning(PushToGitHub::WARNING_UNABLE_TO_GET_BRANCH_NAME);\n        $this->shouldLogWarning(\"Failed to run {$getBranchNameCommand}\");\n        $this->shouldShowOutputErrors($errorMessage);\n\n        app(PushToGitHub::class)();\n    }\n\n    /** @test */\n    function it_logs_a_warning_if_pushing_to_git_hub_fails()\n    {\n        $this->consoleWriter = $this->mock(ConsoleWriter::class);\n\n        config(['lambo.store.push_to_github' => true]);\n\n        $this->shouldLogStep('Pushing new project to GitHub');\n\n        $branchNameProcess = FakeProcess::success()->withOutput('main');\n        $this->shell->shouldReceive('execInProject')\n            ->with('git rev-parse --abbrev-ref HEAD')\n            ->once()\n            ->andReturn($branchNameProcess)\n            ->globally()\n            ->ordered();\n\n        $pushToGitHubCommand = \"git push -u origin {$branchNameProcess->getOutput()}\";\n        $errorMessage = 'Oops, something went wrong.';\n        $failedPushToGitHubProcess = FakeProcess::fail($pushToGitHubCommand)\n            ->withErrorOutput($errorMessage);\n\n        $this->shell->shouldReceive('execInProject')\n            ->with($pushToGitHubCommand)\n            ->once()\n            ->andReturn($failedPushToGitHubProcess)\n            ->globally()\n            ->ordered();\n\n        $this->shouldLogWarning(PushToGitHub::WARNING_FAILED_TO_PUSH);\n        $this->shouldLogWarning(\"Failed to run {$pushToGitHubCommand}\");\n        $this->shouldShowOutputErrors($errorMessage);\n\n        app(PushToGitHub::class)();\n    }\n\n    /** @test */\n    function it_skips_pushing_to_github()\n    {\n        $shell = $this->spy(Shell::class);\n\n        $pushCommand = 'git push -u origin ';\n\n        config(['lambo.store.push_to_github' => null]);\n        app(PushToGitHub::class)();\n        $shell->shouldNotHaveReceived('execInProject', [$pushCommand]);\n\n        config(['lambo.store.push_to_github' => false]);\n        app(PushToGitHub::class)();\n        $shell->shouldNotHaveReceived('execInProject', [$pushCommand]);\n    }\n\n    private function shouldLogWarning(string $warning): void\n    {\n        $this->consoleWriter->shouldReceive('warn')\n            ->with($warning)\n            ->globally()\n            ->ordered();\n    }\n\n    private function shouldLogStep(string $step)\n    {\n        $this->consoleWriter->shouldReceive('logStep')\n            ->with($step)\n            ->globally()\n            ->ordered();\n    }\n\n    private function shouldShowOutputErrors(string $error)\n    {\n        $this->consoleWriter->shouldReceive('showOutputErrors')\n            ->with($error)\n            ->globally()\n            ->ordered();\n    }\n}\n"
  },
  {
    "path": "tests/Feature/RunAfterScriptTest.php",
    "content": "<?php\n\nnamespace Tests\\Feature;\n\nuse App\\Actions\\RunAfterScript;\nuse App\\LamboException;\nuse Illuminate\\Support\\Facades\\File;\nuse Tests\\Feature\\Fakes\\FakeProcess;\nuse Tests\\TestCase;\n\nclass RunAfterScriptTest extends TestCase\n{\n    function setUp(): void\n    {\n        parent::setUp();\n        config(['home_dir' => $this->getHomeDirectory()]);\n        config(['lambo.store.project_path' => $this->getProjectPath()]);\n    }\n\n    /** @test */\n    function it_runs_the_after_script_if_one_exists()\n    {\n        File::shouldReceive('isFile')\n            ->with($this->getAfterScriptPath())\n            ->andReturn(true)\n            ->globally()\n            ->ordered();\n\n        $this->shell->shouldReceive('withTTY')\n            ->once()\n            ->globally()\n            ->ordered();\n\n        $this->shell->shouldReceive('execInProject')\n            ->with($this->getCommand())\n            ->once()\n            ->andReturn(FakeProcess::success())\n            ->globally()\n            ->ordered();\n\n        app(RunAfterScript::class)();\n    }\n\n    /** @test */\n    function it_throws_an_exception_if_the_after_script_fails()\n    {\n        File::shouldReceive('isFile')\n            ->with($this->getAfterScriptPath())\n            ->andReturn(true)\n            ->globally()\n            ->ordered();\n\n        $this->shell->shouldReceive('withTTY')\n            ->once()\n            ->globally()\n            ->ordered();\n\n        $this->shell->shouldReceive('execInProject')\n            ->with($this->getCommand())\n            ->once()\n            ->andReturn(FakeProcess::fail($this->getCommand()))\n            ->globally()\n            ->ordered();\n\n        $this->expectException(LamboException::class);\n\n        app(RunAfterScript::class)();\n    }\n\n    private function getAfterScriptPath(): string\n    {\n        return \"{$this->getHomeDirectory()}/.lambo/after\";\n    }\n\n    private function getHomeDirectory(): string\n    {\n        return '/my/home/dir';\n    }\n\n    private function getCommand(): string\n    {\n        return sprintf('env PROJECTPATH=%s sh %s', $this->getProjectPath(), $this->getAfterScriptPath());\n    }\n\n    private function getProjectPath(): string\n    {\n        return '/my/project/path';\n    }\n}\n"
  },
  {
    "path": "tests/Feature/SignatureBuilderTest.php",
    "content": "<?php\n\nnamespace Tests\\Feature;\n\nuse App\\Commands\\NewCommand;\nuse Tests\\TestCase;\n\nclass SignatureBuilderTest extends TestCase\n{\n    /** @test */\n    function it_offers_short_and_long_codes()\n    {\n        $newCommand = new NewCommand();\n        $output = $newCommand->buildSignatureOption([\n            'short' => 'e',\n            'long' => 'editor',\n            'cli_description' => '',\n        ]);\n\n        $this->assertStringContainsString('-e|editor', $output);\n    }\n\n    /** @test */\n    function it_functions_given_only_long_code()\n    {\n        $newCommand = new NewCommand();\n        $output = $newCommand->buildSignatureOption([\n            'long' => 'editor',\n            'cli_description' => '',\n        ]);\n\n        $this->assertStringContainsString('-editor', $output);\n    }\n\n    /** @test */\n    function it_sets_expectation_for_values_if_option_expects_parameters()\n    {\n        $newCommand = new NewCommand();\n        $output = $newCommand->buildSignatureOption([\n            'long' => 'editor',\n            'param_description' => 'a',\n            'cli_description' => '',\n        ]);\n\n        $this->assertStringContainsString('-editor=', $output);\n    }\n\n    /** @test */\n    function it_does_not_set_expectation_for_values_if_option_does_not_expect_parameters()\n    {\n        $newCommand = new NewCommand();\n        $output = $newCommand->buildSignatureOption([\n            'long' => 'editor',\n            'cli_description' => '',\n        ]);\n\n        $this->assertStringNotContainsString('editor=', $output);\n    }\n\n    /** @test */\n    function it_defines_description()\n    {\n        $newCommand = new NewCommand();\n        $output = $newCommand->buildSignatureOption([\n            'long' => 'editor',\n            'cli_description' => 'The Option Description',\n        ]);\n\n        $this->assertStringContainsString('The Option Description', $output);\n    }\n}\n"
  },
  {
    "path": "tests/Feature/UpgradeSavedConfigurationTest.php",
    "content": "<?php\n\nnamespace Tests\\Feature;\n\nuse App\\Actions\\UpgradeSavedConfiguration;\nuse Carbon\\Carbon;\nuse Tests\\TestCase;\n\nclass UpgradeSavedConfigurationTest extends TestCase\n{\n    private $validConfigurationKeys = [\n        'VALID_KEY',\n        'ANOTHER_VALID_KEY',\n    ];\n\n    private $removedConfigurationKeys = [\n        'OLD_KEY',\n        'ANOTHER_OLD_KEY',\n    ];\n\n    private $newConfiguration = [\n        'THING' => [\n            'commented' => true,\n            'default' => 'flibble',\n            'description' => [\n                'The THING parameter enables Lambo to do a thing.',\n                'Valid options are foo, bar and flibble (default if not specified).',\n            ],\n        ],\n        'ANOTHER_THING' => [\n            'commented' => false,\n            'default' => 'false',\n            'description' => [\n                'The ANOTHER_THING parameter enables Lambo to do a another thing.',\n                'Valid options are true or false (default if not specified).',\n            ],\n        ],\n    ];\n\n    /** @test */\n    function it_upgrades_saved_configuration()\n    {\n        $this->travelTo(Carbon::parse('01-Jan-2000 00:00:00', config('app.timezone')));\n\n        $upgradedConfiguration = app(UpgradeSavedConfiguration::class)->upgrade($this->getConfiguration(), $this->removedConfigurationKeys, $this->newConfiguration);\n\n        $commented =\n        'VALID_KEY=foo\nANOTHER_VALID_KEY=foo\n#OLD_KEY=foo\n#ANOTHER_OLD_KEY=foo\n\n\n# ------------------------------------------------------------------------------\n# 1-Jan-2000 12:00 am (auto-generated by Lambo):\n# ------------------------------------------------------------------------------\n# Lambo has commented out the following configuration items as they\n# are no-longer used. You may safely remove them:\n#   OLD_KEY=foo\n#   ANOTHER_OLD_KEY=foo\n\n# Lambo has introduced new configuration options. They have been added here\n# with sensible defaults; however, you should review them.\n#\n# The THING parameter enables Lambo to do a thing.\n# Valid options are foo, bar and flibble (default if not specified).\n#THING=flibble\n\n# The ANOTHER_THING parameter enables Lambo to do a another thing.\n# Valid options are true or false (default if not specified).\nANOTHER_THING=false\n\n';\n\n        $this->assertEquals(\n            $commented,\n            $upgradedConfiguration\n        );\n    }\n\n    private function getConfiguration(): string\n    {\n        return collect(array_merge($this->validConfigurationKeys, $this->removedConfigurationKeys))->reduce(function ($carry, $configurationKey) {\n            return \"{$carry}{$configurationKey}=foo\\n\";\n        }, '');\n    }\n}\n"
  },
  {
    "path": "tests/Feature/ValetLinkTest.php",
    "content": "<?php\n\nnamespace Tests\\Feature;\n\nuse App\\Actions\\ValetLink;\nuse App\\LamboException;\nuse Tests\\Feature\\Fakes\\FakeProcess;\nuse Tests\\TestCase;\n\nclass ValetLinkTest extends TestCase\n{\n    /** @test */\n    function it_runs_valet_link()\n    {\n        config(['lambo.store.valet_link' => true]);\n\n        $this->shell->shouldReceive('execInProject')\n            ->with('valet link')\n            ->once()\n            ->andReturn(FakeProcess::success());\n\n        app(ValetLink::class)();\n    }\n\n    /** @test */\n    function it_throws_an_exception_if_valet_link_fails()\n    {\n        config(['lambo.store.valet_link' => true]);\n\n        $command = 'valet link';\n        $this->shell->shouldReceive('execInProject')\n            ->with($command)\n            ->once()\n            ->andReturn(FakeProcess::fail($command));\n\n        $this->expectException(LamboException::class);\n\n        app(ValetLink::class)();\n    }\n}\n"
  },
  {
    "path": "tests/Feature/ValetSecureTest.php",
    "content": "<?php\n\nnamespace Tests\\Feature;\n\nuse App\\Actions\\ValetSecure;\nuse App\\LamboException;\nuse Tests\\Feature\\Fakes\\FakeProcess;\nuse Tests\\TestCase;\n\nclass ValetSecureTest extends TestCase\n{\n    /** @test */\n    function it_runs_valet_secure()\n    {\n        config(['lambo.store.valet_secure' => true]);\n\n        $this->shell->shouldReceive('execInProject')\n            ->with('valet secure')\n            ->once()\n            ->andReturn(FakeProcess::success());\n\n        app(ValetSecure::class)();\n    }\n\n    /** @test */\n    function it_throws_an_exception_if_valet_secure_fails()\n    {\n        config(['lambo.store.valet_secure' => true]);\n\n        $this->shell->shouldReceive('execInProject')\n            ->with('valet secure')\n            ->once()\n            ->andReturn(FakeProcess::fail('valet secure'));\n\n        $this->expectException(LamboException::class);\n\n        app(ValetSecure::class)();\n    }\n}\n"
  },
  {
    "path": "tests/Feature/ValidateGitHubConfigurationTest.php",
    "content": "<?php\n\nnamespace Tests\\Feature;\n\nuse App\\Actions\\ValidateGitHubConfiguration;\nuse App\\Configuration\\LamboConfiguration;\nuse App\\ConsoleWriter;\nuse LaravelZero\\Framework\\Commands\\Command;\nuse Tests\\Feature\\Fakes\\FakeProcess;\nuse Tests\\TestCase;\n\n/**\n * @group git-and-github\n */\nclass ValidateGitHubConfigurationTest extends TestCase\n{\n    private $consoleWriter;\n    private $console;\n\n    /** @test */\n    function it_skips_gh_command_line_tool_validation()\n    {\n        config(['lambo.store.tools.gh' => true]);\n        config(['lambo.store.tools.hub' => true]);\n\n        config(['lambo.store.initializeGitHub' => null]);\n        app(ValidateGitHubConfiguration::class)();\n        $this->assertFalse(config('lambo.store.' . LamboConfiguration::INITIALIZE_GITHUB));\n\n        config(['lambo.store.' . LamboConfiguration::INITIALIZE_GITHUB => false]);\n        app(ValidateGitHubConfiguration::class)();\n        $this->assertFalse(config('lambo.store.' . LamboConfiguration::INITIALIZE_GITHUB));\n    }\n\n    /** @test */\n    function it_logs_a_warning_if_github_tooling_is_missing()\n    {\n        $this->consoleWriter = $this->mock(ConsoleWriter::class);\n        $this->console = $this->mock(Command::class);\n\n        config(['lambo.store.' . LamboConfiguration::INITIALIZE_GITHUB => true]);\n        config(['lambo.store.tools.gh' => false]);\n        config(['lambo.store.tools.hub' => false]);\n\n        $this->shouldLogWarning();\n        $this->shouldLogInstructions(ValidateGitHubConfiguration::INSTRUCTIONS_GITHUB_TOOLING_MISSING);\n        $this->shouldAskToContinue();\n\n        app(ValidateGitHubConfiguration::class)();\n\n        $this->assertFalse(config('lambo.store.' . LamboConfiguration::INITIALIZE_GITHUB));\n    }\n\n    /** @test */\n    function configuration_is_valid_if_hub_is_installed()\n    {\n        $this->consoleWriter = $this->mock(ConsoleWriter::class);\n\n        config(['lambo.store.' . LamboConfiguration::INITIALIZE_GITHUB => true]);\n        config(['lambo.store.tools.gh' => true]);\n        config(['lambo.store.tools.hub' => true]);\n\n        $this->shouldLogChosenGitHubTool('hub');\n\n        app(ValidateGitHubConfiguration::class)();\n\n        $this->assertTrue(config('lambo.store.' . LamboConfiguration::INITIALIZE_GITHUB));\n    }\n\n    /** @test */\n    function configuration_is_valid_if_gh_is_installed_and_authenticated()\n    {\n        $this->consoleWriter = $this->mock(ConsoleWriter::class);\n\n        config(['lambo.store.' . LamboConfiguration::INITIALIZE_GITHUB => true]);\n        config(['lambo.store.tools.gh' => true]);\n        config(['lambo.store.tools.hub' => false]);\n\n        $this->shell->shouldReceive('execQuietly')\n            ->with('gh auth status')\n            ->andReturn(FakeProcess::success());\n\n        $this->shouldLogChosenGitHubTool('gh');\n\n        app(ValidateGitHubConfiguration::class)();\n\n        $this->assertTrue(config('lambo.store.' . LamboConfiguration::INITIALIZE_GITHUB));\n    }\n\n    /** @test */\n    function it_logs_a_warning_if_gh_is_not_authenticated_with_github()\n    {\n        $this->consoleWriter = $this->mock(ConsoleWriter::class);\n        $this->console = $this->mock(Command::class);\n\n        config(['lambo.store.' . LamboConfiguration::INITIALIZE_GITHUB => true]);\n        config(['lambo.store.tools.gh' => true]);\n        config(['lambo.store.tools.hub' => false]);\n\n        $this->shell->shouldReceive('execQuietly')\n            ->with('gh auth status')\n            ->andReturn(FakeProcess::fail('gh auth status'));\n\n        $this->shouldLogWarning();\n        $this->shouldLogInstructions(ValidateGitHubConfiguration::INSTRUCTIONS_GH_NOT_AUTHENTICATED);\n        $this->shouldAskToContinue();\n\n        app(ValidateGitHubConfiguration::class)();\n\n        $this->assertFalse(config('lambo.store.' . LamboConfiguration::INITIALIZE_GITHUB));\n    }\n\n    private function shouldLogChosenGitHubTool(string $tool): void\n    {\n        $this->consoleWriter->shouldReceive('note')\n            ->with(sprintf(ValidateGitHubConfiguration::SELECTED_GITHUB_TOOL_MESSAGE_PATTERN, $tool))\n            ->globally()\n            ->ordered();\n    }\n\n    private function shouldLogWarning(): void\n    {\n        $this->consoleWriter->shouldReceive('warn')\n            ->with(ValidateGitHubConfiguration::WARNING_UNABLE_TO_CREATE_REPOSITORY)\n            ->globally()\n            ->ordered();\n    }\n\n    private function shouldAskToContinue(): void\n    {\n        $this->console->shouldReceive('confirm')\n            ->with(ValidateGitHubConfiguration::QUESTION_SHOULD_CONTINUE)\n            ->andReturnTrue()\n            ->globally()\n            ->ordered();\n        $this->swap('console', $this->console);\n    }\n\n    private function shouldLogInstructions(array $instructions): void\n    {\n        $this->consoleWriter->shouldReceive('text')\n            ->with($instructions)\n            ->globally()\n            ->ordered();\n    }\n}\n"
  },
  {
    "path": "tests/Feature/VerifyDependenciesTest.php",
    "content": "<?php\n\nnamespace Tests\\Feature;\n\nuse App\\Actions\\VerifyDependencies;\nuse App\\LamboException;\nuse Symfony\\Component\\Process\\ExecutableFinder;\nuse Tests\\TestCase;\n\nclass VerifyDependenciesTest extends TestCase\n{\n    private $executableFinder;\n\n    public function setUp(): void\n    {\n        parent::setUp();\n        $this->executableFinder = $this->mock(ExecutableFinder::class);\n    }\n\n    /** @test */\n    function it_checks_that_dependencies_are_available()\n    {\n        foreach (['composer', 'valet', 'git', 'hub', 'gh'] as $dependency) {\n            $this->dependencyIsAvailable($dependency);\n        }\n\n        app(VerifyDependencies::class)();\n        $this->assertTrue(config('lambo.store.tools.gh'));\n        $this->assertTrue(config('lambo.store.tools.hub'));\n    }\n\n    /** @test */\n    function it_marks_optional_dependencies_as_missing()\n    {\n        foreach (['composer', 'valet', 'git'] as $dependency) {\n            $this->dependencyIsAvailable($dependency);\n        }\n\n        $this->dependencyIsMissing('gh');\n        $this->dependencyIsMissing('hub');\n\n        app(VerifyDependencies::class)();\n        $this->assertFalse(config('lambo.store.tools.gh'));\n        $this->assertFalse(config('lambo.store.tools.hub'));\n    }\n\n    /** @test */\n    function it_throws_a_lambo_exception_if_composer_is_missing()\n    {\n        foreach (['valet', 'git', 'hub', 'gh'] as $dependency) {\n            $this->dependencyIsAvailable($dependency);\n        }\n\n        $this->dependencyIsMissing('composer');\n\n        $this->expectException(LamboException::class);\n\n        app(VerifyDependencies::class)();\n    }\n\n    /** @test */\n    function it_throws_a_lambo_exception_if_valet_is_missing()\n    {\n        foreach (['composer', 'git', 'hub', 'gh'] as $dependency) {\n            $this->dependencyIsAvailable($dependency);\n        }\n\n        $this->dependencyIsMissing('valet');\n\n        $this->expectException(LamboException::class);\n\n        app(VerifyDependencies::class)();\n    }\n\n    /** @test */\n    function it_throws_a_lambo_exception_if_git_is_missing()\n    {\n        foreach (['composer', 'valet', 'hub', 'gh'] as $dependency) {\n            $this->dependencyIsAvailable($dependency);\n        }\n\n        $this->dependencyIsMissing('git');\n\n        $this->expectException(LamboException::class);\n\n        app(VerifyDependencies::class)();\n    }\n\n    private function dependencyIsAvailable(string $dependency, $isAvailable = true): void\n    {\n        $foo = $this->executableFinder\n            ->shouldReceive('find')\n            ->with($dependency);\n\n        $isAvailable\n            ? $foo->andReturn(\"/path/to/{$dependency}\")\n            : $foo->andReturnNull();\n    }\n\n    private function dependencyIsMissing(string $dependency)\n    {\n        $this->dependencyIsAvailable($dependency, false);\n    }\n}\n"
  },
  {
    "path": "tests/Feature/VerifyPathAvailableTest.php",
    "content": "<?php\n\nnamespace Tests\\Feature;\n\nuse App\\Actions\\VerifyPathAvailable;\nuse App\\LamboException;\nuse Exception;\nuse Illuminate\\Support\\Facades\\File;\nuse Tests\\TestCase;\n\nclass VerifyPathAvailableTest extends TestCase\n{\n    /** @test */\n    function it_checks_if_the_required_directories_are_available()\n    {\n        config(['lambo.store.root_path' => '/some/filesystem/path']);\n        config(['lambo.store.project_path' => '/some/filesystem/path/my-project']);\n\n        File::shouldReceive('isDirectory')\n            ->with('/some/filesystem/path')\n            ->once()\n            ->andReturn(true);\n\n        File::shouldReceive('isDirectory')\n            ->with('/some/filesystem/path/my-project')\n            ->once()\n            ->andReturn(false);\n\n        app(VerifyPathAvailable::class)();\n    }\n\n    /** @test */\n    function it_throws_a_lambo_exception_if_the_root_path_is_not_available()\n    {\n        config(['lambo.store.root_path' => '/non/existent/filesystem/path']);\n\n        File::shouldReceive('isDirectory')\n            ->with('/non/existent/filesystem/path')\n            ->once()\n            ->andReturn(false);\n\n        $this->expectException(LamboException::class);\n\n        app(VerifyPathAvailable::class)();\n    }\n\n    /** @test */\n    function it_throws_a_lambo_exception_if_the_project_path_already_exists()\n    {\n        config(['lambo.store.root_path' => '/some/filesystem/path']);\n        config(['lambo.store.project_path' => '/some/filesystem/path/existing-directory']);\n\n        File::shouldReceive('isDirectory')\n            ->with('/some/filesystem/path')\n            ->once()\n            ->andReturn(true)\n            ->globally()\n            ->ordered();\n\n        File::shouldReceive('isDirectory')\n            ->with('/some/filesystem/path/existing-directory')\n            ->once()\n            ->andReturn(true)\n            ->globally()\n            ->ordered();\n\n        $this->expectException(LamboException::class);\n\n        app(VerifyPathAvailable::class)();\n    }\n\n    /** @test */\n    function it_ignores_a_pre_existing_directory_if_the_force_option_is_specified()\n    {\n        config(['lambo.store.root_path' => '/some/filesystem/path']);\n        config(['lambo.store.project_path' => '/some/filesystem/path/existing-directory']);\n        config(['lambo.store.force_create' => true]);\n\n        File::shouldReceive('isDirectory')\n            ->with('/some/filesystem/path')\n            ->once()\n            ->andReturn(true)\n            ->globally()\n            ->ordered();\n\n        File::shouldReceive('isDirectory')\n            ->with('/some/filesystem/path/existing-directory')\n            ->once()\n            ->andReturn(true)\n            ->globally()\n            ->ordered();\n\n        File::shouldReceive('deleteDirectory')\n            ->with('/some/filesystem/path/existing-directory')\n            ->once()\n            ->andReturn(true)\n            ->globally()\n            ->ordered();\n\n        app(VerifyPathAvailable::class)();\n    }\n\n    /** @test */\n    public function it_throws_a_lambo_exception_if_it_fails_to_delete_the_pre_existing_directory()\n    {\n        config(['lambo.store.root_path' => '/some/filesystem/path']);\n        config(['lambo.store.project_path' => '/some/filesystem/path/existing-directory']);\n        config(['lambo.store.force_create' => true]);\n\n        File::shouldReceive('isDirectory')\n            ->with('/some/filesystem/path')\n            ->once()\n            ->andReturn(true)\n            ->globally()\n            ->ordered();\n\n        File::shouldReceive('isDirectory')\n            ->with('/some/filesystem/path/existing-directory')\n            ->once()\n            ->andReturn(true)\n            ->globally()\n            ->ordered();\n\n        File::shouldReceive('deleteDirectory')\n            ->with('/some/filesystem/path/existing-directory')\n            ->once()\n            ->andReturn(false)\n            ->globally()\n            ->ordered();\n\n        $this->expectException(LamboException::class);\n\n        app(VerifyPathAvailable::class)();\n    }\n\n\n    /** @test */\n    function it_throws_an_exception_if_project_path_is_empty()\n    {\n        config(['lambo.store.root_path' => '/some/filesystem/path']);\n        config(['lambo.store.project_path' => '']);\n\n        File::shouldReceive('isDirectory')\n            ->with('/some/filesystem/path')\n            ->once()\n            ->andReturn(true)\n            ->globally()\n            ->ordered();\n\n        $this->expectException(Exception::class);\n\n        app(VerifyPathAvailable::class)();\n    }\n\n    /** @test */\n    function it_throws_an_exception_if_project_path_is_null()\n    {\n        config(['lambo.store.root_path' => '/some/filesystem/path']);\n        config(['lambo.store.project_path' => null]);\n\n        File::shouldReceive('isDirectory')\n            ->with('/some/filesystem/path')\n            ->once()\n            ->andReturn(true)\n            ->globally()\n            ->ordered();\n\n        $this->expectException(Exception::class);\n\n        app(VerifyPathAvailable::class)();\n    }\n}\n"
  },
  {
    "path": "tests/TestCase.php",
    "content": "<?php\n\nnamespace Tests;\n\nuse App\\ConsoleWriter;\nuse App\\Shell;\nuse LaravelZero\\Framework\\Testing\\TestCase as BaseTestCase;\nuse Tests\\Feature\\Fakes\\FakeProcess;\n\nabstract class TestCase extends BaseTestCase\n{\n    use CreatesApplication;\n\n    protected $shell;\n\n    function setUp(): void\n    {\n        parent::setUp();\n\n        $this->mockConsoleWriter();\n\n        $this->shell = $this->mock(Shell::class);\n    }\n\n    protected function mockConsoleWriter(): void\n    {\n        $consoleWriter = $this->mock(ConsoleWriter::class, function ($consoleWriter) {\n            $consoleWriter->shouldReceive('logStep');\n            $consoleWriter->shouldReceive('title');\n            $consoleWriter->shouldReceive('success');\n            $consoleWriter->shouldReceive('note');\n            $consoleWriter->shouldReceive('text');\n            $consoleWriter->shouldReceive('warn');\n            $consoleWriter->shouldReceive('fail');\n            $consoleWriter->shouldReceive('newLine');\n        });\n\n        $this->swap('console-writer', $consoleWriter);\n        $this->swap(ConsoleWriter::class, $consoleWriter);\n    }\n\n    protected function todo(array $lines)\n    {\n        $this->skipWithMessage($lines, 'TODO');\n    }\n\n    protected function toSTDOUT($out, string $title = null): void\n    {\n        $message = sprintf(\"%s%s\\n\", $title ? \"{$title}\\n\" : '', print_r($out, true));\n        fwrite(STDOUT, $message);\n    }\n\n    protected function shouldExecInProject(string $command, bool $success = true)\n    {\n        $shell = $this->shell->shouldReceive('execInProject')\n            ->with($command)\n            ->once()\n            ->globally()\n            ->ordered();\n\n        $success\n            ? $shell->andReturn(FakeProcess::success())\n            : $shell->andReturn(FakeProcess::fail($command));\n    }\n\n    protected function shouldExecInProjectAndFail(string $command)\n    {\n        $this->shouldExecInProject($command, false);\n    }\n\n    protected function isVerbose(): bool\n    {\n        return $this->isDebug() || in_array('--verbose', $_SERVER['argv'], true);\n    }\n\n    protected function isDebug(): bool\n    {\n        return in_array('--debug', $_SERVER['argv'], true);\n    }\n}\n"
  },
  {
    "path": "tests/Unit/CommandLineConfigurationTest.php",
    "content": "<?php\n\nnamespace Tests\\Unit;\n\nuse App\\Configuration\\CommandLineConfiguration;\nuse Illuminate\\Support\\Arr;\nuse LaravelZero\\Framework\\Commands\\Command;\nuse Tests\\TestCase;\n\nclass CommandLineConfigurationTest extends TestCase\n{\n    private $mockConsole;\n\n    public function setUp(): void\n    {\n        parent::setUp();\n        $this->mockConsole = $this->mock(Command::class);\n        $this->withCommandLineArgument('projectName', 'foo');\n    }\n\n    /** @test */\n    function it_gets_a_command_line_configuration_value()\n    {\n        $this->withoutCommandLineArguments();\n\n        // lambo --command-line-option=foo\n        $this->withCommandLineOptions(['command-line-option' => 'foo']);\n\n        $this->swap('console', $this->mockConsole);\n\n        $commandLineConfiguration = new CommandLineConfiguration([\n            'command-line-option' => 'genericOption',\n        ]);\n\n        $this->assertEquals('foo', $commandLineConfiguration->genericOption);\n    }\n\n    /** @test */\n    function it_returns_null_if_a_command_line_configuration_value_is_missing()\n    {\n        $this->withoutCommandLineArguments();\n        $this->withoutCommandLineOptions();\n\n        $this->swap('console', $this->mockConsole);\n\n        $commandLineConfiguration = new CommandLineConfiguration([\n            'command-line-option' => 'genericOption',\n        ]);\n\n        $this->assertNull($commandLineConfiguration->genericOption);\n    }\n\n    /** @test */\n    function it_returns_null_if_a_command_line_configuration_value_is_empty()\n    {\n        $this->withoutCommandLineArguments();\n\n        // lambo --command-line-option=\n        $this->withCommandLineOptions(['command-line-option' => '']);\n\n        $this->swap('console', $this->mockConsole);\n\n        $commandLineConfiguration = new CommandLineConfiguration([\n            'command-line-option' => 'genericOption',\n        ]);\n\n        $this->assertNull($commandLineConfiguration->genericOption);\n    }\n\n    /** @test */\n    function it_returns_null_if_a_non_existent_property_is_requested()\n    {\n        $this->withoutCommandLineArguments();\n        $this->withoutCommandLineOptions();\n\n        $this->swap('console', $this->mockConsole);\n\n        $commandLineConfiguration = new CommandLineConfiguration([]);\n        $this->assertNull($commandLineConfiguration->foo);\n    }\n\n    protected function withoutEnvironmentVariable(array $keys): void\n    {\n        Arr::forget($_SERVER, $keys);\n    }\n\n    protected function withoutCommandLineOptions(): void\n    {\n        $this->withCommandLineOptions([]);\n    }\n\n    protected function withCommandLineOptions(array $commandLineOptions): void\n    {\n        $this->mockConsole->shouldReceive('options')\n            ->andReturn($commandLineOptions);\n    }\n\n    protected function withCommandLineArgument(string $key, string $value): void\n    {\n        $this->mockConsole->shouldReceive('argument')\n            ->with($key)\n            ->andReturn($value);\n    }\n\n    private function withoutCommandLineArguments()\n    {\n        $this->mockConsole->shouldReceive('arguments')->andReturn([]);\n    }\n}\n"
  },
  {
    "path": "tests/Unit/Fakes/FakeInput.php",
    "content": "<?php\n\nnamespace Tests\\Unit\\Fakes;\n\nuse Exception;\nuse Illuminate\\Support\\Arr;\nuse Symfony\\Component\\Console\\Input\\InputDefinition;\nuse Symfony\\Component\\Console\\Input\\InputInterface;\n\nclass FakeInput implements InputInterface\n{\n    private array $input;\n    private array $parameterOptions;\n\n    public function __construct(array $input = [])\n    {\n        $this->input = $input;\n        $this->parameterOptions = collect($input)->mapWithKeys(function ($value, $key) {\n            return [\"--{$key}\" => $value];\n        })->toArray();\n    }\n\n    public function getFirstArgument(): null|string\n    {\n        throw new Exception('getFirstArgument() has not been implemented.');\n    }\n\n    public function hasParameterOption(array|string $values, bool $onlyParams = false): bool\n    {\n        return Arr::has($this->parameterOptions, (array) $values);\n    }\n\n    public function getParameterOption($values, $default = false, bool $onlyParams = false)\n    {\n        throw new Exception('getParameterOption() has not been implemented.');\n    }\n\n    public function bind(InputDefinition $definition)\n    {\n        throw new Exception('bind() has not been implemented.');\n    }\n\n    public function validate()\n    {\n        throw new Exception('validate() has not been implemented.');\n    }\n\n    public function getArguments(): array\n    {\n        throw new Exception('getArguments() has not been implemented.');\n    }\n\n    public function getArgument(string $name)\n    {\n        throw new Exception('getArgument() has not been implemented.');\n    }\n\n    public function setArgument(string $name, $value)\n    {\n        throw new Exception('setArgument() has not been implemented.');\n    }\n\n    public function hasArgument(string $name): bool\n    {\n        throw new Exception('hasArgument() has not been implemented.');\n    }\n\n    public function getOptions(): array\n    {\n        return $this->input;\n    }\n\n    public function getOption(string $name)\n    {\n        throw new Exception('getOption() has not been implemented.');\n    }\n\n    public function setOption(string $name, $value)\n    {\n        throw new Exception('setOption() has not been implemented.');\n    }\n\n    public function hasOption(string $name): bool\n    {\n        throw new Exception('hasOption() has not been implemented.');\n    }\n\n    public function isInteractive(): bool\n    {\n        throw new Exception('isInteractive() has not been implemented.');\n    }\n\n    public function setInteractive(bool $interactive)\n    {\n        throw new Exception('setInteractive() has not been implemented.');\n    }\n}\n"
  },
  {
    "path": "tests/Unit/GetTimezoneTest.php",
    "content": "<?php\n\nnamespace Tests\\Unit;\n\nuse App\\Helpers\\GetTimezone;\nuse IntlTimeZone;\nuse Tests\\TestCase;\n\nclass GetTimezoneTest extends TestCase\n{\n    /** @test */\n    function it_uses_the_timezone_configured_in_php_ini()\n    {\n        $availableTimezones = array_diff(\n            ['America/New_York', 'Europe/London'],\n            [IntlTimeZone::createDefault()->getId()]\n        );\n        $expectedTimezone = array_pop($availableTimezones);\n\n        ini_set('date.timezone', $expectedTimezone);\n        $this->assertEquals($expectedTimezone, app(GetTimezone::class)());\n    }\n}\n"
  },
  {
    "path": "tests/Unit/SavedConfigurationTest.php",
    "content": "<?php\n\nnamespace Tests\\Unit;\n\nuse App\\Configuration\\SavedConfiguration;\nuse Tests\\TestCase;\n\nclass SavedConfigurationTest extends TestCase\n{\n    public function setUp(): void\n    {\n        parent::setUp();\n\n        config(['home_dir' => base_path('tests/Feature/Fixtures')]);\n        config(['config_dir' => '.lambo']);\n    }\n\n    /** @test */\n    function it_gets_a_saved_configuration_value()\n    {\n        $savedConfiguration = new SavedConfiguration([\n            'CONFIGURATION_OPTION' => 'genericOption',\n        ]);\n\n        $this->assertEquals('foo', $savedConfiguration->genericOption);\n    }\n\n    /** @test */\n    function it_returns_null_if_a_saved_configuration_option_is_missing()\n    {\n        $savedConfiguration = new SavedConfiguration([\n            'MISSING_CONFIGURATION_OPTION' => 'genericOption',\n        ]);\n\n        $this->assertNull($savedConfiguration->genericOption);\n    }\n\n    /** @test */\n    function it_returns_null_if_a_saved_configuration_option_has_no_value()\n    {\n        $savedConfiguration = new SavedConfiguration([\n            'CONFIGURATION_OPTION_NO_VALUE' => 'genericOption',\n        ]);\n\n        $this->assertNull($savedConfiguration->genericOption);\n    }\n\n    /** @test */\n    function it_returns_null_if_a_saved_configuration_option_is_empty()\n    {\n        $savedConfiguration = new SavedConfiguration([\n            'CONFIGURATION_OPTION_EMPTY_VALUE' => 'genericOption',\n        ]);\n\n        $this->assertNull($savedConfiguration->genericOption);\n    }\n\n    /** test */\n    function it_returns_null_when_the_configuration_file_does_not_exist()\n    {\n        config(['config_file' => 'non-existent-configuration_file']);\n\n        $savedConfiguration = new SavedConfiguration([\n            'CONFIGURATION_VALUE' => 'genericOption',\n        ]);\n\n        $this->assertNull((new SavedConfiguration())->genericOption);\n    }\n\n    /** @test */\n    function it_returns_null_if_a_non_existent_property_is_requested()\n    {\n        $savedConfiguration = new SavedConfiguration([]);\n        $this->assertNull($savedConfiguration->foo);\n    }\n\n    /** @test */\n    function it_casts_strings_to_booleans()\n    {\n        $savedConfiguration = new SavedConfiguration([\n            'BOOLEAN_OPTION_1' => 'booleanOption1',\n            'BOOLEAN_OPTION_TRUE' => 'booleanOptionTrue',\n            'BOOLEAN_OPTION_ON' => 'booleanOptionOn',\n            'BOOLEAN_OPTION_YES' => 'booleanOptionYes',\n            'BOOLEAN_OPTION_0' => 'booleanOption0',\n            'BOOLEAN_OPTION_FALSE' => 'booleanOptionFalse',\n            'BOOLEAN_OPTION_OFF' => 'booleanOptionOff',\n            'BOOLEAN_OPTION_NO' => 'booleanOptionNo',\n        ]);\n        $this->assertTrue($savedConfiguration->booleanOption1);\n        $this->assertTrue($savedConfiguration->booleanOptionTrue);\n        $this->assertTrue($savedConfiguration->booleanOptionOn);\n        $this->assertTrue($savedConfiguration->booleanOptionYes);\n        $this->assertFalse($savedConfiguration->booleanOption0);\n        $this->assertFalse($savedConfiguration->booleanOptionFalse);\n        $this->assertFalse($savedConfiguration->booleanOptionOff);\n        $this->assertFalse($savedConfiguration->booleanOptionNo);\n    }\n}\n"
  },
  {
    "path": "tests/Unit/SetConfigTest.php",
    "content": "<?php\n\nnamespace Tests\\Unit;\n\nuse App\\Actions\\InstallBreeze;\nuse App\\Actions\\InstallJetstream;\nuse App\\Commands\\NewCommand;\nuse App\\Configuration\\CommandLineConfiguration;\nuse App\\Configuration\\SavedConfiguration;\nuse App\\Configuration\\SetConfig;\nuse App\\Configuration\\ShellConfiguration;\nuse App\\ConsoleWriter;\nuse App\\LamboException;\nuse Illuminate\\Support\\Facades\\File;\nuse Illuminate\\Support\\Str;\nuse Mockery;\nuse Mockery\\MockInterface;\nuse Tests\\Feature\\LamboTestEnvironment;\nuse Tests\\TestCase;\nuse Tests\\Unit\\Fakes\\FakeInput;\n\nclass SetConfigTest extends TestCase\n{\n    use LamboTestEnvironment;\n\n    /** @test */\n    function it_sets_the_top_level_domain()\n    {\n        File::shouldReceive('isFile')\n            ->with(config('home_dir') . '/.config/valet/config.json')\n            ->once()\n            ->andReturnTrue()\n            ->globally()\n            ->ordered();\n\n        File::shouldReceive('get')\n            ->with(config('home_dir') . '/.config/valet/config.json')\n            ->once()\n            ->andReturn('{\"tld\": \"mytld\"}')\n            ->globally()\n            ->ordered();\n\n        (new SetConfig(\n            $this->mock(CommandLineConfiguration::class),\n            $this->mock(SavedConfiguration::class),\n            $this->mock(ShellConfiguration::class),\n            app(ConsoleWriter::class),\n            new FakeInput()\n        ))(['tld' => null]);\n\n        $this->assertEquals('mytld', config('lambo.store.tld'));\n    }\n\n    /** @test */\n    function it_sets_the_top_level_domain_using_legacy_valet_config()\n    {\n        File::shouldReceive('isFile')\n            ->with(config('home_dir') . '/.config/valet/config.json')\n            ->once()\n            ->andReturnFalse()\n            ->globally()\n            ->ordered();\n\n        File::shouldReceive('isFile')\n            ->once()\n            ->with(config('home_dir') . '/.valet/config.json')\n            ->andReturnTrue()\n            ->globally()\n            ->ordered();\n\n        File::shouldReceive('get')\n            ->with(config('home_dir') . '/.valet/config.json')\n            ->once()\n            ->andReturn('{\"domain\": \"mytld\"}')\n            ->globally()\n            ->ordered();\n\n        (new SetConfig(\n            $this->mock(CommandLineConfiguration::class),\n            $this->mock(SavedConfiguration::class),\n            $this->mock(ShellConfiguration::class),\n            app(ConsoleWriter::class),\n            new FakeInput()\n        ))(['tld' => null]);\n\n        $this->assertEquals('mytld', config('lambo.store.tld'));\n    }\n\n    /** @test */\n    function it_throws_a_LamboException_if_valet_config_is_missing()\n    {\n        File::shouldReceive('isFile')\n            ->with(config('home_dir') . '/.config/valet/config.json')\n            ->once()\n            ->andReturnFalse()\n            ->globally()\n            ->ordered();\n\n        File::shouldReceive('isFile')\n            ->once()\n            ->with(config('home_dir') . '/.valet/config.json')\n            ->andReturnFalse()\n            ->globally()\n            ->ordered();\n\n        $this->expectException(LamboException::class);\n\n        (new SetConfig(\n            $this->mock(CommandLineConfiguration::class),\n            $this->mock(SavedConfiguration::class),\n            $this->mock(ShellConfiguration::class),\n            app(ConsoleWriter::class),\n            new FakeInput()\n        ))(['tld' => null]);\n    }\n\n    /** @test */\n    function it_prioritises_command_line_configuration()\n    {\n        $this->withValetTld();\n\n        $commandLineConfiguration = $this->mock(CommandLineConfiguration::class);\n        $commandLineConfiguration->testKey = 'command-line-parameter';\n\n        $savedConfiguration = $this->mock(SavedConfiguration::class);\n        $savedConfiguration->testKey = 'saved-config-parameter';\n\n        $shellConfiguration = $this->mock(ShellConfiguration::class);\n        $shellConfiguration->testKey = 'shell-environment-parameter';\n\n        (new SetConfig(\n            $commandLineConfiguration,\n            $savedConfiguration,\n            $shellConfiguration,\n            app(ConsoleWriter::class),\n            new FakeInput()\n        ))([\n            'testKey' => 'default',\n        ]);\n\n        $this->assertEquals('command-line-parameter', config('lambo.store.testKey'));\n    }\n\n    /** @test */\n    function it_prioritises_saved_configuration_over_shell_environment_configuration()\n    {\n        $this->withValetTld();\n\n        $commandLineConfiguration = $this->mock(CommandLineConfiguration::class);\n        $commandLineConfiguration->testKey = null;\n\n        $savedConfiguration = $this->mock(SavedConfiguration::class);\n        $savedConfiguration->testKey = 'saved-config-parameter';\n\n        $shellConfiguration = $this->mock(ShellConfiguration::class);\n        $shellConfiguration->testKey = 'shell-environment-parameter';\n\n        (new SetConfig(\n            $commandLineConfiguration,\n            $savedConfiguration,\n            $shellConfiguration,\n            app(ConsoleWriter::class),\n            new FakeInput()\n        ))([\n            'testKey' => 'default',\n        ]);\n\n        $this->assertEquals('saved-config-parameter', config('lambo.store.testKey'));\n    }\n\n    /** @test */\n    function it_prioritises_shell_environment_over_default_configuration()\n    {\n        $this->withValetTld();\n\n        $commandLineConfiguration = $this->mock(CommandLineConfiguration::class);\n        $commandLineConfiguration->testKey = null;\n\n        $savedConfiguration = $this->mock(SavedConfiguration::class);\n        $savedConfiguration->testKey = null;\n\n        $shellConfiguration = $this->mock(ShellConfiguration::class);\n        $shellConfiguration->testKey = 'shell-environment-parameter';\n\n        (new SetConfig(\n            $commandLineConfiguration,\n            $savedConfiguration,\n            $shellConfiguration,\n            app(ConsoleWriter::class),\n            new FakeInput()\n        ))([\n            'testKey' => 'default',\n        ]);\n\n        $this->assertEquals('shell-environment-parameter', config('lambo.store.testKey'));\n    }\n\n    /** @test */\n    function it_uses_a_default_configuration()\n    {\n        $this->withValetTld();\n\n        $commandLineConfiguration = $this->mock(CommandLineConfiguration::class);\n        $commandLineConfiguration->testKey = null;\n\n        $savedConfiguration = $this->mock(SavedConfiguration::class);\n        $savedConfiguration->testKey = null;\n\n        $shellConfiguration = $this->mock(ShellConfiguration::class);\n        $shellConfiguration->testKey = null;\n\n        (new SetConfig(\n            $commandLineConfiguration,\n            $savedConfiguration,\n            $shellConfiguration,\n            app(ConsoleWriter::class),\n            new FakeInput()\n        ))([\n            'testKey' => 'default',\n        ]);\n\n        $this->assertEquals('default', config('lambo.store.testKey'));\n    }\n\n    /** @test */\n    function it_replaces_tilda_in_root_path()\n    {\n        config(['home_dir' => '/home/user']);\n\n        $this->withValetTld();\n\n        $commandLineConfiguration = $this->mock(CommandLineConfiguration::class);\n        $commandLineConfiguration->root_path = '~/path/from/command/line';\n\n        (new SetConfig(\n            $commandLineConfiguration,\n            $this->mock(SavedConfiguration::class),\n            $this->mock(ShellConfiguration::class),\n            app(ConsoleWriter::class),\n            new FakeInput()\n        ))(['root_path' => getcwd()]);\n\n        $this->assertEquals('/home/user/path/from/command/line', config('lambo.store.root_path'));\n\n        config(['lambo.store' => []]);\n\n        $savedConfiguration = $this->mock(SavedConfiguration::class);\n        $savedConfiguration->root_path = '~/path/from/saved/configuration';\n\n        (new SetConfig(\n            $this->mock(CommandLineConfiguration::class),\n            $savedConfiguration,\n            $this->mock(ShellConfiguration::class),\n            app(ConsoleWriter::class),\n            new FakeInput()\n        ))(['root_path' => getcwd()]);\n\n        $this->assertEquals('/home/user/path/from/saved/configuration', config('lambo.store.root_path'));\n    }\n\n    /** @test */\n    function it_replaces_hyphens_with_underscores_in_database_names()\n    {\n        $this->withValetTld();\n        config(['home_dir' => '/home/user']);\n\n        $commandLineConfiguration = $this->mock(CommandLineConfiguration::class);\n        $commandLineConfiguration->project_name = 'foo';\n        $commandLineConfiguration->database_name = 'h-y-p-h-e-n-s';\n\n        (new SetConfig(\n            $commandLineConfiguration,\n            $this->mock(SavedConfiguration::class),\n            $this->mock(ShellConfiguration::class),\n            app(ConsoleWriter::class),\n            new FakeInput()\n        ))([\n            'root_path' => getcwd(),\n            'project_name' => null,\n            'database_name' => null,\n        ]);\n\n        $this->assertEquals('h_y_p_h_e_n_s', config('lambo.store.database_name'));\n    }\n\n    /** @test */\n    function it_sets_the_project_url()\n    {\n        $this->withValetTld('test-domain');\n\n        $commandLineConfiguration = $this->mock(CommandLineConfiguration::class);\n        $commandLineConfiguration->project_name = 'foo';\n\n        (new SetConfig(\n            $commandLineConfiguration,\n            $this->mock(SavedConfiguration::class),\n            $this->mock(ShellConfiguration::class),\n            app(ConsoleWriter::class),\n            new FakeInput()\n        ))([\n            'command' => NewCommand::class,\n            'tld' => null,\n            'project_name' => null,\n            'root_path' => '/some/path',\n            'valet_secure' => false,\n        ]);\n\n        $this->assertEquals('http://foo.test-domain', config('lambo.store.project_url'));\n\n        config(['lambo.store' => []]);\n\n        $commandLineConfiguration->valet_secure = true;\n\n        (new SetConfig(\n            $commandLineConfiguration,\n            $this->mock(SavedConfiguration::class),\n            $this->mock(ShellConfiguration::class),\n            app(ConsoleWriter::class),\n            new FakeInput()\n        ))([\n            'command' => NewCommand::class,\n            'tld' => null,\n            'project_name' => null,\n            'root_path' => '/some/path',\n            'valet_secure' => false,\n        ]);\n\n        $this->assertEquals('https://foo.test-domain', config('lambo.store.project_url'));\n    }\n\n    /** @test */\n    function it_sets_the_project_name()\n    {\n        $this->withValetTld();\n\n        $commandLineConfiguration = $this->mock(CommandLineConfiguration::class);\n        $commandLineConfiguration->project_name = 'foo';\n\n        (new SetConfig(\n            $commandLineConfiguration,\n            $this->mock(SavedConfiguration::class),\n            $this->mock(ShellConfiguration::class),\n            app(ConsoleWriter::class),\n            new FakeInput()\n        ))(['project_name' => null]);\n\n        $this->assertEquals('foo', config('lambo.store.project_name'));\n    }\n\n    /** @test */\n    function it_sets_the_project_path()\n    {\n        $this->withValetTld();\n        config(['home_dir' => '/home/user']);\n\n        $commandLineConfiguration = $this->mock(CommandLineConfiguration::class);\n        $commandLineConfiguration->root_path = '/path/from/command/line';\n        $commandLineConfiguration->project_name = 'my-project';\n\n        (new SetConfig(\n            $commandLineConfiguration,\n            $this->mock(SavedConfiguration::class),\n            $this->mock(ShellConfiguration::class),\n            app(ConsoleWriter::class),\n            new FakeInput()\n        ))([\n            'command' => NewCommand::class,\n            'root_path' => getcwd(),\n            'project_name' => null,\n        ]);\n\n        $this->assertEquals('/path/from/command/line/my-project', config('lambo.store.project_path'));\n    }\n\n    /** @test */\n    function it_sets_the_create_database_configuration()\n    {\n        $this->withValetTld();\n\n        $commandLineConfiguration = $this->mock(CommandLineConfiguration::class);\n        $savedConfiguration = $this->mock(SavedConfiguration::class);\n\n        $commandLineConfiguration->full = true;\n        $commandLineConfiguration->create_database = false;\n        (new SetConfig(\n            $commandLineConfiguration,\n            $savedConfiguration,\n            $this->mock(ShellConfiguration::class),\n            app(ConsoleWriter::class),\n            new FakeInput()\n        ))([\n            'tld' => null,\n            'full' => false,\n            'create_database' => false,\n        ]);\n        $this->assertTrue(config('lambo.store.create_database'));\n\n        config(['lambo.store' => []]);\n\n        $commandLineConfiguration->full = false;\n        $commandLineConfiguration->create_database = true;\n        (new SetConfig(\n            $commandLineConfiguration,\n            $savedConfiguration,\n            $this->mock(ShellConfiguration::class),\n            app(ConsoleWriter::class),\n            new FakeInput()\n        ))([\n            'tld' => null,\n            'full' => false,\n            'create_database' => false,\n        ]);\n        $this->assertTrue(config('lambo.store.create_database'));\n\n        config(['lambo.store' => []]);\n\n        $commandLineConfiguration->full = false;\n        $commandLineConfiguration->create_database = false;\n        (new SetConfig(\n            $commandLineConfiguration,\n            $savedConfiguration,\n            $this->mock(ShellConfiguration::class),\n            app(ConsoleWriter::class),\n            new FakeInput()\n        ))([\n            'tld' => null,\n            'full' => false,\n            'create_database' => false,\n        ]);\n        $this->assertFalse(config('lambo.store.create_database'));\n    }\n\n    /** @test */\n    function it_sets_the_migrate_database_configuration()\n    {\n        $this->withValetTld();\n\n        $commandLineConfiguration = $this->mock(CommandLineConfiguration::class);\n        $savedConfiguration = $this->mock(SavedConfiguration::class);\n\n        $commandLineConfiguration->full = true;\n        $commandLineConfiguration->migrate_database = false;\n        $commandLineConfiguration->inertia = false;\n        $commandLineConfiguration->livewire = false;\n        (new SetConfig(\n            $commandLineConfiguration,\n            $savedConfiguration,\n            $this->mock(ShellConfiguration::class),\n            app(ConsoleWriter::class),\n            new FakeInput()\n        ))([\n            'tld' => null,\n            'full' => false,\n            'migrate_database' => false,\n            'inertia' => false,\n            'livewire' => false,\n        ]);\n        $this->assertTrue(config('lambo.store.migrate_database'));\n\n        config(['lambo.store' => []]);\n\n        $commandLineConfiguration->full = false;\n        $commandLineConfiguration->migrate_database = true;\n        $commandLineConfiguration->inertia = false;\n        $commandLineConfiguration->livewire = false;\n        (new SetConfig(\n            $commandLineConfiguration,\n            $savedConfiguration,\n            $this->mock(ShellConfiguration::class),\n            app(ConsoleWriter::class),\n            new FakeInput()\n        ))([\n            'tld' => null,\n            'full' => false,\n            'migrate_database' => false,\n            'inertia' => false,\n            'livewire' => false,\n        ]);\n        $this->assertTrue(config('lambo.store.migrate_database'));\n\n        config(['lambo.store' => []]);\n\n        $commandLineConfiguration->full = false;\n        $commandLineConfiguration->migrate_database = false;\n        $commandLineConfiguration->inertia = false;\n        $commandLineConfiguration->livewire = false;\n        (new SetConfig(\n            $commandLineConfiguration,\n            $savedConfiguration,\n            $this->mock(ShellConfiguration::class),\n            app(ConsoleWriter::class),\n            new FakeInput()\n        ))([\n            'tld' => null,\n            'full' => false,\n            'migrate_database' => false,\n            'inertia' => false,\n            'livewire' => false,\n        ]);\n        $this->assertFalse(config('lambo.store.migrate_database'));\n\n        config(['lambo.store' => []]);\n\n        $commandLineConfiguration->full = false;\n        $commandLineConfiguration->migrate_database = false;\n        $commandLineConfiguration->inertia = true;\n        $commandLineConfiguration->livewire = false;\n        (new SetConfig(\n            $commandLineConfiguration,\n            $savedConfiguration,\n            $this->mock(ShellConfiguration::class),\n            app(ConsoleWriter::class),\n            new FakeInput()\n        ))([\n            'tld' => null,\n            'full' => false,\n            'migrate_database' => false,\n            'inertia' => false,\n            'livewire' => false,\n        ]);\n        $this->assertTrue(config('lambo.store.migrate_database'));\n\n        config(['lambo.store' => []]);\n\n        $commandLineConfiguration->full = false;\n        $commandLineConfiguration->migrate_database = false;\n        $commandLineConfiguration->inertia = false;\n        $commandLineConfiguration->livewire = true;\n        (new SetConfig(\n            $commandLineConfiguration,\n            $savedConfiguration,\n            $this->mock(ShellConfiguration::class),\n            app(ConsoleWriter::class),\n            new FakeInput()\n        ))([\n            'tld' => null,\n            'full' => false,\n            'migrate_database' => false,\n            'inertia' => false,\n            'livewire' => false,\n        ]);\n        $this->assertTrue(config('lambo.store.migrate_database'));\n    }\n\n    /** @test */\n    function it_sets_the_valet_link_configuration()\n    {\n        $this->withValetTld('test-domain');\n        $commandLineConfiguration = $this->mock(CommandLineConfiguration::class);\n        $savedConfiguration = $this->mock(SavedConfiguration::class);\n\n        $commandLineConfiguration->full = true;\n        $commandLineConfiguration->valet_link = false;\n        (new SetConfig(\n            $commandLineConfiguration,\n            $savedConfiguration,\n            $this->mock(ShellConfiguration::class),\n            app(ConsoleWriter::class),\n            new FakeInput()\n        ))([\n            'tld' => null,\n            'full' => false,\n            'valet_link' => false,\n        ]);\n        $this->assertTrue(config('lambo.store.valet_link'));\n\n        config(['lambo.store' => []]);\n\n        $commandLineConfiguration->full = false;\n        $commandLineConfiguration->valet_link = true;\n        (new SetConfig(\n            $commandLineConfiguration,\n            $savedConfiguration,\n            $this->mock(ShellConfiguration::class),\n            app(ConsoleWriter::class),\n            new FakeInput()\n        ))([\n            'tld' => null,\n            'full' => false,\n            'valet_link' => false,\n        ]);\n        $this->assertTrue(config('lambo.store.valet_link'));\n\n        config(['lambo.store' => []]);\n\n        $commandLineConfiguration->full = false;\n        $commandLineConfiguration->valet_link = false;\n        (new SetConfig(\n            $commandLineConfiguration,\n            $savedConfiguration,\n            $this->mock(ShellConfiguration::class),\n            app(ConsoleWriter::class),\n            new FakeInput()\n        ))([\n            'tld' => null,\n            'full' => false,\n            'valet_link' => false,\n        ]);\n        $this->assertFalse(config('lambo.store.valet_link'));\n    }\n\n    /** @test */\n    function it_sets_the_valet_secure_configuration()\n    {\n        $this->withValetTld('test-domain');\n        $commandLineConfiguration = $this->mock(CommandLineConfiguration::class);\n        $savedConfiguration = $this->mock(SavedConfiguration::class);\n\n        $commandLineConfiguration->full = true;\n        $commandLineConfiguration->valet_secure = false;\n        (new SetConfig(\n            $commandLineConfiguration,\n            $savedConfiguration,\n            $this->mock(ShellConfiguration::class),\n            app(ConsoleWriter::class),\n            new FakeInput()\n        ))([\n            'tld' => null,\n            'full' => true,\n            'valet_secure' => false,\n        ]);\n        $this->assertTrue(config('lambo.store.valet_secure'));\n\n        config(['lambo.store' => []]);\n\n        $commandLineConfiguration->full = false;\n        $commandLineConfiguration->valet_secure = true;\n        (new SetConfig(\n            $commandLineConfiguration,\n            $savedConfiguration,\n            $this->mock(ShellConfiguration::class),\n            app(ConsoleWriter::class),\n            new FakeInput()\n        ))([\n            'tld' => null,\n            'full' => false,\n            'valet_secure' => true,\n        ]);\n        $this->assertTrue(config('lambo.store.valet_secure'));\n\n        config(['lambo.store' => []]);\n\n        $commandLineConfiguration->full = false;\n        $commandLineConfiguration->valet_secure = false;\n        (new SetConfig(\n            $commandLineConfiguration,\n            $savedConfiguration,\n            $this->mock(ShellConfiguration::class),\n            app(ConsoleWriter::class),\n            new FakeInput()\n        ))([\n            'tld' => null,\n            'full' => false,\n            'valet_secure' => false,\n        ]);\n        $this->assertFalse(config('lambo.store.valet_secure'));\n    }\n\n    /**\n     * @test\n     * @group front-end-scaffolding\n     */\n    function it_sets_the_breeze_starter_kit_configuration()\n    {\n        $this->withValetTld('test-domain');\n\n        $commandLineConfiguration = $this->mock(CommandLineConfiguration::class);\n\n        foreach (InstallBreeze::VALID_STACKS as $stack) {\n            config(['lambo.store' => []]);\n            (new SetConfig(\n                $commandLineConfiguration,\n                $this->mock(SavedConfiguration::class),\n                $this->mock(ShellConfiguration::class),\n                app(ConsoleWriter::class),\n                new FakeInput([\n                    'breeze' => $stack,\n                ])\n            ))([\n                'breeze' => false,\n            ]);\n\n            static::assertEquals($stack, config('lambo.store.breeze'));\n            static::assertFalse(config('lambo.store.jetstream'));\n\n            if (in_array('--debug', $_SERVER['argv'], true)) {\n                $this->toSTDOUT(sprintf('[ Options ] -jetstream=%s', $stack));\n                $this->toSTDOUT(sprintf('[ Config  ] lambo.store.jetstream => %s', config('lambo.store.jetstream') ? config('lambo.store.jetstream') : 'false'));\n                $this->toSTDOUT(sprintf('[ Config  ] lambo.store.breeze => %s', config('lambo.store.breeze') ? config('lambo.store.breeze') : 'false'));\n                $this->toSTDOUT('-------------');\n            }\n        }\n    }\n\n    /**\n     * @test\n     * @group front-end-scaffolding\n     */\n    function it_sets_the_jetstream_starter_kit_configuration()\n    {\n        $this->withValetTld('test-domain');\n\n        $commandLineConfiguration = $this->mock(CommandLineConfiguration::class);\n\n        foreach (InstallJetstream::VALID_CONFIGURATIONS as $stack) {\n            config(['lambo.store' => []]);\n            (new SetConfig(\n                $commandLineConfiguration,\n                $this->mock(SavedConfiguration::class),\n                $this->mock(ShellConfiguration::class),\n                app(ConsoleWriter::class),\n                new FakeInput([\n                    'jetstream' => $stack,\n                ])\n            ))([\n                'jetstream' => false,\n            ]);\n\n            static::assertEquals($stack, config('lambo.store.jetstream'));\n            static::assertFalse(config('lambo.store.breeze'));\n\n            if (in_array('--debug', $_SERVER['argv'], true)) {\n                $this->toSTDOUT(sprintf('[ Options ] -jetstream=%s', $stack));\n                $this->toSTDOUT(sprintf('[ Config  ] lambo.store.jetstream => %s', config('lambo.store.jetstream') ? config('lambo.store.jetstream') : 'false'));\n                $this->toSTDOUT(sprintf('[ Config  ] lambo.store.breeze => %s', config('lambo.store.breeze') ? config('lambo.store.breeze') : 'false'));\n                $this->toSTDOUT('-------------');\n            }\n        }\n    }\n\n    /**\n     * @test\n     * @group front-end-scaffolding\n     */\n    function it_ensures_only_one_starter_kit_is_configured()\n    {\n        $this->withValetTld('test-domain');\n\n        $commandLineConfiguration = $this->mock(CommandLineConfiguration::class);\n\n        foreach (InstallBreeze::VALID_STACKS as $breezeStack) {\n            foreach (InstallJetstream::VALID_CONFIGURATIONS as $jetstreamStack) {\n                foreach (['None', 'Laravel Breeze', 'Laravel Jetstream',] as $stackChoice) {\n                    $consoleWriter = $this->mock(ConsoleWriter::class, function (MockInterface $consoleWriter) use ($stackChoice) {\n                        $consoleWriter->shouldReceive('newLine')->once()->globally()->ordered();\n                        $consoleWriter->shouldReceive('note')->once()->globally()->ordered();\n                        $consoleWriter->shouldReceive('choice')->once()->globally()->ordered()->andReturn($stackChoice);\n\n                        $stackChoice === 'None'\n                            ? $consoleWriter->shouldReceive('ok')->with('Skipping starter-kit installation.')->once()->globally()->ordered()\n                            : $consoleWriter->shouldReceive('ok')->with(\"Using {$stackChoice}\")->once()->globally()->ordered();\n                    });\n\n                    config(['lambo.store' => []]);\n                    (new SetConfig(\n                        $commandLineConfiguration,\n                        $this->mock(SavedConfiguration::class),\n                        $this->mock(ShellConfiguration::class),\n                        $consoleWriter,\n                        new FakeInput([\n                            'jetstream' => $jetstreamStack,\n                            'breeze' => $breezeStack,\n                        ])\n                    ))([\n                        'breeze' => false,\n                        'jetstream' => false,\n                    ]);\n\n                    switch ($stackChoice) {\n                        case 'Laravel Breeze':\n                            static::assertEquals($breezeStack, config('lambo.store.breeze'));\n                            static::assertFalse(config('lambo.store.jetstream'));\n                            break;\n                        case 'Laravel Jetstream':\n                            static::assertEquals($jetstreamStack, config('lambo.store.jetstream'));\n                            static::assertFalse(config('lambo.store.breeze'));\n                            break;\n                        case 'None':\n                            static::assertFalse(config('lambo.store.breeze'));\n                            static::assertFalse(config('lambo.store.jetstream'));\n                            break;\n                    }\n\n                    if (in_array('--debug', $_SERVER['argv'], true)) {\n                        $this->toSTDOUT(sprintf('[ Options ] --breeze=%s --jetstream=%s', $breezeStack, $jetstreamStack));\n                        $this->toSTDOUT(sprintf('[ Choice  ] %s', $stackChoice));\n                        $this->toSTDOUT(sprintf('[ Config  ] lambo.store.jetstream => %s', config('lambo.store.jetstream') ? config('lambo.store.jetstream') : 'false'));\n                        $this->toSTDOUT(sprintf('[ Config  ] lambo.store.breeze => %s', config('lambo.store.breeze') ? config('lambo.store.breeze') : 'false'));\n                        $this->toSTDOUT('-------------');\n                    }\n                }\n            }\n        }\n    }\n\n    /**\n     * @test\n     * @group front-end-scaffolding\n     */\n    function it_asks_for_clarification_when_breeze_configuration_is_invalid()\n    {\n        $this->withValetTld('test-domain');\n\n        $commandLineConfiguration = $this->mock(CommandLineConfiguration::class);\n\n        foreach ([null, '', 'invalid'] as $invalidStack) {\n            foreach (array_keys(InstallBreeze::VALID_STACKS) as $stackChoice) {\n                $consoleWriter = $this->mock(ConsoleWriter::class, function (MockInterface $consoleWriter) use ($stackChoice) {\n                    $consoleWriter->shouldReceive('note')->once()->globally()->ordered();\n                    $consoleWriter->shouldReceive('choice')->once()->globally()->ordered()\n                        ->with(Mockery::type('string'), array_keys(InstallBreeze::VALID_STACKS))\n                        ->andReturn($stackChoice);\n                    $consoleWriter->shouldReceive('ok')->once()->globally()->ordered();\n                });\n                config(['lambo.store' => []]);\n\n                (new SetConfig(\n                    $commandLineConfiguration,\n                    $this->mock(SavedConfiguration::class),\n                    $this->mock(ShellConfiguration::class),\n                    $consoleWriter,\n                    new FakeInput([\n                        'breeze' => $invalidStack,\n                    ])\n                ))([\n                    'breeze' => false,\n                ]);\n\n                static::assertEquals(Str::lower($stackChoice), config('lambo.store.breeze'));\n                static::assertFalse(config('lambo.store.jetstream'));\n\n                if (in_array('--debug', $_SERVER['argv'], true)) {\n                    $this->toSTDOUT(sprintf('[ Options ] %s', is_null($invalidStack) ? '--breeze' : \"--breeze={$invalidStack}\"));\n                    $this->toSTDOUT(sprintf('[ Choice  ] %s', $stackChoice));\n                    $this->toSTDOUT(sprintf('[ Config  ] lambo.store.jetstream => %s', config('lambo.store.jetstream') ? config('lambo.store.jetstream') : 'false'));\n                    $this->toSTDOUT(sprintf('[ Config  ] lambo.store.breeze => %s', config('lambo.store.breeze') ? config('lambo.store.breeze') : 'false'));\n                    $this->toSTDOUT('-------------');\n                }\n            }\n        }\n    }\n\n    /**\n     * @test\n     * @group front-end-scaffolding\n     */\n    function it_asks_for_clarification_when_jetstream_configuration_is_invalid()\n    {\n        $this->withValetTld('test-domain');\n\n        $commandLineConfiguration = $this->mock(CommandLineConfiguration::class);\n\n        foreach ([null, '', 'invalid'] as $invalidStack) {\n            foreach (array_keys(InstallJetstream::VALID_STACKS) as $stackChoice) {\n                foreach ([true, false] as $useTeams) {\n                    $consoleWriter = $this->mock(ConsoleWriter::class, function (MockInterface $consoleWriter) use ($useTeams, $stackChoice) {\n                        $consoleWriter->shouldReceive('note')->once()->globally()->ordered();\n                        $consoleWriter->shouldReceive('choice')->once()->globally()->ordered()\n                            ->with(Mockery::type('string'), array_keys(InstallJetstream::VALID_STACKS))\n                            ->andReturn($stackChoice);\n                        $consoleWriter->shouldReceive('confirm')->once()->globally()->ordered()->andReturn($useTeams);\n                        $consoleWriter->shouldReceive('ok')->once()->globally()->ordered();\n                    });\n                    config(['lambo.store' => []]);\n\n                    (new SetConfig(\n                        $commandLineConfiguration,\n                        $this->mock(SavedConfiguration::class),\n                        $this->mock(ShellConfiguration::class),\n                        $consoleWriter,\n                        new FakeInput([\n                            'jetstream' => $invalidStack,\n                        ])\n                    ))([\n                        'jetstream' => false,\n                    ]);\n\n                    static::assertEquals(Str::lower($stackChoice) . ($useTeams ? ',teams' : ''), config('lambo.store.jetstream'));\n                    static::assertFalse(config('lambo.store.breeze'));\n\n                    if (in_array('--debug', $_SERVER['argv'], true)) {\n                        $this->toSTDOUT(sprintf('[ Options ] %s', is_null($invalidStack) ? '--breeze' : \"--breeze={$invalidStack}\"));\n                        $this->toSTDOUT(sprintf('[ Choice  ] %s%s', Str::lower($stackChoice), $useTeams ? ' with teams' : ''));\n                        $this->toSTDOUT(sprintf('[ Config  ] lambo.store.jetstream => %s', config('lambo.store.jetstream') ? config('lambo.store.jetstream') : 'false'));\n                        $this->toSTDOUT(sprintf('[ Config  ] lambo.store.breeze => %s', config('lambo.store.breeze') ? config('lambo.store.breeze') : 'false'));\n                        $this->toSTDOUT('-------------');\n                    }\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tests/Unit/ShellConfigurationTest.php",
    "content": "<?php\n\nnamespace Tests\\Unit;\n\nuse App\\Configuration\\ShellConfiguration;\nuse Illuminate\\Support\\Arr;\nuse Tests\\TestCase;\n\nclass ShellConfigurationTest extends TestCase\n{\n    /** @test */\n    function it_gets_the_value_of_of_a_shell_environment_variable()\n    {\n        Arr::set($_SERVER, 'SHELL_ENVIRONMENT_VARIABLE', 'foo');\n\n        $shellConfiguration = new ShellConfiguration([\n            'SHELL_ENVIRONMENT_VARIABLE' => 'genericOption',\n        ]);\n\n        $this->assertEquals('foo', $shellConfiguration->genericOption);\n    }\n\n    /** @test */\n    function it_returns_null_if_a_shell_environment_variable_is_missing()\n    {\n        Arr::forget($_SERVER, 'SHELL_ENVIRONMENT_VARIABLE');\n\n        $shellConfiguration = new ShellConfiguration([\n            'SHELL_ENVIRONMENT_VARIABLE' => 'genericOption',\n        ]);\n\n        $this->assertNull($shellConfiguration->genericOption);\n    }\n\n    /** @test */\n    function it_returns_null_if_a_shell_environment_variable_is_empty()\n    {\n        Arr::set($_SERVER, 'SHELL_ENVIRONMENT_VARIABLE', '');\n\n        $shellConfiguration = new ShellConfiguration([\n            'SHELL_ENVIRONMENT_VARIABLE' => 'genericOption',\n        ]);\n\n        $this->assertNull($shellConfiguration->genericOption);\n    }\n\n    /** @test */\n    function it_returns_null_if_a_non_existent_property_is_requested()\n    {\n        $shellConfiguration = new ShellConfiguration([]);\n        $this->assertNull($shellConfiguration->foo);\n    }\n}\n"
  },
  {
    "path": "tlint.json",
    "content": "{\n    \"preset\": \"tighten\",\n    \"disabled\": [],\n    \"excluded\": [\n        \"node_modules\"\n    ]\n}\n"
  }
]