diff --git a/.gitattributes b/.gitattributes index d6e6a65..665dec7 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,7 +1,6 @@ * text=auto eol=lf /.github export-ignore -/examples export-ignore /tests export-ignore .codespellrc export-ignore .editorconfig export-ignore diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7336172..2b8691a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -32,10 +32,10 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - php: [ '8.1', '8.2', '8.3', '8.4' ] + php: [ '8.2', '8.3', '8.4' ] dependencies: [ '' ] include: - - php: '8.1' + - php: '8.2' dependencies: '--prefer-lowest' steps: - uses: actions/checkout@v4 diff --git a/composer.json b/composer.json index 905c082..565b12f 100644 --- a/composer.json +++ b/composer.json @@ -11,16 +11,16 @@ "faas" ], "require": { - "php": "^8.0", + "php": "^8.2", "aws/aws-sdk-php": "^3.222", - "bref/bref": "^2.1.8", + "bref/bref": "^2.1.8 || ^3", "bref/laravel-health-check": "^1", "bref/monolog-bridge": "^1.0", - "illuminate/container": "^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0", - "illuminate/contracts": "^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0", - "illuminate/http": "^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0", - "illuminate/queue": "^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0", - "illuminate/support": "^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0", + "illuminate/container": "^10.0 || ^11.0 || ^12.0", + "illuminate/contracts": "^10.0 || ^11.0 || ^12.0", + "illuminate/http": "^10.0 || ^11.0 || ^12.0", + "illuminate/queue": "^10.0 || ^11.0 || ^12.0", + "illuminate/support": "^10.0 || ^11.0 || ^12.0", "laravel/octane": "^1.2 || ^2.0", "laravel/tinker": "^2.0", "riverline/multipart-parser": "^2.0" diff --git a/config/bref.php b/config/bref.php index 5492dad..1c0b883 100644 --- a/config/bref.php +++ b/config/bref.php @@ -8,8 +8,8 @@ |-------------------------------------------------------------------------- | | Here you can configure list of public assets that should be servable - | from your application's domain instead of only being servable via - | the public S3 "asset" bucket or the AWS CloudFront CDN network. + | from your application's domain instead of AWS CloudFront. + | Read https://bref.sh/docs/use-cases/websites for a better solution. | */ @@ -40,4 +40,5 @@ */ 'log_jobs' => true, + ]; diff --git a/examples/README.md b/examples/README.md deleted file mode 100644 index a45e489..0000000 --- a/examples/README.md +++ /dev/null @@ -1,21 +0,0 @@ - -# Examples - -## Getting started - -The [`getting-started`](getting-started/) example contains a `serverless.yml` that: - -- Creates a DynamoDB cache table -- Creates a SQS queue -- Injects the `APP_KEY` at runtime -- Stores the app's versioned assets in S3 - -Be sure to adjust the `service` name and `params`. - -It also includes a GitHub Actions workflow to deploy the app and toggle maintenance mode. The actions require the `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` secrets to be set. - -## Relay - -We like to be transparent on how we're using this package ourselves, so we published the configuration that manages [relay.so](https://relay.so). - -__TODO...__ diff --git a/examples/getting-started/github-workflows/deploy.yml b/examples/getting-started/github-workflows/deploy.yml deleted file mode 100644 index f9ca082..0000000 --- a/examples/getting-started/github-workflows/deploy.yml +++ /dev/null @@ -1,82 +0,0 @@ -name: Deploy - -on: - workflow_dispatch: - inputs: - environment: - description: 'Environment' - type: choice - required: true - options: - - staging - - production - -concurrency: deploy-${{ github.event.inputs.environment }} - -jobs: - deploy: - name: Deploy to AWS - runs-on: ubuntu-latest - - env: - ENVIRONMENT: ${{ github.event.inputs.environment }} - - steps: - - name: Checkout code - uses: actions/checkout@v2 - - - name: Install Serverless CLI - run: npm install -g serverless serverless-scriptable-plugin - - - name: Install AWS CLI - uses: aws-actions/configure-aws-credentials@v1 - with: - aws-region: us-east-1 - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: 8.1 - tools: composer:v2 - - - name: Install Composer dependencies - env: - COMPOSER_MIRROR_PATH_REPOS: 1 - run: composer install --no-dev --prefer-dist --optimize-autoloader - - - name: Build assets - run: npm ci && npm run production - - - name: Build and deploy - run: | - php artisan event:cache - serverless config credentials --provider aws --key $AWS_ACCESS_KEY_ID --secret="$AWS_SECRET_ACCESS_KEY" - serverless deploy --stage=$ENVIRONMENT - - - name: Delete old assets - run: | - sudo apt-get update - sudo apt-get install jq - - wget --no-verbose https://github.com/mikefarah/yq/releases/download/v4.25.1/yq_linux_amd64 -O /usr/bin/yq - chmod +x /usr/bin/yq - - bucket=$(yq '.params.default.bucket' serverless.yml) - assets_bucket=$(yq '.params.default.assetsBucket' serverless.yml) - - deployments=$( - aws s3api list-objects \ - --bucket "$bucket" \ - --prefix "serverless/relay/" \ - --query "Contents[?contains(Key, 'serverless-state.json')].[Key]" - ) - - for key in $(echo "$deployments" | jq -r '.[] | .[0]'); do - deployment=$(aws s3 cp s3://$bucket/$key -) - url=$(echo "$deployment" | jq -r '.service.provider.environment.ASSET_URL') - excludes+="--exclude=${url##*/}/* " - done - - aws s3 rm s3://$assets_bucket/ --recursive $excludes diff --git a/examples/getting-started/github-workflows/maintenance.yml b/examples/getting-started/github-workflows/maintenance.yml deleted file mode 100644 index 1f04d82..0000000 --- a/examples/getting-started/github-workflows/maintenance.yml +++ /dev/null @@ -1,73 +0,0 @@ -name: Maintenance mode - -on: - workflow_dispatch: - inputs: - environment: - description: 'Environment' - type: choice - required: true - options: - - staging - - production - maintenance: - description: 'Maintenance mode' - type: choice - options: - - enable - - disable - -concurrency: deploy-${{ github.event.inputs.environment || 'staging' }} - -jobs: - deploy: - name: Toggle maintenance mode - runs-on: ubuntu-latest - - env: - ENVIRONMENT: ${{ github.event.inputs.environment || 'staging' }} - - steps: - - name: Checkout code - uses: actions/checkout@v2 - - - name: Setup PHP - uses: shivammathur/setup-php@v2 - with: - php-version: 8.1 - tools: composer:v2 - - - name: Install Composer dependencies - run: composer install --no-dev --prefer-dist --optimize-autoloader - - - name: Install Serverless CLI - run: | - npm install -g \ - serverless \ - serverless-scriptable-plugin \ - serverless-plugin-git-variables - serverless config credentials \ - --provider aws \ - --key ${{ secrets.AWS_ACCESS_KEY_ID }} \ - --secret="${{ secrets.AWS_SECRET_ACCESS_KEY }}" - - - name: Enable maintenance mode - if: ${{ github.event.inputs.maintenance == 'enable' }} - run: | - for function in web cli queue; do - serverless deploy function --update-config \ - --aws-profile=default \ - --stage=$ENVIRONMENT \ - --function=$function \ - --param="maintenance=1" - done - - - name: Disable maintenance mode - if: ${{ github.event.inputs.maintenance == 'disable' }} - run: | - for function in web cli queue; do - serverless deploy function --update-config \ - --aws-profile=default \ - --stage=$ENVIRONMENT \ - --function=$function - done diff --git a/examples/getting-started/serverless.yml b/examples/getting-started/serverless.yml deleted file mode 100644 index ab81be9..0000000 --- a/examples/getting-started/serverless.yml +++ /dev/null @@ -1,137 +0,0 @@ -service: example -frameworkVersion: '3' - -params: - default: - assetsBucket: assets.example.com - production: - appUrl: https://example.com - debug: false - logLevel: info - staging: - appUrl: https://staging.example.com - debug: true - queue: null - logLevel: debug - -provider: - name: aws - region: us-east-1 - stage: staging - runtime: provided.al2 - environment: - APP_URL: ${param:appUrl} - APP_ENV: ${sls:stage} - APP_DEBUG: ${param:debug} - ASSET_URL: https://${param:assetsBucket}/${sls:instanceId} - LOG_LEVEL: ${param:logLevel} - QUEUE_CONNECTION: ${param:queue, 'sqs'} - SQS_QUEUE: ${self:service}-${sls:stage} - DYNAMODB_CACHE_TABLE: ${self:service}-${sls:stage}-cache - MAINTENANCE_MODE: ${param:maintenance, null} - # Use S3 for filesystem storage - FILESYSTEM_DISK: s3 - AWS_BUCKET: !Ref StorageBucket - iam: - role: - statements: - - Effect: Allow - Resource: !GetAtt CacheTable.Arn - Action: [dynamodb:DescribeTable, dynamodb:Query, dynamodb:Scan, dynamodb:GetItem, dynamodb:PutItem, dynamodb:UpdateItem, dynamodb:DeleteItem] - - Effect: Allow - Resource: !GetAtt Queue.Arn - Action: [sqs:GetQueueUrl, sqs:GetQueueAttributes, sqs:SendMessage, sqs:receiveMessage, sqs:DeleteMessage, sqs:PurgeQueue] - # Allow Lambda to read and write files in the S3 storage bucket - - Effect: Allow - Action: s3:* - Resource: - - !Sub '${StorageBucket.Arn}' # the storage bucket - - !Sub '${StorageBucket.Arn}/*' # and everything inside - -plugins: - - ./vendor/bref/bref - - serverless-scriptable-plugin - -functions: - web: - handler: Bref\LaravelBridge\Http\OctaneHandler - timeout: 20 - environment: - BREF_LOOP_MAX: 250 - BREF_BINARY_RESPONSES: 1 - layers: - - ${bref:layer.php-82} - events: - - httpApi: '*' - - cli: - handler: artisan - timeout: 720 - layers: - - ${bref:layer.php-82} - - ${bref:layer.console} - events: - - schedule: - rate: rate(1 minute) - input: '"schedule:run"' - - queue: - handler: Bref\LaravelBridge\Queue\QueueHandler - timeout: 59 - layers: - - ${bref:layer.php-82} - events: - - sqs: - arn: !GetAtt Queue.Arn - batchSize: 1 - maximumBatchingWindow: 60 - -package: - patterns: - - '!.*/**' - - '!node_modules' - - '!public/assets' - - '!public/storage' - - '!public/vendor' - - 'public/vendor/**/mix-manifest.json' - - '!resources/css' - - '!resources/js' - - '!storage/framework' - - '!storage/logs' - - '!tests' - - '!composer.lock' - - '!package-lock.json' - -custom: - scriptable: - hooks: - after:package:createDeploymentArtifacts: - - aws s3 cp public/ s3://${param:assetsBucket}/${sls:instanceId}/ --recursive --only-show-errors --acl=public-read --exclude="*.php" - after:deploy:deploy: - - vendor/bin/bref cli ${self:service}-${sls:stage}-cli -- migrate --force - -resources: - Resources: - CacheTable: - Type: AWS::DynamoDB::Table - Properties: - TableName: ${self:service}-${sls:stage}-cache - BillingMode: PAY_PER_REQUEST - AttributeDefinitions: - - AttributeName: key - AttributeType: S - KeySchema: - - AttributeName: key - KeyType: HASH - TimeToLiveSpecification: - AttributeName: expires_at - Enabled: true - Queue: - Type: AWS::SQS::Queue - Properties: - QueueName: ${self:service}-${sls:stage} - VisibilityTimeout: 70 - StorageBucket: - Type: AWS::S3::Bucket - Properties: - Description: Storage bucket for the Laravel application diff --git a/src/BrefServiceProvider.php b/src/BrefServiceProvider.php index 7bb1b1b..2c670b5 100644 --- a/src/BrefServiceProvider.php +++ b/src/BrefServiceProvider.php @@ -5,6 +5,7 @@ use Bref\LaravelBridge\Console\Commands\BrefTinkerCommand; use Bref\LaravelBridge\Queue\QueueHandler; +use Bref\Monolog\CloudWatchFormatter; use Illuminate\Console\Events\ScheduledTaskStarting; use Illuminate\Log\LogManager; @@ -168,6 +169,10 @@ protected function fixDefaultConfiguration() Config::set('logging.default', 'stderr'); } + if (Config::get('logging.channels.stderr.formatter') === null) { + Config::set('logging.channels.stderr.formatter', CloudWatchFormatter::class); + } + if (Config::get('logging.channels.emergency.path') === storage_path('logs/laravel.log')) { Config::set('logging.channels.emergency', Config::get('logging.channels.stderr')); } diff --git a/src/BrefSubscriber.php b/src/BrefSubscriber.php new file mode 100644 index 0000000..4fd5f98 --- /dev/null +++ b/src/BrefSubscriber.php @@ -0,0 +1,58 @@ + /dev/null'; + if (!getenv('BREF_LARAVEL_OMIT_INITLOG')) { + fwrite(STDERR, "Running 'php artisan config:cache' to cache the Laravel configuration\n"); + // 1>&2 redirects the output to STDERR to avoid messing up HTTP responses with FPM + $outputDestination = '1>&2'; + } + + passthru("php $laravelRoot/artisan config:cache {$outputDestination}"); + } + } +} diff --git a/src/bref-init.php b/src/bref-init.php index e2a1643..8f1f1f1 100644 --- a/src/bref-init.php +++ b/src/bref-init.php @@ -1,62 +1,18 @@ /dev/null'; - if (!getenv('BREF_LARAVEL_OMIT_INITLOG')) { - fwrite(STDERR, "Running 'php artisan config:cache' to cache the Laravel configuration\n"); - // 1>&2 redirects the output to STDERR to avoid messing up HTTP responses with FPM - $outputDestination = '1>&2'; - } - - passthru("php $laravelRoot/artisan config:cache {$outputDestination}"); - } -}); +Bref::events()->subscribe(new BrefSubscriber); Bref::setContainer(static fn() => new HandlerResolver); diff --git a/stubs/serverless.yml b/stubs/serverless.yml index 5a2e86b..995eca2 100644 --- a/stubs/serverless.yml +++ b/stubs/serverless.yml @@ -1,6 +1,6 @@ service: laravel -# Set your team ID if you are using Bref Cloud +# Set your team ID if you are using Bref Cloud (https://bref.sh/cloud) #bref: # team: my-team-id @@ -11,30 +11,35 @@ provider: # Environment variables environment: APP_ENV: production # Or use ${sls:stage} if you want the environment to match the stage + # Replace with a secret variable: https://bref.sh/docs/environment/variables#secrets + APP_KEY: 'base64:aqIOr0nFlPj0RtsUR4MTtIu4EBWohrOr9qcw0xVZE6c=' + APP_DEBUG: false SESSION_DRIVER: cookie # Change to database if you have set up a database - -package: - # Files and directories to exclude from deployment - patterns: - - '!node_modules/**' - - '!public/storage' - - '!resources/assets/**' - - '!resources/css/**' - - '!resources/images/**' - - '!resources/js/**' - - '!storage/**' - - '!tests/**' - - '!database/*.sqlite' - # Exclude assets except for the manifest file - - '!public/build/**' - - 'public/build/manifest.json' + # Maintenance mode: https://bref.sh/docs/laravel/maintenance-mode + MAINTENANCE_MODE: ${param:maintenance, null} + # Logging configuration: https://bref.sh/docs/environment/logs + LOG_CHANNEL: stderr + LOG_STDERR_FORMATTER: Bref\Monolog\CloudWatchFormatter + # Database configuration: https://bref.sh/docs/environment/database + #DB_CONNECTION: mysql + #DB_HOST: your-database-endpoint.rds.amazonaws.com + #DB_PORT: 3306 + #DB_DATABASE: laravel + #DB_USERNAME: root + #DB_PASSWORD: ${ssm:/my-app/dev/db-password} + # Queue configuration: https://bref.sh/docs/laravel/queues + #QUEUE_CONNECTION: sqs + #SQS_QUEUE: ${construct:jobs.queueUrl} + # Filesystem configuration: https://bref.sh/docs/laravel/filesystem + #FILESYSTEM_DISK: s3 + #AWS_BUCKET: ${construct:storage.bucketName} functions: # This function runs the Laravel website/API web: handler: public/index.php - runtime: php-82-fpm + runtime: php-84-fpm timeout: 28 # in seconds (API Gateway has a timeout of 29 seconds) events: - httpApi: '*' @@ -42,7 +47,7 @@ functions: # This function lets us run artisan commands in Lambda artisan: handler: artisan - runtime: php-82-console + runtime: php-84-console timeout: 720 # in seconds # Uncomment to also run the scheduler every minute #events: @@ -50,6 +55,62 @@ functions: # rate: rate(1 minute) # input: '"schedule:run"' +constructs: + # SQS queue for Laravel jobs + # See https://bref.sh/docs/laravel/queues + #jobs: + # type: queue + # worker: + # handler: Bref\LaravelBridge\Queue\QueueHandler + # runtime: php-84 + # timeout: 60 # seconds + + # S3 bucket for file storage + #storage: + # type: storage + # lifecycleRules: + # # Files in the `tmp/` folder will be cleaned after 1 day + # - prefix: tmp/ + # expirationInDays: 1 + + # CloudFront + S3 for website assets + # See https://bref.sh/docs/use-cases/websites + #website: + # type: server-side-website + # assets: + # '/build/*': public/build + # #'/vendor/*': public/vendor # Uncomment if the public/vendor exists + # '/favicon.ico': public/favicon.ico + # '/robots.txt': public/robots.txt + +# Environment-specific parameters +# See https://github.com/oss-serverless/serverless/blob/main/docs/guides/parameters.md +params: + #default: + # foo: bar + #production: + # foo: baz + +package: + # Files and directories to exclude from deployment + patterns: + - '!.env' # Don't deploy local config + - '!node_modules/**' + - '!public/storage' + - '!resources/assets/**' + - '!resources/css/**' + - '!resources/images/**' + - '!resources/js/**' + - '!storage/**' + - '!tests/**' + - '!database/*.sqlite' + # Exclude assets except for the manifest file + - '!public/build/**' + - 'public/build/manifest.json' + plugins: # We need to include the Bref plugin - ./vendor/bref/bref + # Uncomment the Serverless Lift plugin if you use constructs + # See https://github.com/getlift/lift + - serverless-lift