Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
f7749a6
chore: expose enums to api
dan2k3k4 Mar 18, 2026
2bb69f2
chore: improve scramble
dan2k3k4 Mar 18, 2026
13a6d74
chore: refactor
dan2k3k4 Mar 18, 2026
6d449e9
chore: fix scramble
dan2k3k4 Mar 18, 2026
19f2358
chore: grammar
dan2k3k4 Mar 18, 2026
0201553
chore: add note about dev workflow
dan2k3k4 Mar 18, 2026
7142c2b
chore: expose enums to api (#104)
dan2k3k4 Mar 18, 2026
e9fdc00
chore: add github actions: protect prod, release trigger
dan2k3k4 Mar 18, 2026
dbde5c6
chore: output lagoon-project-name and claim-script-output
dan2k3k4 Mar 18, 2026
72cd9d8
chore: bump deps
dan2k3k4 Mar 19, 2026
89c73f3
chore: bump scramble
dan2k3k4 Mar 19, 2026
5627a30
chore: allow name to be passed in
dan2k3k4 Mar 19, 2026
0b83a0b
chore: bump to laravel 12
dan2k3k4 Mar 20, 2026
ffba5a8
chore: inject ai key info via api/instance create
dan2k3k4 Mar 20, 2026
127d6fa
chore: default to empty string
dan2k3k4 Mar 20, 2026
46bce27
chore: fix
dan2k3k4 Mar 20, 2026
1d2a77b
chore: bump deps
dan2k3k4 Mar 20, 2026
666abec
chore: handle null as empty
dan2k3k4 Mar 20, 2026
d376b49
chore: update doc
dan2k3k4 Mar 20, 2026
022fb38
Initial plan
Copilot Mar 20, 2026
698760d
fix: use filled() instead of has() for secret check to handle explici…
Copilot Mar 20, 2026
9845150
fix: use `filled()` instead of `has()` for secret presence check (#108)
dan2k3k4 Mar 20, 2026
68cd48d
chore: inject ai key info via api/instance create (#107)
dan2k3k4 Mar 20, 2026
2bff627
chore: add github actions: protect prod, release trigger (#109)
dan2k3k4 Mar 20, 2026
6de9739
chore: fix
dan2k3k4 Mar 20, 2026
b11f0d4
chore: fix csrf token
dan2k3k4 Mar 20, 2026
abd11a4
chore: fix csrf
dan2k3k4 Mar 20, 2026
9cc3da6
chore: fix cookie
dan2k3k4 Mar 20, 2026
4915982
chore: possible fix
dan2k3k4 Mar 20, 2026
754416b
chore: improve app instance table output
dan2k3k4 Mar 20, 2026
3f250e4
chore: fix for url with equals
dan2k3k4 Mar 20, 2026
47bc3fa
chore: run pint
dan2k3k4 Mar 20, 2026
302fca2
bump deps
dan2k3k4 Mar 20, 2026
d9ce417
chore: scope permissions
dan2k3k4 Mar 20, 2026
f5a8e15
chore: bump deps
dan2k3k4 Mar 20, 2026
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
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ SESSION_LIFETIME=120
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_DOMAIN=null
SESSION_SECURE_COOKIE=true

BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local
Expand Down
21 changes: 21 additions & 0 deletions .github/workflows/protect-prod-branch.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: "Protect Prod Branch"
on:
pull_request:
branches: [ "prod" ]

permissions: {}

jobs:
check-source:
runs-on: ubuntu-latest
steps:
- name: Validate Source Branch
run: |
SRC="${{ github.head_ref }}"
if [[ "$SRC" == "dev" || "$SRC" == hotfix* ]]; then
echo "Source branch $SRC is valid."
exit 0
else
echo "Error: Only 'dev' or 'hotfix*' branches can target 'prod'."
exit 1
fi
Comment thread Fixed
28 changes: 28 additions & 0 deletions .github/workflows/release-button.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: "Trigger Release PR"
on:
workflow_dispatch:

jobs:
create-release:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
Comment thread
dan2k3k4 marked this conversation as resolved.
- name: Open PR from Dev to Prod
env:
GH_TOKEN: ${{ github.token }}
run: |
# Pulls unique commits in dev not yet in prod
LOG=$(git log origin/prod..origin/dev --oneline --no-merges)
DATE=$(date +'%Y-%m-%d')

gh pr create \
--base prod \
--head dev \
--title "Release ($DATE)" \
--body "### Changes in this release:
$LOG"
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ Polydock Engine is a Laravel-based application management and deployment platfor

For detailed documentation including features, technical details, setup instructions, and more, please see the [Documentation](docs/README.md).

## Development Workflow

- **Branching**: Always branch from the `dev` branch for new features, bug fixes, or improvements.
- **Pull Requests**: Create Pull Requests (PRs) back into the `dev` branch for review and testing.
- **Releases**: Releases are performed by merging the `dev` branch into the `prod` branch.

## Sponsoring Organizations

- [Workshop Orange](https://www.workshoporange.co) - Project Delivery Professionals
Expand Down
4 changes: 3 additions & 1 deletion app/Console/Commands/RemoveAppInstancesByEmail.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,10 @@ public function handle()

// Check if this is a pattern (contains %) or exact email
$isPattern = str_contains($email, '%');

if (! $isPattern && ! filter_var($email, FILTER_VALIDATE_EMAIL)) {
$this->error('Invalid email address provided. Use % for wildcard patterns (e.g., %@example.com).');

return 1;
}

Expand All @@ -60,6 +61,7 @@ public function handle()

if ($instances->isEmpty()) {
$this->info("No app instances found matching {$searchType}: {$email}");

return 0;
}

Expand Down
3 changes: 3 additions & 0 deletions app/Enums/PolydockStoreAppStatusEnum.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@

namespace App\Enums;

use App\Traits\HasEnumOptions;
use Filament\Support\Contracts\HasColor;
use Filament\Support\Contracts\HasIcon;
use Filament\Support\Contracts\HasLabel;

enum PolydockStoreAppStatusEnum: string implements HasColor, HasIcon, HasLabel
{
use HasEnumOptions;

case AVAILABLE = 'available';
case UNAVAILABLE = 'unavailable';

Expand Down
3 changes: 3 additions & 0 deletions app/Enums/PolydockStoreStatusEnum.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@

namespace App\Enums;

use App\Traits\HasEnumOptions;
use Filament\Support\Contracts\HasColor;
use Filament\Support\Contracts\HasIcon;
use Filament\Support\Contracts\HasLabel;

enum PolydockStoreStatusEnum: string implements HasColor, HasIcon, HasLabel
{
use HasEnumOptions;

case UNAVAILABLE = 'unavailable';
case PUBLIC = 'public';
case PRIVATE = 'private';
Expand Down
3 changes: 3 additions & 0 deletions app/Enums/PolydockStoreWebhookCallStatusEnum.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@

namespace App\Enums;

use App\Traits\HasEnumOptions;
use Filament\Support\Contracts\HasColor;
use Filament\Support\Contracts\HasIcon;
use Filament\Support\Contracts\HasLabel;

enum PolydockStoreWebhookCallStatusEnum: string implements HasColor, HasIcon, HasLabel
{
use HasEnumOptions;

case PENDING = 'pending';
case PROCESSING = 'processing';
case SUCCESS = 'success';
Expand Down
3 changes: 3 additions & 0 deletions app/Enums/PolydockVariableScopeEnum.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@

namespace App\Enums;

use App\Traits\HasEnumOptions;
use Filament\Support\Contracts\HasColor;
use Filament\Support\Contracts\HasIcon;
use Filament\Support\Contracts\HasLabel;

enum PolydockVariableScopeEnum: string implements HasColor, HasIcon, HasLabel
{
use HasEnumOptions;

case GLOBAL = 'global';
case BUILD = 'build';
case RUNTIME = 'runtime';
Expand Down
11 changes: 3 additions & 8 deletions app/Enums/UserGroupRoleEnum.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@

namespace App\Enums;

use App\Traits\HasEnumOptions;
use Filament\Support\Contracts\HasColor;
use Filament\Support\Contracts\HasIcon;
use Filament\Support\Contracts\HasLabel;

enum UserGroupRoleEnum: string implements HasColor, HasIcon, HasLabel
{
use HasEnumOptions;

case OWNER = 'owner';
case MEMBER = 'member';
case VIEWER = 'viewer';
Expand Down Expand Up @@ -43,12 +46,4 @@ public static function getValues(): array
{
return array_column(self::cases(), 'value');
}

public static function getOptions(): array
{
return collect(self::cases())
->mapWithKeys(fn ($role) => [
$role->value => $role->getLabel(),
])->all();
}
}
11 changes: 3 additions & 8 deletions app/Enums/UserRemoteRegistrationStatusEnum.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@

namespace App\Enums;

use App\Traits\HasEnumOptions;
use Filament\Support\Contracts\HasColor;
use Filament\Support\Contracts\HasIcon;
use Filament\Support\Contracts\HasLabel;

enum UserRemoteRegistrationStatusEnum: string implements HasColor, HasIcon, HasLabel
{
use HasEnumOptions;

case PENDING = 'pending';
case PROCESSING = 'processing';
case SUCCESS = 'success';
Expand Down Expand Up @@ -47,12 +50,4 @@ public static function getValues(): array
{
return array_column(self::cases(), 'value');
}

public static function getOptions(): array
{
return collect(self::cases())
->mapWithKeys(fn ($status) => [
$status->value => $status->getLabel(),
])->all();
}
}
3 changes: 3 additions & 0 deletions app/Enums/UserRemoteRegistrationType.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@

namespace App\Enums;

use App\Traits\HasEnumOptions;
use Filament\Support\Contracts\HasColor;
use Filament\Support\Contracts\HasIcon;
use Filament\Support\Contracts\HasLabel;

enum UserRemoteRegistrationType: string implements HasColor, HasIcon, HasLabel
{
use HasEnumOptions;

case TEST_FAIL = 'TEST_FAIL';
case REQUEST_TRIAL = 'REQUEST_TRIAL';
case REQUEST_TRIAL_UNLISTED_REGION = 'REQUEST_TRIAL_UNLISTED_REGION';
Expand Down
87 changes: 54 additions & 33 deletions app/Filament/Admin/Resources/PolydockAppInstanceResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Filament\Forms;
use Filament\Forms\Form;
use Filament\Infolists\Components\Grid;
use Filament\Infolists\Components\KeyValueEntry;
use Filament\Infolists\Components\Section;
use Filament\Infolists\Components\TextEntry;
use Filament\Infolists\Infolist;
Expand Down Expand Up @@ -245,6 +246,15 @@ public static function infolist(Infolist $infolist): Infolist
),
),
]),
Grid::make(2)
->schema([
TextEntry::make('app_url')
->label('App URL')
->url(fn ($record) => $record->app_url)
->openUrlInNewTab()
->icon('heroicon-m-link')
->iconColor('primary'),
]),
])
->columnSpan(2),

Expand All @@ -266,14 +276,14 @@ public static function infolist(Infolist $infolist): Infolist

Section::make('Instance Configuration')
->description('Instance-specific settings configured at creation.')
->schema(fn ($record) => self::getRenderedInstanceConfigForRecord($record))
->visible(fn ($record) => self::hasInstanceConfigFields($record))
->schema(self::getRenderedInstanceConfigForRecord(...))
->visible(self::hasInstanceConfigFields(...))
->columnSpan(3)
->collapsible(),

Section::make('Instance Data')
->description('Safe data that can be shared with webhooks')
->schema(fn ($record) => self::getRenderedSafeDataForRecord($record))
->schema(self::getRenderedSafeDataForRecord(...))
->columnSpan(3)
->collapsible(),
])
Expand All @@ -282,35 +292,46 @@ public static function infolist(Infolist $infolist): Infolist

public static function getRenderedSafeDataForRecord(PolydockAppInstance $record): array
{
$safeData = $record->data;
$sensitiveKeys = $record->getSensitiveDataKeys();
$renderedArray = [];
foreach ($safeData as $key => $value) {
if ($record->shouldFilterKey($key, $sensitiveKeys)) {
$value = 'REDACTED';
}

if ($value === null) {
$value = 'N/A';
}

$renderKey = 'webhook_data_'.$key;
$renderedItem = TextEntry::make($renderKey)
->label($key)
->markdown()
->columnSpanFull()
->bulleted();

if (is_array($value)) {
$renderedItem->state($value);
} else {
$renderedItem->state([$value]);
}

$renderedArray[] = $renderedItem;
}

return $renderedArray;
return [
Grid::make()
->schema([
KeyValueEntry::make('data')
->label('')
->state(function (PolydockAppInstance $record) {
$safeData = $record->data ?? [];
$sensitiveKeys = $record->getSensitiveDataKeys();
$redactedData = [];

foreach ($safeData as $key => $value) {
// Split combined key-value strings like "instance_config_VAR=VALUE"
// but skip if it's a URL or if the key is already a non-numeric string.
if (\is_int($key) && \is_string($value) && str_contains($value, '=') && ! str_starts_with($value, 'http')) {
[$newKey, $newValue] = explode('=', (string) $value, 2);
$key = $newKey;
$value = $newValue;
}

if ($record->shouldFilterKey($key, $sensitiveKeys)) {
$value = 'REDACTED';
}

if ($value === null) {
$value = 'N/A';
}

if (\is_array($value)) {
$value = json_encode($value);
}

$redactedData[$key] = $value;
}

return $redactedData;
})
->columnSpan(2),
])
->columns(3),
];
}

/**
Expand Down Expand Up @@ -363,7 +384,7 @@ public static function getRenderedInstanceConfigForRecord(PolydockAppInstance $r
// Check if value should be masked (for encrypted fields)
$isEncrypted = $record->isPolydockVariableEncrypted($fieldName);

$renderedItem = TextEntry::make('instance_config_display_'.$fieldName)
$renderedItem = TextEntry::make("instance_config_display_{$fieldName}")
->label($labelName);

if ($isEncrypted && $value !== null && $value !== '') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

use App\Enums\PolydockStoreAppStatusEnum;
use App\Filament\Admin\Resources\PolydockAppInstanceResource;
use App\Jobs\ProcessUserRemoteRegistration;
use App\Models\PolydockStoreApp;
use App\Models\UserRemoteRegistration;
use App\Services\PolydockAppClassDiscovery;
Expand Down Expand Up @@ -217,9 +216,6 @@ public function create(): void
'registration_uuid' => $registration->uuid,
]);

// Dispatch the job to process the registration
ProcessUserRemoteRegistration::dispatch($registration);

Notification::make()
->title('Instance creation started')
->body('The app instance is being created. You can track its progress in the App Instances list.')
Expand Down
Loading
Loading