Skip to content
Merged
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
45 changes: 45 additions & 0 deletions .github/workflows/update-sam-template.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: Update SAM Template

on:
pull_request:
paths:
- "examples/**"

permissions:
contents: write

concurrency:
group: ${{ github.head_ref }}-${{ github.run_id}}-sam-template
cancel-in-progress: true

jobs:
update-template:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
ref: ${{ github.head_ref }}

- name: Set up Python 3.13
uses: actions/setup-python@v5
with:
python-version: "3.13"

- name: Install PyYAML
run: pip install PyYAML

- name: Generate SAM template
run: python examples/scripts/generate_sam_template.py

- name: Commit and push changes
run: |
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
git add .
if git diff --staged --quiet; then
echo "No changes to commit"
else
git commit -m "chore: update SAM template" --no-verify
git push
fi
65 changes: 0 additions & 65 deletions examples/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,59 +215,6 @@ def bootstrap_account():
return True


def generate_sam_template(*, skip_durable_config=False):
"""Generate SAM template for all examples."""
catalog = load_catalog()

template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Transform": "AWS::Serverless-2016-10-31",
"Globals": {
"Function": {
"Runtime": "python3.13",
"Timeout": 60,
"MemorySize": 128,
"Environment": {
"Variables": {"AWS_ENDPOINT_URL_LAMBDA": {"Ref": "LambdaEndpoint"}}
},
}
},
"Parameters": {
"LambdaEndpoint": {
"Type": "String",
"Default": "https://lambda.us-west-2.amazonaws.com",
}
},
"Resources": {},
}

for example in catalog["examples"]:
# Convert handler name to PascalCase (e.g., hello_world -> HelloWorld)
handler_base = example["handler"].replace(".handler", "")
function_name = "".join(word.capitalize() for word in handler_base.split("_"))
template["Resources"][function_name] = {
"Type": "AWS::Serverless::Function",
"Properties": {
"CodeUri": "build/",
"Handler": example["handler"],
"Description": example["description"],
},
}

if not skip_durable_config and "durableConfig" in example:
template["Resources"][function_name]["Properties"]["DurableConfig"] = (
example["durableConfig"]
)

import yaml

template_path = Path(__file__).parent / "template.yaml"
with open(template_path, "w") as f:
yaml.dump(template, f, default_flow_style=False, sort_keys=False)

return True


def create_deployment_package(example_name: str) -> Path:
"""Create deployment package for example."""

Expand Down Expand Up @@ -484,16 +431,6 @@ def main():
# List command
subparsers.add_parser("list", help="List available examples")

# SAM template command
sam_parser = subparsers.add_parser(
"sam", help="Generate SAM template for all examples"
)
sam_parser.add_argument(
"--skip-durable-config",
action="store_true",
help="Skip adding DurableConfig properties to functions",
)

# Deploy command
deploy_parser = subparsers.add_parser("deploy", help="Deploy an example")
deploy_parser.add_argument("example_name", help="Name of example to deploy")
Expand Down Expand Up @@ -529,8 +466,6 @@ def main():
build_examples()
elif args.command == "list":
list_examples()
elif args.command == "sam":
generate_sam_template(skip_durable_config=args.skip_durable_config)
elif args.command == "deploy":
deploy_function(args.example_name, args.function_name)
elif args.command == "invoke":
Expand Down
106 changes: 106 additions & 0 deletions examples/scripts/generate_sam_template.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#!/usr/bin/env python3

import json
from pathlib import Path

import yaml


def load_catalog():
"""Load examples catalog."""
catalog_path = Path(__file__).parent.parent / "examples-catalog.json"
with open(catalog_path) as f:
return json.load(f)


def generate_sam_template():
"""Generate SAM template for all examples."""
catalog = load_catalog()

template = {
"AWSTemplateFormatVersion": "2010-09-09",
"Transform": "AWS::Serverless-2016-10-31",
"Globals": {
"Function": {
"Runtime": "python3.13",
"Timeout": 60,
"MemorySize": 128,
"Environment": {
"Variables": {"AWS_ENDPOINT_URL_LAMBDA": {"Ref": "LambdaEndpoint"}}
},
}
},
"Parameters": {
"LambdaEndpoint": {
"Type": "String",
"Default": "https://lambda.us-west-2.amazonaws.com",
}
},
"Resources": {
"DurableFunctionRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {"Service": "lambda.amazonaws.com"},
"Action": "sts:AssumeRole",
}
],
},
"ManagedPolicyArns": [
"arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
],
"Policies": [
{
"PolicyName": "DurableExecutionPolicy",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"lambda:CheckpointDurableExecution",
"lambda:GetDurableExecutionState",
],
"Resource": "*",
}
],
},
}
],
},
}
},
}

for example in catalog["examples"]:
# Convert handler name to PascalCase (e.g., hello_world -> HelloWorld)
handler_base = example["handler"].replace(".handler", "")
function_name = "".join(word.capitalize() for word in handler_base.split("_"))
template["Resources"][function_name] = {
"Type": "AWS::Serverless::Function",
"Properties": {
"CodeUri": "build/",
"Handler": example["handler"],
"Description": example["description"],
"Role": {"Fn::GetAtt": ["DurableFunctionRole", "Arn"]},
},
}

if "durableConfig" in example:
template["Resources"][function_name]["Properties"]["DurableConfig"] = (
example["durableConfig"]
)

template_path = Path(__file__).parent.parent / "template.yaml"
with open(template_path, "w") as f:
yaml.dump(template, f, default_flow_style=False, sort_keys=False)

print(f"Generated SAM template at {template_path}")


if __name__ == "__main__":
generate_sam_template()
Loading
Loading