#!/usr/bin/env php $version) { if (in_array($name, $names, true)) { unset($data['require-dev'][$name]); } } file_put_contents(__DIR__.'/composer.json', json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); } function remove_composer_script($scriptName) { $data = json_decode(file_get_contents(__DIR__.'/composer.json'), true); foreach ($data['scripts'] as $name => $script) { if ($scriptName === $name) { unset($data['scripts'][$name]); break; } } file_put_contents(__DIR__.'/composer.json', json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); } function remove_readme_paragraphs(string $file): void { $contents = file_get_contents($file); file_put_contents( $file, preg_replace('/.*/s', '', $contents) ?: $contents ); } function safeUnlink(string $filename) { if (file_exists($filename) && is_file($filename)) { unlink($filename); } } function determineSeparator(string $path): string { return str_replace('/', DIRECTORY_SEPARATOR, $path); } function replaceForWindows(): array { return preg_split('/\\r\\n|\\r|\\n/', run('dir /S /B * | findstr /v /i .git\ | findstr /v /i .vendor\ | findstr /v /i '.basename(__FILE__).' | findstr /r /i /M /F:/ ":author :vendor :package VendorName skeleton migration_table_name vendor_name vendor_slug author@domain.com"')); } function replaceForAllOtherOSes(): array { return explode(PHP_EOL, run('grep -E -r -l -i ":author|:vendor|:package|VendorName|skeleton|migration_table_name|vendor_name|vendor_slug|author@domain.com" --exclude-dir=vendor ./* ./.github/* | grep -v '.basename(__FILE__))); } function getGitHubApiEndpoint(string $endpoint): ?stdClass { try { $curl = curl_init("https://api.github.com/{$endpoint}"); curl_setopt_array($curl, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_FOLLOWLOCATION => true, CURLOPT_HTTPGET => true, CURLOPT_HTTPHEADER => [ 'User-Agent: spatie-configure-script/1.0', ], ]); $response = curl_exec($curl); $statusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); curl_close($curl); if ($statusCode === 200) { return json_decode($response); } } catch (Exception $e) { // ignore } return null; } function searchCommitsForGitHubUsername(): string { $authorName = strtolower(trim(shell_exec('git config user.name'))); $committersRaw = shell_exec("git log --author='@users.noreply.github.com' --pretty='%an:%ae' --reverse"); $committersLines = explode("\n", $committersRaw ?? ''); $committers = array_filter(array_map(function ($line) use ($authorName) { $line = trim($line); [$name, $email] = explode(':', $line) + [null, null]; return [ 'name' => $name, 'email' => $email, 'isMatch' => strtolower($name) === $authorName && ! str_contains($name, '[bot]'), ]; }, $committersLines), fn ($item) => $item['isMatch']); if (empty($committers)) { return ''; } $firstCommitter = reset($committers); return explode('@', $firstCommitter['email'])[0] ?? ''; } function guessGitHubUsernameUsingCli() { try { if (preg_match('/ogged in to github\.com as ([a-zA-Z-_]+).+/', shell_exec('gh auth status -h github.com 2>&1'), $matches)) { return $matches[1]; } } catch (Exception $e) { // ignore } return ''; } function guessGitHubUsername(): string { $username = searchCommitsForGitHubUsername(); if (! empty($username)) { return $username; } $username = guessGitHubUsernameUsingCli(); if (! empty($username)) { return $username; } // fall back to using the username from the git remote $remoteUrl = shell_exec('git config remote.origin.url') ?? ''; $remoteUrlParts = explode('/', str_replace(':', '/', trim($remoteUrl))); return $remoteUrlParts[1] ?? ''; } function guessGitHubVendorInfo($authorName, $username): array { $remoteUrl = shell_exec('git config remote.origin.url') ?? ''; $remoteUrlParts = explode('/', str_replace(':', '/', trim($remoteUrl))); if (! isset($remoteUrlParts[1])) { return [$authorName, $username]; } $response = getGitHubApiEndpoint("orgs/{$remoteUrlParts[1]}"); if ($response === null) { return [$authorName, $username]; } return [$response->name ?? $authorName, $response->login ?? $username]; } $gitName = run('git config user.name'); $authorName = ask('Author name', $gitName); $gitEmail = run('git config user.email'); $authorEmail = ask('Author email', $gitEmail); $authorUsername = ask('Author username', guessGitHubUsername()); $guessGitHubVendorInfo = guessGitHubVendorInfo($authorName, $authorUsername); $vendorName = ask('Vendor name', $guessGitHubVendorInfo[0]); $vendorUsername = ask('Vendor username', $guessGitHubVendorInfo[1] ?? slugify($vendorName)); $vendorSlug = slugify($vendorUsername); $vendorNamespace = str_replace('-', '', ucwords($vendorName)); $vendorNamespace = ask('Vendor namespace', $vendorNamespace); $currentDirectory = getcwd(); $folderName = basename($currentDirectory); $packageName = ask('Package name', $folderName); $packageSlug = slugify($packageName); $packageSlugWithoutPrefix = remove_prefix('laravel-', $packageSlug); $className = title_case($packageName); $className = ask('Class name', $className); $variableName = lcfirst($className); $description = ask('Package description', "This is my package {$packageSlug}"); $usePhpStan = confirm('Enable PhpStan?', true); $useLaravelPint = confirm('Enable Laravel Pint?', true); $useDependabot = confirm('Enable Dependabot?', true); $useLaravelRay = confirm('Use Ray for debugging?', true); $useUpdateChangelogWorkflow = confirm('Use automatic changelog updater workflow?', true); writeln('------'); writeln("Author : {$authorName} ({$authorUsername}, {$authorEmail})"); writeln("Vendor : {$vendorName} ({$vendorSlug})"); writeln("Package : {$packageSlug} <{$description}>"); writeln("Namespace : {$vendorNamespace}\\{$className}"); writeln("Class name : {$className}"); writeln('---'); writeln('Packages & Utilities'); writeln('Use Laravel/Pint : '.($useLaravelPint ? 'yes' : 'no')); writeln('Use Larastan/PhpStan : '.($usePhpStan ? 'yes' : 'no')); writeln('Use Dependabot : '.($useDependabot ? 'yes' : 'no')); writeln('Use Ray App : '.($useLaravelRay ? 'yes' : 'no')); writeln('Use Auto-Changelog : '.($useUpdateChangelogWorkflow ? 'yes' : 'no')); writeln('------'); writeln('This script will replace the above values in all relevant files in the project directory.'); if (! confirm('Modify files?', true)) { exit(1); } $files = (str_starts_with(strtoupper(PHP_OS), 'WIN') ? replaceForWindows() : replaceForAllOtherOSes()); foreach ($files as $file) { replace_in_file($file, [ ':author_name' => $authorName, ':author_username' => $authorUsername, 'author@domain.com' => $authorEmail, ':vendor_name' => $vendorName, ':vendor_slug' => $vendorSlug, 'VendorName' => $vendorNamespace, ':package_name' => $packageName, ':package_slug' => $packageSlug, ':package_slug_without_prefix' => $packageSlugWithoutPrefix, 'Skeleton' => $className, 'skeleton' => $packageSlug, 'migration_table_name' => title_snake($packageSlug), 'variable' => $variableName, ':package_description' => $description, ]); match (true) { str_contains($file, determineSeparator('src/Skeleton.php')) => rename($file, determineSeparator('./src/'.$className.'.php')), str_contains($file, determineSeparator('src/SkeletonServiceProvider.php')) => rename($file, determineSeparator('./src/'.$className.'ServiceProvider.php')), str_contains($file, determineSeparator('src/Facades/Skeleton.php')) => rename($file, determineSeparator('./src/Facades/'.$className.'.php')), str_contains($file, determineSeparator('src/Commands/SkeletonCommand.php')) => rename($file, determineSeparator('./src/Commands/'.$className.'Command.php')), str_contains($file, determineSeparator('database/migrations/create_skeleton_table.php.stub')) => rename($file, determineSeparator('./database/migrations/create_'.title_snake($packageSlugWithoutPrefix).'_table.php.stub')), str_contains($file, determineSeparator('config/skeleton.php')) => rename($file, determineSeparator('./config/'.$packageSlugWithoutPrefix.'.php')), str_contains($file, 'README.md') => remove_readme_paragraphs($file), default => [], }; } if (! $useLaravelPint) { safeUnlink(__DIR__.'/.github/workflows/fix-php-code-style-issues.yml'); safeUnlink(__DIR__.'/pint.json'); } if (! $usePhpStan) { safeUnlink(__DIR__.'/phpstan.neon.dist'); safeUnlink(__DIR__.'/phpstan-baseline.neon'); safeUnlink(__DIR__.'/.github/workflows/phpstan.yml'); remove_composer_deps([ 'phpstan/extension-installer', 'phpstan/phpstan-deprecation-rules', 'phpstan/phpstan-phpunit', 'larastan/larastan', ]); remove_composer_script('phpstan'); } if (! $useDependabot) { safeUnlink(__DIR__.'/.github/dependabot.yml'); safeUnlink(__DIR__.'/.github/workflows/dependabot-auto-merge.yml'); } if (! $useLaravelRay) { remove_composer_deps(['spatie/laravel-ray']); } if (! $useUpdateChangelogWorkflow) { safeUnlink(__DIR__.'/.github/workflows/update-changelog.yml'); } confirm('Execute `composer install` and run tests?') && run('composer install && composer test'); confirm('Let this script delete itself?', true) && unlink(__FILE__);