Skip to content

Publish PyPI Package to Lambda Layer #33

Publish PyPI Package to Lambda Layer

Publish PyPI Package to Lambda Layer #33

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==${{ inputs.package_version }} \
--python-version ${{ matrix.python-version }} \
--platform manylinux2014_${{ matrix.architecture }} \
-t layer/python/ \
--only-binary=:all:
- 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}"
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}"
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"