diff --git a/.github/scripts/build_lambda_layer.py b/.github/scripts/build_lambda_layer.py index ff903405..e10f468d 100644 --- a/.github/scripts/build_lambda_layer.py +++ b/.github/scripts/build_lambda_layer.py @@ -14,18 +14,9 @@ from pathlib import Path -ARCHITECTURE_PLATFORMS = { - "x86_64": "manylinux2014_x86_64", - "arm64": "manylinux2014_aarch64", -} -SUPPORTED_PYTHON_VERSIONS = ("3.11", "3.12", "3.13", "3.14") - - @dataclass(frozen=True) class BuildConfig: output: Path - target_python: str - architecture: str sdk_distribution: Path otel_distribution: Path build_dir: Path | None = None @@ -51,20 +42,6 @@ def build_layer(config: BuildConfig) -> Path: def _validate_config(config: BuildConfig) -> None: - if config.architecture not in ARCHITECTURE_PLATFORMS: - supported = ", ".join(sorted(ARCHITECTURE_PLATFORMS)) - raise ValueError( - f"Unsupported architecture: {config.architecture}. " - f"Supported architectures: {supported}" - ) - - if config.target_python not in SUPPORTED_PYTHON_VERSIONS: - supported = ", ".join(SUPPORTED_PYTHON_VERSIONS) - raise ValueError( - f"Unsupported Python version: {config.target_python}. " - f"Supported versions: {supported}" - ) - for distribution in (config.sdk_distribution, config.otel_distribution): if not distribution.is_file(): raise FileNotFoundError(distribution) @@ -76,10 +53,6 @@ def _validate_output_location(output: Path, build_dir: Path) -> None: def _install_layer_dependencies(config: BuildConfig, target_dir: Path) -> None: - python_version = config.target_python - abi = f"cp{python_version.replace('.', '')}" - platform = ARCHITECTURE_PLATFORMS[config.architecture] - command = [ sys.executable, "-m", @@ -88,14 +61,6 @@ def _install_layer_dependencies(config: BuildConfig, target_dir: Path) -> None: "--upgrade", "--target", str(target_dir), - "--platform", - platform, - "--implementation", - "cp", - "--python-version", - python_version, - "--abi", - abi, "--only-binary", ":all:", "--no-compile", @@ -122,18 +87,6 @@ def main(argv: list[str] | None = None) -> int: description="Build the AWS Durable Execution SDK OTel plugin Lambda layer.", ) parser.add_argument("--output", type=Path, required=True) - parser.add_argument( - "--target-python", - choices=SUPPORTED_PYTHON_VERSIONS, - required=True, - help="Lambda Python minor version.", - ) - parser.add_argument( - "--architecture", - choices=sorted(ARCHITECTURE_PLATFORMS), - required=True, - help="Lambda instruction set architecture.", - ) parser.add_argument("--sdk-distribution", type=Path, required=True) parser.add_argument("--otel-distribution", type=Path, required=True) parser.add_argument( @@ -146,8 +99,6 @@ def main(argv: list[str] | None = None) -> int: output = build_layer( BuildConfig( output=args.output, - target_python=args.target_python, - architecture=args.architecture, sdk_distribution=args.sdk_distribution, otel_distribution=args.otel_distribution, build_dir=args.build_dir, diff --git a/.github/scripts/tests/test_build_lambda_layer.py b/.github/scripts/tests/test_build_lambda_layer.py index 7671b48a..7d0b86db 100644 --- a/.github/scripts/tests/test_build_lambda_layer.py +++ b/.github/scripts/tests/test_build_lambda_layer.py @@ -42,16 +42,16 @@ def fake_run(command: list[str], check: bool) -> subprocess.CompletedProcess[str output = build_layer( BuildConfig( output=tmp_path / "layer.zip", - target_python="3.12", - architecture="arm64", sdk_distribution=sdk_wheel, otel_distribution=otel_wheel, ) ) assert output == tmp_path / "layer.zip" - assert commands[0][commands[0].index("--platform") + 1] == "manylinux2014_aarch64" - assert commands[0][commands[0].index("--abi") + 1] == "cp312" + assert "--platform" not in commands[0] + assert "--implementation" not in commands[0] + assert "--python-version" not in commands[0] + assert "--abi" not in commands[0] assert "--no-compile" in commands[0] assert str(sdk_wheel) in commands[0] assert str(otel_wheel) in commands[0] @@ -66,43 +66,11 @@ def fake_run(command: list[str], check: bool) -> subprocess.CompletedProcess[str ) -@pytest.mark.parametrize( - ("target_python", "architecture", "error"), - [ - ("3.10", "x86_64", "Unsupported Python version"), - ("3.12", "sparc", "Unsupported architecture"), - ], -) -def test_build_layer_rejects_unsupported_targets( - target_python: str, - architecture: str, - error: str, - tmp_path: Path, -) -> None: - sdk_wheel = tmp_path / "sdk.whl" - otel_wheel = tmp_path / "otel.whl" - sdk_wheel.write_text("sdk") - otel_wheel.write_text("otel") - - with pytest.raises(ValueError, match=error): - build_layer( - BuildConfig( - output=tmp_path / "layer.zip", - target_python=target_python, - architecture=architecture, - sdk_distribution=sdk_wheel, - otel_distribution=otel_wheel, - ) - ) - - def test_build_layer_requires_built_distributions(tmp_path: Path) -> None: with pytest.raises(FileNotFoundError): build_layer( BuildConfig( output=tmp_path / "layer.zip", - target_python="3.13", - architecture="x86_64", sdk_distribution=tmp_path / "missing-sdk.whl", otel_distribution=tmp_path / "missing-otel.whl", ) @@ -120,8 +88,6 @@ def test_build_layer_rejects_output_inside_build_directory(tmp_path: Path) -> No build_layer( BuildConfig( output=build_dir / "layer.zip", - target_python="3.13", - architecture="x86_64", sdk_distribution=sdk_wheel, otel_distribution=otel_wheel, build_dir=build_dir, diff --git a/.github/scripts/tests/test_lambda_layer_publish_workflow.py b/.github/scripts/tests/test_lambda_layer_publish_workflow.py new file mode 100644 index 00000000..0d575573 --- /dev/null +++ b/.github/scripts/tests/test_lambda_layer_publish_workflow.py @@ -0,0 +1,18 @@ +from pathlib import Path + + +REPOSITORY_ROOT = Path(__file__).resolve().parents[3] +WORKFLOW = REPOSITORY_ROOT / ".github" / "workflows" / "lambda-layer-publish.yml" + + +def test_workflow_builds_and_publishes_one_agnostic_layer() -> None: + workflow = WORKFLOW.read_text() + + assert "strategy:" not in workflow + assert "matrix." not in workflow + assert "--target-python" not in workflow + assert "--architecture" not in workflow + assert "--compatible-runtime" not in workflow + assert "--compatible-architecture" not in workflow + assert "otel-plugin-layer" in workflow + assert "${{ env.LAYER_NAME }}.zip" in workflow diff --git a/.github/workflows/lambda-layer-publish.yml b/.github/workflows/lambda-layer-publish.yml index 221296a6..42d9e6ff 100644 --- a/.github/workflows/lambda-layer-publish.yml +++ b/.github/workflows/lambda-layer-publish.yml @@ -143,42 +143,6 @@ jobs: build-layers: needs: build-distributions runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - include: - - target_python: "3.11" - runtime: python3.11 - runtime_slug: python311 - architecture: x86_64 - - target_python: "3.11" - runtime: python3.11 - runtime_slug: python311 - architecture: arm64 - - target_python: "3.12" - runtime: python3.12 - runtime_slug: python312 - architecture: x86_64 - - target_python: "3.12" - runtime: python3.12 - runtime_slug: python312 - architecture: arm64 - - target_python: "3.13" - runtime: python3.13 - runtime_slug: python313 - architecture: x86_64 - - target_python: "3.13" - runtime: python3.13 - runtime_slug: python313 - architecture: arm64 - - target_python: "3.14" - runtime: python3.14 - runtime_slug: python314 - architecture: x86_64 - - target_python: "3.14" - runtime: python3.14 - runtime_slug: python314 - architecture: arm64 steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 @@ -188,7 +152,7 @@ jobs: - name: Set up Python uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: - python-version: ${{ matrix.target_python }} + python-version: "3.11" - name: Download wheels uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 @@ -199,22 +163,20 @@ jobs: - name: Build layer zip id: build-layer env: - LAYER_ZIP: dist/${{ env.LAYER_NAME }}-${{ matrix.runtime_slug }}-${{ matrix.architecture }}.zip + LAYER_ZIP: dist/${{ env.LAYER_NAME }}.zip run: | SDK_WHEEL=$(find release-dists -name 'aws_durable_execution_sdk_python-*.whl' -print -quit) OTEL_WHEEL=$(find release-dists -name 'aws_durable_execution_sdk_python_otel-*.whl' -print -quit) python .github/scripts/build_lambda_layer.py \ --sdk-distribution "$SDK_WHEEL" \ --otel-distribution "$OTEL_WHEEL" \ - --target-python "${{ matrix.target_python }}" \ - --architecture "${{ matrix.architecture }}" \ --output "$LAYER_ZIP" echo "layer_zip=${LAYER_ZIP}" >> "$GITHUB_OUTPUT" - name: Upload layer artifact uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - name: otel-plugin-layer-${{ matrix.runtime_slug }}-${{ matrix.architecture }} + name: otel-plugin-layer path: ${{ steps.build-layer.outputs.layer_zip }} if-no-files-found: error retention-days: 30 @@ -229,60 +191,24 @@ jobs: id-token: write env: LAYER_REGIONS: ${{ inputs.regions || vars.LAYER_PUBLISH_REGIONS }} - strategy: - fail-fast: false - matrix: - include: - - target_python: "3.11" - runtime: python3.11 - runtime_slug: python311 - architecture: x86_64 - - target_python: "3.11" - runtime: python3.11 - runtime_slug: python311 - architecture: arm64 - - target_python: "3.12" - runtime: python3.12 - runtime_slug: python312 - architecture: x86_64 - - target_python: "3.12" - runtime: python3.12 - runtime_slug: python312 - architecture: arm64 - - target_python: "3.13" - runtime: python3.13 - runtime_slug: python313 - architecture: x86_64 - - target_python: "3.13" - runtime: python3.13 - runtime_slug: python313 - architecture: arm64 - - target_python: "3.14" - runtime: python3.14 - runtime_slug: python314 - architecture: x86_64 - - target_python: "3.14" - runtime: python3.14 - runtime_slug: python314 - architecture: arm64 steps: - name: Download layer artifact uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: - name: otel-plugin-layer-${{ matrix.runtime_slug }}-${{ matrix.architecture }} + name: otel-plugin-layer path: dist/ - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c # v6.2.3 with: role-to-assume: ${{ secrets.LAYER_PUBLISH_ROLE_ARN }} - role-session-name: otelLayerPublish-${{ matrix.runtime_slug }}-${{ matrix.architecture }} + role-session-name: otelLayerPublish aws-region: us-east-1 - name: Publish layer versions env: - LAYER_ZIP: dist/${{ env.LAYER_NAME }}-${{ matrix.runtime_slug }}-${{ matrix.architecture }}.zip + LAYER_ZIP: dist/${{ env.LAYER_NAME }}.zip SDK_VERSION: ${{ needs.build-distributions.outputs.sdk_version }} OTEL_VERSION: ${{ needs.build-distributions.outputs.otel_version }} run: | @@ -297,11 +223,9 @@ jobs: continue fi - LAYER_DESCRIPTION="AWS Durable Execution SDK ${SDK_VERSION} OTel plugin ${OTEL_VERSION} (${{ matrix.runtime }}/${{ matrix.architecture }}) sha256:${LOCAL_CODE_SHA256}" + LAYER_DESCRIPTION="AWS Durable Execution SDK ${SDK_VERSION} OTel plugin ${OTEL_VERSION} sha256:${LOCAL_CODE_SHA256}" if ! EXISTING_RESULT=$(aws lambda list-layer-versions \ --layer-name "$LAYER_NAME" \ - --compatible-runtime "${{ matrix.runtime }}" \ - --compatible-architecture "${{ matrix.architecture }}" \ --region "$REGION" \ --query "LayerVersions[?Description=='${LAYER_DESCRIPTION}'] | [0].[LayerVersionArn,Version]" \ --output text 2>&1); then @@ -339,8 +263,6 @@ jobs: --layer-name "$LAYER_NAME" \ --description "$LAYER_DESCRIPTION" \ --zip-file "fileb://${LAYER_ZIP}" \ - --compatible-runtimes "${{ matrix.runtime }}" \ - --compatible-architectures "${{ matrix.architecture }}" \ --license-info Apache-2.0 \ --region "$REGION" \ --query '[LayerVersionArn,Version,Content.CodeSha256]' \ diff --git a/.github/workflows/test-parser.yml b/.github/workflows/test-parser.yml index fde37133..018f4938 100644 --- a/.github/workflows/test-parser.yml +++ b/.github/workflows/test-parser.yml @@ -6,12 +6,14 @@ on: - '.github/scripts/build_lambda_layer.py' - '.github/scripts/parse_sdk_branch.py' - '.github/scripts/tests/**' + - '.github/workflows/lambda-layer-publish.yml' push: branches: [ main ] paths: - '.github/scripts/build_lambda_layer.py' - '.github/scripts/parse_sdk_branch.py' - '.github/scripts/tests/**' + - '.github/workflows/lambda-layer-publish.yml' permissions: contents: read @@ -29,4 +31,5 @@ jobs: run: | python -m pytest \ .github/scripts/tests/test_build_lambda_layer.py \ + .github/scripts/tests/test_lambda_layer_publish_workflow.py \ .github/scripts/tests/test_parse_sdk_branch.py diff --git a/RELEASING.md b/RELEASING.md index 6bae46b5..74ed7f8e 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -71,8 +71,8 @@ The workflow runs on the `release: [published]` event, so it fires whenever a re Releases containing an `otel-v` tag also trigger the [`lambda-layer-publish.yml`](.github/workflows/lambda-layer-publish.yml) -workflow. It builds the SDK and OTel plugin into Lambda layers for each -supported Python runtime and architecture, then publishes public versions of the +workflow. It builds the SDK and OTel plugin into a Python-version- and +architecture-agnostic Lambda layer, then publishes a public version of the `aws-durable-execution-sdk-python-otel-plugin` layer. For OTel-only releases, the workflow downloads the exact SDK version pinned by `layer.sdk-version` in `.github/lambda-layer-publish.toml`; that version must @@ -85,9 +85,9 @@ environment variable to a comma-separated list of AWS Regions. When unset, the workflow publishes to every commercial AWS Region supported by Lambda. The workflow can also be run manually from the Actions tab on `main`; its optional `regions` input overrides `LAYER_PUBLISH_REGIONS` for that run. -Each runtime and architecture layer archive is built once and retained as a -workflow artifact so retries publish the exact same resolved dependencies. Its -SHA-256 is included in the layer description and verified before reuse. +The layer archive is built once and retained as a workflow artifact so retries +publish the exact same resolved dependencies. Its SHA-256 is included in the +layer description and verified before reuse. The publishing role must allow `lambda:PublishLayerVersion` and `lambda:AddLayerVersionPermission`, as well as `lambda:ListLayerVersions` and `lambda:GetLayerVersion` for identity-checked, idempotent release retries.