Publish PyPI Package to Lambda Layer #31
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Publish PyPI Package to Lambda Layer | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| package_version: | |
| description: 'Package version to download' | |
| required: true | |
| type: string | |
| python_version: | |
| description: 'Python version' | |
| required: true | |
| default: 'ALL' | |
| type: choice | |
| options: ['ALL', '3.10', '3.11', '3.12', '3.13'] | |
| architecture: | |
| description: 'Architecture' | |
| required: true | |
| default: 'ALL' | |
| type: choice | |
| options: ['ALL', 'x86_64', 'aarch64'] | |
| region: | |
| description: 'AWS region' | |
| required: true | |
| default: 'ALL' | |
| type: choice | |
| # Only non opt-in regions included for now | |
| options: ['ALL', 'us-east-1', 'us-east-2', 'us-west-1', 'us-west-2', 'ap-south-1', 'ap-northeast-1', 'ap-northeast-2', 'ap-northeast-3', 'ap-southeast-1', 'ap-southeast-2', 'ca-central-1', 'eu-central-1', 'eu-west-1', 'eu-west-2', 'eu-west-3', 'eu-north-1', 'sa-east-1'] | |
| confirm: | |
| description: 'Type "Create Lambda Layer {PyPI version}" to confirm publishing the layer' | |
| required: true | |
| type: string | |
| env: | |
| BUCKET_NAME: strands-agents-lambda-layer | |
| jobs: | |
| validate: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Validate confirmation | |
| run: | | |
| CONFIRM="${{ inputs.confirm }}" | |
| EXPECTED="Create Lambda Layer ${{ inputs.package_version }}" | |
| if [ "$CONFIRM" != "$EXPECTED" ]; then | |
| echo "Confirmation failed. You must type exactly '$EXPECTED' to proceed." | |
| exit 1 | |
| fi | |
| echo "Confirmation validated" | |
| create-buckets: | |
| needs: validate | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| region: ${{ inputs.region == 'ALL' && fromJson('["us-east-1", "us-east-2", "us-west-1", "us-west-2", "ap-south-1", "ap-northeast-1", "ap-northeast-2", "ap-northeast-3", "ap-southeast-1", "ap-southeast-2", "ca-central-1", "eu-central-1", "eu-west-1", "eu-west-2", "eu-west-3", "eu-north-1", "sa-east-1"]') || fromJson(format('["{0}"]', inputs.region)) }} | |
| permissions: | |
| id-token: write | |
| steps: | |
| - name: Configure AWS credentials | |
| uses: aws-actions/configure-aws-credentials@v4 | |
| with: | |
| role-to-assume: ${{ secrets.STRANDS_LAMBDA_LAYER_PUBLISHER_ROLE }} | |
| aws-region: ${{ matrix.region }} | |
| - name: Create S3 bucket | |
| run: | | |
| REGION="${{ matrix.region }}" | |
| ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) | |
| REGIONAL_BUCKET="${{ env.BUCKET_NAME }}-${ACCOUNT_ID}-${REGION}" | |
| if ! aws s3api head-bucket --bucket "$REGIONAL_BUCKET" 2>/dev/null; then | |
| if [ "$REGION" = "us-east-1" ]; then | |
| aws s3api create-bucket --bucket "$REGIONAL_BUCKET" --region "$REGION" 2>/dev/null || echo "Bucket $REGIONAL_BUCKET already exists" | |
| else | |
| aws s3api create-bucket --bucket "$REGIONAL_BUCKET" --region "$REGION" --create-bucket-configuration LocationConstraint="$REGION" 2>/dev/null || echo "Bucket $REGIONAL_BUCKET already exists" | |
| fi | |
| echo "S3 bucket ready: $REGIONAL_BUCKET" | |
| else | |
| echo "S3 bucket already exists: $REGIONAL_BUCKET" | |
| fi | |
| package-and-upload: | |
| needs: create-buckets | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| python-version: ${{ inputs.python_version == 'ALL' && fromJson('["3.10", "3.11", "3.12", "3.13"]') || fromJson(format('["{0}"]', inputs.python_version)) }} | |
| architecture: ${{ inputs.architecture == 'ALL' && fromJson('["x86_64", "aarch64"]') || fromJson(format('["{0}"]', inputs.architecture)) }} | |
| region: ${{ inputs.region == 'ALL' && fromJson('["us-east-1", "us-east-2", "us-west-1", "us-west-2", "ap-south-1", "ap-northeast-1", "ap-northeast-2", "ap-northeast-3", "ap-southeast-1", "ap-southeast-2", "ca-central-1", "eu-central-1", "eu-west-1", "eu-west-2", "eu-west-3", "eu-north-1", "sa-east-1"]') || fromJson(format('["{0}"]', inputs.region)) }} | |
| permissions: | |
| id-token: write | |
| steps: | |
| - name: Set up Python | |
| uses: actions/setup-python@v4 | |
| with: | |
| python-version: ${{ matrix.python-version }} | |
| - name: Configure AWS credentials | |
| uses: aws-actions/configure-aws-credentials@v4 | |
| with: | |
| role-to-assume: ${{ secrets.STRANDS_LAMBDA_LAYER_PUBLISHER_ROLE }} | |
| aws-region: ${{ matrix.region }} | |
| - name: Create layer directory structure | |
| run: | | |
| mkdir -p layer/python | |
| - name: Download and install package | |
| run: | | |
| pip install strands-agents[all]==${{ inputs.package_version }} \ | |
| --python-version ${{ matrix.python-version }} \ | |
| --platform manylinux2014_${{ matrix.architecture }} \ | |
| -t layer/python/ \ | |
| --only-binary=:all: | |
| - name: Remove grpc files to reduce size | |
| run: | | |
| find layer/python -name "*grpc*" -type f -delete | |
| find layer/python -name "*grpc*" -type d -exec rm -rf {} + 2>/dev/null || true | |
| - name: Create layer zip | |
| run: | | |
| cd layer | |
| zip -r ../lambda-layer.zip . | |
| - name: Upload to S3 | |
| run: | | |
| PYTHON_VERSION="${{ matrix.python-version }}" | |
| ARCH="${{ matrix.architecture }}" | |
| REGION="${{ matrix.region }}" | |
| LAYER_NAME="strands-agents-py${PYTHON_VERSION//./_}-${ARCH}-all" | |
| ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) | |
| BUCKET_NAME="${{ env.BUCKET_NAME }}-${ACCOUNT_ID}-${REGION}" | |
| LAYER_KEY="$LAYER_NAME/v${{ inputs.package_version }}/lambda-layer.zip" | |
| aws s3 cp lambda-layer.zip "s3://$BUCKET_NAME/$LAYER_KEY" --region "$REGION" | |
| echo "Uploaded layer to s3://$BUCKET_NAME/$LAYER_KEY" | |
| publish-layer: | |
| needs: package-and-upload | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| python-version: ${{ inputs.python_version == 'ALL' && fromJson('["3.10", "3.11", "3.12", "3.13"]') || fromJson(format('["{0}"]', inputs.python_version)) }} | |
| architecture: ${{ inputs.architecture == 'ALL' && fromJson('["x86_64", "aarch64"]') || fromJson(format('["{0}"]', inputs.architecture)) }} | |
| region: ${{ inputs.region == 'ALL' && fromJson('["us-east-1", "us-east-2", "us-west-1", "us-west-2", "ap-south-1", "ap-northeast-1", "ap-northeast-2", "ap-northeast-3", "ap-southeast-1", "ap-southeast-2", "ca-central-1", "eu-central-1", "eu-west-1", "eu-west-2", "eu-west-3", "eu-north-1", "sa-east-1"]') || fromJson(format('["{0}"]', inputs.region)) }} | |
| permissions: | |
| id-token: write | |
| steps: | |
| - name: Configure AWS credentials | |
| uses: aws-actions/configure-aws-credentials@v4 | |
| with: | |
| role-to-assume: ${{ secrets.STRANDS_LAMBDA_LAYER_PUBLISHER_ROLE }} | |
| aws-region: ${{ matrix.region }} | |
| - name: Publish layer | |
| run: | | |
| PYTHON_VERSION="${{ matrix.python-version }}" | |
| ARCH="${{ matrix.architecture }}" | |
| REGION="${{ matrix.region }}" | |
| LAYER_NAME="strands-agents-py${PYTHON_VERSION//./_}-${ARCH}-all" | |
| ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) | |
| REGION_BUCKET="${{ env.BUCKET_NAME }}-${ACCOUNT_ID}-${REGION}" | |
| LAYER_KEY="$LAYER_NAME/v${{ inputs.package_version }}/lambda-layer.zip" | |
| DESCRIPTION="PyPI package: strands-agents v${{ inputs.package_version }} (Python $PYTHON_VERSION, $ARCH)" | |
| LAYER_OUTPUT=$(aws lambda publish-layer-version \ | |
| --layer-name $LAYER_NAME \ | |
| --description "$DESCRIPTION" \ | |
| --content S3Bucket=$REGION_BUCKET,S3Key=$LAYER_KEY \ | |
| --compatible-runtimes python${{ matrix.python-version }} \ | |
| --region "$REGION" \ | |
| --license-info Apache-2.0 \ | |
| --output json) | |
| LAYER_ARN=$(echo "$LAYER_OUTPUT" | jq -r '.LayerArn') | |
| LAYER_VERSION=$(echo "$LAYER_OUTPUT" | jq -r '.Version') | |
| echo "Published layer version $LAYER_VERSION with ARN: $LAYER_ARN in region $REGION" | |
| aws lambda add-layer-version-permission \ | |
| --layer-name $LAYER_NAME \ | |
| --version-number $LAYER_VERSION \ | |
| --statement-id public \ | |
| --action lambda:GetLayerVersion \ | |
| --principal '*' \ | |
| --region "$REGION" | |
| echo "Successfully published layer version $LAYER_VERSION in region $REGION" |