Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AUDIT_BASELINE.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Every subsequent phase should keep these gates green unless a phase explicitly u

1. `./vendor/bin/phpunit --display-warnings` must pass.
2. `npm run test:frontend -- --runInBand` must pass.
3. `notur:install` must reject archives with missing/invalid checksums by default.
3. `notur:add` must reject archives with missing/invalid checksums by default.
4. Signature-required installs from registry must fail fast on missing/invalid `.sig`.
5. `notur:new` frontend scaffolds must not hardcode Bun-only commands.
6. CI frontend test step must not ignore failures.
6 changes: 3 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ All notable changes to the Notur Extension Library will be documented in this fi
- **Installer Git safe.directory handling**: bind-mounted Docker installs no longer emit Composer/Git “dubious ownership” failures for `/app`.
- **Installer Yarn recovery**: if Yarn is selected but cannot parse/install the existing lockfile while another supported lockfile manager is also present, the installer can fall back cleanly instead of aborting immediately.
- **Admin extension removal feedback**: web-triggered extension removal now respects the `notur:remove` exit code and surfaces failures back to the UI instead of always claiming success.
- Missing reverse patches for `index.tsx` and `admin.blade.php` so `notur:uninstall` now restores the panel to pristine source.
- Missing reverse patches for `index.tsx` and `admin.blade.php` so `notur:framework:uninstall` now restores the panel to pristine source.
- Malformed third hunk in `FileManager.tsx.patch` that emitted a "No such line 117" warning during install.

## [1.3.2] - 2026-04-03
Expand Down Expand Up @@ -74,8 +74,8 @@ All notable changes to the Notur Extension Library will be documented in this fi

- **`ExtensionManager` refactored** — Entrypoint resolution extracted to `EntrypointResolver` (~300 lines moved). Manager reduced from 803 to 567 lines.
- **`NewCommand` refactored** — File-writing logic extracted to `ScaffoldGenerator` (~690 lines moved). Command reduced from 1,031 to 326 lines.
- **`InstallCommand` / `RemoveCommand`** — Now extend `ExtensionLifecycleCommand` base class with shared `clearNoturCaches()` and `removeExtensionFiles()`.
- **`UninstallCommand`** — Uses `ManagesFilesystem` trait instead of duplicated `deleteDirectory()`.
- **`AddCommand` / `RemoveCommand`** — Now extend `ExtensionLifecycleCommand` base class with shared `clearNoturCaches()` and `removeExtensionFiles()`.
- **`FrameworkUninstallCommand`** — Uses `ManagesFilesystem` trait instead of duplicated `deleteDirectory()`.
- **`ExtensionManifest`** — Throws `ManifestException` instead of `InvalidArgumentException`.
- **`DependencyResolver`** — Throws `DependencyResolutionException` instead of `RuntimeException`.
- **`ErrorBoundary`** — Uses `recordDiagnosticError()` instead of direct array push.
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ npm run test:frontend # Vitest tests

**Database tables:** `notur_extensions`, `notur_migrations`, `notur_settings`, `notur_activity_logs` (migrations in `database/migrations/`).

**Artisan commands** (17 total in `src/Console/Commands/`): `notur:install`, `notur:remove`, `notur:enable`, `notur:disable`, `notur:list`, `notur:update`, `notur:dev`, `notur:dev:pull`, `notur:build`, `notur:export`, `notur:registry:sync`, `notur:uninstall`, `notur:new`, `notur:validate`, `notur:status`, `notur:keygen`, `notur:registry:status`.
**Artisan commands** (17 total in `src/Console/Commands/`): `notur:add`, `notur:remove`, `notur:enable`, `notur:disable`, `notur:list`, `notur:update`, `notur:dev`, `notur:dev:pull`, `notur:build`, `notur:export`, `notur:registry:sync`, `notur:framework:uninstall`, `notur:new`, `notur:validate`, `notur:status`, `notur:keygen`, `notur:registry:status`.

### Frontend Bridge (`bridge/src/`)

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ A standalone extension framework for [Pterodactyl Panel](https://pterodactyl.io/
Near-term focus areas:

- Frontend test coverage expansion (SlotRenderWhen, CssVariables, ThemeProvider)
- Command integration tests (InstallCommand, BuildCommand)
- Command integration tests (AddCommand, BuildCommand)
- Extension dev hot-reload (file-watcher + auto-rebuild)
- Pelican Panel compatibility investigation

Expand Down Expand Up @@ -146,7 +146,7 @@ Panel Response (HTML)
## Extension Lifecycle

```bash
php artisan notur:install acme/server-analytics # Install from registry
php artisan notur:add acme/server-analytics # Install from registry
php artisan notur:enable acme/server-analytics # Enable
php artisan notur:disable acme/server-analytics # Disable
php artisan notur:remove acme/server-analytics # Uninstall + rollback migrations
Expand Down
4 changes: 2 additions & 2 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@

## Phase 2: CLI + Extension Lifecycle

- [x] **`notur:uninstall` command** — Full framework removal:
- [x] **`notur:framework:uninstall` command** — Full framework removal:
- Restore patched React files from `.notur-backup` copies
- Roll back all Notur database migrations (4 tables)
- Remove Blade injection (`@include('notur::scripts')`)
- Delete `notur/` directory and `public/notur/` assets
- Run `composer remove notur/notur`
- Trigger frontend rebuild (`bun run build:production`)
- [x] **Reverse patches** — Generate and ship reverse `.patch` files for each React patch so uninstall can cleanly revert without relying on backup copies
- [x] **`notur:install` command** — Install extensions from registry or local path
- [x] **`notur:add` command** — Install extensions from registry or local path
- [x] **`notur:remove` command** — Remove an installed extension (rollback migrations, delete files, update manifest)
- [x] **`notur:enable` / `notur:disable`** — Toggle extensions without removing files
- [x] **`notur:update`** — Update an extension to a newer version (run new migrations, swap files)
Expand Down
2 changes: 1 addition & 1 deletion docker/e2e/install-uninstall.sh
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ if ! mysql_notur_tables | grep -q "notur_extensions"; then
fi

echo "[E2E] Running non-interactive Notur uninstall..."
COMPOSER_ALLOW_SUPERUSER=1 COMPOSER_DISABLE_AUDIT=1 php artisan notur:uninstall --confirm --no-interaction
COMPOSER_ALLOW_SUPERUSER=1 COMPOSER_DISABLE_AUDIT=1 php artisan notur:framework:uninstall --confirm --no-interaction

echo "[E2E] Verifying panel still responds after Notur uninstall..."
panel_available
Expand Down
2 changes: 1 addition & 1 deletion docker/e2e/setup-panel.sh
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ php artisan migrate --path=vendor/notur/notur/database/migrations --force
rm -rf /tmp/hello-world-src /tmp/hello-world.notur
cp -R /opt/notur/examples/hello-world /tmp/hello-world-src
php -r 'require "vendor/autoload.php"; \Notur\Support\NoturArchive::pack("/tmp/hello-world-src", "/tmp/hello-world.notur");'
php artisan notur:install /tmp/hello-world.notur --force
php artisan notur:add /tmp/hello-world.notur --force

rm -rf notur/extensions/notur/full-extension
mkdir -p notur/extensions/notur
Expand Down
4 changes: 2 additions & 2 deletions installer/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1409,10 +1409,10 @@ info "Web user: ${WEB_USER}"
echo ""
info "Next steps:"
if is_docker_env; then
info " Install an extension: docker exec -it <container> php artisan notur:install vendor/name"
info " Install an extension: docker exec -it <container> php artisan notur:add vendor/name"
info " List extensions: docker exec -it <container> php artisan notur:list"
else
info " Install an extension: php artisan notur:install vendor/name"
info " Install an extension: php artisan notur:add vendor/name"
info " List extensions: php artisan notur:list"
fi
info " Manage extensions: Browse to /admin/notur/extensions"
Expand Down
2 changes: 1 addition & 1 deletion resources/views/admin/extensions.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ class="form-control"
@if($extensions->isEmpty())
<div class="callout callout-info m-[15px]">
<p>No extensions are installed. Use the form above or the command line:</p>
<code>php artisan notur:install vendor/extension-name</code>
<code>php artisan notur:add vendor/extension-name</code>
</div>
@else
<table class="table table-hover" data-testid="installed-extensions-table">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@
use Notur\Support\RegistryClient;
use Notur\Support\SignatureVerifier;

class InstallCommand extends ExtensionLifecycleCommand
class AddCommand extends ExtensionLifecycleCommand
{
protected $signature = 'notur:install
protected $signature = 'notur:add
{extension : The extension ID (vendor/name) or path to a .notur file}
{--force : Overwrite if already installed}
{--no-migrate : Skip running migrations}';

protected $description = 'Install a Notur extension';
protected $description = 'Add a Notur extension';

public function handle(
ExtensionManager $manager,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@
use Illuminate\Support\Facades\Schema;
use Notur\Console\Concerns\ManagesFilesystem;

class UninstallCommand extends Command
class FrameworkUninstallCommand extends Command
{
use ManagesFilesystem;

protected $signature = 'notur:uninstall
protected $signature = 'notur:framework:uninstall
{--confirm : Skip interactive confirmation}';

protected $description = 'Completely remove the Notur extension framework from this Pterodactyl panel';
Expand Down
4 changes: 2 additions & 2 deletions src/Console/Commands/UpdateCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ private function updateSingle(string $extensionId, RegistryClient $registry): in
}

// Reinstall with --force to update
return $this->call('notur:install', [
return $this->call('notur:add', [
'extension' => $extensionId,
'--force' => true,
]);
Expand Down Expand Up @@ -108,7 +108,7 @@ private function updateAll(RegistryClient $registry): int
}

foreach ($updates as $update) {
$this->call('notur:install', [
$this->call('notur:add', [
'extension' => $update['id'],
'--force' => true,
]);
Expand Down
4 changes: 2 additions & 2 deletions src/Console/UI/Components/StatusDashboard.php
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ public function renderExtensions(): void

if ($extensions->isEmpty()) {
$this->command->line(' ' . NoturTheme::muted('No extensions installed.'));
$this->command->line(' ' . NoturTheme::muted('Run: php artisan notur:install <extension-id>'));
$this->command->line(' ' . NoturTheme::muted('Run: php artisan notur:add <extension-id>'));
$this->command->newLine();

return;
Expand Down Expand Up @@ -234,7 +234,7 @@ public function renderQuickActions(): void
$this->renderSectionHeader('Quick Actions');

$actions = [
['key' => 'notur:install <id>', 'desc' => 'Install an extension'],
['key' => 'notur:add <id>', 'desc' => 'Install an extension'],
['key' => 'notur:update', 'desc' => 'Update all extensions'],
['key' => 'notur:list', 'desc' => 'List installed extensions'],
['key' => 'notur:new', 'desc' => 'Create a new extension'],
Expand Down
6 changes: 3 additions & 3 deletions src/Http/Controllers/ExtensionAdminController.php
Original file line number Diff line number Diff line change
Expand Up @@ -471,12 +471,12 @@ public function install(Request $request): RedirectResponse
$tmpPath = sys_get_temp_dir() . '/notur-upload-' . uniqid('', true) . '.notur';
$uploadedFile->move(dirname($tmpPath), basename($tmpPath));

$exitCode = Artisan::call('notur:install', [
$exitCode = Artisan::call('notur:add', [
'extension' => $tmpPath,
'--force' => true,
]);
} else {
$exitCode = Artisan::call('notur:install', [
$exitCode = Artisan::call('notur:add', [
'extension' => $registryId,
'--force' => true,
]);
Expand Down Expand Up @@ -530,7 +530,7 @@ public function remove(string $extensionId): RedirectResponse
} catch (\Throwable $e) {
return redirect()
->route('admin.notur.extensions')
->with('error', 'Removal failed: ' . $e->getMessage());
->with('error', 'Installation failed: ' . $e->getMessage());
}
}

Expand Down
8 changes: 4 additions & 4 deletions src/NoturServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@
use Notur\Console\Commands\DisableCommand;
use Notur\Console\Commands\EnableCommand;
use Notur\Console\Commands\ExportCommand;
use Notur\Console\Commands\InstallCommand;
use Notur\Console\Commands\AddCommand;
use Notur\Console\Commands\KeygenCommand;
use Notur\Console\Commands\ListCommand;
use Notur\Console\Commands\NewCommand;
use Notur\Console\Commands\RegistryStatusCommand;
use Notur\Console\Commands\RegistrySyncCommand;
use Notur\Console\Commands\RemoveCommand;
use Notur\Console\Commands\StatusCommand;
use Notur\Console\Commands\UninstallCommand;
use Notur\Console\Commands\FrameworkUninstallCommand;
use Notur\Console\Commands\UpdateCommand;
use Notur\Console\Commands\ValidateCommand;
use Notur\Support\ActivityLogger;
Expand Down Expand Up @@ -90,7 +90,7 @@ public function boot(): void

// Register artisan commands (unconditionally so Artisan::call() works from web controllers)
$this->commands([
InstallCommand::class,
AddCommand::class,
RemoveCommand::class,
EnableCommand::class,
DisableCommand::class,
Expand All @@ -105,7 +105,7 @@ public function boot(): void
RegistryStatusCommand::class,
NewCommand::class,
ValidateCommand::class,
UninstallCommand::class,
FrameworkUninstallCommand::class,
StatusCommand::class,
]);

Expand Down
8 changes: 4 additions & 4 deletions tests/Integration/Console/CommandCoverageTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public function test_export_command_fails_for_missing_path(): void
->assertExitCode(1);
}

public function test_install_command_fails_when_registry_entry_missing(): void
public function test_add_command_fails_when_registry_entry_missing(): void
{
$registry = Mockery::mock(RegistryClient::class);
$registry->shouldReceive('getExtension')
Expand All @@ -73,7 +73,7 @@ public function test_install_command_fails_when_registry_entry_missing(): void
->andReturn(null);
$this->app->instance(RegistryClient::class, $registry);

$this->artisan('notur:install', ['extension' => 'acme/missing'])
$this->artisan('notur:add', ['extension' => 'acme/missing'])
->assertExitCode(1);
}

Expand Down Expand Up @@ -180,9 +180,9 @@ public function test_status_command_outputs_json(): void
->assertExitCode(0);
}

public function test_uninstall_command_can_be_cancelled_interactively(): void
public function test_framework_uninstall_command_can_be_cancelled_interactively(): void
{
$this->artisan('notur:uninstall')
$this->artisan('notur:framework:uninstall')
->expectsConfirmation('Are you sure you want to uninstall Notur?', 'no')
->expectsOutput('Uninstall cancelled.')
->assertExitCode(0);
Expand Down
24 changes: 12 additions & 12 deletions website/docs/admin/guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,13 @@ When running commands without arguments in an interactive terminal, Notur provid

```bash
# Install an extension by ID
php artisan notur:install acme/server-analytics
php artisan notur:add acme/server-analytics

# Install from a local .notur file
php artisan notur:install /path/to/extension.notur
php artisan notur:add /path/to/extension.notur

# Force reinstall an existing extension
php artisan notur:install acme/server-analytics --force
php artisan notur:add acme/server-analytics --force
```

#### System Status Dashboard
Expand Down Expand Up @@ -106,10 +106,10 @@ All commands support `--no-interaction` or `-n` flag for CI/CD pipelines:

```bash
# Install without prompts
php artisan notur:install acme/analytics -n
php artisan notur:add acme/analytics -n

# Works in CI environments automatically
CI=true php artisan notur:install acme/analytics
CI=true php artisan notur:add acme/analytics
```

### Listing Extensions
Expand All @@ -133,16 +133,16 @@ Extensions can be installed from the registry or from a local `.notur` archive f

```bash
# Install from the Notur registry
php artisan notur:install acme/server-analytics
php artisan notur:add acme/server-analytics

# Install from a local .notur file
php artisan notur:install /path/to/acme-server-analytics-1.0.0.notur
php artisan notur:add /path/to/acme-server-analytics-1.0.0.notur

# Force reinstall (overwrite existing)
php artisan notur:install acme/server-analytics --force
php artisan notur:add acme/server-analytics --force

# Install without running migrations
php artisan notur:install acme/server-analytics --no-migrate
php artisan notur:add acme/server-analytics --no-migrate
```

During installation, Notur will:
Expand Down Expand Up @@ -505,13 +505,13 @@ cd /var/www/pterodactyl

# This will remove all extensions, restore patched files, drop Notur tables,
# remove directories, and trigger a frontend rebuild.
php artisan notur:uninstall
php artisan notur:framework:uninstall

# Or skip interactive confirmation
php artisan notur:uninstall --confirm
php artisan notur:framework:uninstall --confirm
```

The uninstall command performs:
The framework uninstall command performs:

1. Restores patched React files from `.notur-backup` copies (or applies reverse patches)
2. Rolls back Notur database migrations (drops `notur_extensions`, `notur_migrations`, `notur_settings`, `notur_activity_logs`)
Expand Down
4 changes: 2 additions & 2 deletions website/docs/admin/registry.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ The registry is a static JSON file (`registry.json`) hosted on a GitHub reposito

1. **Sync**: `php artisan notur:registry:sync` fetches `registry.json` from the configured registry URL and caches it locally at `storage/notur/registry-cache.json`. The cache TTL is configurable.
2. **Search**: `php artisan notur:registry:sync --search "analytics"` searches the cached (or freshly fetched) index by ID, name, description, and tags.
3. **Install**: `php artisan notur:install acme/analytics` looks up the extension in the registry, downloads the `.notur` archive from its GitHub release, and installs it.
3. **Install**: `php artisan notur:add acme/analytics` looks up the extension in the registry, downloads the `.notur` archive from its GitHub release, and installs it.
4. **Update**: `php artisan notur:update` compares installed versions against the registry and offers to update any extensions with newer versions available.
5. **Status**: `php artisan notur:registry:status` shows cache age, TTL, size, and extension count.

Expand Down Expand Up @@ -325,7 +325,7 @@ Notur supports Ed25519 signatures on `.notur` archives to verify their integrity

1. The publisher signs the archive with their Ed25519 secret key.
2. The signature is stored in a `.sig` file alongside the archive.
3. When `notur.require_signatures` is `true`, the `InstallCommand` verifies the signature using the configured public key before installation.
3. When `notur.require_signatures` is `true`, the `AddCommand` verifies the signature using the configured public key before installation.
4. If verification fails, installation is aborted.

### Configuration
Expand Down
4 changes: 2 additions & 2 deletions website/docs/extensions/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -583,7 +583,7 @@ All commands are prefixed with `notur:` in Artisan.

| Command | Signature | Description |
|---|---|---|
| `notur:install` | `notur:install {extension} [--force] [--no-migrate]` | Install from registry or `.notur` file |
| `notur:add` | `notur:add {extension} [--force] [--no-migrate]` | Install from registry or `.notur` file |
| `notur:remove` | `notur:remove {extension} [--keep-data]` | Remove an extension |
| `notur:enable` | `notur:enable {extension}` | Enable a disabled extension |
| `notur:disable` | `notur:disable {extension}` | Disable an extension without removing files |
Expand All @@ -598,7 +598,7 @@ All commands are prefixed with `notur:` in Artisan.
| `notur:keygen` | `notur:keygen` | Generate an Ed25519 keypair for extension signing |
| `notur:registry:sync` | `notur:registry:sync [--search=] [--force]` | Sync or search the extension registry |
| `notur:registry:status` | `notur:registry:status [--json]` | Show registry cache status |
| `notur:uninstall` | `notur:uninstall [--confirm]` | Completely remove Notur from the panel |
| `notur:framework:uninstall` | `notur:framework:uninstall [--confirm]` | Completely remove Notur from the panel |

### Preset Definitions

Expand Down
2 changes: 1 addition & 1 deletion website/docs/extensions/frontend-sdk.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ A `.notur` file is a tar.gz archive containing:

Upload the `.notur` file via:
1. **Admin UI** (recommended): Navigate to `/admin/notur/extensions` and use the upload form
2. **CLI**: `php artisan notur:install /path/to/extension.notur`
2. **CLI**: `php artisan notur:add /path/to/extension.notur`

---

Expand Down
Loading
Loading