• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

orchestral / workbench / 12402649321

18 Dec 2024 10:48PM UTC coverage: 91.602% (+0.01%) from 91.589%
12402649321

Pull #69

github

web-flow
Merge 57172861e into c490ec92a
Pull Request #69: Fixes generated namespace via `workbench:install`

3 of 4 new or added lines in 2 files covered. (75.0%)

23 existing lines in 2 files now uncovered.

589 of 643 relevant lines covered (91.6%)

14.92 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.15
/src/Console/InstallCommand.php
1
<?php
2

3
namespace Orchestra\Workbench\Console;
4

5
use Composer\InstalledVersions;
6
use Illuminate\Console\Command;
7
use Illuminate\Contracts\Console\PromptsForMissingInput;
8
use Illuminate\Filesystem\Filesystem;
9
use Illuminate\Support\Collection;
10
use Orchestra\Testbench\Foundation\Console\Actions\GeneratesFile;
11
use Orchestra\Workbench\Workbench;
12
use Symfony\Component\Console\Attribute\AsCommand;
13
use Symfony\Component\Console\Input\InputInterface;
14
use Symfony\Component\Console\Input\InputOption;
15
use Symfony\Component\Console\Output\OutputInterface;
16

17
use function Laravel\Prompts\confirm;
18
use function Laravel\Prompts\select;
19
use function Orchestra\Testbench\join_paths;
20
use function Orchestra\Testbench\package_path;
21

22
#[AsCommand(name: 'workbench:install', description: 'Setup Workbench for package development')]
23
class InstallCommand extends Command implements PromptsForMissingInput
24
{
25
    /**
26
     * The `testbench.yaml` default configuration file.
27
     */
28
    public static ?string $configurationBaseFile = null;
29

30
    /**
31
     * Determine if Package also uses Testbench Dusk.
32
     */
33
    protected ?bool $hasTestbenchDusk = null;
34

35
    /** {@inheritDoc} */
36
    #[\Override]
37
    protected function initialize(InputInterface $input, OutputInterface $output)
38
    {
39
        $this->hasTestbenchDusk = InstalledVersions::isInstalled('orchestra/testbench-dusk');
23✔
40

41
        parent::initialize($input, $output);
23✔
42
    }
43

44
    /**
45
     * Execute the console command.
46
     *
47
     * @return int
48
     */
49
    public function handle(Filesystem $filesystem)
50
    {
51
        if (! $this->option('skip-devtool')) {
23✔
52
            $devtool = match (true) {
23✔
53
                \is_bool($this->option('devtool')) => $this->option('devtool'),
23✔
54
                default => $this->components->confirm('Install Workbench DevTool?', true),
×
55
            };
23✔
56

57
            if ($devtool === true) {
23✔
58
                $this->call('workbench:devtool', [
4✔
59
                    '--force' => $this->option('force'),
4✔
60
                    '--no-install' => true,
4✔
61
                    '--basic' => $this->option('basic'),
4✔
62
                ]);
4✔
63
            }
64
        }
65

66
        $workingPath = package_path();
23✔
67

68
        $this->copyTestbenchConfigurationFile($filesystem, $workingPath);
23✔
69
        $this->copyTestbenchDotEnvFile($filesystem, $workingPath);
23✔
70
        $this->prepareWorkbenchDirectories($filesystem, $workingPath);
23✔
71

72
        $this->replaceDefaultLaravelSkeletonInTestbenchConfigurationFile($filesystem, $workingPath);
23✔
73

74
        $this->call('workbench:create-sqlite-db', ['--force' => true]);
23✔
75

76
        return Command::SUCCESS;
23✔
77
    }
78

79
    /**
80
     * Prepare workbench directories.
81
     */
82
    protected function prepareWorkbenchDirectories(Filesystem $filesystem, string $workingPath): void
83
    {
84
        if (! $this->input->isInteractive()) {
23✔
85
            return;
1✔
86
        }
87

88
        $workbenchWorkingPath = join_paths($workingPath, 'workbench');
22✔
89

90
        foreach (['app' => true, 'providers' => false] as $bootstrap => $default) {
22✔
91
            if (! confirm("Generate `workbench/bootstrap/{$bootstrap}.php` file?", default: $default)) {
22✔
92
                continue;
22✔
93
            }
94

NEW
95
            (new GeneratesFile(
×
96
                filesystem: $filesystem,
×
97
                components: $this->components,
×
98
                force: (bool) $this->option('force'),
×
99
            ))->handle(
×
100
                (string) realpath(join_paths(__DIR__, 'stubs', 'bootstrap', "{$bootstrap}.php")),
×
101
                join_paths($workbenchWorkingPath, 'bootstrap', "{$bootstrap}.php")
×
102
            );
×
103
        }
104
    }
105

106
    /**
107
     * Copy the "testbench.yaml" file.
108
     */
109
    protected function copyTestbenchConfigurationFile(Filesystem $filesystem, string $workingPath): void
110
    {
111
        $from = ! \is_null(static::$configurationBaseFile)
23✔
112
            ? (string) realpath(static::$configurationBaseFile)
×
113
            : (string) Workbench::stubFile($this->option('basic') === true ? 'config.basic' : 'config');
23✔
114

115
        $to = join_paths($workingPath, 'testbench.yaml');
23✔
116

117
        (new GeneratesFile(
23✔
118
            filesystem: $filesystem,
23✔
119
            components: $this->components,
23✔
120
            force: (bool) $this->option('force'),
23✔
121
        ))->handle($from, $to);
23✔
122

123
        $workbenchAppNamespacePrefix = Workbench::detectNamespace('app') ?? 'Workbench\App\\';
23✔
124
        $workbenchSeederNamespacePrefix = Workbench::detectNamespace('database/seeders') ?? 'Workbench\Database\Seeders\\';
23✔
125

126
        $serviceProvider = \sprintf('%sProviders\WorkbenchServiceProvider', $workbenchAppNamespacePrefix);
23✔
127
        $databaseSeeder = \sprintf('%sDatabaseSeeder', $workbenchSeederNamespacePrefix);
23✔
128

129
        $filesystem->replaceInFile(
23✔
130
            [
23✔
131
                '{{WorkbenchAppNamespace}}',
23✔
132
                '{{ WorkbenchAppNamespace }}',
23✔
133
                '{{WorkbenchSeederNamespace}}',
23✔
134
                '{{ WorkbenchSeederNamespace }}',
23✔
135

136
                '{{WorkbenchServiceProvider}}',
23✔
137
                '{{ WorkbenchServiceProvider }}',
23✔
138
                'Workbench\App\Providers\WorkbenchServiceProvider',
23✔
139

140
                '{{WorkbenchDatabaseSeeder}}',
23✔
141
                '{{ WorkbenchDatabaseSeeder }}',
23✔
142
                'Workbench\Database\Seeders\DatabaseSeeder',
23✔
143

144
                '    - migrate-fresh',
23✔
145
            ],
23✔
146
            [
23✔
147
                $workbenchAppNamespacePrefix,
23✔
148
                $workbenchAppNamespacePrefix,
23✔
149
                $workbenchSeederNamespacePrefix,
23✔
150
                $workbenchSeederNamespacePrefix,
23✔
151

152
                $serviceProvider,
23✔
153
                $serviceProvider,
23✔
154
                $serviceProvider,
23✔
155

156
                $databaseSeeder,
23✔
157
                $databaseSeeder,
23✔
158
                $databaseSeeder,
23✔
159

160
                $databaseSeeder === 'Database\Seeders\DatabaseSeeder'
23✔
UNCOV
161
                    ? '    - migrate-fresh'
×
162
                    : '    - migrate-fresh:'.PHP_EOL.'        --seed: true',
23✔
163
            ],
23✔
164
            $to
23✔
165
        );
23✔
166
    }
167

168
    /**
169
     * Copy the ".env" file.
170
     */
171
    protected function copyTestbenchDotEnvFile(Filesystem $filesystem, string $workingPath): void
172
    {
173
        $workbenchWorkingPath = join_paths($workingPath, 'workbench');
23✔
174

175
        $from = $this->laravel->basePath('.env.example');
23✔
176

177
        if (! $filesystem->isFile($this->laravel->basePath('.env.example'))) {
23✔
UNCOV
178
            return;
×
179
        }
180

181
        /** @var \Illuminate\Support\Collection<int, string> $choices */
182
        $choices = Collection::make($this->environmentFiles())
23✔
183
            ->reject(static fn ($file) => $filesystem->isFile(join_paths($workbenchWorkingPath, $file)))
23✔
184
            ->values();
23✔
185

186
        if (! $this->option('force') && $choices->isEmpty()) {
23✔
187
            $this->components->twoColumnDetail(
1✔
188
                'File [.env] already exists', '<fg=yellow;options=bold>SKIPPED</>'
1✔
189
            );
1✔
190

191
            return;
1✔
192
        }
193

194
        /** @var string|null $targetEnvironmentFile */
195
        $targetEnvironmentFile = $this->input->isInteractive()
22✔
196
            ? select(
22✔
197
                "Export '.env' file as?",
22✔
198
                $choices->prepend('Skip exporting .env'), // @phpstan-ignore argument.type
22✔
199
            ) : null;
22✔
200

201
        if (\in_array($targetEnvironmentFile, [null, 'Skip exporting .env'])) {
22✔
202
            return;
7✔
203
        }
204

205
        $filesystem->ensureDirectoryExists($workbenchWorkingPath);
15✔
206

207
        $this->generateSeparateEnvironmentFileForTestbenchDusk($filesystem, $workbenchWorkingPath, $targetEnvironmentFile);
15✔
208

209
        (new GeneratesFile(
15✔
210
            filesystem: $filesystem,
15✔
211
            components: $this->components,
15✔
212
            force: (bool) $this->option('force'),
15✔
213
        ))->handle(
15✔
214
            $from,
15✔
215
            join_paths($workbenchWorkingPath, $targetEnvironmentFile)
15✔
216
        );
15✔
217

218
        (new GeneratesFile(
15✔
219
            filesystem: $filesystem,
15✔
220
            force: (bool) $this->option('force'),
15✔
221
        ))->handle(
15✔
222
            (string) Workbench::stubFile('gitignore'),
15✔
223
            join_paths($workbenchWorkingPath, '.gitignore')
15✔
224
        );
15✔
225
    }
226

227
    /**
228
     * Replace the default `laravel` skeleton for Testbench Dusk.
229
     *
230
     * @codeCoverageIgnore
231
     */
232
    protected function replaceDefaultLaravelSkeletonInTestbenchConfigurationFile(Filesystem $filesystem, string $workingPath): void
233
    {
234
        if ($this->hasTestbenchDusk === false) {
235
            return;
236
        }
237

238
        $filesystem->replaceInFile(["laravel: '@testbench'"], ["laravel: '@testbench-dusk'"], join_paths($workingPath, 'testbench.yaml'));
239
    }
240

241
    /**
242
     * Generate separate `.env.dusk` equivalent for Testbench Dusk.
243
     *
244
     * @codeCoverageIgnore
245
     */
246
    protected function generateSeparateEnvironmentFileForTestbenchDusk(Filesystem $filesystem, string $workbenchWorkingPath, string $targetEnvironmentFile): void
247
    {
248
        if ($this->hasTestbenchDusk === false) {
249
            return;
250
        }
251

252
        if ($this->components->confirm('Create separate environment file for Testbench Dusk?', false)) {
253
            (new GeneratesFile(
254
                filesystem: $filesystem,
255
                components: $this->components,
256
                force: (bool) $this->option('force'),
257
            ))->handle(
258
                $this->laravel->basePath('.env.example'),
259
                join_paths($workbenchWorkingPath, str_replace('.env', '.env.dusk', $targetEnvironmentFile))
260
            );
261
        }
262
    }
263

264
    /**
265
     * Get possible environment files.
266
     *
267
     * @return array<int, string>
268
     */
269
    protected function environmentFiles(): array
270
    {
271
        return [
23✔
272
            '.env',
23✔
273
            '.env.example',
23✔
274
            '.env.dist',
23✔
275
        ];
23✔
276
    }
277

278
    /**
279
     * Prompt the user for any missing arguments.
280
     *
281
     * @return void
282
     */
283
    protected function promptForMissingArguments(InputInterface $input, OutputInterface $output)
284
    {
285
        $devtool = null;
22✔
286

287
        if ($input->getOption('skip-devtool') === true) {
22✔
UNCOV
288
            $devtool = false;
×
289
        } elseif (\is_null($input->getOption('devtool'))) {
22✔
290
            $devtool = confirm('Run Workbench DevTool installation?', true);
1✔
291
        }
292

293
        if (! \is_null($devtool)) {
22✔
294
            $input->setOption('devtool', $devtool);
1✔
295
        }
296
    }
297

298
    /**
299
     * Get the console command options.
300
     *
301
     * @return array
302
     */
303
    protected function getOptions()
304
    {
305
        return [
23✔
306
            ['force', 'f', InputOption::VALUE_NONE, 'Overwrite any existing files'],
23✔
307
            ['devtool', null, InputOption::VALUE_NEGATABLE, 'Run DevTool installation'],
23✔
308
            ['basic', null, InputOption::VALUE_NONE, 'Skipped routes and discovers installation'],
23✔
309

310
            /** @deprecated */
311
            ['skip-devtool', null, InputOption::VALUE_NONE, 'Skipped DevTool installation (deprecated)'],
23✔
312
        ];
23✔
313
    }
314
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc