Contributing
Contributing
Contributions to externa-core should match the existing Laravel + Inertia React stack, keep enums as the source of truth for permissions/field types/roles, and leave tests green.
Work in the externa-core repository. Operators on Packagist / Docker prod paths can skip contributor hooks.
Local git hooks (Lefthook)
After npm ci (or npx lefthook install), clones get:
| Hook | Checks |
|---|---|
pre-commit | Pint (--dirty), ESLint + Prettier on staged frontend files |
commit-msg | commitlint Conventional Commits |
pre-push | pint --test + eslint — not full Pest / browser |
GitHub Actions remains the source of truth. Skip locally with LEFTHOOK=0 or git commit --no-verify (emergency only). See repo CONTRIBUTING.md.
Tooling
PHP — Pint
Laravel Pint formats PHP. Composer scripts:
composer lint # pint --parallel
composer lint:check # pint --parallel --test
composer test runs lint:check before Pest.
Frontend — ESLint & Prettier
From externa-core:
npm run lint # eslint . --fix
npm run lint:check # eslint .
npm run format # prettier --write resources/
npm run format:check # prettier --check resources/
npm run types:check # tsc --noEmit
CI-style gate:
composer ci:check
(runs npm lint/format/types checks + composer test).
Wayfinder
Laravel Wayfinder + @laravel/vite-plugin-wayfinder generates typed route/action helpers under resources/js during Vite build/dev.
- Prefer Wayfinder helpers over hard-coded URL strings in React.
- After adding/renaming named routes, run Vite (
npm run devorbuild) so generated clients refresh. - Do not hand-edit generated route trees as a long-term source of truth.
Pest
Feature and browser tests use Pest 4. See Testing for layout and how to run suites.
composer test
# or focused:
php artisan test --filter=SomeTestName
Coding conventions
Enums are the source of truth
| Concern | Enum | Sync / mirror |
|---|---|---|
| Permissions | App\Enums\PermissionEnum | php artisan permissions:sync → DB + resources/js/enums/permission-enum.ts |
| Roles (built-in names) | App\Enums\RoleEnum | Seeded in RoleSeeder |
| Field types | App\Enums\FieldTypeEnum | Validation, normalizer, UI catalog, AI instructions |
| File kinds | App\Enums\FileTypeEnum | File manager / storage helpers |
Do not invent permission or field-type strings in controllers or React without updating the enum first. Extension recipes: Extending Externa.
Actions & Services
app/Services/— domain orchestration used by controllers, jobs, and AI tools (FileService,Authorization\EffectivePermissionResolver, Collections services, …).app/Actions/— single-purpose actions (Fortify actions live underapp/Actions/Fortify). Prefer a small Action class when a use-case is reused or would bloat a controller.- Controllers stay thin: Form Requests validate/authorize; Services/Actions mutate state; Jobs handle async work.
HTTP & auth
- Admin/files/AI routes: enforce permissions with middleware or Form Requests (
AuthorizesWithPermission/EffectivePermissionResolver). - Collections HTTP currently relies on UI + AI checks — if you harden that, document it and add tests (Collections API).
- AI tools must use
ChecksAiPermissionsfor mutating/domain operations.
Frontend
- Pages under
resources/js/pages/…matching Inertia names. - Gate UI with
useCan()+PermissionEnumfrom the generated TS enum. - Follow existing shadcn/Radix + Tailwind patterns; avoid one-off design systems.
Jobs & schedule
New async work should use the queue (not dispatchSync in request paths for heavy I/O). If you add a scheduled command, register it in routes/console.php and document it under Operations.
Graphify knowledge graph
This monorepo maintains a graphify index under graphify-out/ at the Externa workspace root.
After you change code (PHP/TS that affects architecture or APIs):
cd /path/to/Externa
graphify update .
Use graphify query "…" when exploring relationships before large refactors. Skip graphify update for docs-only or pure config-comment edits that do not change behaviour.
Dependency audits (CI)
PRs and pushes to develop / main run .github/workflows/audit.yml:
| Check | Command | Policy (beta.3) |
|---|---|---|
| Composer | composer audit --locked --ignore-severity=low --ignore-severity=medium | Blocking on high/critical |
| npm (runtime) | npm audit --omit=dev --audit-level=high | Blocking on high/critical |
DevDependencies are not audited in the blocking job. Do not run npm audit fix --force in CI or as a blind local fix.
Reading failures
- Open the failed audit job log; note package + advisory ID / CVE.
- Prefer upgrading within the current major (
composer update pkg/npm update pkg) and re-run locally. - False positives / accepted risk: document in the PR with justification. Composer: temporary
--ignoreentries only with a tracked follow-up. npm: avoid.npmrcforce; prefer upgrading or switching package. - Path to GA (#24): keep audits blocking (no
continue-on-error).
See also Security checklist · repo SECURITY.md.
Dependabot
.github/dependabot.yml opens weekly PRs (Monday) for Composer, npm, and GitHub Actions. Minor/patch updates are grouped per ecosystem; open-PR limits keep noise down. Majors arrive as separate PRs.
Reviewing a Dependabot PR
- Confirm CI green (lint, tests, audit jobs).
- Skim release notes / changelog for breaking changes — do not merge major bumps blind.
- Prefer one ecosystem group merge at a time; re-run audits locally if unsure.
- After merge, note notable upgrades in
CHANGELOG.mdwhen cutting a release (see Release process / #22).
PR expectations
- Pint / ESLint / Prettier /
tscclean. - Pest coverage for new behaviour (see Testing).
- Enum +
permissions:syncwhen permissions change; commit regenerated TS enum. - Note public route or config contract changes in the PR so product docs can be updated.
graphify update .when the code graph should reflect your changes.- Dependency audit CI green (or justified ignore with follow-up).
Releases
Maintainers cutting a version: Releasing & versions (CHANGELOG, semver, GitHub Release template). Prefer noting user-facing changes in CHANGELOG.md Unreleased while the PR is open.