Skip to content

Commit 04a108b

Browse files
committed
create workflow
1 parent ef79b22 commit 04a108b

File tree

13 files changed

+267
-22
lines changed

13 files changed

+267
-22
lines changed

.github/workflows/ci.yml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: ci
2+
3+
on:
4+
push:
5+
branches: [ "main" ]
6+
pull_request:
7+
branches: [ "main" ]
8+
9+
permissions:
10+
contents: read
11+
12+
jobs:
13+
ci:
14+
15+
runs-on: ubuntu-latest
16+
17+
steps:
18+
- uses: actions/checkout@v4
19+
20+
- name: Cache Composer packages
21+
id: composer-cache
22+
uses: actions/cache@v3
23+
with:
24+
path: vendor
25+
key: ${{ runner.os }}-ci-${{ hashFiles('**/composer.json') }}
26+
restore-keys: |
27+
${{ runner.os }}-ci-
28+
29+
- name: Install dependencies
30+
run: composer install --prefer-dist --no-progress
31+
32+
- name: Run ci checks
33+
run: composer ci

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# CodeBuddy
22

3-
## All-in-One Code Quality Tool for Laravel
3+
## All-in-One Code Quality Tool for your codebase
44

5-
> **Note:** This package is currently compatible only with the Laravel framework.
5+
> **Note:** This package is currently compatible only with the Laravel framework ^11.
66
77
## About
88
CodeBuddy is a wrapper around essential development tools that help maintain code quality in your Laravel projects. It integrates:
@@ -18,6 +18,12 @@ CodeBuddy is a wrapper around essential development tools that help maintain cod
1818
- Automated fixes for coding standards.
1919
- Code health reporting with email support.
2020

21+
## Installation
22+
23+
```
24+
composer require codebuddyphp/codebuddy --dev
25+
```
26+
2127
## Commands
2228

2329
### Configure Code Quality Tools

composer.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,22 @@
1616
}
1717
},
1818
"scripts": {
19+
"lint": "XDEBUG_MODE=off pint --test",
20+
"lint.fix": "XDEBUG_MODE=off pint",
21+
"stan": "XDEBUG_MODE=off phpstan analyse --ansi --memory-limit=-1",
22+
"test.arch": "XDEBUG_MODE=off pest --testsuite=Architecture",
23+
"test.unit": "XDEBUG_MODE=off pest --testsuite=Unit",
24+
"test.feature": "XDEBUG_MODE=coverage pest --testsuite=Feature --coverage --min=90",
25+
"rector": "XDEBUG_MODE=off rector process --dry-run",
26+
"rector.fix": "XDEBUG_MODE=off rector process",
27+
"ci": [
28+
"@lint",
29+
"@rector"
30+
],
31+
"ci.fix": [
32+
"@rector.fix",
33+
"@lint.fix"
34+
]
1935
},
2036
"extra": {
2137
"laravel": {

phpstan.neon

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
includes:
2+
- vendor/larastan/larastan/extension.neon
3+
- vendor/nesbot/carbon/extension.neon
4+
5+
parameters:
6+
7+
paths:
8+
- src/
9+
10+
level: max

rector.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Rector\Config\RectorConfig;
6+
7+
return RectorConfig::configure()
8+
->withPaths([
9+
__DIR__.'/config',
10+
__DIR__.'/src',
11+
])
12+
// uncomment to reach your current PHP version
13+
// ->withPhpSets()
14+
->withTypeCoverageLevel(0)
15+
->withDeadCodeLevel(0)
16+
->withCodeQualityLevel(0);

resources/views/banner.blade.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<div class="m-1">
2+
<div class="px-1 bg-sky-600">CodeBuddy</div>
3+
<em class="ml-1 underline">
4+
All-in-one tool for your codebase quality
5+
</em>
6+
</div>

src/CodebuddyServiceProvider.php

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
namespace Codebuddyphp\Codebuddy;
44

5+
use Codebuddyphp\Codebuddy\Commands\Check;
56
use Codebuddyphp\Codebuddy\Commands\Configure;
7+
use Codebuddyphp\Codebuddy\Commands\Score;
68
use Illuminate\Support\ServiceProvider;
79

810
class CodebuddyServiceProvider extends ServiceProvider
@@ -17,18 +19,27 @@ public function boot()
1719
if ($this->app->runningInConsole()) {
1820
$this->commands([
1921
Configure::class,
22+
Check::class,
23+
Score::class,
2024
]);
2125

2226
$this->publishes([
2327
__DIR__.'/../config/codebuddy.php' => config_path('codebuddy.php'),
2428
]);
2529
}
30+
31+
$this->loadViewsFrom(__DIR__.'/../resources/views', 'codebuddy');
2632
}
2733

28-
public function provides()
34+
/**
35+
* @return array<string>
36+
*/
37+
public function provides(): array
2938
{
3039
return [
3140
Configure::class,
41+
Check::class,
42+
Score::class,
3243
];
3344
}
34-
}
45+
}

src/Commands/Check.php

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
3+
namespace Codebuddyphp\Codebuddy\Commands;
4+
5+
use Illuminate\Console\Command;
6+
7+
use function Termwind\render;
8+
9+
class Check extends Command
10+
{
11+
protected $signature = 'codebuddy:check';
12+
13+
protected $description = 'Run ci checks';
14+
15+
public function handle(): void
16+
{
17+
$checks = [
18+
'vendor/bin/pint --test',
19+
'vendor/bin/rector --dry-run',
20+
'vendor/bin/phpstan analyse',
21+
];
22+
23+
// @phpstan-ignore-next-line
24+
render(view('codebuddy::banner'));
25+
26+
$this->info('Running CI checks...');
27+
$this->newLine();
28+
29+
$failed = false;
30+
31+
foreach ($checks as $check) {
32+
$this->info("Running: $check");
33+
$process = proc_open($check, [
34+
0 => ['pipe', 'r'],
35+
1 => ['pipe', 'w'],
36+
2 => ['pipe', 'w'],
37+
], $pipes);
38+
39+
$output = stream_get_contents($pipes[1]);
40+
$errors = stream_get_contents($pipes[2]);
41+
fclose($pipes[0]);
42+
fclose($pipes[1]);
43+
fclose($pipes[2]);
44+
45+
$exitCode = proc_close($process);
46+
47+
if ($exitCode !== 0) {
48+
$this->error("Failed: $check");
49+
$this->line($output);
50+
$this->error($errors);
51+
$failed = true;
52+
} else {
53+
$this->info("Passed: $check");
54+
}
55+
56+
$this->newLine();
57+
}
58+
59+
if ($failed) {
60+
$this->error('CI checks failed.');
61+
exit(1);
62+
} else {
63+
$this->info('All CI checks passed successfully!');
64+
}
65+
}
66+
}

src/Commands/Configure.php

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Illuminate\Console\Command;
66
use Illuminate\Filesystem\Filesystem;
7+
78
use function Termwind\render;
89

910
class Configure extends Command
@@ -14,43 +15,58 @@ class Configure extends Command
1415

1516
public function handle(): void
1617
{
17-
$filesystem = new Filesystem();
18+
$filesystem = new Filesystem;
1819

1920
$configs = [
20-
'rector.php',
21-
'phpstan.neon',
22-
'pint.json',
21+
'rector' => [
22+
'stub_file' => __DIR__.'/../../stubs/rector.php.stub',
23+
'config_file' => 'rector.php',
24+
'description' => 'Rector makes upgrading and maintaining code easier.',
25+
],
26+
'phpstan' => [
27+
'stub_file' => __DIR__.'/../../stubs/phpstan.neon.stub',
28+
'config_file' => 'phpstan.neon',
29+
'description' => 'PHPStan helps detect errors at compile time instead of runtime.',
30+
],
31+
'pint' => [
32+
'stub_file' => __DIR__.'/../../stubs/pint.json.stub',
33+
'config_file' => 'pint.json',
34+
'description' => 'Pint ensures your code follows consistent formatting rules.',
35+
],
2336
];
2437

25-
foreach ($configs as $file) {
26-
$sourceFile = __DIR__ . "/../../config/laravel/{$file}";
27-
$destinationFile = base_path($file);
38+
render(view('codebuddy::banner'));
39+
40+
foreach ($configs as $key => $config) {
41+
$this->newLine();
42+
$this->info("Configuring {$key}...");
43+
44+
$stubFile = $config['stub_file'];
45+
$destinationFile = base_path($config['config_file']);
2846

29-
if (!$filesystem->exists($sourceFile)) {
30-
$this->error("Source file not found: {$sourceFile}");
47+
if (! $filesystem->exists($stubFile)) {
48+
$this->error("Stub file not found: {$stubFile}");
3149
continue;
3250
}
3351

3452
if ($filesystem->exists($destinationFile)) {
3553
$overwrite = $this->confirmOverwrite($destinationFile);
36-
if (!$overwrite) {
37-
$this->warn("Skipped: {$destinationFile} already exists");
54+
if (! $overwrite) {
55+
$this->warn(sprintf('Skipped: %s already exists', basename($destinationFile)));
3856
continue;
3957
}
4058
}
4159

42-
$filesystem->copy($sourceFile, $destinationFile);
43-
$this->info("Copied: {$file} to project root");
60+
$content = file_get_contents($stubFile);
61+
62+
$filesystem->put($destinationFile, $content);
63+
$this->info(sprintf('Created: %s in project root', $config['config_file']));
4464
$this->newLine();
4565
}
4666
}
4767

4868
private function confirmOverwrite(string $file): bool
4969
{
50-
$this->info(
51-
sprintf('%s already exists. Do you want to overwrite it?', $file)
52-
);
53-
54-
return $this->ask('Overwrite?', false);
70+
return $this->confirm(sprintf('%s already exists. Overwrite?', basename($file)), true);
5571
}
5672
}

src/Commands/Score.php

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
3+
namespace Codebuddyphp\Codebuddy\Commands;
4+
5+
use Illuminate\Console\Command;
6+
use Symfony\Component\Process\Process;
7+
8+
class Score extends Command
9+
{
10+
protected $signature = 'codebuddy:score';
11+
12+
protected $description = 'Calculate the Codebuddy Score for your codebase';
13+
14+
public function handle()
15+
{
16+
$this->info('🔍 Running Codebuddy analysis...');
17+
18+
$scores = [
19+
'formatting' => $this->runPint(),
20+
'static_analysis' => $this->runPHPStan(),
21+
'code_refactoring' => $this->runRector(),
22+
'test_coverage' => $this->runPestCoverage(),
23+
];
24+
25+
$totalScore = round(array_sum($scores) / count($scores));
26+
27+
$this->line("\n🚀 <info>Codebuddy Score: {$totalScore}/100</info>");
28+
$this->table(['Metric', 'Score (%)'], [
29+
['Code Formatting (Pint)', $scores['formatting']],
30+
['Static Analysis (PHPStan)', $scores['static_analysis']],
31+
['Code Refactoring (Rector)', $scores['code_refactoring']],
32+
['Test Coverage (PestPHP)', $scores['test_coverage']],
33+
]);
34+
35+
return Command::SUCCESS;
36+
}
37+
38+
private function runPint(): int
39+
{
40+
return $this->executeCommand(['vendor/bin/pint', '--test']) ? 100 : 80;
41+
}
42+
43+
private function runPHPStan(): int
44+
{
45+
return $this->executeCommand(['vendor/bin/phpstan', 'analyse', '--level=max']) ? 90 : 60;
46+
}
47+
48+
private function runRector(): int
49+
{
50+
return $this->executeCommand(['vendor/bin/rector', 'process', '--dry-run']) ? 95 : 70;
51+
}
52+
53+
private function runPestCoverage(): int
54+
{
55+
return $this->executeCommand(['vendor/bin/pest', '--coverage']) ? 85 : 50;
56+
}
57+
58+
private function executeCommand(array $command): bool
59+
{
60+
$process = new Process($command);
61+
$process->run();
62+
63+
return $process->isSuccessful();
64+
}
65+
}

0 commit comments

Comments
 (0)