Skip to content
Open
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
51 changes: 8 additions & 43 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,59 +18,25 @@ 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.*
testbench: ^8.23
- laravel: 11.*
testbench: 9.*
testbench: ^9.5
- laravel: 12.*
testbench: 10.*
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
Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.claude/
/vendor
/storage/framework/testing
.env
Expand Down
91 changes: 91 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -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 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
12 changes: 6 additions & 6 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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.23|^9.5|^10",
"mockery/mockery": "^1.3.3",
"phpunit/phpunit": ">=8.5.23|^9|^10"
"phpunit/phpunit": "^9|^10|^11"
},
"autoload": {
"psr-4": {
Expand Down
2 changes: 1 addition & 1 deletion config/sidecar.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
15 changes: 12 additions & 3 deletions docs/events.md
Original file line number Diff line number Diff line change
@@ -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.
You can use these for logging, monitoring, or modifying payloads before they're sent to Lambda.
55 changes: 35 additions & 20 deletions docs/functions/customization.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion docs/functions/deploying.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
35 changes: 34 additions & 1 deletion docs/functions/performance.md
Original file line number Diff line number Diff line change
@@ -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.

14 changes: 8 additions & 6 deletions docs/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 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)
Expand Down
Loading