Skip to content

Commit 2b4521b

Browse files
[ Kibble ] Link and Unlink commands (#33)
* reduce version constraints to make installable on community * Add KibbleGitIgnore action to manage .gitignore for kibble.json * Add LinkCommand to manage Kibble monorepo repository linking * Add UnlinkCommand to manage Kibble monorepo repository unlinking * Register LinkCommand and UnlinkCommand in KibbleServiceProvider * lint * remove package cross-contamination * Improve UnlinkCommand validation and error handling
1 parent d11b955 commit 2b4521b

File tree

6 files changed

+228
-2
lines changed

6 files changed

+228
-2
lines changed

packages/gh/composer.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"type": "library",
55
"license": "MIT",
66
"require": {
7-
"illuminate/support": "^11.36"
7+
"illuminate/support": "^11.9"
88
},
99
"require-dev": {
1010
"larastan/larastan": "^v3.0.2",

packages/kibble/composer.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"license": "MIT",
66
"require": {
77
"artisan-build/gh": "*",
8-
"illuminate/support": "^11.36"
8+
"illuminate/support": "^11.9"
99
},
1010
"require-dev": {
1111
"larastan/larastan": "^v3.0.2",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace ArtisanBuild\Kibble\Actions;
6+
7+
// TODO: Don't want to add this dependency just for this one attribute.
8+
// use ArtisanBuild\Bench\Attributes\ChatGPT;
9+
10+
class KibbleGitIgnore
11+
{
12+
// #[ChatGPT]
13+
public function __invoke(): void
14+
{
15+
if (! shell_exec('git check-ignore kibble.json')) {
16+
$gitignore = file_get_contents('.gitignore');
17+
$lines = explode("\n", $gitignore);
18+
$kibbleIndex = -1;
19+
20+
// Find appropriate spot to insert kibble.json alphabetically
21+
for ($i = 0; $i < count($lines); $i++) {
22+
if (trim($lines[$i]) === '') {
23+
continue;
24+
}
25+
if (strcasecmp('kibble.json', $lines[$i]) > 0) {
26+
continue;
27+
}
28+
$kibbleIndex = $i;
29+
break;
30+
}
31+
32+
if ($kibbleIndex === -1) {
33+
$lines[] = 'kibble.json';
34+
} else {
35+
array_splice($lines, $kibbleIndex, 0, 'kibble.json');
36+
}
37+
38+
file_put_contents('.gitignore', implode("\n", $lines));
39+
}
40+
}
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace ArtisanBuild\Kibble\Commands;
6+
7+
use ArtisanBuild\Kibble\Actions\KibbleGitIgnore;
8+
use Illuminate\Console\Command;
9+
10+
class LinkCommand extends Command
11+
{
12+
protected $signature = 'kibble:link {repository}';
13+
14+
protected $description = 'Add a Kibble monorepo to your composer project';
15+
16+
public function handle(): int
17+
{
18+
app(KibbleGitIgnore::class)();
19+
20+
if (! file_exists('kibble.json')) {
21+
file_put_contents(
22+
filename: 'kibble.json',
23+
data: json_encode(
24+
value: [],
25+
flags: JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES
26+
).PHP_EOL
27+
);
28+
}
29+
30+
$kibble = json_decode(file_get_contents('kibble.json'), true);
31+
32+
$repository = $this->argument('repository');
33+
34+
if (! isset($kibble[$repository])) {
35+
if (! $this->confirm(
36+
question: "{$repository} repository not found in kibble.json. Would you like to add it?",
37+
default: true,
38+
)) {
39+
return Command::FAILURE;
40+
}
41+
42+
$path = $this->ask('Enter the path to the repository (without trailing slash)');
43+
44+
$path = str_ends_with((string) $path, '/*') ? $path : $path.'/*';
45+
46+
// "dogfood": {
47+
// "type": "path",
48+
// "url": "/Users/gopher/Code/artisan-build/dogfood/packages/*",
49+
// "options": {
50+
// "symlink": true
51+
// }
52+
// },
53+
$kibble[$repository] = [
54+
'type' => 'path',
55+
'url' => $path,
56+
'options' => [
57+
'symlink' => true,
58+
],
59+
];
60+
61+
file_put_contents(
62+
filename: 'kibble.json',
63+
data: json_encode(
64+
value: $kibble,
65+
flags: JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES
66+
).PHP_EOL
67+
);
68+
}
69+
70+
/**
71+
* Now start work on the composer.json file.
72+
*/
73+
$composer_json = file_get_contents('composer.json');
74+
$trailing_newline = str_ends_with($composer_json, "\n");
75+
76+
$composer = json_decode($composer_json, true);
77+
78+
if (! isset($composer['repositories'])) {
79+
$composer['repositories'] = [];
80+
}
81+
82+
if (
83+
isset($composer['repositories'][$repository]) &&
84+
$composer['repositories'][$repository] === $kibble[$repository]
85+
) {
86+
$this->info("{$repository} already in composer.json");
87+
88+
return Command::SUCCESS;
89+
}
90+
91+
if (
92+
isset($composer['repositories'][$repository]) &&
93+
$composer['repositories'][$repository] !== $kibble[$repository]
94+
) {
95+
$this->error("{$repository} already in composer.json but with different configuration.");
96+
97+
return Command::FAILURE;
98+
}
99+
100+
$composer['repositories'][$repository] = $kibble[$repository];
101+
102+
file_put_contents(
103+
filename: 'composer.json',
104+
data: json_encode(
105+
value: $composer,
106+
flags: JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES
107+
).($trailing_newline ? "\n" : '')
108+
);
109+
110+
$this->info("{$repository} added to composer.json");
111+
112+
return Command::SUCCESS;
113+
}
114+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace ArtisanBuild\Kibble\Commands;
6+
7+
use ArtisanBuild\Kibble\Actions\KibbleGitIgnore;
8+
use Illuminate\Console\Command;
9+
10+
class UnlinkCommand extends Command
11+
{
12+
protected $signature = 'kibble:unlink {repository}';
13+
14+
protected $description = 'Temporarily remove a Kibble monorepo from your composer project';
15+
16+
public function handle(): int
17+
{
18+
app(KibbleGitIgnore::class)();
19+
20+
if (! file_exists('kibble.json')) {
21+
$this->error('kibble.json file not found');
22+
23+
return Command::FAILURE;
24+
}
25+
26+
$repository = $this->argument('repository');
27+
$kibble = json_decode(file_get_contents('kibble.json'), true);
28+
29+
if (! isset($kibble[$repository])) {
30+
$this->error("{$repository} repository not found in kibble.json");
31+
32+
return Command::FAILURE;
33+
}
34+
35+
$composer_json = file_get_contents('composer.json');
36+
$trailing_newline = str_ends_with($composer_json, "\n");
37+
$composer = json_decode($composer_json, true);
38+
39+
if (! isset($composer['repositories'][$repository])) {
40+
$this->error("{$repository} repository not found in composer.json");
41+
42+
return Command::FAILURE;
43+
}
44+
45+
if ($composer['repositories'][$repository] !== $kibble[$repository]) {
46+
$this->error("{$repository} repository in composer.json does not match {$repository} repository in kibble.json");
47+
48+
return Command::FAILURE;
49+
}
50+
51+
unset($composer['repositories'][$repository]);
52+
53+
file_put_contents(
54+
filename: 'composer.json',
55+
data: json_encode(
56+
value: $composer,
57+
flags: JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES
58+
).($trailing_newline ? "\n" : '')
59+
);
60+
61+
$this->info("{$repository} repository removed from composer.json");
62+
$this->info('Run `composer update` to remove the repository from your project.');
63+
64+
return Command::SUCCESS;
65+
}
66+
}

packages/kibble/src/Providers/KibbleServiceProvider.php

+5
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44

55
use ArtisanBuild\Kibble\Commands\CreatePackageCommand;
66
use ArtisanBuild\Kibble\Commands\ImportPackageCommand;
7+
use ArtisanBuild\Kibble\Commands\LinkCommand;
78
use ArtisanBuild\Kibble\Commands\SplitPackagesCommand;
9+
use ArtisanBuild\Kibble\Commands\UnlinkCommand;
810
use Illuminate\Support\ServiceProvider;
911

1012
class KibbleServiceProvider extends ServiceProvider
@@ -21,7 +23,10 @@ public function boot(): void
2123
CreatePackageCommand::class,
2224
ImportPackageCommand::class,
2325
SplitPackagesCommand::class,
26+
LinkCommand::class,
27+
UnlinkCommand::class,
2428
]);
29+
2530
$this->publishes([
2631
__DIR__.'/../../config/kibble.php' => config_path('kibble.php'),
2732
], 'kibble');

0 commit comments

Comments
 (0)