-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconfigure.php
More file actions
333 lines (284 loc) · 10 KB
/
configure.php
File metadata and controls
333 lines (284 loc) · 10 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
#!/usr/bin/env php
<?php
/**
* It asks a question, and returns the answer
*
* @param string $question The question to ask the user.
* @param string $default The default value to return if the user doesn't enter anything.
* @return string The answer to the question.
*/
function ask(string $question, string $default = ''): string
{
$answer = readline($question . ($default ? " ({$default})" : null) . ': ');
if (!$answer) {
return $default;
}
return $answer;
}
/**
* It asks the user a question, and returns true if the user answers "y" or "yes", and false otherwise
*
* @param string $question The question to ask the user.
* @param bool $default The default value to return if the user just presses enter.
* @return bool A boolean value.
*/
function confirm(string $question, bool $default = false): bool
{
$answer = ask($question . ' (' . ($default ? 'Y/n' : 'y/N') . ')');
if (!$answer) {
return $default;
}
return strtolower($answer) === 'y';
}
/**
* Prints it to the screen with a newline character at the end.
*
* @param string $line The line to write to the console.
*/
function writeln(string $line): void
{
echo $line . PHP_EOL;
}
/**
* It runs a command and returns the output
*
* @param string $command The command to run.
* @return string The output of the command.
*/
function run(string $command): string
{
$out = shell_exec($command);
return trim((string) $out);
}
/**
* Return the string after the last position of the search string.
*
* @param string $subject The string to search in
* @param string $search The string to search for.
* @return string
*/
function str_after(string $subject, string $search): string
{
$pos = strrpos($subject, $search);
if ($pos === false) {
return $subject;
}
return substr($subject, $pos + strlen($search));
}
/**
* It takes a slugify string
*
* @param string $subject The string to slugify.
* @return string
*/
function slugify(string $subject): string
{
return strtolower(trim(preg_replace('/[^A-Za-z0-9-]+/', '-', $subject), '-'));
}
/**
* It takes a title cased string
*
* @param string $subject The string to be converted to title case.
* @return string
*/
function title_case(string $subject): string
{
return str_replace(' ', '', ucwords(str_replace(['-', '_'], ' ', $subject)));
}
/**
* It replaces all occurrences of the keys in the file with the values
*
* @param string $file The file to replace the contents in.
* @param array $replacements An array of key/value pairs.
* @return void
*/
function replace_in_file(string $file, array $replacements): void
{
$contents = file_get_contents($file);
file_put_contents(
$file,
str_replace(
array_keys($replacements),
array_values($replacements),
$contents
)
);
}
/**
* If the content starts with the prefix, remove the prefix from the content.
*
* @param string $prefix The prefix to remove from the content.
* @param string $content The content to remove the prefix from.
* @return string The string without the prefix.
*/
function remove_prefix(string $prefix, string $content): string
{
if (str_starts_with($content, $prefix)) {
return substr($content, strlen($prefix));
}
return $content;
}
/**
* It removes the given dependencies from the `require-dev` section of the `composer.json` file
*
* @param array $names An array of package names to remove from the composer.json file.
* @return void
*/
function remove_composer_deps(array $names): void
{
$data = json_decode(file_get_contents(__DIR__ . '/composer.json'), true);
foreach ($data['require-dev'] as $name => $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));
}
/**
* It removes a script from the composer.json file
*
* @param string $script The name of the script to remove.
* @return void
*/
function remove_composer_script(string $script): void
{
$data = json_decode(file_get_contents(__DIR__ . '/composer.json'), true);
foreach ($data['scripts'] as $name => $command) {
if ($script === $name) {
unset($data['scripts'][$name]);
break;
}
}
file_put_contents(__DIR__ . '/composer.json', json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
}
/**
* It removes the paragraphs between the `<!--delete-->` and `<!--/delete-->` comments in the given file
*
* @param string $file The path to the file to be modified.
*/
function remove_readme_paragraphs(string $file): void
{
$contents = file_get_contents($file);
file_put_contents(
$file,
preg_replace('/<!--delete-->.*<!--\/delete-->/s', '', $contents) ?: $contents
);
}
/**
* If the file exists and is a file, delete it.
*
* @param string $filename The name of the file to delete.
*/
function safe_unlink(string $filename)
{
if (file_exists($filename) && is_file($filename)) {
unlink($filename);
}
}
/**
* It returns the directory separator
*
* @param string $path The path to the file or directory.
* @return string the path with the correct separator for the operating system.
*/
function replace_separator(string $path): string
{
return str_replace('/', DIRECTORY_SEPARATOR, $path);
}
/**
* It returns an array of all files in the project that contain the strings
* `:author`, `:vendor`, `:package`, `VendorName`, `skeleton`, `vendor_name`, `vendor_slug`, or `author@domain.com`
*
* @return array An array of files to replace.
*/
function find_files_to_replace(): array
{
if (str_starts_with(strtoupper(PHP_OS), 'WIN')) {
// Windows
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_ VendorNamespace PackageNamespace Skeleton :namespace vendor@domain.com author@domain.com"'));
} else {
// Unix
return explode(PHP_EOL, run('grep -E -r -l -i "author_|vendor_|package_|VendorNamespace|PackageNamespace|Skeleton|:namespace|vendor@domain.com|author@domain.com" --exclude-dir=vendor ./* ./.github/* | grep -v ' . basename(__FILE__)));
}
}
// ===============
// Main
// ===============
$gitName = run('git config user.name');
$gitEmail = run('git config user.email');
$username = explode(':', run('git config remote.origin.url'))[1];
$username = basename(dirname($username));
$authorName = ask('Author name', $gitName);
$authorEmail = ask('Author email', $gitEmail);
$authorSlug = ask('Author username', $username);
$vendorName = ask('Vendor name', title_case($username));
$vendorEmail = ask('Vendor email', $authorEmail);
$vendorSlug = ask('Vendor slug', $authorSlug === $username ? slugify($vendorName) : $username);
$vendorNamespace = ask('Vendor namespace', title_case($vendorSlug));
$currentDirectory = getcwd();
$folderName = basename($currentDirectory);
$packageName = ask('Package name', $folderName);
$packageSlug = ask('Package slug', slugify($packageName));
$packageNamespace = ask('Package namespace', title_case($packageSlug));
$className = ask('Class name', remove_prefix('Laravel', title_case($packageSlug)));
$description = ask('Package description', "This is my package {$packageSlug}");
$usePsalm = confirm('Enable Psalm?', true);
$useDependabot = confirm('Enable Dependabot?', true);
$useUpdateChangelogWorkflow = confirm('Use automatic changelog updater workflow?', true);
writeln('------');
writeln("Author : {$authorName} ({$authorSlug}, {$authorEmail})");
writeln("Vendor : {$vendorName} ({$vendorSlug}, {$vendorEmail})");
writeln("Package : {$packageName} ({$vendorSlug}/{$packageSlug})");
writeln(" {$description}");
writeln("Namespace : {$vendorNamespace}\\{$packageNamespace}");
writeln("Class name : {$className}");
writeln('---');
writeln('Packages & Utilities');
writeln('Use Psalm : ' . ($usePsalm ? 'yes' : 'no'));
writeln('Use Dependabot : ' . ($useDependabot ? '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 = find_files_to_replace();
foreach ($files as $file) {
replace_in_file($file, [
'author_name' => $authorName,
'author_username' => $authorSlug,
'author@domain.com' => $authorEmail,
'vendor_name' => $vendorName,
'vendor_slug' => $vendorSlug,
'vendor@domain.com' => $vendorEmail,
'VendorNamespace' => $vendorNamespace,
'package_name' => $packageName,
'package_slug' => $packageSlug,
'package_description' => $description,
'PackageNamespace' => $packageNamespace,
':namespace' => trim(json_encode("{$vendorNamespace}\\{$packageNamespace}"), '"'),
'Skeleton' => $className,
]);
match (true) {
str_contains($file, replace_separator('src/Skeleton.php')) => rename($file, replace_separator('./src/' . $className . '.php')),
str_contains($file, 'README.md') => remove_readme_paragraphs($file),
default => [],
};
}
if (!$usePsalm) {
safe_unlink(__DIR__ . '/psalm.xml.dist');
safe_unlink(__DIR__ . '/.github/workflows/psalm.yml');
remove_composer_deps([
'vimeo/psalm',
]);
remove_composer_script('analyse');
}
if (!$useDependabot) {
safe_unlink(__DIR__ . '/.github/dependabot.yml');
safe_unlink(__DIR__ . '/.github/workflows/dependabot-auto-merge.yml');
}
if (!$useUpdateChangelogWorkflow) {
safe_unlink(__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__) && unlink(__DIR__ . DIRECTORY_SEPARATOR . 'undo-configure.php');