From b590927dd05ef9ebb2600b79743847f0ee349fb7 Mon Sep 17 00:00:00 2001 From: Aaron Francis Date: Wed, 26 Nov 2025 12:38:21 -0600 Subject: [PATCH 01/12] Modernize codebase with PHP 8.1+ features MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add declare(strict_types=1) to all PHP files - Use constructor property promotion in Event classes, Result classes, and BaseAction - Add native typed properties throughout (int, string, bool, array, class types) - Add explicit return types to methods - Fix Finder::shouldExclude() to pass pathname string instead of SplFileInfo - Fix SettledResult::parseLogs() to handle null LogResult - Remove constructor from ResultContract interface (anti-pattern) All 95 tests pass. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/Architecture.php | 2 + src/Clients/CloudWatchLogsClient.php | 2 + .../Configurations/AwsClientConfiguration.php | 4 +- src/Clients/LambdaClient.php | 2 + src/Clients/S3Client.php | 2 + src/Commands/Actions/BaseAction.php | 26 +++------- src/Commands/Actions/CreateBucket.php | 22 +++----- src/Commands/Actions/CreateDeploymentUser.php | 14 ++--- src/Commands/Actions/CreateExecutionRole.php | 9 ++-- src/Commands/Actions/DestroyAdminKeys.php | 14 +++-- src/Commands/Actions/DetermineRegion.php | 22 +++----- src/Commands/Activate.php | 2 + src/Commands/Configure.php | 32 +++--------- src/Commands/Deploy.php | 2 + src/Commands/EnvironmentAwareCommand.php | 4 +- src/Commands/Install.php | 2 + src/Commands/Warm.php | 2 + src/Concerns/HandlesLogging.php | 12 ++--- src/Concerns/ManagesEnvironments.php | 22 +++----- src/Contracts/AwsClientConfiguration.php | 6 +-- src/Deployment.php | 12 ++--- src/Events/AfterFunctionExecuted.php | 30 +++-------- src/Events/AfterFunctionsActivated.php | 11 ++-- src/Events/AfterFunctionsDeployed.php | 11 ++-- src/Events/BeforeFunctionExecuted.php | 22 +++----- src/Events/BeforeFunctionsActivated.php | 11 ++-- src/Events/BeforeFunctionsDeployed.php | 11 ++-- src/Exceptions/ConfigurationException.php | 2 + src/Exceptions/FunctionNotFoundException.php | 4 +- src/Exceptions/LambdaExecutionException.php | 2 + .../NoFunctionsRegisteredException.php | 4 +- src/Exceptions/SidecarException.php | 2 + src/Finder.php | 13 +++-- src/LambdaFunction.php | 2 + src/Manager.php | 7 ++- src/Package.php | 34 +++---------- src/Providers/SidecarServiceProvider.php | 8 +-- src/Region.php | 4 +- src/Results/PendingResult.php | 41 ++++----------- src/Results/ResultContract.php | 12 ++--- src/Results/SettledResult.php | 51 +++++++------------ src/Runtime.php | 2 + src/Sidecar.php | 2 + src/WarmingConfig.php | 8 +-- 44 files changed, 200 insertions(+), 309 deletions(-) diff --git a/src/Architecture.php b/src/Architecture.php index 02082f2..e7c1e27 100644 --- a/src/Architecture.php +++ b/src/Architecture.php @@ -1,5 +1,7 @@ */ diff --git a/src/Clients/Configurations/AwsClientConfiguration.php b/src/Clients/Configurations/AwsClientConfiguration.php index a5d4c52..6eb4147 100644 --- a/src/Clients/Configurations/AwsClientConfiguration.php +++ b/src/Clients/Configurations/AwsClientConfiguration.php @@ -1,12 +1,14 @@ 'latest', diff --git a/src/Clients/LambdaClient.php b/src/Clients/LambdaClient.php index d8efe93..a598e20 100644 --- a/src/Clients/LambdaClient.php +++ b/src/Clients/LambdaClient.php @@ -1,5 +1,7 @@ */ diff --git a/src/Clients/S3Client.php b/src/Clients/S3Client.php index a2414a1..943f745 100644 --- a/src/Clients/S3Client.php +++ b/src/Clients/S3Client.php @@ -1,5 +1,7 @@ */ @@ -10,26 +12,14 @@ abstract class BaseAction { - /** - * @var Configure - */ - public $command; - - /** - * @var string - */ - public $region; - - public function __construct($region, Configure $command) - { - $this->region = $region; - - $this->command = $command; - } + public function __construct( + public ?string $region, + public Configure $command + ) {} - abstract public function invoke(); + abstract public function invoke(): mixed; - protected function progress($message) + protected function progress(string $message): void { $this->command->text("==> $message"); } diff --git a/src/Commands/Actions/CreateBucket.php b/src/Commands/Actions/CreateBucket.php index 084f9f1..67d8321 100644 --- a/src/Commands/Actions/CreateBucket.php +++ b/src/Commands/Actions/CreateBucket.php @@ -1,5 +1,7 @@ */ @@ -7,25 +9,17 @@ namespace Hammerstone\Sidecar\Commands\Actions; use Aws\S3\S3Client; +use Exception; use Illuminate\Support\Str; use Throwable; class CreateBucket extends BaseAction { - /** - * @var S3Client - */ - protected $client; - - /** - * @var string - */ - protected $bucket; - - /** - * @return string - */ - public function invoke() + protected S3Client $client; + + protected string $bucket; + + public function invoke(): string { $this->client = $this->command->client(S3Client::class); diff --git a/src/Commands/Actions/CreateDeploymentUser.php b/src/Commands/Actions/CreateDeploymentUser.php index 2dbf386..10c7a96 100644 --- a/src/Commands/Actions/CreateDeploymentUser.php +++ b/src/Commands/Actions/CreateDeploymentUser.php @@ -1,5 +1,7 @@ */ @@ -12,15 +14,9 @@ class CreateDeploymentUser extends BaseAction { - /** - * @var IamClient - */ - protected $client; - - /** - * @return array - */ - public function invoke() + protected IamClient $client; + + public function invoke(): array { $this->client = $this->command->client(IamClient::class); diff --git a/src/Commands/Actions/CreateExecutionRole.php b/src/Commands/Actions/CreateExecutionRole.php index 710b853..299811e 100644 --- a/src/Commands/Actions/CreateExecutionRole.php +++ b/src/Commands/Actions/CreateExecutionRole.php @@ -1,5 +1,7 @@ */ @@ -12,12 +14,9 @@ class CreateExecutionRole extends BaseAction { - /** - * @var IamClient - */ - protected $client; + protected IamClient $client; - public function invoke() + public function invoke(): string { $this->progress('Creating an execution role for your functions...'); diff --git a/src/Commands/Actions/DestroyAdminKeys.php b/src/Commands/Actions/DestroyAdminKeys.php index ba265c5..ac93d4e 100644 --- a/src/Commands/Actions/DestroyAdminKeys.php +++ b/src/Commands/Actions/DestroyAdminKeys.php @@ -1,5 +1,7 @@ */ @@ -11,23 +13,23 @@ class DestroyAdminKeys extends BaseAction { - public $key; + public ?string $key = null; - public function setKey($key) + public function setKey(string $key): static { $this->key = $key; return $this; } - public function invoke() + public function invoke(): mixed { $client = $this->command->client(IamClient::class); try { $user = $client->getUser(); } catch (Throwable $e) { - return; + return null; } $name = $user['User']['UserName']; @@ -52,7 +54,7 @@ public function invoke() if (!$this->command->confirm($question, $default = $isSidecar)) { $this->progress('Not deleting keys'); - return; + return null; } $this->progress('Deleting admin keys...'); @@ -67,5 +69,7 @@ public function invoke() } $this->progress('Admin keys deleted'); + + return null; } } diff --git a/src/Commands/Actions/DetermineRegion.php b/src/Commands/Actions/DetermineRegion.php index b7ccb56..996525b 100644 --- a/src/Commands/Actions/DetermineRegion.php +++ b/src/Commands/Actions/DetermineRegion.php @@ -1,5 +1,7 @@ */ @@ -8,23 +10,15 @@ use Aws\S3\S3Client; use Illuminate\Support\Facades\File; +use Throwable; class DetermineRegion extends BaseAction { - /** - * @var S3Client - */ - protected $client; - - /** - * @var bool - */ - protected $isVapor; - - /** - * @return string - */ - public function invoke() + protected S3Client $client; + + protected bool $isVapor = false; + + public function invoke(): string { $region = config('sidecar.aws_region'); diff --git a/src/Commands/Activate.php b/src/Commands/Activate.php index c3012db..5cdde0f 100644 --- a/src/Commands/Activate.php +++ b/src/Commands/Activate.php @@ -1,5 +1,7 @@ */ diff --git a/src/Commands/Configure.php b/src/Commands/Configure.php index 089bdb6..463e86f 100644 --- a/src/Commands/Configure.php +++ b/src/Commands/Configure.php @@ -1,5 +1,7 @@ */ @@ -17,39 +19,17 @@ class Configure extends Command { - /** - * The name and signature of the console command. - * - * @var string - */ protected $signature = 'sidecar:configure'; - /** - * The console command description. - * - * @var string - */ protected $description = 'Interactively configure your Sidecar AWS environment variables'; - /** - * @var string - */ - protected $key; + protected ?string $key = null; - /** - * @var string - */ - protected $secret; + protected ?string $secret = null; - /** - * @var string - */ - protected $region; + protected ?string $region = null; - /** - * @var int - */ - protected $width = 75; + protected int $width = 75; /** * @throws Exception diff --git a/src/Commands/Deploy.php b/src/Commands/Deploy.php index 7c2382e..d856111 100644 --- a/src/Commands/Deploy.php +++ b/src/Commands/Deploy.php @@ -1,5 +1,7 @@ */ diff --git a/src/Commands/EnvironmentAwareCommand.php b/src/Commands/EnvironmentAwareCommand.php index b943d75..a925b5d 100644 --- a/src/Commands/EnvironmentAwareCommand.php +++ b/src/Commands/EnvironmentAwareCommand.php @@ -1,5 +1,7 @@ */ @@ -22,7 +24,7 @@ public function __construct() $this->getDefinition()->addOptions(Parser::parse($environment)[2]); } - public function overrideEnvironment() + public function overrideEnvironment(): void { if ($environment = $this->option('env')) { Sidecar::overrideEnvironment($environment); diff --git a/src/Commands/Install.php b/src/Commands/Install.php index 71cc09f..fea8483 100644 --- a/src/Commands/Install.php +++ b/src/Commands/Install.php @@ -1,5 +1,7 @@ */ diff --git a/src/Commands/Warm.php b/src/Commands/Warm.php index 5946d11..ec7b1de 100644 --- a/src/Commands/Warm.php +++ b/src/Commands/Warm.php @@ -1,5 +1,7 @@ */ diff --git a/src/Concerns/HandlesLogging.php b/src/Concerns/HandlesLogging.php index bc90921..b83302d 100644 --- a/src/Concerns/HandlesLogging.php +++ b/src/Concerns/HandlesLogging.php @@ -1,5 +1,7 @@ */ @@ -11,15 +13,9 @@ trait HandlesLogging { - /** - * @var array - */ - protected $loggers = []; + protected array $loggers = []; - /** - * @var bool - */ - protected $sublog = false; + protected bool $sublog = false; /** * @return $this diff --git a/src/Concerns/ManagesEnvironments.php b/src/Concerns/ManagesEnvironments.php index d832b1f..64cba46 100644 --- a/src/Concerns/ManagesEnvironments.php +++ b/src/Concerns/ManagesEnvironments.php @@ -1,5 +1,7 @@ */ @@ -8,31 +10,19 @@ trait ManagesEnvironments { - /** - * @var string - */ - protected $environment; + protected ?string $environment = null; - /** - * @param string $environment - */ - public function overrideEnvironment($environment) + public function overrideEnvironment(string $environment): void { $this->environment = $environment; } - /** - * Clear the environment override. - */ - public function clearEnvironment() + public function clearEnvironment(): void { $this->environment = null; } - /** - * @return string - */ - public function getEnvironment() + public function getEnvironment(): string { return $this->environment ?? config('sidecar.env') ?? config('app.env'); } diff --git a/src/Contracts/AwsClientConfiguration.php b/src/Contracts/AwsClientConfiguration.php index ea10ecc..295ffe4 100644 --- a/src/Contracts/AwsClientConfiguration.php +++ b/src/Contracts/AwsClientConfiguration.php @@ -1,5 +1,7 @@ */ @@ -17,15 +19,9 @@ class Deployment { - /** - * @var array - */ - protected $functions; + protected array $functions; - /** - * @var LambdaClient - */ - protected $lambda; + protected LambdaClient $lambda; /** * @return static diff --git a/src/Events/AfterFunctionExecuted.php b/src/Events/AfterFunctionExecuted.php index ecee279..b0c1f7c 100644 --- a/src/Events/AfterFunctionExecuted.php +++ b/src/Events/AfterFunctionExecuted.php @@ -1,5 +1,7 @@ */ @@ -12,27 +14,9 @@ class AfterFunctionExecuted { - /** - * @var LambdaFunction - */ - public $function; - - /** - * @var mixed - */ - public $payload; - - /** - * @var PendingResult|SettledResult - */ - public $result; - - public function __construct($function, $payload, $result) - { - $this->payload = $payload; - - $this->function = $function; - - $this->result = $result; - } + public function __construct( + public LambdaFunction $function, + public mixed $payload, + public PendingResult|SettledResult $result + ) {} } diff --git a/src/Events/AfterFunctionsActivated.php b/src/Events/AfterFunctionsActivated.php index 4cf0d8c..89919e4 100644 --- a/src/Events/AfterFunctionsActivated.php +++ b/src/Events/AfterFunctionsActivated.php @@ -1,5 +1,7 @@ */ @@ -8,10 +10,7 @@ class AfterFunctionsActivated { - public $functions = []; - - public function __construct($functions) - { - $this->functions = $functions; - } + public function __construct( + public array $functions = [] + ) {} } diff --git a/src/Events/AfterFunctionsDeployed.php b/src/Events/AfterFunctionsDeployed.php index 2fef2f1..5ac01c6 100644 --- a/src/Events/AfterFunctionsDeployed.php +++ b/src/Events/AfterFunctionsDeployed.php @@ -1,5 +1,7 @@ */ @@ -8,10 +10,7 @@ class AfterFunctionsDeployed { - public $functions = []; - - public function __construct($functions) - { - $this->functions = $functions; - } + public function __construct( + public array $functions = [] + ) {} } diff --git a/src/Events/BeforeFunctionExecuted.php b/src/Events/BeforeFunctionExecuted.php index 28237e8..460ede1 100644 --- a/src/Events/BeforeFunctionExecuted.php +++ b/src/Events/BeforeFunctionExecuted.php @@ -1,5 +1,7 @@ */ @@ -10,20 +12,8 @@ class BeforeFunctionExecuted { - /** - * @var LambdaFunction - */ - public $function; - - /** - * @var mixed - */ - public $payload; - - public function __construct($function, $payload) - { - $this->payload = $payload; - - $this->function = $function; - } + public function __construct( + public LambdaFunction $function, + public mixed $payload + ) {} } diff --git a/src/Events/BeforeFunctionsActivated.php b/src/Events/BeforeFunctionsActivated.php index d7f5ff4..e042939 100644 --- a/src/Events/BeforeFunctionsActivated.php +++ b/src/Events/BeforeFunctionsActivated.php @@ -1,5 +1,7 @@ */ @@ -8,10 +10,7 @@ class BeforeFunctionsActivated { - public $functions = []; - - public function __construct($functions) - { - $this->functions = $functions; - } + public function __construct( + public array $functions = [] + ) {} } diff --git a/src/Events/BeforeFunctionsDeployed.php b/src/Events/BeforeFunctionsDeployed.php index 187ad4e..98753d0 100644 --- a/src/Events/BeforeFunctionsDeployed.php +++ b/src/Events/BeforeFunctionsDeployed.php @@ -1,5 +1,7 @@ */ @@ -8,10 +10,7 @@ class BeforeFunctionsDeployed { - public $functions = []; - - public function __construct($functions) - { - $this->functions = $functions; - } + public function __construct( + public array $functions = [] + ) {} } diff --git a/src/Exceptions/ConfigurationException.php b/src/Exceptions/ConfigurationException.php index 19ee472..5598c95 100644 --- a/src/Exceptions/ConfigurationException.php +++ b/src/Exceptions/ConfigurationException.php @@ -1,5 +1,7 @@ */ diff --git a/src/Exceptions/FunctionNotFoundException.php b/src/Exceptions/FunctionNotFoundException.php index 5a84e56..4fff8aa 100644 --- a/src/Exceptions/FunctionNotFoundException.php +++ b/src/Exceptions/FunctionNotFoundException.php @@ -1,5 +1,7 @@ */ @@ -11,7 +13,7 @@ class FunctionNotFoundException extends SidecarException { - public static function make(LambdaFunction $function) + public static function make(LambdaFunction $function): static { $env = Sidecar::getEnvironment(); diff --git a/src/Exceptions/LambdaExecutionException.php b/src/Exceptions/LambdaExecutionException.php index c514928..a667595 100644 --- a/src/Exceptions/LambdaExecutionException.php +++ b/src/Exceptions/LambdaExecutionException.php @@ -1,5 +1,7 @@ */ diff --git a/src/Exceptions/NoFunctionsRegisteredException.php b/src/Exceptions/NoFunctionsRegisteredException.php index 896f40e..3324fd8 100644 --- a/src/Exceptions/NoFunctionsRegisteredException.php +++ b/src/Exceptions/NoFunctionsRegisteredException.php @@ -1,5 +1,7 @@ */ @@ -10,7 +12,7 @@ class NoFunctionsRegisteredException extends SidecarException { - public function __construct($message = '', $code = 0, ?Throwable $previous = null) + public function __construct(string $message = '', int $code = 0, ?Throwable $previous = null) { $message = "No Sidecar functions have been configured. \n" . "Please check your config/sidecar.php file to ensure you have registered your functions. \n" . diff --git a/src/Exceptions/SidecarException.php b/src/Exceptions/SidecarException.php index 82e2261..4a5fcfb 100644 --- a/src/Exceptions/SidecarException.php +++ b/src/Exceptions/SidecarException.php @@ -1,5 +1,7 @@ */ diff --git a/src/Finder.php b/src/Finder.php index 54462fe..401a19c 100644 --- a/src/Finder.php +++ b/src/Finder.php @@ -1,5 +1,7 @@ in($this->includedDirectories()); foreach ($finder->getIterator() as $file) { - if ($this->shouldExclude($file)) { + if ($this->shouldExclude($file->getPathname())) { continue; } diff --git a/src/LambdaFunction.php b/src/LambdaFunction.php index 6a40251..b77b7af 100644 --- a/src/LambdaFunction.php +++ b/src/LambdaFunction.php @@ -1,5 +1,7 @@ */ diff --git a/src/Manager.php b/src/Manager.php index 303eaa6..81a4c88 100644 --- a/src/Manager.php +++ b/src/Manager.php @@ -1,5 +1,7 @@ */ @@ -25,10 +27,7 @@ class Manager { use HandlesLogging, Macroable, ManagesEnvironments; - /** - * @var string - */ - public $executionVersion = 'active'; + public string $executionVersion = 'active'; /** * @param null $callback diff --git a/src/Package.php b/src/Package.php index 67920f8..94f9076 100644 --- a/src/Package.php +++ b/src/Package.php @@ -1,5 +1,7 @@ */ @@ -25,40 +27,20 @@ class Package * a handler function. Use this constant instead. * * @see https://hammerstone.dev/sidecar/docs/main/functions/handlers-and-packages - * - * @var string */ public const CONTAINER_HANDLER = 'container'; - /** - * @var array - */ - protected $include = []; + protected array $include = []; - /** - * @var array - */ - protected $exactIncludes = []; + protected array $exactIncludes = []; - /** - * @var array - */ - protected $stringContents = []; + protected array $stringContents = []; - /** - * @var array - */ - protected $exclude = []; + protected array $exclude = []; - /** - * @var Collection|null - */ - protected $files; + protected ?Collection $files = null; - /** - * @var string - */ - protected $basePath; + protected ?string $basePath = null; /** * @param array $paths diff --git a/src/Providers/SidecarServiceProvider.php b/src/Providers/SidecarServiceProvider.php index 4c9d635..172d522 100644 --- a/src/Providers/SidecarServiceProvider.php +++ b/src/Providers/SidecarServiceProvider.php @@ -1,5 +1,7 @@ */ @@ -21,7 +23,7 @@ class SidecarServiceProvider extends ServiceProvider { - public function register() + public function register(): void { $this->app->singleton(Manager::class); @@ -42,12 +44,12 @@ public function register() }); } - protected function getAwsClientConfiguration() + protected function getAwsClientConfiguration(): array { return $this->app->make(AwsClientConfigurationContract::class)->getConfiguration(); } - public function boot() + public function boot(): void { if ($this->app->runningInConsole()) { $this->commands([ diff --git a/src/Region.php b/src/Region.php index 1b6ba93..5dab444 100644 --- a/src/Region.php +++ b/src/Region.php @@ -1,5 +1,7 @@ */ @@ -56,7 +58,7 @@ class Region const SA_EAST_1 = 'sa-east-1'; // South America (São Paulo) - public static function all() + public static function all(): array { return (new ReflectionClass(static::class))->getConstants(); } diff --git a/src/Results/PendingResult.php b/src/Results/PendingResult.php index 0a9dec2..720b0af 100644 --- a/src/Results/PendingResult.php +++ b/src/Results/PendingResult.php @@ -1,5 +1,7 @@ */ @@ -15,34 +17,14 @@ class PendingResult implements Responsable, ResultContract { - /** - * @var SettledResult - */ - protected $settled; + protected ?SettledResult $settled = null; - /** - * @var PromiseInterface - */ - protected $raw; + public function __construct( + protected PromiseInterface $raw, + protected LambdaFunction $function + ) {} - /** - * @var LambdaFunction - */ - protected $function; - - /** - * @param PromiseInterface $raw - */ - public function __construct($raw, LambdaFunction $function) - { - $this->raw = $raw; - $this->function = $function; - } - - /** - * @return SettledResult - */ - public function settled() + public function settled(): SettledResult { if ($this->settled) { return $this->settled; @@ -51,10 +33,7 @@ public function settled() return $this->settled = $this->function->toSettledResult($this->raw->wait()); } - /** - * @return PromiseInterface - */ - public function rawPromise() + public function rawPromise(): PromiseInterface { return $this->raw; } @@ -67,7 +46,7 @@ public function rawPromise() * * @throws Exception */ - public function toResponse($request) + public function toResponse(mixed $request) { return $this->settled()->toResponse($request); } diff --git a/src/Results/ResultContract.php b/src/Results/ResultContract.php index 91628fe..d6f74d3 100644 --- a/src/Results/ResultContract.php +++ b/src/Results/ResultContract.php @@ -1,27 +1,23 @@ */ namespace Hammerstone\Sidecar\Results; -use Hammerstone\Sidecar\LambdaFunction; use Illuminate\Http\Request; use Illuminate\Http\Response; interface ResultContract { - public function __construct($raw, LambdaFunction $function); - /** * @param Request $request * @return Response */ - public function toResponse($response); + public function toResponse(mixed $response); - /** - * @return SettledResult - */ - public function settled(); + public function settled(): SettledResult; } diff --git a/src/Results/SettledResult.php b/src/Results/SettledResult.php index d7778d2..9aff69f 100644 --- a/src/Results/SettledResult.php +++ b/src/Results/SettledResult.php @@ -1,5 +1,7 @@ */ @@ -20,36 +22,16 @@ class SettledResult implements Responsable, ResultContract { - /** - * @var Result - */ - protected $raw; - - /** - * @var LambdaFunction - */ - protected $function; + protected array $report = []; - /** - * @var array - */ - protected $report = []; + protected ?string $requestId = null; - /** - * @var string - */ - protected $requestId; - - /** - * @var array - */ - protected $logs = []; - - public function __construct($raw, LambdaFunction $function) - { - $this->raw = $raw; - $this->function = $function; + protected array $logs = []; + public function __construct( + protected Result $raw, + protected LambdaFunction $function + ) { $this->logs = $this->parseLogs(); } @@ -73,10 +55,8 @@ public function rawAwsResult() * This is here as a little nicety for the developer, so that they * can call `settled` on either kind of result (PendingResult * or SettledResult) and get a SettledResult back. - * - * @return $this */ - public function settled() + public function settled(): SettledResult { return $this; } @@ -87,7 +67,7 @@ public function settled() * * @throws Exception */ - public function toResponse($request) + public function toResponse(mixed $request) { return $this->function->toResponse($request, $this); } @@ -211,9 +191,14 @@ public function errorAsString(int $numberOfBacktraces = 2) return $message; } - protected function parseLogs() + protected function parseLogs(): array { - $lines = base64_decode($this->raw->get('LogResult')); + $logResult = $this->raw->get('LogResult'); + if ($logResult === null) { + return []; + } + + $lines = base64_decode($logResult); $lines = explode("\n", $lines); $lines = array_map(function ($line) use (&$reportLineReached) { diff --git a/src/Runtime.php b/src/Runtime.php index 2095ef8..0b56b39 100644 --- a/src/Runtime.php +++ b/src/Runtime.php @@ -1,5 +1,7 @@ */ diff --git a/src/WarmingConfig.php b/src/WarmingConfig.php index ba8c066..2059294 100644 --- a/src/WarmingConfig.php +++ b/src/WarmingConfig.php @@ -1,5 +1,7 @@ */ @@ -8,13 +10,13 @@ class WarmingConfig { - public $instances = 0; + public int $instances = 0; - public $payload = [ + public array $payload = [ 'warming' => true ]; - public function __construct($instances = 0) + public function __construct(int $instances = 0) { $this->instances = $instances; } From 50df4efa194817e01841e339d51eade4ab7ef56b Mon Sep 17 00:00:00 2001 From: Aaron Francis Date: Wed, 26 Nov 2025 12:53:17 -0600 Subject: [PATCH 02/12] Convert Runtime and Architecture to PHP 8.1 backed enums MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Convert Runtime abstract class to backed enum with string values - Convert Architecture abstract class to backed enum with string values - Add RuntimeConstants and ArchitectureConstants as deprecated aliases - Add runtimeValue() and architectureValue() methods for string resolution - Allow runtime() and architecture() to return enum or string (Runtime|string) - Add new runtimes: nodejs24.x, nodejs22.x, python3.14, python3.13, java25, dotnet9, ruby3.4 - Mark newly deprecated AWS runtimes: nodejs18.x, dotnet6 Usage examples: - Override runtime(): return Runtime::PYTHON_312; // enum - Override runtime(): return 'python3.12'; // string (legacy) - Get string value: $function->runtimeValue(); 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- config/sidecar.php | 2 +- src/Architecture.php | 7 +-- src/ArchitectureConstants.php | 17 ++++++ src/Deployment.php | 4 +- src/LambdaFunction.php | 36 +++++++++--- src/Runtime.php | 87 +++++++++++++-------------- src/RuntimeConstants.php | 107 ++++++++++++++++++++++++++++++++++ 7 files changed, 201 insertions(+), 59 deletions(-) create mode 100644 src/ArchitectureConstants.php create mode 100644 src/RuntimeConstants.php diff --git a/config/sidecar.php b/config/sidecar.php index 53739af..48a64e2 100644 --- a/config/sidecar.php +++ b/config/sidecar.php @@ -46,7 +46,7 @@ * The default architecture your function runs on. * Available options are: x86_64, arm64 */ - 'architecture' => env('SIDECAR_ARCH', Architecture::X86_64), + 'architecture' => env('SIDECAR_ARCH', Architecture::X86_64->value), /* * The base path for your package files. If you e.g. keep diff --git a/src/Architecture.php b/src/Architecture.php index e7c1e27..ec21a57 100644 --- a/src/Architecture.php +++ b/src/Architecture.php @@ -4,9 +4,8 @@ namespace Hammerstone\Sidecar; -abstract class Architecture +enum Architecture: string { - public const X86_64 = 'x86_64'; - - public const ARM_64 = 'arm64'; + case X86_64 = 'x86_64'; + case ARM_64 = 'arm64'; } diff --git a/src/ArchitectureConstants.php b/src/ArchitectureConstants.php new file mode 100644 index 0000000..2e66719 --- /dev/null +++ b/src/ArchitectureConstants.php @@ -0,0 +1,17 @@ +value; + + /** @deprecated Use Architecture::ARM_64 instead */ + public const ARM_64 = Architecture::ARM_64->value; +} diff --git a/src/Deployment.php b/src/Deployment.php index b9ae14a..e69d3ab 100644 --- a/src/Deployment.php +++ b/src/Deployment.php @@ -101,10 +101,10 @@ public function activate($prewarm = false) protected function deploySingle(LambdaFunction $function) { Sidecar::log('Environment: ' . Sidecar::getEnvironment()); - Sidecar::log('Architecture: ' . $function->architecture()); + Sidecar::log('Architecture: ' . $function->architectureValue()); Sidecar::log('Package Type: ' . $function->packageType()); if ($function->packageType() === 'Zip') { - Sidecar::log('Runtime: ' . $function->runtime()); + Sidecar::log('Runtime: ' . $function->runtimeValue()); } $function->beforeDeployment(); diff --git a/src/LambdaFunction.php b/src/LambdaFunction.php index b77b7af..86252ce 100644 --- a/src/LambdaFunction.php +++ b/src/LambdaFunction.php @@ -232,22 +232,40 @@ public function toPendingResult(PromiseInterface $raw) * The runtime environment for the Lambda function. * * @see https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html - * - * @return string */ - public function runtime() + public function runtime(): Runtime|string { return Runtime::NODEJS_20; } + /** + * Resolve the runtime to a string value. + */ + public function runtimeValue(): string + { + $runtime = $this->runtime(); + + return $runtime instanceof Runtime ? $runtime->value : $runtime; + } + /** * The architecture for the Lambda function. - * - * @return string */ - public function architecture() + public function architecture(): Architecture|string + { + $arch = config('sidecar.architecture', Architecture::X86_64->value); + + return $arch instanceof Architecture ? $arch : $arch; + } + + /** + * Resolve the architecture to a string value. + */ + public function architectureValue(): string { - return config('sidecar.architecture', Architecture::X86_64); + $arch = $this->architecture(); + + return $arch instanceof Architecture ? $arch->value : $arch; } /** @@ -428,7 +446,7 @@ public function toDeploymentArray() { $config = [ 'FunctionName' => $this->nameWithPrefix(), - 'Runtime' => $this->runtime(), + 'Runtime' => $this->runtimeValue(), 'Role' => config('sidecar.execution_role'), 'Handler' => $this->normalizedHandler(), 'Code' => $this->packageType() === 'Zip' @@ -443,7 +461,7 @@ public function toDeploymentArray() 'Layers' => $this->layers(), 'Publish' => true, 'PackageType' => $this->packageType(), - 'Architectures' => [$this->architecture()], + 'Architectures' => [$this->architectureValue()], 'Tags' => $this->tags(), ]; diff --git a/src/Runtime.php b/src/Runtime.php index 0b56b39..836561d 100644 --- a/src/Runtime.php +++ b/src/Runtime.php @@ -4,64 +4,65 @@ namespace Hammerstone\Sidecar; -abstract class Runtime +enum Runtime: string { - public const NODEJS_20 = 'nodejs20.x'; + case NODEJS_24 = 'nodejs24.x'; + case NODEJS_22 = 'nodejs22.x'; + case NODEJS_20 = 'nodejs20.x'; - public const NODEJS_18 = 'nodejs18.x'; + /** @deprecated AWS has deprecated this runtime */ + case NODEJS_18 = 'nodejs18.x'; - /** @deprecated */ - public const NODEJS_16 = 'nodejs16.x'; + /** @deprecated AWS has deprecated this runtime */ + case NODEJS_16 = 'nodejs16.x'; - /** @deprecated */ - public const NODEJS_14 = 'nodejs14.x'; + /** @deprecated AWS has deprecated this runtime */ + case NODEJS_14 = 'nodejs14.x'; - public const PYTHON_312 = 'python3.12'; + case PYTHON_314 = 'python3.14'; + case PYTHON_313 = 'python3.13'; + case PYTHON_312 = 'python3.12'; + case PYTHON_311 = 'python3.11'; + case PYTHON_310 = 'python3.10'; + case PYTHON_39 = 'python3.9'; - public const PYTHON_311 = 'python3.11'; + /** @deprecated AWS has deprecated this runtime */ + case PYTHON_38 = 'python3.8'; - public const PYTHON_310 = 'python3.10'; + /** @deprecated AWS has deprecated this runtime */ + case PYTHON_37 = 'python3.7'; - public const PYTHON_39 = 'python3.9'; + case JAVA_25 = 'java25'; + case JAVA_21 = 'java21'; + case JAVA_17 = 'java17'; + case JAVA_11 = 'java11'; + case JAVA_8_LINUX2 = 'java8.al2'; - /** @deprecated */ - public const PYTHON_38 = 'python3.8'; + /** @deprecated AWS has deprecated this runtime */ + case JAVA_8 = 'java8'; - /** @deprecated */ - public const PYTHON_37 = 'python3.7'; + case DOT_NET_9 = 'dotnet9'; + case DOT_NET_8 = 'dotnet8'; - public const JAVA_21 = 'java21'; + /** @deprecated AWS has deprecated this runtime */ + case DOT_NET_7 = 'dotnet7'; - public const JAVA_17 = 'java17'; + /** @deprecated AWS has deprecated this runtime */ + case DOT_NET_6 = 'dotnet6'; - public const JAVA_11 = 'java11'; + case RUBY_34 = 'ruby3.4'; + case RUBY_33 = 'ruby3.3'; + case RUBY_32 = 'ruby3.2'; - public const JAVA_8_LINUX2 = 'java8.al2'; + /** @deprecated AWS has deprecated this runtime */ + case RUBY_27 = 'ruby2.7'; - /** @deprecated */ - public const JAVA_8 = 'java8'; + /** @deprecated AWS has deprecated this runtime */ + case GO_1X = 'go1.x'; - public const DOT_NET_8 = 'dotnet8'; + case PROVIDED_AL2023 = 'provided.al2023'; + case PROVIDED_AL2 = 'provided.al2'; - /** @deprecated */ - public const DOT_NET_7 = 'dotnet7'; - - public const DOT_NET_6 = 'dotnet6'; - - public const RUBY_33 = 'ruby3.3'; - - public const RUBY_32 = 'ruby3.2'; - - /** @deprecated */ - public const RUBY_27 = 'ruby2.7'; - - /** @deprecated */ - public const GO_1X = 'go1.x'; - - public const PROVIDED_AL2023 = 'provided.al2023'; - - public const PROVIDED_AL2 = 'provided.al2'; - - /** @deprecated */ - public const PROVIDED = 'provided'; + /** @deprecated AWS has deprecated this runtime */ + case PROVIDED = 'provided'; } diff --git a/src/RuntimeConstants.php b/src/RuntimeConstants.php new file mode 100644 index 0000000..bffc91d --- /dev/null +++ b/src/RuntimeConstants.php @@ -0,0 +1,107 @@ +value; + + /** @deprecated Use Runtime::NODEJS_22 instead */ + public const NODEJS_22 = Runtime::NODEJS_22->value; + + /** @deprecated Use Runtime::NODEJS_20 instead */ + public const NODEJS_20 = Runtime::NODEJS_20->value; + + /** @deprecated Use Runtime::NODEJS_18 instead (also deprecated by AWS) */ + public const NODEJS_18 = Runtime::NODEJS_18->value; + + /** @deprecated Use Runtime::NODEJS_16 instead (also deprecated by AWS) */ + public const NODEJS_16 = Runtime::NODEJS_16->value; + + /** @deprecated Use Runtime::NODEJS_14 instead (also deprecated by AWS) */ + public const NODEJS_14 = Runtime::NODEJS_14->value; + + /** @deprecated Use Runtime::PYTHON_314 instead */ + public const PYTHON_314 = Runtime::PYTHON_314->value; + + /** @deprecated Use Runtime::PYTHON_313 instead */ + public const PYTHON_313 = Runtime::PYTHON_313->value; + + /** @deprecated Use Runtime::PYTHON_312 instead */ + public const PYTHON_312 = Runtime::PYTHON_312->value; + + /** @deprecated Use Runtime::PYTHON_311 instead */ + public const PYTHON_311 = Runtime::PYTHON_311->value; + + /** @deprecated Use Runtime::PYTHON_310 instead */ + public const PYTHON_310 = Runtime::PYTHON_310->value; + + /** @deprecated Use Runtime::PYTHON_39 instead */ + public const PYTHON_39 = Runtime::PYTHON_39->value; + + /** @deprecated Use Runtime::PYTHON_38 instead (also deprecated by AWS) */ + public const PYTHON_38 = Runtime::PYTHON_38->value; + + /** @deprecated Use Runtime::PYTHON_37 instead (also deprecated by AWS) */ + public const PYTHON_37 = Runtime::PYTHON_37->value; + + /** @deprecated Use Runtime::JAVA_25 instead */ + public const JAVA_25 = Runtime::JAVA_25->value; + + /** @deprecated Use Runtime::JAVA_21 instead */ + public const JAVA_21 = Runtime::JAVA_21->value; + + /** @deprecated Use Runtime::JAVA_17 instead */ + public const JAVA_17 = Runtime::JAVA_17->value; + + /** @deprecated Use Runtime::JAVA_11 instead */ + public const JAVA_11 = Runtime::JAVA_11->value; + + /** @deprecated Use Runtime::JAVA_8_LINUX2 instead */ + public const JAVA_8_LINUX2 = Runtime::JAVA_8_LINUX2->value; + + /** @deprecated Use Runtime::JAVA_8 instead (also deprecated by AWS) */ + public const JAVA_8 = Runtime::JAVA_8->value; + + /** @deprecated Use Runtime::DOT_NET_9 instead */ + public const DOT_NET_9 = Runtime::DOT_NET_9->value; + + /** @deprecated Use Runtime::DOT_NET_8 instead */ + public const DOT_NET_8 = Runtime::DOT_NET_8->value; + + /** @deprecated Use Runtime::DOT_NET_7 instead (also deprecated by AWS) */ + public const DOT_NET_7 = Runtime::DOT_NET_7->value; + + /** @deprecated Use Runtime::DOT_NET_6 instead (also deprecated by AWS) */ + public const DOT_NET_6 = Runtime::DOT_NET_6->value; + + /** @deprecated Use Runtime::RUBY_34 instead */ + public const RUBY_34 = Runtime::RUBY_34->value; + + /** @deprecated Use Runtime::RUBY_33 instead */ + public const RUBY_33 = Runtime::RUBY_33->value; + + /** @deprecated Use Runtime::RUBY_32 instead */ + public const RUBY_32 = Runtime::RUBY_32->value; + + /** @deprecated Use Runtime::RUBY_27 instead (also deprecated by AWS) */ + public const RUBY_27 = Runtime::RUBY_27->value; + + /** @deprecated Use Runtime::GO_1X instead (also deprecated by AWS) */ + public const GO_1X = Runtime::GO_1X->value; + + /** @deprecated Use Runtime::PROVIDED_AL2023 instead */ + public const PROVIDED_AL2023 = Runtime::PROVIDED_AL2023->value; + + /** @deprecated Use Runtime::PROVIDED_AL2 instead */ + public const PROVIDED_AL2 = Runtime::PROVIDED_AL2->value; + + /** @deprecated Use Runtime::PROVIDED instead (also deprecated by AWS) */ + public const PROVIDED = Runtime::PROVIDED->value; +} From 2370029a7a447c6771dd2f3c4b0547277dcbc3d4 Mon Sep 17 00:00:00 2001 From: Aaron Francis Date: Wed, 26 Nov 2025 14:25:53 -0600 Subject: [PATCH 03/12] Add tests for Runtime and Architecture enums MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tests cover enum values, backwards compatibility with constants classes, and the runtimeValue()/architectureValue() resolution methods. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- tests/Unit/ArchitectureTest.php | 47 ++++++++ tests/Unit/FunctionTest.php | 75 +++++++++++++ tests/Unit/RuntimeTest.php | 102 ++++++++++++++++++ .../Unit/Support/EnumRuntimeTestFunction.php | 30 ++++++ .../Support/StringRuntimeTestFunction.php | 30 ++++++ 5 files changed, 284 insertions(+) create mode 100644 tests/Unit/ArchitectureTest.php create mode 100644 tests/Unit/RuntimeTest.php create mode 100644 tests/Unit/Support/EnumRuntimeTestFunction.php create mode 100644 tests/Unit/Support/StringRuntimeTestFunction.php diff --git a/tests/Unit/ArchitectureTest.php b/tests/Unit/ArchitectureTest.php new file mode 100644 index 0000000..3984387 --- /dev/null +++ b/tests/Unit/ArchitectureTest.php @@ -0,0 +1,47 @@ + + */ + +namespace Hammerstone\Sidecar\Tests\Unit; + +use Hammerstone\Sidecar\Architecture; +use Hammerstone\Sidecar\ArchitectureConstants; + +class ArchitectureTest extends Base +{ + public function test_architecture_is_a_backed_enum() + { + $this->assertInstanceOf(\BackedEnum::class, Architecture::X86_64); + } + + public function test_x86_64_has_correct_value() + { + $this->assertEquals('x86_64', Architecture::X86_64->value); + } + + public function test_arm64_has_correct_value() + { + $this->assertEquals('arm64', Architecture::ARM_64->value); + } + + public function test_architecture_can_be_created_from_string() + { + $this->assertEquals(Architecture::X86_64, Architecture::from('x86_64')); + $this->assertEquals(Architecture::ARM_64, Architecture::from('arm64')); + } + + public function test_architecture_constants_class_exists_for_backwards_compatibility() + { + $this->assertTrue(class_exists(ArchitectureConstants::class)); + } + + public function test_architecture_constants_match_enum_values() + { + $this->assertEquals(Architecture::X86_64->value, ArchitectureConstants::X86_64); + $this->assertEquals(Architecture::ARM_64->value, ArchitectureConstants::ARM_64); + } +} diff --git a/tests/Unit/FunctionTest.php b/tests/Unit/FunctionTest.php index c1c5194..524619e 100644 --- a/tests/Unit/FunctionTest.php +++ b/tests/Unit/FunctionTest.php @@ -1,12 +1,18 @@ */ namespace Hammerstone\Sidecar\Tests\Unit; +use Hammerstone\Sidecar\Architecture; +use Hammerstone\Sidecar\Runtime; use Hammerstone\Sidecar\Tests\Unit\Support\EmptyTestFunction; +use Hammerstone\Sidecar\Tests\Unit\Support\EnumRuntimeTestFunction; +use Hammerstone\Sidecar\Tests\Unit\Support\StringRuntimeTestFunction; class FunctionTest extends Base { @@ -115,4 +121,73 @@ public function test_memory_and_timeout_and_storage_get_cast_to_ints() $this->assertSame(500, $array['MemorySize']); $this->assertSame(1024, $array['EphemeralStorage']['Size']); } + + public function test_default_runtime_returns_enum() + { + $function = new EmptyTestFunction; + + $this->assertInstanceOf(Runtime::class, $function->runtime()); + $this->assertEquals(Runtime::NODEJS_20, $function->runtime()); + } + + public function test_runtime_value_resolves_enum_to_string() + { + $function = new EnumRuntimeTestFunction; + + $this->assertInstanceOf(Runtime::class, $function->runtime()); + $this->assertEquals('python3.12', $function->runtimeValue()); + } + + public function test_runtime_value_passes_through_string() + { + $function = new StringRuntimeTestFunction; + + $this->assertIsString($function->runtime()); + $this->assertEquals('custom-runtime', $function->runtimeValue()); + } + + public function test_default_architecture_from_config() + { + config(['sidecar.architecture' => 'arm64']); + + $function = new EmptyTestFunction; + + $this->assertEquals('arm64', $function->architectureValue()); + } + + public function test_architecture_value_resolves_enum_to_string() + { + config(['sidecar.architecture' => Architecture::ARM_64]); + + $function = new EmptyTestFunction; + + $this->assertEquals('arm64', $function->architectureValue()); + } + + public function test_architecture_value_passes_through_string() + { + config(['sidecar.architecture' => 'x86_64']); + + $function = new EmptyTestFunction; + + $this->assertEquals('x86_64', $function->architectureValue()); + } + + public function test_deployment_array_contains_architecture() + { + config(['sidecar.architecture' => 'arm64']); + + $function = new EmptyTestFunction; + $array = $function->toDeploymentArray(); + + $this->assertEquals(['arm64'], $array['Architectures']); + } + + public function test_deployment_array_contains_runtime() + { + $function = new EmptyTestFunction; + $array = $function->toDeploymentArray(); + + $this->assertEquals('nodejs20.x', $array['Runtime']); + } } diff --git a/tests/Unit/RuntimeTest.php b/tests/Unit/RuntimeTest.php new file mode 100644 index 0000000..46d0217 --- /dev/null +++ b/tests/Unit/RuntimeTest.php @@ -0,0 +1,102 @@ + + */ + +namespace Hammerstone\Sidecar\Tests\Unit; + +use Hammerstone\Sidecar\Runtime; +use Hammerstone\Sidecar\RuntimeConstants; + +class RuntimeTest extends Base +{ + public function test_runtime_is_a_backed_enum() + { + $this->assertInstanceOf(\BackedEnum::class, Runtime::NODEJS_20); + } + + public function test_nodejs_runtimes_have_correct_values() + { + $this->assertEquals('nodejs24.x', Runtime::NODEJS_24->value); + $this->assertEquals('nodejs22.x', Runtime::NODEJS_22->value); + $this->assertEquals('nodejs20.x', Runtime::NODEJS_20->value); + $this->assertEquals('nodejs18.x', Runtime::NODEJS_18->value); + $this->assertEquals('nodejs16.x', Runtime::NODEJS_16->value); + $this->assertEquals('nodejs14.x', Runtime::NODEJS_14->value); + } + + public function test_python_runtimes_have_correct_values() + { + $this->assertEquals('python3.14', Runtime::PYTHON_314->value); + $this->assertEquals('python3.13', Runtime::PYTHON_313->value); + $this->assertEquals('python3.12', Runtime::PYTHON_312->value); + $this->assertEquals('python3.11', Runtime::PYTHON_311->value); + $this->assertEquals('python3.10', Runtime::PYTHON_310->value); + $this->assertEquals('python3.9', Runtime::PYTHON_39->value); + $this->assertEquals('python3.8', Runtime::PYTHON_38->value); + $this->assertEquals('python3.7', Runtime::PYTHON_37->value); + } + + public function test_java_runtimes_have_correct_values() + { + $this->assertEquals('java25', Runtime::JAVA_25->value); + $this->assertEquals('java21', Runtime::JAVA_21->value); + $this->assertEquals('java17', Runtime::JAVA_17->value); + $this->assertEquals('java11', Runtime::JAVA_11->value); + $this->assertEquals('java8.al2', Runtime::JAVA_8_LINUX2->value); + $this->assertEquals('java8', Runtime::JAVA_8->value); + } + + public function test_dotnet_runtimes_have_correct_values() + { + $this->assertEquals('dotnet9', Runtime::DOT_NET_9->value); + $this->assertEquals('dotnet8', Runtime::DOT_NET_8->value); + $this->assertEquals('dotnet7', Runtime::DOT_NET_7->value); + $this->assertEquals('dotnet6', Runtime::DOT_NET_6->value); + } + + public function test_ruby_runtimes_have_correct_values() + { + $this->assertEquals('ruby3.4', Runtime::RUBY_34->value); + $this->assertEquals('ruby3.3', Runtime::RUBY_33->value); + $this->assertEquals('ruby3.2', Runtime::RUBY_32->value); + $this->assertEquals('ruby2.7', Runtime::RUBY_27->value); + } + + public function test_provided_runtimes_have_correct_values() + { + $this->assertEquals('provided.al2023', Runtime::PROVIDED_AL2023->value); + $this->assertEquals('provided.al2', Runtime::PROVIDED_AL2->value); + $this->assertEquals('provided', Runtime::PROVIDED->value); + } + + public function test_go_runtime_has_correct_value() + { + $this->assertEquals('go1.x', Runtime::GO_1X->value); + } + + public function test_runtime_can_be_created_from_string() + { + $this->assertEquals(Runtime::NODEJS_20, Runtime::from('nodejs20.x')); + $this->assertEquals(Runtime::PYTHON_312, Runtime::from('python3.12')); + } + + public function test_runtime_constants_class_exists_for_backwards_compatibility() + { + $this->assertTrue(class_exists(RuntimeConstants::class)); + } + + public function test_runtime_constants_match_enum_values() + { + $this->assertEquals(Runtime::NODEJS_24->value, RuntimeConstants::NODEJS_24); + $this->assertEquals(Runtime::NODEJS_22->value, RuntimeConstants::NODEJS_22); + $this->assertEquals(Runtime::NODEJS_20->value, RuntimeConstants::NODEJS_20); + $this->assertEquals(Runtime::PYTHON_312->value, RuntimeConstants::PYTHON_312); + $this->assertEquals(Runtime::JAVA_21->value, RuntimeConstants::JAVA_21); + $this->assertEquals(Runtime::DOT_NET_8->value, RuntimeConstants::DOT_NET_8); + $this->assertEquals(Runtime::RUBY_33->value, RuntimeConstants::RUBY_33); + } +} diff --git a/tests/Unit/Support/EnumRuntimeTestFunction.php b/tests/Unit/Support/EnumRuntimeTestFunction.php new file mode 100644 index 0000000..9ffc3a4 --- /dev/null +++ b/tests/Unit/Support/EnumRuntimeTestFunction.php @@ -0,0 +1,30 @@ + + */ + +namespace Hammerstone\Sidecar\Tests\Unit\Support; + +use Hammerstone\Sidecar\LambdaFunction; +use Hammerstone\Sidecar\Runtime; + +class EnumRuntimeTestFunction extends LambdaFunction +{ + public function handler() + { + return 'index.handler'; + } + + public function package() + { + return []; + } + + public function runtime(): Runtime|string + { + return Runtime::PYTHON_312; + } +} diff --git a/tests/Unit/Support/StringRuntimeTestFunction.php b/tests/Unit/Support/StringRuntimeTestFunction.php new file mode 100644 index 0000000..df81417 --- /dev/null +++ b/tests/Unit/Support/StringRuntimeTestFunction.php @@ -0,0 +1,30 @@ + + */ + +namespace Hammerstone\Sidecar\Tests\Unit\Support; + +use Hammerstone\Sidecar\LambdaFunction; +use Hammerstone\Sidecar\Runtime; + +class StringRuntimeTestFunction extends LambdaFunction +{ + public function handler() + { + return 'index.handler'; + } + + public function package() + { + return []; + } + + public function runtime(): Runtime|string + { + return 'custom-runtime'; + } +} From 6e8f2e49201a0644af6aadd166619ba588144d8a Mon Sep 17 00:00:00 2001 From: Aaron Francis Date: Wed, 26 Nov 2025 14:28:40 -0600 Subject: [PATCH 04/12] Fix LambdaClientTest expectations for architecture values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tests expected Architecture::X86_64 enum but the actual code passes the string value 'x86_64'. Updated test expectations to match. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- tests/Unit/LambdaClientTest.php | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/tests/Unit/LambdaClientTest.php b/tests/Unit/LambdaClientTest.php index 295dc0f..4e15c0a 100644 --- a/tests/Unit/LambdaClientTest.php +++ b/tests/Unit/LambdaClientTest.php @@ -7,7 +7,6 @@ namespace Hammerstone\Sidecar\Tests\Unit; use Aws\Lambda\Exception\LambdaException; -use Hammerstone\Sidecar\Architecture; use Hammerstone\Sidecar\Clients\LambdaClient; use Hammerstone\Sidecar\Tests\Unit\Support\DeploymentTestFunction; use Hammerstone\Sidecar\Tests\Unit\Support\DeploymentTestFunctionWithImage; @@ -180,9 +179,7 @@ public function test_update_existing_function() ], 'MemorySize' => 'test-MemorySize', 'Layers' => 'test-Layers', - 'Architectures' => [ - Architecture::X86_64 - ], + 'Architectures' => ['x86_64'], 'Tags' => [], ]); @@ -193,9 +190,7 @@ public function test_update_existing_function() 'S3Bucket' => 'test-bucket', 'S3Key' => 'test-key', 'Publish' => 'test-Publish', - 'Architectures' => [ - Architecture::X86_64 - ] + 'Architectures' => ['x86_64'] ]); $this->lambda->updateExistingFunction($function); @@ -222,9 +217,7 @@ public function test_update_existing_image_function() ], 'Layers' => [], 'PackageType' => 'Image', - 'Architectures' => [ - Architecture::X86_64 - ], + 'Architectures' => ['x86_64'], 'Tags' => [], ]); @@ -234,9 +227,7 @@ public function test_update_existing_image_function() 'FunctionName' => 'test-FunctionName', 'Publish' => 'test-Publish', 'ImageUri' => '123.dkr.ecr.us-west-2.amazonaws.com/image:latest', - 'Architectures' => [ - Architecture::X86_64 - ] + 'Architectures' => ['x86_64'] ]); $this->lambda->updateExistingFunction($function); From fa80caa5f5d8700f39c7558b2ac8ded73f8d2d70 Mon Sep 17 00:00:00 2001 From: Aaron Francis Date: Wed, 26 Nov 2025 19:34:46 -0600 Subject: [PATCH 05/12] Remove unused properties and import from DetermineRegion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Removed unused S3Client import and two unused class properties ($client and $isVapor) that were never assigned or read. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/Commands/Actions/DetermineRegion.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Commands/Actions/DetermineRegion.php b/src/Commands/Actions/DetermineRegion.php index 996525b..cf0260f 100644 --- a/src/Commands/Actions/DetermineRegion.php +++ b/src/Commands/Actions/DetermineRegion.php @@ -8,16 +8,11 @@ namespace Hammerstone\Sidecar\Commands\Actions; -use Aws\S3\S3Client; use Illuminate\Support\Facades\File; use Throwable; class DetermineRegion extends BaseAction { - protected S3Client $client; - - protected bool $isVapor = false; - public function invoke(): string { $region = config('sidecar.aws_region'); From a0506ecd5993f0475cd9dc6710cb2ba9e580726f Mon Sep 17 00:00:00 2001 From: Aaron Francis Date: Wed, 26 Nov 2025 19:36:09 -0600 Subject: [PATCH 06/12] Fix potential null return in getEnvironment() under strict types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added 'production' as a safe default fallback when neither sidecar.env nor app.env are configured, ensuring the string return type is satisfied. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/Concerns/ManagesEnvironments.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Concerns/ManagesEnvironments.php b/src/Concerns/ManagesEnvironments.php index 64cba46..b31d96c 100644 --- a/src/Concerns/ManagesEnvironments.php +++ b/src/Concerns/ManagesEnvironments.php @@ -24,6 +24,6 @@ public function clearEnvironment(): void public function getEnvironment(): string { - return $this->environment ?? config('sidecar.env') ?? config('app.env'); + return $this->environment ?? config('sidecar.env') ?? config('app.env', 'production'); } } From e29df018dad1625ea1e3714d5a1a9bd30e196f49 Mon Sep 17 00:00:00 2001 From: Aaron Francis Date: Wed, 26 Nov 2025 22:34:55 -0600 Subject: [PATCH 07/12] Docs --- .gitignore | 1 + CLAUDE.md | 91 +++++++++++++++++++++++++++++++++ docs/events.md | 15 ++++-- docs/functions/customization.md | 55 ++++++++++++-------- docs/functions/deploying.md | 2 +- docs/functions/performance.md | 35 ++++++++++++- docs/overview.md | 14 ++--- 7 files changed, 182 insertions(+), 31 deletions(-) create mode 100644 CLAUDE.md diff --git a/.gitignore b/.gitignore index f190ee6..cf89ac8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.claude/ /vendor /storage/framework/testing .env diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..0a68eed --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,91 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## What is Sidecar? + +Sidecar is a Laravel package that lets you deploy and run AWS Lambda functions directly from your Laravel app. Write a function in Node, Python, Ruby, Java, or any Lambda-supported runtime, and call it from PHP as easily as `MyFunction::execute(['key' => 'value'])`. + +The package handles all the AWS complexity: packaging your code, uploading to S3, creating/updating Lambda functions, managing versions, and invoking them. + +## Testing + +```bash +# Run all tests +./vendor/bin/phpunit + +# Run a single test file +./vendor/bin/phpunit tests/Unit/FunctionTest.php + +# Run a specific test method +./vendor/bin/phpunit --filter test_runtime_value_resolves_enum_to_string +``` + +Tests use Orchestra Testbench. The test suite has unit tests in `tests/Unit/` and integration tests in `tests/Integration/`. Most development work uses unit tests with mocked AWS clients. + +## How the Code is Organized + +### The Main Players + +**`LambdaFunction`** is the abstract base class that users extend. Every function needs two methods: +- `handler()` - tells Lambda which file/function to run (e.g., `'image.handler'`) +- `package()` - lists files to include in the deployment ZIP + +Users call static methods like `MyFunction::execute($payload)` to run their functions. + +**`Manager`** (accessed via the `Sidecar` facade) does the actual work of invoking Lambda functions. It prepares payloads, calls the AWS SDK, and wraps responses in result objects. + +**`Deployment`** handles the deploy workflow: package the code, create or update the Lambda function, then optionally activate it by pointing an alias to the new version. + +**`Package`** builds the ZIP file for deployment. It collects files based on include/exclude patterns, computes a hash for change detection, and streams the ZIP directly to S3. + +### AWS Clients + +The `src/Clients/` directory has thin wrappers around AWS SDK clients: +- `LambdaClient` extends the SDK client and adds retry logic for Lambda's "Pending" state +- `S3Client` and `CloudWatchLogsClient` are simpler wrappers + +### Results + +When you execute a function: +- Sync calls return a `SettledResult` with the response body, logs, and error info +- Async calls return a `PendingResult` that resolves to `SettledResult` when you call `->settled()` + +### Runtime and Architecture + +`Runtime` and `Architecture` are PHP 8.1 backed enums. There are also deprecated `RuntimeConstants` and `ArchitectureConstants` classes for backwards compatibility. + +When working with these, use `runtimeValue()` and `architectureValue()` methods to get the string values - they handle both enum and string inputs. + +## Configuration + +Everything lives in `config/sidecar.php`: +- `functions` - array of `LambdaFunction` classes to deploy +- `env` - environment name, used to namespace function names (defaults to `APP_ENV`) +- `timeout`, `memory`, `storage` - default Lambda settings +- AWS credentials (`aws_key`, `aws_secret`, `aws_region`, `aws_bucket`, `execution_role`) + +Function names are automatically prefixed with app name and environment to avoid collisions. + +## Artisan Commands + +These are what users run (not needed for package development, but good to know): +- `sidecar:deploy` - packages and deploys functions to Lambda +- `sidecar:activate` - points the "active" alias to latest version +- `sidecar:deploy --activate` - both in one step +- `sidecar:warm` - pre-warms function instances to reduce cold starts +- `sidecar:configure` - interactive wizard to set up AWS resources + +## Events + +The package fires Laravel events you can hook into: +- `BeforeFunctionsDeployed` / `AfterFunctionsDeployed` +- `BeforeFunctionsActivated` / `AfterFunctionsActivated` +- `BeforeFunctionExecuted` / `AfterFunctionExecuted` + +## Things to Know + +- This package supports Laravel 8 through 12 and PHP 8.1+ +- All source files use `declare(strict_types=1)` +- The codebase uses constructor property promotion and typed properties throughout +- Tests use Mockery for mocking AWS clients - check existing tests for patterns diff --git a/docs/events.md b/docs/events.md index 5635198..ef54a66 100644 --- a/docs/events.md +++ b/docs/events.md @@ -1,13 +1,22 @@ # Events -Sidecar fires a few events related to deployment that you can hook into: +Sidecar fires events during deployment, activation, and execution that you can hook into. + +## Deployment & Activation Events - `BeforeFunctionsDeployed` - `AfterFunctionsDeployed` - `BeforeFunctionsActivated` - `AfterFunctionsActivated` -Each of these events has a public `functions` property that holds all the functions that are being deployed or activated. +Each of these events has a public `functions` property that holds all the functions being deployed or activated. You can use these to build packages, install dependencies, or clean up after deployment. + +## Execution Events + +- `BeforeFunctionExecuted` +- `AfterFunctionExecuted` + +These events fire every time a function is executed. `BeforeFunctionExecuted` has `function` and `payload` properties. `AfterFunctionExecuted` adds the `result` property. -You can use these events to build packages, install dependencies, or clean up after they are deployed. \ No newline at end of file +You can use these for logging, monitoring, or modifying payloads before they're sent to Lambda. \ No newline at end of file diff --git a/docs/functions/customization.md b/docs/functions/customization.md index 22d2875..6ff18a1 100644 --- a/docs/functions/customization.md +++ b/docs/functions/customization.md @@ -5,37 +5,52 @@ The only two things _required_ for a Sidecar function are the [package and the h ## Runtime -Lambda supports multiple languages through the use of runtimes. You can choose any of the following runtimes by returning its corresponding identifier: - -- Node.js 20: `nodejs20.x` -- Node.js 18: `nodejs18.x` -- Python 3.12: `python3.12` -- Python 3.11: `python3.11` -- Python 3.10: `python3.10` -- Python 3.9: `python3.9` -- Java 21: `java21` -- Java 17: `java17` -- Java 11: `java11` -- Java 8: `java8.al2` -- .NET 8: `dotnet8` -- .NET 6: `dotnet6` -- Ruby 3.3: `ruby3.3` -- Ruby 3.2: `ruby3.2` -- OS-only runtime (Amazon Linux 2023): `provided.al2023` -- OS-only runtime (Amazon Linux 2): `provided.al2` - -E.g. to use the Python 3.12 runtime, you would return `python3.12`: +Lambda supports multiple languages through the use of runtimes. Sidecar provides a `Runtime` enum with all supported runtimes, or you can return the runtime identifier as a string. + +Available runtimes include: + +- Node.js 24: `Runtime::NODEJS_24` or `'nodejs24.x'` +- Node.js 22: `Runtime::NODEJS_22` or `'nodejs22.x'` +- Node.js 20: `Runtime::NODEJS_20` or `'nodejs20.x'` +- Python 3.14: `Runtime::PYTHON_314` or `'python3.14'` +- Python 3.13: `Runtime::PYTHON_313` or `'python3.13'` +- Python 3.12: `Runtime::PYTHON_312` or `'python3.12'` +- Python 3.11: `Runtime::PYTHON_311` or `'python3.11'` +- Python 3.10: `Runtime::PYTHON_310` or `'python3.10'` +- Python 3.9: `Runtime::PYTHON_39` or `'python3.9'` +- Java 25: `Runtime::JAVA_25` or `'java25'` +- Java 21: `Runtime::JAVA_21` or `'java21'` +- Java 17: `Runtime::JAVA_17` or `'java17'` +- Java 11: `Runtime::JAVA_11` or `'java11'` +- Java 8: `Runtime::JAVA_8_LINUX2` or `'java8.al2'` +- .NET 9: `Runtime::DOT_NET_9` or `'dotnet9'` +- .NET 8: `Runtime::DOT_NET_8` or `'dotnet8'` +- Ruby 3.4: `Runtime::RUBY_34` or `'ruby3.4'` +- Ruby 3.3: `Runtime::RUBY_33` or `'ruby3.3'` +- Ruby 3.2: `Runtime::RUBY_32` or `'ruby3.2'` +- OS-only runtime (Amazon Linux 2023): `Runtime::PROVIDED_AL2023` or `'provided.al2023'` +- OS-only runtime (Amazon Linux 2): `Runtime::PROVIDED_AL2` or `'provided.al2'` + +You can use either the enum or a string: ```php +use Hammerstone\Sidecar\Runtime; + class ExampleFunction extends LambdaFunction { public function runtime() // [tl! focus:3] { + // Using the enum (recommended) + return Runtime::PYTHON_312; + + // Or using a string return 'python3.12'; } } ``` +The default runtime is `Runtime::NODEJS_20`. + Read more in the [AWS Documentation](https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtimes.html). ## Memory diff --git a/docs/functions/deploying.md b/docs/functions/deploying.md index 962a010..db550b4 100644 --- a/docs/functions/deploying.md +++ b/docs/functions/deploying.md @@ -14,7 +14,7 @@ When you run that, you'll see an output log similar to the one below: ```text [Sidecar] Deploying App\Sidecar\OgImage to Lambda as `SC-Laravel-local-Sidecar-OgImage`. ↳ Environment: local - ↳ Runtime: nodejs12.x + ↳ Runtime: nodejs20.x ↳ Packaging function code. ↳ Creating a new zip file. ↳ Zip file created at s3://sidecar-us-east-2-XXX/sidecar/001-79a5915eaec296be04a0f4fb7cc80e40.zip diff --git a/docs/functions/performance.md b/docs/functions/performance.md index aef475f..022bab5 100644 --- a/docs/functions/performance.md +++ b/docs/functions/performance.md @@ -1,3 +1,36 @@ -# Performance +# Performance Tips + +Here are some tips for getting the best performance out of your Sidecar functions. + +## Reduce Cold Starts + +Cold starts happen when Lambda spins up a new container to handle your request. To minimize their impact: + +- **Use warming** - Configure `warmingConfig()` on your functions and schedule `sidecar:warm` to run regularly. See [Warming Functions](warming) for details. +- **Use pre-warming** - Add `--pre-warm` when activating to warm functions before they go live. +- **Keep functions warm** - Schedule `sidecar:warm` to run every 5 minutes to prevent containers from being frozen. + +## Optimize Your Package Size + +Smaller packages deploy faster and have quicker cold starts: + +- **Separate your node_modules** - Keep Lambda dependencies in a separate `package.json` from your main app. +- **Use NCC** - Compile Node.js handlers into a single file with [NCC](https://github.com/vercel/ncc). See [Handlers & Packages](handlers-and-packages#compiling-your-handler-with-ncc). +- **Only include what you need** - Be specific about what goes in your `package()` method. + +## Right-Size Memory + +Lambda allocates CPU proportionally to memory. More memory means more CPU: + +- **Profile your functions** - Use `$result->report()` to see memory usage and execution time. +- **Test different memory settings** - Sometimes paying for more memory results in faster execution and lower total cost. + +## Use Async When Possible + +If you don't need the result immediately: + +- **Use `executeAsync()`** - Let your code continue while Lambda runs in the background. +- **Use `executeMany()`** - Run multiple invocations in parallel instead of sequentially. +- **Use `executeAsEvent()`** - For fire-and-forget scenarios where you don't need the response. diff --git a/docs/overview.md b/docs/overview.md index d5f3a15..47223bb 100644 --- a/docs/overview.md +++ b/docs/overview.md @@ -3,25 +3,27 @@ Sidecar packages, deploys, and executes AWS Lambda functions from your Laravel application. {.text-xl .font-bold} -It works with _any_ Laravel 7, 8, 9 or 10 application, hosted _anywhere_, including your local machine. {.font-bold} +It works with _any_ Laravel 8, 9, 10, 11, or 12 application, hosted _anywhere_, including your local machine. {.font-bold} You can write functions in any of the following runtimes and execute them straight from PHP: +- Node.js 24 +- Node.js 22 - Node.js 20 -- Node.js 18 -- Node.js 16 +- Python 3.14 +- Python 3.13 - Python 3.12 - Python 3.11 - Python 3.10 - Python 3.9 -- Python 3.8 +- Java 25 - Java 21 - Java 17 - Java 11 - Java 8 +- .NET 9 - .NET 8 -- .NET 7 -- .NET 6 +- Ruby 3.4 - Ruby 3.3 - Ruby 3.2 - OS-only runtime (Amazon Linux 2023) From 01a0b4bd391d0631898140f5bf2858758c2f9f24 Mon Sep 17 00:00:00 2001 From: Aaron Francis Date: Thu, 27 Nov 2025 21:30:27 -0600 Subject: [PATCH 08/12] Drop Laravel 8 and 9 support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Update composer.json to require Laravel 10+ - Remove Guzzle 6.x support (Laravel 10+ requires Guzzle 7) - Update CI matrix to only test Laravel 10, 11, 12 - Update documentation to reflect supported versions This resolves CI failures caused by Composer blocking Laravel 8/9 due to security advisories. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .github/workflows/tests.yml | 45 +++++-------------------------------- CLAUDE.md | 2 +- composer.json | 12 +++++----- docs/overview.md | 2 +- 4 files changed, 13 insertions(+), 48 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c997905..24233c8 100755 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -18,14 +18,9 @@ jobs: fail-fast: false matrix: php: ['8.1', '8.2', '8.3'] - laravel: ['8.*', '9.*', '10.*', '11.*', '12.*'] - guzzle: ['6.*', '7.*'] + laravel: ['10.*', '11.*', '12.*'] dependency-version: [prefer-lowest, prefer-stable] include: - - laravel: 8.* - testbench: 6.* - - laravel: 9.* - testbench: 7.* - laravel: 10.* testbench: 8.* - laravel: 11.* @@ -33,44 +28,15 @@ jobs: - laravel: 12.* testbench: 10.* exclude: - # Laravel 8 exclusions - - laravel: 8.* - php: 8.1 - dependency-version: prefer-lowest - - laravel: 8.* - php: 8.2 - dependency-version: prefer-lowest - - laravel: 8.* - php: 8.3 - dependency-version: prefer-lowest - - # Laravel 9 exclusions - - laravel: 9.* - php: 8.2 - dependency-version: prefer-lowest - - laravel: 9.* - php: 8.3 - dependency-version: prefer-lowest - - # Laravel 11 exclusions + # Laravel 11 requires PHP 8.2+ - laravel: 11.* php: 8.1 - # Laravel 12 exclusions + # Laravel 12 requires PHP 8.2+ - laravel: 12.* php: 8.1 - # Guzzle exclusions - - laravel: 9.* - guzzle: 6.* - - laravel: 10.* - guzzle: 6.* - - laravel: 11.* - guzzle: 6.* - - laravel: 12.* - guzzle: 6.* - - name: P${{ matrix.php }} / L${{ matrix.laravel }} / G${{ matrix.guzzle }} / ${{ matrix.dependency-version }} + name: P${{ matrix.php }} / L${{ matrix.laravel }} / ${{ matrix.dependency-version }} steps: - name: Checkout code @@ -91,8 +57,7 @@ jobs: - name: Install dependencies run: | - composer self-update ${{ matrix.composer-version }} - composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" "guzzlehttp/guzzle:${{ matrix.guzzle }}" --no-interaction --no-update --dev + composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" --no-interaction --no-update --dev composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction - name: Execute tests diff --git a/CLAUDE.md b/CLAUDE.md index 0a68eed..7f3a532 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -85,7 +85,7 @@ The package fires Laravel events you can hook into: ## Things to Know -- This package supports Laravel 8 through 12 and PHP 8.1+ +- This package supports Laravel 10, 11, and 12 with PHP 8.1+ - All source files use `declare(strict_types=1)` - The codebase uses constructor property promotion and typed properties throughout - Tests use Mockery for mocking AWS clients - check existing tests for patterns diff --git a/composer.json b/composer.json index ed48027..7b740af 100644 --- a/composer.json +++ b/composer.json @@ -11,17 +11,17 @@ ], "require": { "php": "^8.1", - "illuminate/filesystem": "^8|^9|^10|^11|^12.0", - "illuminate/console": "^8|^9|^10|^11|^12.0", - "illuminate/support": "^8|^9|^10|^11|^12.0", + "illuminate/filesystem": "^10|^11|^12", + "illuminate/console": "^10|^11|^12", + "illuminate/support": "^10|^11|^12", "maennchen/zipstream-php": "^3.1", - "guzzlehttp/guzzle": "^6.5.8|^7.2", + "guzzlehttp/guzzle": "^7.2", "aws/aws-sdk-php": "^3.216.1" }, "require-dev": { - "orchestra/testbench": "^5|^6|^7|^8|^9|^10.0", + "orchestra/testbench": "^8|^9|^10", "mockery/mockery": "^1.3.3", - "phpunit/phpunit": ">=8.5.23|^9|^10" + "phpunit/phpunit": "^9|^10|^11" }, "autoload": { "psr-4": { diff --git a/docs/overview.md b/docs/overview.md index 47223bb..88075d8 100644 --- a/docs/overview.md +++ b/docs/overview.md @@ -3,7 +3,7 @@ Sidecar packages, deploys, and executes AWS Lambda functions from your Laravel application. {.text-xl .font-bold} -It works with _any_ Laravel 8, 9, 10, 11, or 12 application, hosted _anywhere_, including your local machine. {.font-bold} +It works with _any_ Laravel 10, 11, or 12 application, hosted _anywhere_, including your local machine. {.font-bold} You can write functions in any of the following runtimes and execute them straight from PHP: From e770cd5eca39151425dff74da3948c7f3c47bb2a Mon Sep 17 00:00:00 2001 From: Aaron Francis Date: Thu, 27 Nov 2025 21:38:47 -0600 Subject: [PATCH 09/12] Fix PHP 8.1 compatibility for deprecated constants classes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using EnumCase->value in class constant initializers is not supported in PHP 8.1 (only available from 8.2+). Hardcode the string values instead to maintain backward compatibility. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/ArchitectureConstants.php | 4 +-- src/RuntimeConstants.php | 64 +++++++++++++++++------------------ 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/ArchitectureConstants.php b/src/ArchitectureConstants.php index 2e66719..05d9971 100644 --- a/src/ArchitectureConstants.php +++ b/src/ArchitectureConstants.php @@ -10,8 +10,8 @@ abstract class ArchitectureConstants { /** @deprecated Use Architecture::X86_64 instead */ - public const X86_64 = Architecture::X86_64->value; + public const X86_64 = 'x86_64'; /** @deprecated Use Architecture::ARM_64 instead */ - public const ARM_64 = Architecture::ARM_64->value; + public const ARM_64 = 'arm64'; } diff --git a/src/RuntimeConstants.php b/src/RuntimeConstants.php index bffc91d..d4d30a6 100644 --- a/src/RuntimeConstants.php +++ b/src/RuntimeConstants.php @@ -10,98 +10,98 @@ abstract class RuntimeConstants { /** @deprecated Use Runtime::NODEJS_24 instead */ - public const NODEJS_24 = Runtime::NODEJS_24->value; + public const NODEJS_24 = 'nodejs24.x'; /** @deprecated Use Runtime::NODEJS_22 instead */ - public const NODEJS_22 = Runtime::NODEJS_22->value; + public const NODEJS_22 = 'nodejs22.x'; /** @deprecated Use Runtime::NODEJS_20 instead */ - public const NODEJS_20 = Runtime::NODEJS_20->value; + public const NODEJS_20 = 'nodejs20.x'; /** @deprecated Use Runtime::NODEJS_18 instead (also deprecated by AWS) */ - public const NODEJS_18 = Runtime::NODEJS_18->value; + public const NODEJS_18 = 'nodejs18.x'; /** @deprecated Use Runtime::NODEJS_16 instead (also deprecated by AWS) */ - public const NODEJS_16 = Runtime::NODEJS_16->value; + public const NODEJS_16 = 'nodejs16.x'; /** @deprecated Use Runtime::NODEJS_14 instead (also deprecated by AWS) */ - public const NODEJS_14 = Runtime::NODEJS_14->value; + public const NODEJS_14 = 'nodejs14.x'; /** @deprecated Use Runtime::PYTHON_314 instead */ - public const PYTHON_314 = Runtime::PYTHON_314->value; + public const PYTHON_314 = 'python3.14'; /** @deprecated Use Runtime::PYTHON_313 instead */ - public const PYTHON_313 = Runtime::PYTHON_313->value; + public const PYTHON_313 = 'python3.13'; /** @deprecated Use Runtime::PYTHON_312 instead */ - public const PYTHON_312 = Runtime::PYTHON_312->value; + public const PYTHON_312 = 'python3.12'; /** @deprecated Use Runtime::PYTHON_311 instead */ - public const PYTHON_311 = Runtime::PYTHON_311->value; + public const PYTHON_311 = 'python3.11'; /** @deprecated Use Runtime::PYTHON_310 instead */ - public const PYTHON_310 = Runtime::PYTHON_310->value; + public const PYTHON_310 = 'python3.10'; /** @deprecated Use Runtime::PYTHON_39 instead */ - public const PYTHON_39 = Runtime::PYTHON_39->value; + public const PYTHON_39 = 'python3.9'; /** @deprecated Use Runtime::PYTHON_38 instead (also deprecated by AWS) */ - public const PYTHON_38 = Runtime::PYTHON_38->value; + public const PYTHON_38 = 'python3.8'; /** @deprecated Use Runtime::PYTHON_37 instead (also deprecated by AWS) */ - public const PYTHON_37 = Runtime::PYTHON_37->value; + public const PYTHON_37 = 'python3.7'; /** @deprecated Use Runtime::JAVA_25 instead */ - public const JAVA_25 = Runtime::JAVA_25->value; + public const JAVA_25 = 'java25'; /** @deprecated Use Runtime::JAVA_21 instead */ - public const JAVA_21 = Runtime::JAVA_21->value; + public const JAVA_21 = 'java21'; /** @deprecated Use Runtime::JAVA_17 instead */ - public const JAVA_17 = Runtime::JAVA_17->value; + public const JAVA_17 = 'java17'; /** @deprecated Use Runtime::JAVA_11 instead */ - public const JAVA_11 = Runtime::JAVA_11->value; + public const JAVA_11 = 'java11'; /** @deprecated Use Runtime::JAVA_8_LINUX2 instead */ - public const JAVA_8_LINUX2 = Runtime::JAVA_8_LINUX2->value; + public const JAVA_8_LINUX2 = 'java8.al2'; /** @deprecated Use Runtime::JAVA_8 instead (also deprecated by AWS) */ - public const JAVA_8 = Runtime::JAVA_8->value; + public const JAVA_8 = 'java8'; /** @deprecated Use Runtime::DOT_NET_9 instead */ - public const DOT_NET_9 = Runtime::DOT_NET_9->value; + public const DOT_NET_9 = 'dotnet9'; /** @deprecated Use Runtime::DOT_NET_8 instead */ - public const DOT_NET_8 = Runtime::DOT_NET_8->value; + public const DOT_NET_8 = 'dotnet8'; /** @deprecated Use Runtime::DOT_NET_7 instead (also deprecated by AWS) */ - public const DOT_NET_7 = Runtime::DOT_NET_7->value; + public const DOT_NET_7 = 'dotnet7'; /** @deprecated Use Runtime::DOT_NET_6 instead (also deprecated by AWS) */ - public const DOT_NET_6 = Runtime::DOT_NET_6->value; + public const DOT_NET_6 = 'dotnet6'; /** @deprecated Use Runtime::RUBY_34 instead */ - public const RUBY_34 = Runtime::RUBY_34->value; + public const RUBY_34 = 'ruby3.4'; /** @deprecated Use Runtime::RUBY_33 instead */ - public const RUBY_33 = Runtime::RUBY_33->value; + public const RUBY_33 = 'ruby3.3'; /** @deprecated Use Runtime::RUBY_32 instead */ - public const RUBY_32 = Runtime::RUBY_32->value; + public const RUBY_32 = 'ruby3.2'; /** @deprecated Use Runtime::RUBY_27 instead (also deprecated by AWS) */ - public const RUBY_27 = Runtime::RUBY_27->value; + public const RUBY_27 = 'ruby2.7'; /** @deprecated Use Runtime::GO_1X instead (also deprecated by AWS) */ - public const GO_1X = Runtime::GO_1X->value; + public const GO_1X = 'go1.x'; /** @deprecated Use Runtime::PROVIDED_AL2023 instead */ - public const PROVIDED_AL2023 = Runtime::PROVIDED_AL2023->value; + public const PROVIDED_AL2023 = 'provided.al2023'; /** @deprecated Use Runtime::PROVIDED_AL2 instead */ - public const PROVIDED_AL2 = Runtime::PROVIDED_AL2->value; + public const PROVIDED_AL2 = 'provided.al2'; /** @deprecated Use Runtime::PROVIDED instead (also deprecated by AWS) */ - public const PROVIDED = Runtime::PROVIDED->value; + public const PROVIDED = 'provided'; } From b984e20abb4bdb5cb1f946685aff21da9831f223 Mon Sep 17 00:00:00 2001 From: Aaron Francis Date: Thu, 27 Nov 2025 21:41:30 -0600 Subject: [PATCH 10/12] Bump minimum testbench version to fix prefer-lowest CI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Testbench v9.0.0/9.0.1 has a bug with undeclared $latestResponse static property that causes all tests to fail. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 7b740af..58ce79a 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,7 @@ "aws/aws-sdk-php": "^3.216.1" }, "require-dev": { - "orchestra/testbench": "^8|^9|^10", + "orchestra/testbench": "^8.23|^9.1|^10", "mockery/mockery": "^1.3.3", "phpunit/phpunit": "^9|^10|^11" }, From 423a3239f8c428f825ff2145c78b35879fa90d5d Mon Sep 17 00:00:00 2001 From: Aaron Francis Date: Thu, 27 Nov 2025 21:44:13 -0600 Subject: [PATCH 11/12] Update CI testbench constraints to match composer.json minimums MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .github/workflows/tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 24233c8..fa86191 100755 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -22,11 +22,11 @@ jobs: dependency-version: [prefer-lowest, prefer-stable] include: - laravel: 10.* - testbench: 8.* + testbench: ^8.23 - laravel: 11.* - testbench: 9.* + testbench: ^9.1 - laravel: 12.* - testbench: 10.* + testbench: ^10 exclude: # Laravel 11 requires PHP 8.2+ - laravel: 11.* From 7d8f848561ceb435caaf06c4a8eac75fb2e709e5 Mon Sep 17 00:00:00 2001 From: Aaron Francis Date: Thu, 27 Nov 2025 21:46:08 -0600 Subject: [PATCH 12/12] Bump testbench to ^9.5 for Laravel 11 to fix early version bugs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .github/workflows/tests.yml | 2 +- composer.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index fa86191..0345655 100755 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -24,7 +24,7 @@ jobs: - laravel: 10.* testbench: ^8.23 - laravel: 11.* - testbench: ^9.1 + testbench: ^9.5 - laravel: 12.* testbench: ^10 exclude: diff --git a/composer.json b/composer.json index 58ce79a..241513d 100644 --- a/composer.json +++ b/composer.json @@ -19,7 +19,7 @@ "aws/aws-sdk-php": "^3.216.1" }, "require-dev": { - "orchestra/testbench": "^8.23|^9.1|^10", + "orchestra/testbench": "^8.23|^9.5|^10", "mockery/mockery": "^1.3.3", "phpunit/phpunit": "^9|^10|^11" },