Skip to content

Commit c1de808

Browse files
committed
docs: add AGENTS.md
1 parent 8733118 commit c1de808

1 file changed

Lines changed: 261 additions & 0 deletions

File tree

AGENTS.md

Lines changed: 261 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
# AGENTS.md — Laravel Environment Importer
2+
3+
Agent instructions for working in this repository.
4+
5+
---
6+
7+
## Project Overview
8+
9+
A Laravel package that imports a production/staging environment (database + files) into a local
10+
environment. Supports SSH tunnels, multiple DB engines, file sync via `rsync`, and a processor
11+
plugin system for post-import data transformations.
12+
13+
- **Namespace root:** `VanOns\LaravelEnvironmentImporter`
14+
- **PHP:** `^8.0` | **Laravel:** `>=10`
15+
- **Source:** `src/` | **Config:** `config/`
16+
17+
---
18+
19+
## Commands
20+
21+
### Static Analysis
22+
23+
```bash
24+
composer analyse # PHPStan level 5 (paths: config/, src/)
25+
```
26+
27+
### Code Formatting
28+
29+
```bash
30+
composer format # php-cs-fixer fix (PSR-12 + project rules)
31+
```
32+
33+
### Tests
34+
35+
There is currently no test suite. `orchestra/testbench` is available in `require-dev` for future
36+
tests. When tests are added:
37+
38+
```bash
39+
vendor/bin/phpunit # all tests
40+
vendor/bin/phpunit tests/Unit/SomeTest.php # single file
41+
vendor/bin/phpunit --filter testMethodName # single method
42+
vendor/bin/phpunit --filter 'SomeTest::testMethodName' # class + method
43+
```
44+
45+
---
46+
47+
## Code Style
48+
49+
### Tooling
50+
51+
- **php-cs-fixer** (`^3.64`) with `.php-cs-fixer.dist.php`
52+
- **PHPStan** level 5 with `phpstan.neon.dist`
53+
- Always run `composer format` before committing; run `composer analyse` to verify no new issues.
54+
55+
### Key Formatter Rules (`.php-cs-fixer.dist.php`)
56+
57+
| Rule | Value |
58+
|---|---|
59+
| Base standard | `@PSR12` |
60+
| Array syntax | Short (`[]`) |
61+
| String quotes | Single quotes preferred |
62+
| Trailing commas | Required in multiline arrays/calls |
63+
| Unused imports | Removed automatically |
64+
| PHPDoc spans | Multi-line |
65+
| Method chaining | Consistent indentation |
66+
67+
---
68+
69+
## File & Namespace Structure
70+
71+
```
72+
src/
73+
├── Commands/ # Artisan command(s)
74+
├── Exceptions/ # Custom exception classes
75+
├── Notifications/ # Mail/Slack notifications
76+
├── Processors/
77+
│ ├── Data/ # Post-import row-level processors (extend DataProcessor)
78+
│ └── Database/ # Dump-level processors (extend DatabaseProcessor)
79+
├── Support/ # Utilities (AsyncProcess, etc.)
80+
└── LaravelEnvironmentImporterServiceProvider.php
81+
config/
82+
└── environment-importer.php
83+
```
84+
85+
Sub-namespace mirrors directory path exactly (e.g. `Processors\Data\AnonymizeUsers`).
86+
87+
---
88+
89+
## Naming Conventions
90+
91+
| Element | Convention | Example |
92+
|---|---|---|
93+
| Classes / Interfaces | PascalCase | `ImportEnvironmentCommand` |
94+
| Methods | camelCase | `importDatabase()`, `isDbTunnelReady()` |
95+
| Properties | camelCase | `$backupPath`, `$dbTunnelProcess` |
96+
| Variables | camelCase | `$dumpFile`, `$processorClass` |
97+
| Config keys | snake_case strings | `'ssh_host'`, `'backup_path'` |
98+
| Config file names | kebab-case | `environment-importer.php` |
99+
| Artisan signatures | `namespace:verb` kebab-case | `environment:import` |
100+
101+
---
102+
103+
## Imports & Use Statements
104+
105+
- One `use` per line — no grouped braces.
106+
- Order: external/framework classes first, then internal (`VanOns\...`) classes last.
107+
- Function imports (`use function ...`) appear after all class imports.
108+
- No unused imports (enforced by formatter).
109+
110+
```php
111+
<?php
112+
113+
namespace VanOns\LaravelEnvironmentImporter\Commands;
114+
115+
use Carbon\Carbon;
116+
use Exception;
117+
use Illuminate\Console\Command;
118+
use Spatie\DbDumper\Databases\MySql;
119+
use VanOns\LaravelEnvironmentImporter\Exceptions\ImportEnvironmentException;
120+
use function Laravel\Prompts\select;
121+
```
122+
123+
---
124+
125+
## Type Hints
126+
127+
Use explicit types everywhere — no implicit `mixed` or untyped signatures.
128+
129+
```php
130+
// Return types on every method
131+
public function handle(): int {}
132+
public function importDatabase(): void {}
133+
public function getEnvironments(): array {}
134+
public function sshAuth(): string {}
135+
public function dbUseSsh(): bool {}
136+
public function getConfigValue(string $key, mixed $default = null): mixed {}
137+
138+
// Nullable types
139+
protected ?Process $dbTunnelProcess = null;
140+
public function getPasswordOverride(): ?string {}
141+
142+
// Union types (PHP 8.0+)
143+
public function resolve(string|int $key): mixed {}
144+
145+
// Constructor property promotion for simple value objects
146+
public function __construct(
147+
protected Carbon $startedAt,
148+
protected string $exception,
149+
) {}
150+
```
151+
152+
- Add `@throws ClassName` PHPDoc when a method can throw.
153+
- Use `@var class-string<...>` or `@phpstan-ignore-next-line` only when PHPStan cannot infer.
154+
155+
---
156+
157+
## Error Handling
158+
159+
- **Custom exception:** `ImportEnvironmentException extends Exception` — use for all
160+
package-specific failures.
161+
- **Descriptive messages:** Always include context in the message string.
162+
`"The \"{$this->target}\" environment does not exist"`
163+
- **Throw early (guard clauses):** Check preconditions at the top of a method and throw/return
164+
immediately rather than nesting logic.
165+
- **Single top-level catch:** Artisan `handle()` wraps the full workflow in one `try/catch
166+
(Exception $e)` — do not add nested try/catch blocks unless truly necessary.
167+
- **Nullsafe operator** over manual null checks: `$this->process?->isRunning()`.
168+
169+
```php
170+
// Good — guard clause
171+
if (!array_key_exists($key, $this->config)) {
172+
throw new ImportEnvironmentException("Missing config key: \"{$key}\"");
173+
}
174+
175+
// Good — top-level handler
176+
try {
177+
$this->runImport();
178+
} catch (Exception $e) {
179+
$this->error($e->getMessage());
180+
$this->sendFailureNotification($e);
181+
return static::FAILURE;
182+
}
183+
```
184+
185+
---
186+
187+
## Project-Specific Patterns
188+
189+
### Config Access
190+
191+
Never call `config()` directly inside methods. Use the dedicated accessors:
192+
193+
```php
194+
$this->getConfigValue('ssh_host');
195+
$this->getEnvironmentConfigValue('db_type', 'mysql');
196+
```
197+
198+
Both use `Arr::get()` internally for dot-notation support.
199+
200+
### Processor Pattern
201+
202+
Processors are registered in config under `data_processors` / `database_processors`. Two syntaxes:
203+
204+
```php
205+
// Without options
206+
[AnonymizeUsers::class]
207+
208+
// With options (key => options array)
209+
[AnonymizeUsers::class => ['model' => User::class, 'fields' => ['email']]]
210+
```
211+
212+
Implement `applies(): bool` to guard whether the processor should run, and `process(): void` for
213+
the logic.
214+
215+
### Match Expressions
216+
217+
Prefer `match (true)` over `if/elseif` chains for multi-branch routing:
218+
219+
```php
220+
$client = match (true) {
221+
$this->dbUsesMariaDb() => new MariaDb(),
222+
$this->dbUsesMongo() => new MongoDb(),
223+
default => new MySql(),
224+
};
225+
```
226+
227+
### Console Output Prefix
228+
229+
Prefix all output lines with the subsystem in brackets:
230+
231+
```php
232+
$this->info('[DB] Starting database import...');
233+
$this->warn('[Files] Skipping rsync — no paths configured');
234+
```
235+
236+
### Named Arguments
237+
238+
Use named arguments for clarity when a function has many parameters or boolean flags:
239+
240+
```php
241+
File::makeDirectory($path, recursive: true);
242+
Artisan::call('optimize:clear', outputBuffer: $this->output);
243+
```
244+
245+
### Backward Compatibility
246+
247+
When renaming config keys, keep the old key working via a fallback and add a comment:
248+
249+
```php
250+
// Backwards compatibility: 'sensitive_tables' was renamed to 'empty_tables'
251+
$tables = $this->getConfigValue('empty_tables')
252+
?? $this->getConfigValue('sensitive_tables', []);
253+
```
254+
255+
---
256+
257+
## PHPStan
258+
259+
- Level 5 — no baseline; all new code must pass cleanly.
260+
- Analysed paths: `config/`, `src/` only.
261+
- Run `composer analyse` before opening a PR.

0 commit comments

Comments
 (0)