Skip to content

Commit 95bf749

Browse files
authoredOct 10, 2024··
Initial commit
0 parents  commit 95bf749

33 files changed

+1139
-0
lines changed
 

‎.editorconfig

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
root = true
2+
3+
[*]
4+
charset = utf-8
5+
end_of_line = lf
6+
indent_size = 4
7+
indent_style = space
8+
insert_final_newline = true
9+
trim_trailing_whitespace = true
10+
11+
[*.md]
12+
trim_trailing_whitespace = false
13+
14+
[*.{yml,yaml}]
15+
indent_size = 2

‎.gitattributes

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Path-based git attributes
2+
# https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html
3+
4+
# Ignore all test and documentation with "export-ignore".
5+
/.github export-ignore
6+
/.gitattributes export-ignore
7+
/.gitignore export-ignore
8+
/phpunit.xml.dist export-ignore
9+
/art export-ignore
10+
/docs export-ignore
11+
/tests export-ignore
12+
/workbench export-ignore
13+
/.editorconfig export-ignore
14+
/.php_cs.dist.php export-ignore
15+
/psalm.xml export-ignore
16+
/psalm.xml.dist export-ignore
17+
/testbench.yaml export-ignore
18+
/UPGRADING.md export-ignore
19+
/phpstan.neon.dist export-ignore
20+
/phpstan-baseline.neon export-ignore

‎.github/FUNDING.yml

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
github: :vendor_name

‎.github/ISSUE_TEMPLATE/bug.yml

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
name: Bug Report
2+
description: Report an Issue or Bug with the Package
3+
title: "[Bug]: "
4+
labels: ["bug"]
5+
body:
6+
- type: markdown
7+
attributes:
8+
value: |
9+
We're sorry to hear you have a problem. Can you help us solve it by providing the following details.
10+
- type: textarea
11+
id: what-happened
12+
attributes:
13+
label: What happened?
14+
description: What did you expect to happen?
15+
placeholder: I cannot currently do X thing because when I do, it breaks X thing.
16+
validations:
17+
required: true
18+
- type: textarea
19+
id: how-to-reproduce
20+
attributes:
21+
label: How to reproduce the bug
22+
description: How did this occur, please add any config values used and provide a set of reliable steps if possible.
23+
placeholder: When I do X I see Y.
24+
validations:
25+
required: true
26+
- type: input
27+
id: package-version
28+
attributes:
29+
label: Package Version
30+
description: What version of our Package are you running? Please be as specific as possible
31+
placeholder: 2.0.0
32+
validations:
33+
required: true
34+
- type: input
35+
id: php-version
36+
attributes:
37+
label: PHP Version
38+
description: What version of PHP are you running? Please be as specific as possible
39+
placeholder: 8.2.0
40+
validations:
41+
required: true
42+
- type: input
43+
id: laravel-version
44+
attributes:
45+
label: Laravel Version
46+
description: What version of Laravel are you running? Please be as specific as possible
47+
placeholder: 9.0.0
48+
validations:
49+
required: true
50+
- type: dropdown
51+
id: operating-systems
52+
attributes:
53+
label: Which operating systems does with happen with?
54+
description: You may select more than one.
55+
multiple: true
56+
options:
57+
- macOS
58+
- Windows
59+
- Linux
60+
- type: textarea
61+
id: notes
62+
attributes:
63+
label: Notes
64+
description: Use this field to provide any other notes that you feel might be relevant to the issue.
65+
validations:
66+
required: false

‎.github/ISSUE_TEMPLATE/config.yml

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
blank_issues_enabled: false
2+
contact_links:
3+
- name: Ask a question
4+
url: https://github.com/:vendor_slug/:package_name/discussions/new?category=q-a
5+
about: Ask the community for help
6+
- name: Request a feature
7+
url: https://github.com/:vendor_slug/:package_name/discussions/new?category=ideas
8+
about: Share ideas for new features
9+
- name: Report a security issue
10+
url: https://github.com/:vendor_slug/:package_name/security/policy
11+
about: Learn how to notify us for sensitive bugs

‎.github/dependabot.yml

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Please see the documentation for all configuration options:
2+
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
3+
4+
version: 2
5+
updates:
6+
7+
- package-ecosystem: "github-actions"
8+
directory: "/"
9+
schedule:
10+
interval: "weekly"
11+
labels:
12+
- "dependencies"
13+
14+
- package-ecosystem: "composer"
15+
directory: "/"
16+
schedule:
17+
interval: "weekly"
18+
labels:
19+
- "dependencies"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: dependabot-auto-merge
2+
on: pull_request_target
3+
4+
permissions:
5+
pull-requests: write
6+
contents: write
7+
8+
jobs:
9+
dependabot:
10+
runs-on: ubuntu-latest
11+
timeout-minutes: 5
12+
if: ${{ github.actor == 'dependabot[bot]' }}
13+
steps:
14+
15+
- name: Dependabot metadata
16+
id: metadata
17+
uses: dependabot/fetch-metadata@v2.2.0
18+
with:
19+
github-token: "${{ secrets.GITHUB_TOKEN }}"
20+
21+
- name: Auto-merge Dependabot PRs for semver-minor updates
22+
if: ${{steps.metadata.outputs.update-type == 'version-update:semver-minor'}}
23+
run: gh pr merge --auto --merge "$PR_URL"
24+
env:
25+
PR_URL: ${{github.event.pull_request.html_url}}
26+
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
27+
28+
- name: Auto-merge Dependabot PRs for semver-patch updates
29+
if: ${{steps.metadata.outputs.update-type == 'version-update:semver-patch'}}
30+
run: gh pr merge --auto --merge "$PR_URL"
31+
env:
32+
PR_URL: ${{github.event.pull_request.html_url}}
33+
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: Fix PHP code style issues
2+
3+
on:
4+
push:
5+
paths:
6+
- '**.php'
7+
8+
permissions:
9+
contents: write
10+
11+
jobs:
12+
php-code-styling:
13+
runs-on: ubuntu-latest
14+
timeout-minutes: 5
15+
16+
steps:
17+
- name: Checkout code
18+
uses: actions/checkout@v4
19+
with:
20+
ref: ${{ github.head_ref }}
21+
22+
- name: Fix PHP code style issues
23+
uses: aglipanci/laravel-pint-action@2.4
24+
25+
- name: Commit changes
26+
uses: stefanzweifel/git-auto-commit-action@v5
27+
with:
28+
commit_message: Fix styling

‎.github/workflows/phpstan.yml

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: PHPStan
2+
3+
on:
4+
push:
5+
paths:
6+
- '**.php'
7+
- 'phpstan.neon.dist'
8+
- '.github/workflows/phpstan.yml'
9+
10+
jobs:
11+
phpstan:
12+
name: phpstan
13+
runs-on: ubuntu-latest
14+
timeout-minutes: 5
15+
steps:
16+
- uses: actions/checkout@v4
17+
18+
- name: Setup PHP
19+
uses: shivammathur/setup-php@v2
20+
with:
21+
php-version: '8.2'
22+
coverage: none
23+
24+
- name: Install composer dependencies
25+
uses: ramsey/composer-install@v3
26+
27+
- name: Run PHPStan
28+
run: ./vendor/bin/phpstan --error-format=github

‎.github/workflows/run-tests.yml

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
name: run-tests
2+
3+
on:
4+
push:
5+
paths:
6+
- '**.php'
7+
- '.github/workflows/run-tests.yml'
8+
- 'phpunit.xml.dist'
9+
- 'composer.json'
10+
- 'composer.lock'
11+
12+
jobs:
13+
test:
14+
runs-on: ${{ matrix.os }}
15+
timeout-minutes: 5
16+
strategy:
17+
fail-fast: true
18+
matrix:
19+
os: [ubuntu-latest, windows-latest]
20+
php: [8.3, 8.2]
21+
laravel: [11.*, 10.*]
22+
stability: [prefer-lowest, prefer-stable]
23+
include:
24+
- laravel: 11.*
25+
testbench: 9.*
26+
carbon: ^2.63
27+
- laravel: 10.*
28+
testbench: 8.*
29+
carbon: ^2.63
30+
31+
name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} - ${{ matrix.os }}
32+
33+
steps:
34+
- name: Checkout code
35+
uses: actions/checkout@v4
36+
37+
- name: Setup PHP
38+
uses: shivammathur/setup-php@v2
39+
with:
40+
php-version: ${{ matrix.php }}
41+
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick, fileinfo
42+
coverage: none
43+
44+
- name: Setup problem matchers
45+
run: |
46+
echo "::add-matcher::${{ runner.tool_cache }}/php.json"
47+
echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
48+
49+
- name: Install dependencies
50+
run: |
51+
composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" "nesbot/carbon:${{ matrix.os == 'windows-latest' && '^^^' || '' }}${{ matrix.carbon }}" --no-interaction --no-update
52+
composer update --${{ matrix.stability }} --prefer-dist --no-interaction
53+
54+
- name: List Installed Dependencies
55+
run: composer show -D
56+
57+
- name: Execute tests
58+
run: vendor/bin/pest --ci
+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: "Update Changelog"
2+
3+
on:
4+
release:
5+
types: [released]
6+
7+
permissions:
8+
contents: write
9+
10+
jobs:
11+
update:
12+
runs-on: ubuntu-latest
13+
timeout-minutes: 5
14+
15+
steps:
16+
- name: Checkout code
17+
uses: actions/checkout@v4
18+
with:
19+
ref: main
20+
21+
- name: Update Changelog
22+
uses: stefanzweifel/changelog-updater-action@v1
23+
with:
24+
latest-version: ${{ github.event.release.name }}
25+
release-notes: ${{ github.event.release.body }}
26+
27+
- name: Commit updated CHANGELOG
28+
uses: stefanzweifel/git-auto-commit-action@v5
29+
with:
30+
branch: main
31+
commit_message: Update CHANGELOG
32+
file_pattern: CHANGELOG.md

‎.gitignore

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Composer Related
2+
composer.lock
3+
/vendor
4+
5+
# Frontend Assets
6+
/node_modules
7+
8+
# Logs
9+
npm-debug.log
10+
yarn-error.log
11+
12+
# Caches
13+
.phpunit.cache
14+
.phpunit.result.cache
15+
/build
16+
17+
# IDE Helper
18+
_ide_helper.php
19+
_ide_helper_models.php
20+
.phpstorm.meta.php
21+
22+
# Editors
23+
/.idea
24+
/.fleet
25+
/.vscode
26+
27+
# Misc
28+
phpunit.xml
29+
phpstan.neon
30+
testbench.yaml
31+
/docs
32+
/coverage

‎CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Changelog
2+
3+
All notable changes to `:package_name` will be documented in this file.

‎LICENSE.md

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) :vendor_name <author@domain.com>
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.

‎README.md

+93
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
# :package_description
2+
3+
[![Latest Version on Packagist](https://img.shields.io/packagist/v/:vendor_slug/:package_slug.svg?style=flat-square)](https://packagist.org/packages/:vendor_slug/:package_slug)
4+
[![GitHub Tests Action Status](https://img.shields.io/github/actions/workflow/status/:vendor_slug/:package_slug/run-tests.yml?branch=main&label=tests&style=flat-square)](https://github.com/:vendor_slug/:package_slug/actions?query=workflow%3Arun-tests+branch%3Amain)
5+
[![GitHub Code Style Action Status](https://img.shields.io/github/actions/workflow/status/:vendor_slug/:package_slug/fix-php-code-style-issues.yml?branch=main&label=code%20style&style=flat-square)](https://github.com/:vendor_slug/:package_slug/actions?query=workflow%3A"Fix+PHP+code+style+issues"+branch%3Amain)
6+
[![Total Downloads](https://img.shields.io/packagist/dt/:vendor_slug/:package_slug.svg?style=flat-square)](https://packagist.org/packages/:vendor_slug/:package_slug)
7+
<!--delete-->
8+
---
9+
This repo can be used to scaffold a Laravel package. Follow these steps to get started:
10+
11+
1. Press the "Use this template" button at the top of this repo to create a new repo with the contents of this skeleton.
12+
2. Run "php ./configure.php" to run a script that will replace all placeholders throughout all the files.
13+
3. Have fun creating your package.
14+
4. If you need help creating a package, consider picking up our <a href="https://laravelpackage.training">Laravel Package Training</a> video course.
15+
---
16+
<!--/delete-->
17+
This is where your description should go. Limit it to a paragraph or two. Consider adding a small example.
18+
19+
## Support us
20+
21+
[<img src="https://github-ads.s3.eu-central-1.amazonaws.com/:package_name.jpg?t=1" width="419px" />](https://spatie.be/github-ad-click/:package_name)
22+
23+
We invest a lot of resources into creating [best in class open source packages](https://spatie.be/open-source). You can support us by [buying one of our paid products](https://spatie.be/open-source/support-us).
24+
25+
We highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. You'll find our address on [our contact page](https://spatie.be/about-us). We publish all received postcards on [our virtual postcard wall](https://spatie.be/open-source/postcards).
26+
27+
## Installation
28+
29+
You can install the package via composer:
30+
31+
```bash
32+
composer require :vendor_slug/:package_slug
33+
```
34+
35+
You can publish and run the migrations with:
36+
37+
```bash
38+
php artisan vendor:publish --tag=":package_slug-migrations"
39+
php artisan migrate
40+
```
41+
42+
You can publish the config file with:
43+
44+
```bash
45+
php artisan vendor:publish --tag=":package_slug-config"
46+
```
47+
48+
This is the contents of the published config file:
49+
50+
```php
51+
return [
52+
];
53+
```
54+
55+
Optionally, you can publish the views using
56+
57+
```bash
58+
php artisan vendor:publish --tag=":package_slug-views"
59+
```
60+
61+
## Usage
62+
63+
```php
64+
$variable = new VendorName\Skeleton();
65+
echo $variable->echoPhrase('Hello, VendorName!');
66+
```
67+
68+
## Testing
69+
70+
```bash
71+
composer test
72+
```
73+
74+
## Changelog
75+
76+
Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.
77+
78+
## Contributing
79+
80+
Please see [CONTRIBUTING](CONTRIBUTING.md) for details.
81+
82+
## Security Vulnerabilities
83+
84+
Please review [our security policy](../../security/policy) on how to report security vulnerabilities.
85+
86+
## Credits
87+
88+
- [:author_name](https://github.com/:author_username)
89+
- [All Contributors](../../contributors)
90+
91+
## License
92+
93+
The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

‎composer.json

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
{
2+
"name": ":vendor_slug/:package_slug",
3+
"description": ":package_description",
4+
"keywords": [
5+
":vendor_name",
6+
"laravel",
7+
":package_slug"
8+
],
9+
"homepage": "https://github.com/:vendor_slug/:package_slug",
10+
"license": "MIT",
11+
"authors": [
12+
{
13+
"name": ":author_name",
14+
"email": "author@domain.com",
15+
"role": "Developer"
16+
}
17+
],
18+
"require": {
19+
"php": "^8.2",
20+
"spatie/laravel-package-tools": "^1.16",
21+
"illuminate/contracts": "^10.0||^11.0"
22+
},
23+
"require-dev": {
24+
"laravel/pint": "^1.14",
25+
"nunomaduro/collision": "^8.1.1||^7.10.0",
26+
"larastan/larastan": "^2.9",
27+
"orchestra/testbench": "^9.0.0||^8.22.0",
28+
"pestphp/pest": "^2.34",
29+
"pestphp/pest-plugin-arch": "^2.7",
30+
"pestphp/pest-plugin-laravel": "^2.3",
31+
"phpstan/extension-installer": "^1.3",
32+
"phpstan/phpstan-deprecation-rules": "^1.1",
33+
"phpstan/phpstan-phpunit": "^1.3",
34+
"spatie/laravel-ray": "^1.35"
35+
},
36+
"autoload": {
37+
"psr-4": {
38+
"VendorName\\Skeleton\\": "src/",
39+
"VendorName\\Skeleton\\Database\\Factories\\": "database/factories/"
40+
}
41+
},
42+
"autoload-dev": {
43+
"psr-4": {
44+
"VendorName\\Skeleton\\Tests\\": "tests/",
45+
"Workbench\\App\\": "workbench/app/"
46+
}
47+
},
48+
"scripts": {
49+
"post-autoload-dump": "@composer run prepare",
50+
"clear": "@php vendor/bin/testbench package:purge-skeleton --ansi",
51+
"prepare": "@php vendor/bin/testbench package:discover --ansi",
52+
"build": [
53+
"@composer run prepare",
54+
"@php vendor/bin/testbench workbench:build --ansi"
55+
],
56+
"start": [
57+
"Composer\\Config::disableProcessTimeout",
58+
"@composer run build",
59+
"@php vendor/bin/testbench serve"
60+
],
61+
"analyse": "vendor/bin/phpstan analyse",
62+
"test": "vendor/bin/pest",
63+
"test-coverage": "vendor/bin/pest --coverage",
64+
"format": "vendor/bin/pint"
65+
},
66+
"config": {
67+
"sort-packages": true,
68+
"allow-plugins": {
69+
"pestphp/pest-plugin": true,
70+
"phpstan/extension-installer": true
71+
}
72+
},
73+
"extra": {
74+
"laravel": {
75+
"providers": [
76+
"VendorName\\Skeleton\\SkeletonServiceProvider"
77+
],
78+
"aliases": {
79+
"Skeleton": "VendorName\\Skeleton\\Facades\\Skeleton"
80+
}
81+
}
82+
},
83+
"minimum-stability": "dev",
84+
"prefer-stable": true
85+
}

‎config/skeleton.php

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?php
2+
3+
// config for VendorName/Skeleton
4+
return [
5+
6+
];

‎configure.php

+366
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,366 @@
1+
#!/usr/bin/env php
2+
<?php
3+
4+
function ask(string $question, string $default = ''): string
5+
{
6+
$answer = readline($question.($default ? " ({$default})" : null).': ');
7+
8+
if (! $answer) {
9+
return $default;
10+
}
11+
12+
return $answer;
13+
}
14+
15+
function confirm(string $question, bool $default = false): bool
16+
{
17+
$answer = ask($question.' ('.($default ? 'Y/n' : 'y/N').')');
18+
19+
if (! $answer) {
20+
return $default;
21+
}
22+
23+
return strtolower($answer) === 'y';
24+
}
25+
26+
function writeln(string $line): void
27+
{
28+
echo $line.PHP_EOL;
29+
}
30+
31+
function run(string $command): string
32+
{
33+
return trim((string) shell_exec($command));
34+
}
35+
36+
function str_after(string $subject, string $search): string
37+
{
38+
$pos = strrpos($subject, $search);
39+
40+
if ($pos === false) {
41+
return $subject;
42+
}
43+
44+
return substr($subject, $pos + strlen($search));
45+
}
46+
47+
function slugify(string $subject): string
48+
{
49+
return strtolower(trim(preg_replace('/[^A-Za-z0-9-]+/', '-', $subject), '-'));
50+
}
51+
52+
function title_case(string $subject): string
53+
{
54+
return str_replace(' ', '', ucwords(str_replace(['-', '_'], ' ', $subject)));
55+
}
56+
57+
function title_snake(string $subject, string $replace = '_'): string
58+
{
59+
return str_replace(['-', '_'], $replace, $subject);
60+
}
61+
62+
function replace_in_file(string $file, array $replacements): void
63+
{
64+
$contents = file_get_contents($file);
65+
66+
file_put_contents(
67+
$file,
68+
str_replace(
69+
array_keys($replacements),
70+
array_values($replacements),
71+
$contents
72+
)
73+
);
74+
}
75+
76+
function remove_prefix(string $prefix, string $content): string
77+
{
78+
if (str_starts_with($content, $prefix)) {
79+
return substr($content, strlen($prefix));
80+
}
81+
82+
return $content;
83+
}
84+
85+
function remove_composer_deps(array $names)
86+
{
87+
$data = json_decode(file_get_contents(__DIR__.'/composer.json'), true);
88+
89+
foreach ($data['require-dev'] as $name => $version) {
90+
if (in_array($name, $names, true)) {
91+
unset($data['require-dev'][$name]);
92+
}
93+
}
94+
95+
file_put_contents(__DIR__.'/composer.json', json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
96+
}
97+
98+
function remove_composer_script($scriptName)
99+
{
100+
$data = json_decode(file_get_contents(__DIR__.'/composer.json'), true);
101+
102+
foreach ($data['scripts'] as $name => $script) {
103+
if ($scriptName === $name) {
104+
unset($data['scripts'][$name]);
105+
break;
106+
}
107+
}
108+
109+
file_put_contents(__DIR__.'/composer.json', json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
110+
}
111+
112+
function remove_readme_paragraphs(string $file): void
113+
{
114+
$contents = file_get_contents($file);
115+
116+
file_put_contents(
117+
$file,
118+
preg_replace('/<!--delete-->.*<!--\/delete-->/s', '', $contents) ?: $contents
119+
);
120+
}
121+
122+
function safeUnlink(string $filename)
123+
{
124+
if (file_exists($filename) && is_file($filename)) {
125+
unlink($filename);
126+
}
127+
}
128+
129+
function determineSeparator(string $path): string
130+
{
131+
return str_replace('/', DIRECTORY_SEPARATOR, $path);
132+
}
133+
134+
function replaceForWindows(): array
135+
{
136+
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"'));
137+
}
138+
139+
function replaceForAllOtherOSes(): array
140+
{
141+
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__)));
142+
}
143+
144+
function getGitHubApiEndpoint(string $endpoint): ?stdClass
145+
{
146+
try {
147+
$curl = curl_init("https://api.github.com/{$endpoint}");
148+
curl_setopt_array($curl, [
149+
CURLOPT_RETURNTRANSFER => true,
150+
CURLOPT_FOLLOWLOCATION => true,
151+
CURLOPT_HTTPGET => true,
152+
CURLOPT_HTTPHEADER => [
153+
'User-Agent: spatie-configure-script/1.0',
154+
],
155+
]);
156+
157+
$response = curl_exec($curl);
158+
$statusCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
159+
160+
curl_close($curl);
161+
162+
if ($statusCode === 200) {
163+
return json_decode($response);
164+
}
165+
} catch (Exception $e) {
166+
// ignore
167+
}
168+
169+
return null;
170+
}
171+
172+
function searchCommitsForGitHubUsername(): string
173+
{
174+
$authorName = strtolower(trim(shell_exec('git config user.name')));
175+
176+
$committersRaw = shell_exec("git log --author='@users.noreply.github.com' --pretty='%an:%ae' --reverse");
177+
$committersLines = explode("\n", $committersRaw ?? '');
178+
$committers = array_filter(array_map(function ($line) use ($authorName) {
179+
$line = trim($line);
180+
[$name, $email] = explode(':', $line) + [null, null];
181+
182+
return [
183+
'name' => $name,
184+
'email' => $email,
185+
'isMatch' => strtolower($name) === $authorName && ! str_contains($name, '[bot]'),
186+
];
187+
}, $committersLines), fn ($item) => $item['isMatch']);
188+
189+
if (empty($committers)) {
190+
return '';
191+
}
192+
193+
$firstCommitter = reset($committers);
194+
195+
return explode('@', $firstCommitter['email'])[0] ?? '';
196+
}
197+
198+
function guessGitHubUsernameUsingCli()
199+
{
200+
try {
201+
if (preg_match('/ogged in to github\.com as ([a-zA-Z-_]+).+/', shell_exec('gh auth status -h github.com 2>&1'), $matches)) {
202+
return $matches[1];
203+
}
204+
} catch (Exception $e) {
205+
// ignore
206+
}
207+
208+
return '';
209+
}
210+
211+
function guessGitHubUsername(): string
212+
{
213+
$username = searchCommitsForGitHubUsername();
214+
if (! empty($username)) {
215+
return $username;
216+
}
217+
218+
$username = guessGitHubUsernameUsingCli();
219+
if (! empty($username)) {
220+
return $username;
221+
}
222+
223+
// fall back to using the username from the git remote
224+
$remoteUrl = shell_exec('git config remote.origin.url');
225+
$remoteUrlParts = explode('/', str_replace(':', '/', trim($remoteUrl)));
226+
227+
return $remoteUrlParts[1] ?? '';
228+
}
229+
230+
function guessGitHubVendorInfo($authorName, $username): array
231+
{
232+
$remoteUrl = shell_exec('git config remote.origin.url');
233+
$remoteUrlParts = explode('/', str_replace(':', '/', trim($remoteUrl)));
234+
235+
$response = getGitHubApiEndpoint("orgs/{$remoteUrlParts[1]}");
236+
237+
if ($response === null) {
238+
return [$authorName, $username];
239+
}
240+
241+
return [$response->name ?? $authorName, $response->login ?? $username];
242+
}
243+
244+
$gitName = run('git config user.name');
245+
$authorName = ask('Author name', $gitName);
246+
247+
$gitEmail = run('git config user.email');
248+
$authorEmail = ask('Author email', $gitEmail);
249+
$authorUsername = ask('Author username', guessGitHubUsername());
250+
251+
$guessGitHubVendorInfo = guessGitHubVendorInfo($authorName, $authorUsername);
252+
253+
$vendorName = ask('Vendor name', $guessGitHubVendorInfo[0]);
254+
$vendorUsername = ask('Vendor username', $guessGitHubVendorInfo[1] ?? slugify($vendorName));
255+
$vendorSlug = slugify($vendorUsername);
256+
257+
$vendorNamespace = str_replace('-', '', ucwords($vendorName));
258+
$vendorNamespace = ask('Vendor namespace', $vendorNamespace);
259+
260+
$currentDirectory = getcwd();
261+
$folderName = basename($currentDirectory);
262+
263+
$packageName = ask('Package name', $folderName);
264+
$packageSlug = slugify($packageName);
265+
$packageSlugWithoutPrefix = remove_prefix('laravel-', $packageSlug);
266+
267+
$className = title_case($packageName);
268+
$className = ask('Class name', $className);
269+
$variableName = lcfirst($className);
270+
$description = ask('Package description', "This is my package {$packageSlug}");
271+
272+
$usePhpStan = confirm('Enable PhpStan?', true);
273+
$useLaravelPint = confirm('Enable Laravel Pint?', true);
274+
$useDependabot = confirm('Enable Dependabot?', true);
275+
$useLaravelRay = confirm('Use Ray for debugging?', true);
276+
$useUpdateChangelogWorkflow = confirm('Use automatic changelog updater workflow?', true);
277+
278+
writeln('------');
279+
writeln("Author : {$authorName} ({$authorUsername}, {$authorEmail})");
280+
writeln("Vendor : {$vendorName} ({$vendorSlug})");
281+
writeln("Package : {$packageSlug} <{$description}>");
282+
writeln("Namespace : {$vendorNamespace}\\{$className}");
283+
writeln("Class name : {$className}");
284+
writeln('---');
285+
writeln('Packages & Utilities');
286+
writeln('Use Laravel/Pint : '.($useLaravelPint ? 'yes' : 'no'));
287+
writeln('Use Larastan/PhpStan : '.($usePhpStan ? 'yes' : 'no'));
288+
writeln('Use Dependabot : '.($useDependabot ? 'yes' : 'no'));
289+
writeln('Use Ray App : '.($useLaravelRay ? 'yes' : 'no'));
290+
writeln('Use Auto-Changelog : '.($useUpdateChangelogWorkflow ? 'yes' : 'no'));
291+
writeln('------');
292+
293+
writeln('This script will replace the above values in all relevant files in the project directory.');
294+
295+
if (! confirm('Modify files?', true)) {
296+
exit(1);
297+
}
298+
299+
$files = (str_starts_with(strtoupper(PHP_OS), 'WIN') ? replaceForWindows() : replaceForAllOtherOSes());
300+
301+
foreach ($files as $file) {
302+
replace_in_file($file, [
303+
':author_name' => $authorName,
304+
':author_username' => $authorUsername,
305+
'author@domain.com' => $authorEmail,
306+
':vendor_name' => $vendorName,
307+
':vendor_slug' => $vendorSlug,
308+
'VendorName' => $vendorNamespace,
309+
':package_name' => $packageName,
310+
':package_slug' => $packageSlug,
311+
':package_slug_without_prefix' => $packageSlugWithoutPrefix,
312+
'Skeleton' => $className,
313+
'skeleton' => $packageSlug,
314+
'migration_table_name' => title_snake($packageSlug),
315+
'variable' => $variableName,
316+
':package_description' => $description,
317+
]);
318+
319+
match (true) {
320+
str_contains($file, determineSeparator('src/Skeleton.php')) => rename($file, determineSeparator('./src/'.$className.'.php')),
321+
str_contains($file, determineSeparator('src/SkeletonServiceProvider.php')) => rename($file, determineSeparator('./src/'.$className.'ServiceProvider.php')),
322+
str_contains($file, determineSeparator('src/Facades/Skeleton.php')) => rename($file, determineSeparator('./src/Facades/'.$className.'.php')),
323+
str_contains($file, determineSeparator('src/Commands/SkeletonCommand.php')) => rename($file, determineSeparator('./src/Commands/'.$className.'Command.php')),
324+
str_contains($file, determineSeparator('database/migrations/create_skeleton_table.php.stub')) => rename($file, determineSeparator('./database/migrations/create_'.title_snake($packageSlugWithoutPrefix).'_table.php.stub')),
325+
str_contains($file, determineSeparator('config/skeleton.php')) => rename($file, determineSeparator('./config/'.$packageSlugWithoutPrefix.'.php')),
326+
str_contains($file, 'README.md') => remove_readme_paragraphs($file),
327+
default => [],
328+
};
329+
}
330+
331+
if (! $useLaravelPint) {
332+
safeUnlink(__DIR__.'/.github/workflows/fix-php-code-style-issues.yml');
333+
safeUnlink(__DIR__.'/pint.json');
334+
}
335+
336+
if (! $usePhpStan) {
337+
safeUnlink(__DIR__.'/phpstan.neon.dist');
338+
safeUnlink(__DIR__.'/phpstan-baseline.neon');
339+
safeUnlink(__DIR__.'/.github/workflows/phpstan.yml');
340+
341+
remove_composer_deps([
342+
'phpstan/extension-installer',
343+
'phpstan/phpstan-deprecation-rules',
344+
'phpstan/phpstan-phpunit',
345+
'larastan/larastan',
346+
]);
347+
348+
remove_composer_script('phpstan');
349+
}
350+
351+
if (! $useDependabot) {
352+
safeUnlink(__DIR__.'/.github/dependabot.yml');
353+
safeUnlink(__DIR__.'/.github/workflows/dependabot-auto-merge.yml');
354+
}
355+
356+
if (! $useLaravelRay) {
357+
remove_composer_deps(['spatie/laravel-ray']);
358+
}
359+
360+
if (! $useUpdateChangelogWorkflow) {
361+
safeUnlink(__DIR__.'/.github/workflows/update-changelog.yml');
362+
}
363+
364+
confirm('Execute `composer install` and run tests?') && run('composer install && composer test');
365+
366+
confirm('Let this script delete itself?', true) && unlink(__FILE__);

‎database/factories/ModelFactory.php

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
namespace VendorName\Skeleton\Database\Factories;
4+
5+
use Illuminate\Database\Eloquent\Factories\Factory;
6+
7+
/*
8+
class ModelFactory extends Factory
9+
{
10+
protected $model = YourModel::class;
11+
12+
public function definition()
13+
{
14+
return [
15+
16+
];
17+
}
18+
}
19+
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class extends Migration
8+
{
9+
public function up()
10+
{
11+
Schema::create('migration_table_name_table', function (Blueprint $table) {
12+
$table->id();
13+
14+
// add fields
15+
16+
$table->timestamps();
17+
});
18+
}
19+
};

‎phpstan-baseline.neon

Whitespace-only changes.

‎phpstan.neon.dist

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
includes:
2+
- phpstan-baseline.neon
3+
4+
parameters:
5+
level: 5
6+
paths:
7+
- src
8+
- config
9+
- database
10+
tmpDir: build/phpstan
11+
checkOctaneCompatibility: true
12+
checkModelProperties: true

‎phpunit.xml.dist

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<phpunit
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.3/phpunit.xsd"
5+
backupGlobals="false"
6+
bootstrap="vendor/autoload.php"
7+
colors="true"
8+
processIsolation="false"
9+
stopOnFailure="false"
10+
executionOrder="random"
11+
failOnWarning="true"
12+
failOnRisky="true"
13+
failOnEmptyTestSuite="true"
14+
beStrictAboutOutputDuringTests="true"
15+
cacheDirectory=".phpunit.cache"
16+
backupStaticProperties="false"
17+
>
18+
<testsuites>
19+
<testsuite name="VendorName Test Suite">
20+
<directory>tests</directory>
21+
</testsuite>
22+
</testsuites>
23+
<logging>
24+
<junit outputFile="build/report.junit.xml"/>
25+
</logging>
26+
<source>
27+
<include>
28+
<directory suffix=".php">./src</directory>
29+
</include>
30+
</source>
31+
</phpunit>

‎resources/views/.gitkeep

Whitespace-only changes.

‎src/Commands/SkeletonCommand.php

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
namespace VendorName\Skeleton\Commands;
4+
5+
use Illuminate\Console\Command;
6+
7+
class SkeletonCommand extends Command
8+
{
9+
public $signature = 'skeleton';
10+
11+
public $description = 'My command';
12+
13+
public function handle(): int
14+
{
15+
$this->comment('All done');
16+
17+
return self::SUCCESS;
18+
}
19+
}

‎src/Facades/Skeleton.php

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
namespace VendorName\Skeleton\Facades;
4+
5+
use Illuminate\Support\Facades\Facade;
6+
7+
/**
8+
* @see \VendorName\Skeleton\Skeleton
9+
*/
10+
class Skeleton extends Facade
11+
{
12+
protected static function getFacadeAccessor(): string
13+
{
14+
return \VendorName\Skeleton\Skeleton::class;
15+
}
16+
}

‎src/Skeleton.php

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?php
2+
3+
namespace VendorName\Skeleton;
4+
5+
class Skeleton {}

‎src/SkeletonServiceProvider.php

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
namespace VendorName\Skeleton;
4+
5+
use Spatie\LaravelPackageTools\Package;
6+
use Spatie\LaravelPackageTools\PackageServiceProvider;
7+
use VendorName\Skeleton\Commands\SkeletonCommand;
8+
9+
class SkeletonServiceProvider extends PackageServiceProvider
10+
{
11+
public function configurePackage(Package $package): void
12+
{
13+
/*
14+
* This class is a Package Service Provider
15+
*
16+
* More info: https://github.com/spatie/laravel-package-tools
17+
*/
18+
$package
19+
->name('skeleton')
20+
->hasConfigFile()
21+
->hasViews()
22+
->hasMigration('create_migration_table_name_table')
23+
->hasCommand(SkeletonCommand::class);
24+
}
25+
}

‎tests/ArchTest.php

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?php
2+
3+
arch('it will not use debugging functions')
4+
->expect(['dd', 'dump', 'ray'])
5+
->each->not->toBeUsed();

‎tests/ExampleTest.php

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?php
2+
3+
it('can test', function () {
4+
expect(true)->toBeTrue();
5+
});

‎tests/Pest.php

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?php
2+
3+
use VendorName\Skeleton\Tests\TestCase;
4+
5+
uses(TestCase::class)->in(__DIR__);

‎tests/TestCase.php

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
namespace VendorName\Skeleton\Tests;
4+
5+
use Illuminate\Database\Eloquent\Factories\Factory;
6+
use Orchestra\Testbench\TestCase as Orchestra;
7+
use VendorName\Skeleton\SkeletonServiceProvider;
8+
9+
class TestCase extends Orchestra
10+
{
11+
protected function setUp(): void
12+
{
13+
parent::setUp();
14+
15+
Factory::guessFactoryNamesUsing(
16+
fn (string $modelName) => 'VendorName\\Skeleton\\Database\\Factories\\'.class_basename($modelName).'Factory'
17+
);
18+
}
19+
20+
protected function getPackageProviders($app)
21+
{
22+
return [
23+
SkeletonServiceProvider::class,
24+
];
25+
}
26+
27+
public function getEnvironmentSetUp($app)
28+
{
29+
config()->set('database.default', 'testing');
30+
31+
/*
32+
$migration = include __DIR__.'/../database/migrations/create_skeleton_table.php.stub';
33+
$migration->up();
34+
*/
35+
}
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
namespace Workbench\App\Providers;
4+
5+
use Illuminate\Support\Facades\Route;
6+
use Illuminate\Support\ServiceProvider;
7+
8+
class WorkbenchServiceProvider extends ServiceProvider
9+
{
10+
/**
11+
* Register services.
12+
*/
13+
public function register(): void
14+
{
15+
//
16+
}
17+
18+
/**
19+
* Bootstrap services.
20+
*/
21+
public function boot(): void
22+
{
23+
Route::view('/', 'welcome');
24+
}
25+
}

0 commit comments

Comments
 (0)
Please sign in to comment.