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

orchestral / workbench / 12326242523

14 Dec 2024 02:54AM UTC coverage: 92.965% (+0.1%) from 92.833%
12326242523

Pull #63

github

web-flow
Merge 38f85e738 into b8a55318e
Pull Request #63: Ensure generated `testbench.yaml` is configured with `App\\` and `Database\\` instead of `Workbench\App\\` and `Workbench\Database\\` based on configured namespaces

16 of 17 new or added lines in 2 files covered. (94.12%)

11 existing lines in 1 file now uncovered.

555 of 597 relevant lines covered (92.96%)

14.39 hits per line

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

86.39
/src/Console/DevToolCommand.php
1
<?php
2

3
namespace Orchestra\Workbench\Console;
4

5
use Composer\InstalledVersions;
6
use Illuminate\Console\Command;
7
use Illuminate\Filesystem\Filesystem;
8
use Illuminate\Support\Arr;
9
use Illuminate\Support\Collection;
10
use Orchestra\Testbench\Foundation\Console\Actions\EnsureDirectoryExists;
11
use Orchestra\Testbench\Foundation\Console\Actions\GeneratesFile;
12
use Orchestra\Workbench\Actions\DumpComposerAutoloads;
13
use Orchestra\Workbench\Actions\ModifyComposer;
14
use Orchestra\Workbench\Events\InstallEnded;
15
use Orchestra\Workbench\Events\InstallStarted;
16
use Orchestra\Workbench\Workbench;
17
use Symfony\Component\Console\Attribute\AsCommand;
18
use Symfony\Component\Console\Input\InputOption;
19

20
use function Orchestra\Testbench\join_paths;
21
use function Orchestra\Testbench\package_path;
22

23
#[AsCommand(name: 'workbench:devtool', description: 'Configure Workbench for package development')]
24
class DevToolCommand extends Command
25
{
26
    use Concerns\InteractsWithFiles;
27

28
    /**
29
     * Namespace prefix for Workbench environment.
30
     *
31
     * @var string
32
     */
33
    protected string $workbenchNamespacePrefix = 'Workbench\\';
34

35
    /**
36
     * Execute the console command.
37
     *
38
     * @return int
39
     */
40
    public function handle(Filesystem $filesystem)
41
    {
42
        $workingPath = package_path();
14✔
43

44
        event(new InstallStarted($this->input, $this->output, $this->components));
14✔
45

46
        $this->prepareWorkbenchDirectories($filesystem, $workingPath);
14✔
47
        $this->prepareWorkbenchNamespaces($filesystem, $workingPath);
14✔
48

49
        if ($this->option('install') === true && $this->option('skip-install') === false) {
14✔
50
            $this->call('workbench:install', [
8✔
51
                '--force' => $this->option('force'),
8✔
52
                '--no-devtool' => true,
8✔
53
                '--basic' => $this->option('basic'),
8✔
54
            ]);
8✔
55
        }
56

57
        return tap(Command::SUCCESS, function ($exitCode) use ($workingPath) {
14✔
58
            event(new InstallEnded($this->input, $this->output, $this->components, $exitCode));
14✔
59

60
            (new DumpComposerAutoloads($workingPath))->handle();
14✔
61
        });
14✔
62
    }
63

64
    /**
65
     * Prepare workbench directories.
66
     */
67
    protected function prepareWorkbenchDirectories(Filesystem $filesystem, string $workingPath): void
68
    {
69
        $workbenchWorkingPath = join_paths($workingPath, 'workbench');
14✔
70

71
        (new EnsureDirectoryExists(
14✔
72
            filesystem: $filesystem,
14✔
73
            components: $this->components,
14✔
74
        ))->handle(
14✔
75
            Collection::make([
14✔
76
                join_paths('app', 'Models'),
14✔
77
                join_paths('database', 'factories'),
14✔
78
                join_paths('database', 'migrations'),
14✔
79
                join_paths('database', 'seeders'),
14✔
80
            ])->when(
14✔
81
                $this->option('basic') === false,
14✔
82
                fn ($directories) => $directories->push(...['routes', join_paths('resources', 'views')])
14✔
83
            )->map(static fn ($directory) => join_paths($workbenchWorkingPath, $directory))
14✔
84
        );
14✔
85

86
        $this->callSilently('make:provider', [
14✔
87
            'name' => 'WorkbenchServiceProvider',
14✔
88
            '--preset' => 'workbench',
14✔
89
            '--force' => (bool) $this->option('force'),
14✔
90
        ]);
14✔
91

92
        $this->prepareWorkbenchDatabaseSchema($filesystem, $workbenchWorkingPath);
14✔
93

94
        if ($this->option('basic') === false) {
14✔
95
            foreach (['api', 'console', 'web'] as $route) {
10✔
96
                (new GeneratesFile(
10✔
97
                    filesystem: $filesystem,
10✔
98
                    components: $this->components,
10✔
99
                    force: (bool) $this->option('force'),
10✔
100
                ))->handle(
10✔
101
                    (string) Workbench::stubFile("routes.{$route}"),
10✔
102
                    join_paths($workbenchWorkingPath, 'routes', "{$route}.php")
10✔
103
                );
10✔
104
            }
105
        }
106
    }
107

108
    /**
109
     * Prepare workbench namespace to `composer.json`.
110
     */
111
    protected function prepareWorkbenchNamespaces(Filesystem $filesystem, string $workingPath): void
112
    {
113
        (new ModifyComposer($workingPath))
14✔
114
            ->handle(fn (array $content) => $this->appendScriptsToComposer(
14✔
115
                $this->appendAutoloadDevToComposer($content, $filesystem), $filesystem
14✔
116
            ));
14✔
117
    }
118

119
    /**
120
     * Prepare workbench database schema including user model, factory and seeder.
121
     */
122
    protected function prepareWorkbenchDatabaseSchema(Filesystem $filesystem, string $workingPath): void
123
    {
124
        $this->callSilently('make:user-model', [
14✔
125
            '--preset' => 'workbench',
14✔
126
            '--force' => (bool) $this->option('force'),
14✔
127
        ]);
14✔
128

129
        $this->callSilently('make:user-factory', [
14✔
130
            '--preset' => 'workbench',
14✔
131
            '--force' => (bool) $this->option('force'),
14✔
132
        ]);
14✔
133

134
        (new GeneratesFile(
14✔
135
            filesystem: $filesystem,
14✔
136
            components: $this->components,
14✔
137
            force: (bool) $this->option('force'),
14✔
138
        ))->handle(
14✔
139
            (string) Workbench::stubFile('seeders.database'),
14✔
140
            join_paths($workingPath, 'database', 'seeders', 'DatabaseSeeder.php')
14✔
141
        );
14✔
142

143
        if ($filesystem->isFile(join_paths($workingPath, 'database', 'factories', 'UserFactory.php'))) {
14✔
144
            $this->replaceInFile($filesystem, [
14✔
145
                'use Orchestra\Testbench\Factories\UserFactory;',
14✔
146
            ], [
14✔
147
                'use Workbench\Database\Factories\UserFactory;',
14✔
148
            ], join_paths($workingPath, 'database', 'seeders', 'DatabaseSeeder.php'));
14✔
149
        }
150
    }
151

152
    /**
153
     * Append `scripts` to `composer.json`.
154
     */
155
    protected function appendScriptsToComposer(array $content, Filesystem $filesystem): array
156
    {
157
        $hasScriptsSection = \array_key_exists('scripts', $content);
14✔
158
        $hasTestbenchDusk = InstalledVersions::isInstalled('orchestra/testbench-dusk');
14✔
159

160
        if (! $hasScriptsSection) {
14✔
161
            $content['scripts'] = [];
14✔
162
        }
163

164
        $postAutoloadDumpScripts = array_filter([
14✔
165
            '@clear',
14✔
166
            '@prepare',
14✔
167
            $hasTestbenchDusk ? '@dusk:install-chromedriver' : null,
14✔
168
        ]);
14✔
169

170
        if (! \array_key_exists('post-autoload-dump', $content['scripts'])) {
14✔
171
            $content['scripts']['post-autoload-dump'] = $postAutoloadDumpScripts;
14✔
172
        } else {
173
            $content['scripts']['post-autoload-dump'] = array_values(array_unique([
×
174
                ...$postAutoloadDumpScripts,
×
UNCOV
175
                ...Arr::wrap($content['scripts']['post-autoload-dump']),
×
UNCOV
176
            ]));
×
177
        }
178

179
        $content['scripts']['clear'] = '@php vendor/bin/testbench package:purge-skeleton --ansi';
14✔
180
        $content['scripts']['prepare'] = '@php vendor/bin/testbench package:discover --ansi';
14✔
181

182
        if ($hasTestbenchDusk) {
14✔
UNCOV
183
            $content['scripts']['dusk:install-chromedriver'] = '@php vendor/bin/dusk-updater detect --auto-update --ansi';
×
184
        }
185

186
        $content['scripts']['build'] = '@php vendor/bin/testbench workbench:build --ansi';
14✔
187
        $content['scripts']['serve'] = [
14✔
188
            'Composer\\Config::disableProcessTimeout',
14✔
189
            '@build',
14✔
190
            $hasTestbenchDusk && \defined('TESTBENCH_DUSK')
14✔
UNCOV
191
                ? '@php vendor/bin/testbench-dusk serve --ansi'
×
192
                : '@php vendor/bin/testbench serve --ansi',
14✔
193
        ];
14✔
194

195
        if (! \array_key_exists('lint', $content['scripts'])) {
14✔
196
            $lintScripts = [];
14✔
197

198
            if (InstalledVersions::isInstalled('laravel/pint')) {
14✔
199
                $lintScripts[] = '@php vendor/bin/pint --ansi';
14✔
UNCOV
200
            } elseif ($filesystem->isFile(Workbench::packagePath('pint.json'))) {
×
UNCOV
201
                $lintScripts[] = 'pint';
×
202
            }
203

204
            if (InstalledVersions::isInstalled('phpstan/phpstan')) {
14✔
205
                $lintScripts[] = '@php vendor/bin/phpstan analyse --verbose --ansi';
14✔
206
            }
207

208
            if (\count($lintScripts) > 0) {
14✔
209
                $content['scripts']['lint'] = $lintScripts;
14✔
210
            }
211
        }
212

213
        if (
214
            $filesystem->isFile(Workbench::packagePath('phpunit.xml'))
14✔
215
            || $filesystem->isFile(Workbench::packagePath('phpunit.xml.dist'))
14✔
216
        ) {
217
            if (! \array_key_exists('test', $content['scripts'])) {
×
218
                $content['scripts']['test'] = [
×
219
                    '@clear',
×
220
                    InstalledVersions::isInstalled('pestphp/pest')
×
221
                        ? '@php vendor/bin/pest'
×
UNCOV
222
                        : '@php vendor/bin/phpunit',
×
UNCOV
223
                ];
×
224
            }
225
        }
226

227
        return $content;
14✔
228
    }
229

230
    /**
231
     * Append `autoload-dev` to `composer.json`.
232
     */
233
    protected function appendAutoloadDevToComposer(array $content, Filesystem $filesystem): array
234
    {
235
        /** @var array{autoload-dev?: array{psr-4?: array<string, string>}} $content */
236
        if (! \array_key_exists('autoload-dev', $content)) {
14✔
237
            $content['autoload-dev'] = [];
14✔
238
        }
239

240
        /** @var array{autoload-dev: array{psr-4?: array<string, string>}} $content */
241
        if (! \array_key_exists('psr-4', $content['autoload-dev'])) {
14✔
242
            $content['autoload-dev']['psr-4'] = [];
14✔
243
        }
244

245
        if ($this->components->confirm('Prefix with `Workbench` namespace?', default: false) === false) {
14✔
NEW
UNCOV
246
            $this->workbenchNamespacePrefix = '';
×
247
        }
248

249
        $namespaces = [
14✔
250
            'workbench/app/' => $this->workbenchNamespacePrefix.'App\\',
14✔
251
            'workbench/database/factories/' => $this->workbenchNamespacePrefix.'Database\\Factories\\',
14✔
252
            'workbench/database/seeders/' => $this->workbenchNamespacePrefix.'Database\\Seeders\\',
14✔
253
        ];
14✔
254

255
        $autoloads = array_flip($content['autoload-dev']['psr-4']);
14✔
256

257
        foreach ($namespaces as $path => $namespace) {
14✔
258
            if (! \array_key_exists($path, $autoloads)) {
14✔
259
                $content['autoload-dev']['psr-4'][$namespace] = $path;
14✔
260

261
                $this->components->task(\sprintf(
14✔
262
                    'Added [%s] for [%s] to Composer', $namespace, './'.rtrim($path, '/')
14✔
263
                ));
14✔
264
            } else {
265
                $this->components->twoColumnDetail(
×
266
                    \sprintf('Composer already contains [%s] path assigned to [%s] namespace', './'.rtrim($path, '/'), $autoloads[$path]),
×
UNCOV
267
                    '<fg=yellow;options=bold>SKIPPED</>'
×
UNCOV
268
                );
×
269
            }
270
        }
271

272
        return $content;
14✔
273
    }
274

275
    /**
276
     * Get the console command options.
277
     *
278
     * @return array
279
     */
280
    protected function getOptions()
281
    {
282
        return [
14✔
283
            ['force', 'f', InputOption::VALUE_NONE, 'Overwrite any existing files'],
14✔
284
            ['install', null, InputOption::VALUE_NEGATABLE, 'Run Workbench installation'],
14✔
285
            ['basic', null, InputOption::VALUE_NONE, 'Workbench installation without discovers and routes'],
14✔
286

287
            /** @deprecated */
288
            ['skip-install', null, InputOption::VALUE_NONE, 'Skipped Workbench installation (deprecated)'],
14✔
289
        ];
14✔
290
    }
291
}
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