repro-lambda 0.6.1__tar.gz → 0.7.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- repro_lambda-0.7.1/.release-please-manifest.json +3 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/CHANGELOG.md +14 -0
- repro_lambda-0.7.1/LICENSE +21 -0
- repro_lambda-0.7.1/PKG-INFO +104 -0
- repro_lambda-0.7.1/README.md +71 -0
- repro_lambda-0.7.1/SETUP.md +262 -0
- repro_lambda-0.6.1/SETUP.md → repro_lambda-0.7.1/USAGE.md +27 -263
- repro_lambda-0.7.1/examples/complete/README.md +100 -0
- repro_lambda-0.7.1/examples/complete/builds/catalog.json +20 -0
- repro_lambda-0.7.1/examples/complete/lambdas.toml +26 -0
- repro_lambda-0.7.1/examples/complete/main.tf +25 -0
- repro_lambda-0.7.1/examples/complete/outputs.tf +9 -0
- repro_lambda-0.7.1/examples/complete/src/app/app.py +3 -0
- repro_lambda-0.7.1/examples/complete/src/app/requirements.arm64.lock +3 -0
- repro_lambda-0.7.1/examples/complete/variables.tf +11 -0
- repro_lambda-0.7.1/examples/complete/versions.tf +14 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/pyproject.toml +3 -3
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/src/repro_lambda/__init__.py +1 -1
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/src/repro_lambda/cli.py +1 -1
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/src/repro_lambda/manifest.py +1 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/tests/test_manifest.py +18 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/uv.lock +1 -1
- repro_lambda-0.6.1/.release-please-manifest.json +0 -3
- repro_lambda-0.6.1/LICENSE +0 -177
- repro_lambda-0.6.1/PKG-INFO +0 -75
- repro_lambda-0.6.1/README.md +0 -42
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/.github/workflows/build.yml +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/.github/workflows/ci.yml +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/.github/workflows/move-major-tag.yml +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/.github/workflows/promote.yml +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/.github/workflows/publish.yml +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/.github/workflows/release.yml +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/.gitignore +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/.pre-commit-config.yaml +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/release-please-config.json +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/src/repro_lambda/__main__.py +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/src/repro_lambda/build.py +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/src/repro_lambda/catalog.py +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/src/repro_lambda/docker_runner.py +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/src/repro_lambda/git_guard.py +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/src/repro_lambda/hasher.py +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/src/repro_lambda/promote.py +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/src/repro_lambda/s3_uploader.py +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/src/repro_lambda/source_locker.py +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/src/repro_lambda/source_stager.py +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/src/repro_lambda/sources.py +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/src/repro_lambda/verify.py +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/src/repro_lambda/zip_packager.py +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/tests/__init__.py +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/tests/conftest.py +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/tests/fixtures/sample_nodejs_lambda/handler/index.js +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/tests/fixtures/sample_nodejs_lambda/handler/package-lock.json +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/tests/fixtures/sample_nodejs_lambda/handler/package.json +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/tests/fixtures/sample_nodejs_lambda/lambdas.toml +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/tests/fixtures/sample_python_lambda/handler/app.py +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/tests/fixtures/sample_python_lambda/handler/requirements.arm64.lock +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/tests/fixtures/sample_python_lambda/handler/requirements.in +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/tests/fixtures/sample_python_lambda/lambdas.toml +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/tests/test_build_integration.py +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/tests/test_build_nodejs.py +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/tests/test_catalog.py +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/tests/test_cli_build.py +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/tests/test_cli_lock.py +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/tests/test_cli_smoke.py +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/tests/test_docker_runner.py +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/tests/test_docker_runner_nodejs.py +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/tests/test_e2e_nodejs_lambda.py +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/tests/test_e2e_python_lambda.py +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/tests/test_extra_files.py +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/tests/test_git_guard.py +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/tests/test_hasher.py +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/tests/test_per_lambda_builder.py +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/tests/test_promote.py +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/tests/test_python_byte_compat_regression.py +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/tests/test_s3_uploader.py +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/tests/test_source_locker.py +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/tests/test_source_stager.py +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/tests/test_sources.py +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/tests/test_sources_hash.py +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/tests/test_sources_schema.py +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/tests/test_verify.py +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/tests/test_zip_excludes.py +0 -0
- {repro_lambda-0.6.1 → repro_lambda-0.7.1}/tests/test_zip_packager.py +0 -0
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.7.1](https://github.com/antonbabenko/repro-lambda/compare/v0.7.0...v0.7.1) (2026-06-23)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Documentation
|
|
7
|
+
|
|
8
|
+
* split SETUP into SETUP+USAGE, add runnable terraform example, restructure README ([#11](https://github.com/antonbabenko/repro-lambda/issues/11)) ([768ebda](https://github.com/antonbabenko/repro-lambda/commit/768ebda07aefa51cc15c0a5b52adbd251b7891d2))
|
|
9
|
+
|
|
10
|
+
## [0.7.0](https://github.com/antonbabenko/repro-lambda/compare/v0.6.1...v0.7.0) (2026-06-23)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* support the python3.14 lambda runtime ([#9](https://github.com/antonbabenko/repro-lambda/issues/9)) ([c3fcd8c](https://github.com/antonbabenko/repro-lambda/commit/c3fcd8ceb400891e152293ba06ada747aaa21f9f))
|
|
16
|
+
|
|
3
17
|
## [0.6.1](https://github.com/antonbabenko/repro-lambda/compare/v0.6.0...v0.6.1) (2026-06-22)
|
|
4
18
|
|
|
5
19
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Anton Babenko
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: repro-lambda
|
|
3
|
+
Version: 0.7.1
|
|
4
|
+
Summary: Build reproducible AWS Lambda packages outside Terraform, optimized for terraform-aws-lambda by serverless.tf.
|
|
5
|
+
Project-URL: Homepage, https://github.com/antonbabenko/repro-lambda
|
|
6
|
+
Project-URL: Repository, https://github.com/antonbabenko/repro-lambda
|
|
7
|
+
Project-URL: Issues, https://github.com/antonbabenko/repro-lambda/issues
|
|
8
|
+
Author-email: Anton Babenko <anton@antonbabenko.com>
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: aws,lambda,reproducible-builds,serverless,terraform
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: MacOS :: MacOS X
|
|
14
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
20
|
+
Requires-Python: >=3.11
|
|
21
|
+
Requires-Dist: boto3>=1.34
|
|
22
|
+
Requires-Dist: repro-zipfile>=0.3.1
|
|
23
|
+
Requires-Dist: tomlkit>=0.13
|
|
24
|
+
Requires-Dist: typer>=0.12
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: moto[s3]>=5; extra == 'dev'
|
|
27
|
+
Requires-Dist: pre-commit>=3.7; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest-mock>=3.12; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
30
|
+
Requires-Dist: pyyaml>=6; extra == 'dev'
|
|
31
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
|
|
34
|
+
# repro-lambda
|
|
35
|
+
|
|
36
|
+
Build reproducible AWS Lambda packages outside Terraform, optimized for
|
|
37
|
+
[terraform-aws-lambda](https://registry.terraform.io/modules/terraform-aws-modules/lambda/aws/latest) by [serverless.tf](https://serverless.tf/).
|
|
38
|
+
|
|
39
|
+
Produces byte-identical zip files across local dev (macOS) and CI (Linux),
|
|
40
|
+
uploads to S3 by content-hash key, and lets Terraform read `s3_existing_package`
|
|
41
|
+
instead of building during `terraform plan`/`apply`.
|
|
42
|
+
|
|
43
|
+
## Install
|
|
44
|
+
|
|
45
|
+
pip install repro-lambda
|
|
46
|
+
|
|
47
|
+
## Quick start
|
|
48
|
+
|
|
49
|
+
repro-lambda lock # regenerate per-arch requirement + source locks
|
|
50
|
+
repro-lambda build --bucket <bucket> # build all lambdas in lambdas.toml, upload to S3
|
|
51
|
+
repro-lambda build --verify --dry-run # two-pass byte-reproducibility check (no upload)
|
|
52
|
+
repro-lambda promote \
|
|
53
|
+
--dev-bucket <dev> --prod-bucket <prod> # copy dev -> prod by content sha (no rebuild)
|
|
54
|
+
|
|
55
|
+
`--bucket` (or `REPRO_LAMBDA_BUCKET`) is required for a real build; add
|
|
56
|
+
`--dry-run` to build without uploading. There is no `init` command yet (the
|
|
57
|
+
subcommand is currently a stub).
|
|
58
|
+
|
|
59
|
+
## Documentation
|
|
60
|
+
|
|
61
|
+
What each document covers, by section:
|
|
62
|
+
|
|
63
|
+
### Setup - one-time AWS provisioning ([SETUP.md](./SETUP.md))
|
|
64
|
+
|
|
65
|
+
Provision the supporting infrastructure once per AWS account and environment.
|
|
66
|
+
|
|
67
|
+
- [Architecture](./SETUP.md#architecture) - artifact buckets, key-level immutability, the content-hash model
|
|
68
|
+
- [Terraform - per-account bootstrap](./SETUP.md#terraform---per-account-bootstrap) - the buckets, the GitHub OIDC builder role, and outputs
|
|
69
|
+
- [GitHub OIDC provider](./SETUP.md#github-oidc-provider) - declaring the shared per-account OIDC provider
|
|
70
|
+
- [Next steps](./SETUP.md#next-steps) - where to go after provisioning
|
|
71
|
+
|
|
72
|
+
### Usage - day-to-day ([USAGE.md](./USAGE.md))
|
|
73
|
+
|
|
74
|
+
Using `repro-lambda` once the infrastructure exists.
|
|
75
|
+
|
|
76
|
+
- [Source-repo CI workflow](./USAGE.md#source-repo-ci-workflow) - wiring the reusable build workflow into CI/CD
|
|
77
|
+
- [Per-Lambda manifest](./USAGE.md#per-lambda-manifest) - the `lambdas.toml` fields
|
|
78
|
+
- [Per-lambda builder overrides](./USAGE.md#per-lambda-builder-overrides) - per-lambda base image and file filters
|
|
79
|
+
- [Declarative sources](./USAGE.md#declarative-sources---lambdasource) - pinned external artifacts via `[[lambda.source]]`
|
|
80
|
+
- [Terraform consumer (`s3_existing_package`)](./USAGE.md#terraform-consumer---s3_existing_package) - wiring `terraform-aws-modules/lambda/aws` to the built artifact
|
|
81
|
+
- [Smoke test](./USAGE.md#smoke-test) - first-build verification and the clean migration plan diff
|
|
82
|
+
- [Troubleshooting](./USAGE.md#troubleshooting) - upload 403s, `PreconditionFailed`, noisy plans
|
|
83
|
+
- [Node.js (npm) Lambdas](./USAGE.md#nodejs-npm-lambdas) - npm packaging support
|
|
84
|
+
- [Manifest fields for npm specs](./USAGE.md#manifest-fields-for-npm-specs)
|
|
85
|
+
- [Lockfile regeneration](./USAGE.md#lockfile-regeneration)
|
|
86
|
+
- [Lambda@Edge example](./USAGE.md#lambdaedge-example) - `us-east-1` artifacts for CloudFront
|
|
87
|
+
- [Caveats](./USAGE.md#caveats) - npm workspaces, native deps, symlinks
|
|
88
|
+
|
|
89
|
+
### Example - runnable ([examples/complete/](./examples/complete/))
|
|
90
|
+
|
|
91
|
+
A self-contained consumer setup: manifest, catalog, and Terraform using
|
|
92
|
+
`terraform-aws-modules/lambda/aws`.
|
|
93
|
+
|
|
94
|
+
- [What this example shows](./examples/complete/README.md#what-this-example-shows) - files and layout
|
|
95
|
+
- [The build-outside-Terraform flow](./examples/complete/README.md#the-build-outside-terraform-flow) - build, inspect the catalog, apply
|
|
96
|
+
- [Expected plan diff](./examples/complete/README.md#expected-plan-diff) - the `s3_key`-only diff to expect
|
|
97
|
+
|
|
98
|
+
## License
|
|
99
|
+
|
|
100
|
+
MIT - see [LICENSE](./LICENSE).
|
|
101
|
+
|
|
102
|
+
## Author
|
|
103
|
+
|
|
104
|
+
Anton Babenko - [@antonbabenko](https://twitter.com/antonbabenko) on Twitter, [antonbabenko](https://www.linkedin.com/in/antonbabenko/) on LinkedIn.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# repro-lambda
|
|
2
|
+
|
|
3
|
+
Build reproducible AWS Lambda packages outside Terraform, optimized for
|
|
4
|
+
[terraform-aws-lambda](https://registry.terraform.io/modules/terraform-aws-modules/lambda/aws/latest) by [serverless.tf](https://serverless.tf/).
|
|
5
|
+
|
|
6
|
+
Produces byte-identical zip files across local dev (macOS) and CI (Linux),
|
|
7
|
+
uploads to S3 by content-hash key, and lets Terraform read `s3_existing_package`
|
|
8
|
+
instead of building during `terraform plan`/`apply`.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
pip install repro-lambda
|
|
13
|
+
|
|
14
|
+
## Quick start
|
|
15
|
+
|
|
16
|
+
repro-lambda lock # regenerate per-arch requirement + source locks
|
|
17
|
+
repro-lambda build --bucket <bucket> # build all lambdas in lambdas.toml, upload to S3
|
|
18
|
+
repro-lambda build --verify --dry-run # two-pass byte-reproducibility check (no upload)
|
|
19
|
+
repro-lambda promote \
|
|
20
|
+
--dev-bucket <dev> --prod-bucket <prod> # copy dev -> prod by content sha (no rebuild)
|
|
21
|
+
|
|
22
|
+
`--bucket` (or `REPRO_LAMBDA_BUCKET`) is required for a real build; add
|
|
23
|
+
`--dry-run` to build without uploading. There is no `init` command yet (the
|
|
24
|
+
subcommand is currently a stub).
|
|
25
|
+
|
|
26
|
+
## Documentation
|
|
27
|
+
|
|
28
|
+
What each document covers, by section:
|
|
29
|
+
|
|
30
|
+
### Setup - one-time AWS provisioning ([SETUP.md](./SETUP.md))
|
|
31
|
+
|
|
32
|
+
Provision the supporting infrastructure once per AWS account and environment.
|
|
33
|
+
|
|
34
|
+
- [Architecture](./SETUP.md#architecture) - artifact buckets, key-level immutability, the content-hash model
|
|
35
|
+
- [Terraform - per-account bootstrap](./SETUP.md#terraform---per-account-bootstrap) - the buckets, the GitHub OIDC builder role, and outputs
|
|
36
|
+
- [GitHub OIDC provider](./SETUP.md#github-oidc-provider) - declaring the shared per-account OIDC provider
|
|
37
|
+
- [Next steps](./SETUP.md#next-steps) - where to go after provisioning
|
|
38
|
+
|
|
39
|
+
### Usage - day-to-day ([USAGE.md](./USAGE.md))
|
|
40
|
+
|
|
41
|
+
Using `repro-lambda` once the infrastructure exists.
|
|
42
|
+
|
|
43
|
+
- [Source-repo CI workflow](./USAGE.md#source-repo-ci-workflow) - wiring the reusable build workflow into CI/CD
|
|
44
|
+
- [Per-Lambda manifest](./USAGE.md#per-lambda-manifest) - the `lambdas.toml` fields
|
|
45
|
+
- [Per-lambda builder overrides](./USAGE.md#per-lambda-builder-overrides) - per-lambda base image and file filters
|
|
46
|
+
- [Declarative sources](./USAGE.md#declarative-sources---lambdasource) - pinned external artifacts via `[[lambda.source]]`
|
|
47
|
+
- [Terraform consumer (`s3_existing_package`)](./USAGE.md#terraform-consumer---s3_existing_package) - wiring `terraform-aws-modules/lambda/aws` to the built artifact
|
|
48
|
+
- [Smoke test](./USAGE.md#smoke-test) - first-build verification and the clean migration plan diff
|
|
49
|
+
- [Troubleshooting](./USAGE.md#troubleshooting) - upload 403s, `PreconditionFailed`, noisy plans
|
|
50
|
+
- [Node.js (npm) Lambdas](./USAGE.md#nodejs-npm-lambdas) - npm packaging support
|
|
51
|
+
- [Manifest fields for npm specs](./USAGE.md#manifest-fields-for-npm-specs)
|
|
52
|
+
- [Lockfile regeneration](./USAGE.md#lockfile-regeneration)
|
|
53
|
+
- [Lambda@Edge example](./USAGE.md#lambdaedge-example) - `us-east-1` artifacts for CloudFront
|
|
54
|
+
- [Caveats](./USAGE.md#caveats) - npm workspaces, native deps, symlinks
|
|
55
|
+
|
|
56
|
+
### Example - runnable ([examples/complete/](./examples/complete/))
|
|
57
|
+
|
|
58
|
+
A self-contained consumer setup: manifest, catalog, and Terraform using
|
|
59
|
+
`terraform-aws-modules/lambda/aws`.
|
|
60
|
+
|
|
61
|
+
- [What this example shows](./examples/complete/README.md#what-this-example-shows) - files and layout
|
|
62
|
+
- [The build-outside-Terraform flow](./examples/complete/README.md#the-build-outside-terraform-flow) - build, inspect the catalog, apply
|
|
63
|
+
- [Expected plan diff](./examples/complete/README.md#expected-plan-diff) - the `s3_key`-only diff to expect
|
|
64
|
+
|
|
65
|
+
## License
|
|
66
|
+
|
|
67
|
+
MIT - see [LICENSE](./LICENSE).
|
|
68
|
+
|
|
69
|
+
## Author
|
|
70
|
+
|
|
71
|
+
Anton Babenko - [@antonbabenko](https://twitter.com/antonbabenko) on Twitter, [antonbabenko](https://www.linkedin.com/in/antonbabenko/) on LinkedIn.
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
# Setting up `repro-lambda` for your project
|
|
2
|
+
|
|
3
|
+
`repro-lambda` builds and uploads Lambda artifacts to S3 outside of Terraform.
|
|
4
|
+
This guide covers the one-time job: provisioning the supporting AWS
|
|
5
|
+
infrastructure (the artifact buckets plus the GitHub OIDC builder role) once per
|
|
6
|
+
AWS account and region, so that Terraform can later read those artifacts via
|
|
7
|
+
`s3_existing_package`.
|
|
8
|
+
|
|
9
|
+
This is a copy-paste-able reference. Adapt the names and account IDs to your
|
|
10
|
+
environment. Once the infrastructure exists, see [USAGE.md](./USAGE.md) for
|
|
11
|
+
day-to-day use (the CI workflow, the per-Lambda manifest, and consuming
|
|
12
|
+
artifacts from Terraform).
|
|
13
|
+
|
|
14
|
+
## Architecture
|
|
15
|
+
|
|
16
|
+
For each environment (e.g. `dev`, `prod`) you typically need:
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
${env}-my-lambda-artifacts S3 bucket, region = eu-west-1 (or your primary region)
|
|
20
|
+
${env}-my-lambda-artifacts-us-east-1 S3 bucket, region = us-east-1 (for Lambda@Edge - optional)
|
|
21
|
+
|
|
22
|
+
gha-lambda-builder-${env} IAM role, assumed via GitHub OIDC by source-repo CI to upload
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Both buckets enforce **key-level immutability** via bucket policy:
|
|
26
|
+
|
|
27
|
+
- `s3:PutObject` denied unless `If-None-Match=*` is set (writes are first-write-wins)
|
|
28
|
+
- `s3:DeleteObject` and `s3:DeleteObjectVersion` denied
|
|
29
|
+
- The Lambda service is allowed `s3:GetObject` so functions can load their zip
|
|
30
|
+
|
|
31
|
+
Once an artifact with a content-hash key (`lambdas/<name>/<sha256>.zip`) is
|
|
32
|
+
uploaded, the key is permanently bound to those bytes. `repro-lambda` treats
|
|
33
|
+
HTTP 412 PreconditionFailed on a duplicate upload as success.
|
|
34
|
+
|
|
35
|
+
## Terraform - per-account bootstrap
|
|
36
|
+
|
|
37
|
+
The Terraform below assumes you already have a configured AWS provider in the
|
|
38
|
+
target account. Drop it into your bootstrap directory (or anywhere you provision
|
|
39
|
+
account-wide infrastructure that has no dependencies on other Terraform state).
|
|
40
|
+
|
|
41
|
+
```hcl
|
|
42
|
+
# us-east-1 is needed only for Lambda@Edge artifacts. Skip if you don't use L@E.
|
|
43
|
+
provider "aws" {
|
|
44
|
+
alias = "us_east_1"
|
|
45
|
+
region = "us-east-1"
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
locals {
|
|
49
|
+
env = "dev" # change per environment
|
|
50
|
+
lambda_artifacts_bucket_primary = "${local.env}-my-lambda-artifacts"
|
|
51
|
+
lambda_artifacts_bucket_us_east_1 = "${local.env}-my-lambda-artifacts-us-east-1"
|
|
52
|
+
|
|
53
|
+
# GitHub OIDC subjects allowed to assume the builder role.
|
|
54
|
+
# Pattern: repo:<owner>/<repo>:* - narrow to specific refs in production.
|
|
55
|
+
lambda_builder_oidc_subjects = [
|
|
56
|
+
"repo:my-org/my-source-repo:*",
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# Immutability policy applied to every artifact bucket.
|
|
61
|
+
data "aws_iam_policy_document" "lambda_artifacts_immutability" {
|
|
62
|
+
for_each = toset([
|
|
63
|
+
local.lambda_artifacts_bucket_primary,
|
|
64
|
+
local.lambda_artifacts_bucket_us_east_1,
|
|
65
|
+
])
|
|
66
|
+
|
|
67
|
+
statement {
|
|
68
|
+
sid = "DenyOverwrites"
|
|
69
|
+
effect = "Deny"
|
|
70
|
+
|
|
71
|
+
principals {
|
|
72
|
+
type = "*"
|
|
73
|
+
identifiers = ["*"]
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
actions = ["s3:PutObject"]
|
|
77
|
+
resources = ["arn:aws:s3:::${each.value}/*"]
|
|
78
|
+
|
|
79
|
+
condition {
|
|
80
|
+
test = "StringNotEquals"
|
|
81
|
+
variable = "s3:If-None-Match"
|
|
82
|
+
values = ["*"]
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
statement {
|
|
87
|
+
sid = "DenyDelete"
|
|
88
|
+
effect = "Deny"
|
|
89
|
+
|
|
90
|
+
principals {
|
|
91
|
+
type = "*"
|
|
92
|
+
identifiers = ["*"]
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
actions = [
|
|
96
|
+
"s3:DeleteObject",
|
|
97
|
+
"s3:DeleteObjectVersion",
|
|
98
|
+
]
|
|
99
|
+
resources = ["arn:aws:s3:::${each.value}/*"]
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
statement {
|
|
103
|
+
sid = "AllowLambdaServiceRead"
|
|
104
|
+
effect = "Allow"
|
|
105
|
+
|
|
106
|
+
principals {
|
|
107
|
+
type = "Service"
|
|
108
|
+
identifiers = ["lambda.amazonaws.com"]
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
actions = ["s3:GetObject"]
|
|
112
|
+
resources = ["arn:aws:s3:::${each.value}/*"]
|
|
113
|
+
|
|
114
|
+
condition {
|
|
115
|
+
test = "StringEquals"
|
|
116
|
+
variable = "aws:SourceAccount"
|
|
117
|
+
values = [data.aws_caller_identity.current.account_id]
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
data "aws_caller_identity" "current" {}
|
|
123
|
+
|
|
124
|
+
# Primary-region bucket
|
|
125
|
+
module "lambda_artifacts_bucket_primary" {
|
|
126
|
+
source = "terraform-aws-modules/s3-bucket/aws"
|
|
127
|
+
version = "~> 4.0"
|
|
128
|
+
|
|
129
|
+
bucket = local.lambda_artifacts_bucket_primary
|
|
130
|
+
|
|
131
|
+
versioning = {
|
|
132
|
+
enabled = false # sha-as-key IS the versioning
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
server_side_encryption_configuration = {
|
|
136
|
+
rule = {
|
|
137
|
+
apply_server_side_encryption_by_default = {
|
|
138
|
+
sse_algorithm = "AES256"
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
attach_policy = true
|
|
144
|
+
policy = data.aws_iam_policy_document.lambda_artifacts_immutability[local.lambda_artifacts_bucket_primary].json
|
|
145
|
+
|
|
146
|
+
block_public_acls = true
|
|
147
|
+
block_public_policy = true
|
|
148
|
+
ignore_public_acls = true
|
|
149
|
+
restrict_public_buckets = true
|
|
150
|
+
|
|
151
|
+
force_destroy = false
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
# us-east-1 bucket - only needed for Lambda@Edge. Remove if not using L@E.
|
|
155
|
+
module "lambda_artifacts_bucket_us_east_1" {
|
|
156
|
+
source = "terraform-aws-modules/s3-bucket/aws"
|
|
157
|
+
version = "~> 4.0"
|
|
158
|
+
|
|
159
|
+
providers = {
|
|
160
|
+
aws = aws.us_east_1
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
bucket = local.lambda_artifacts_bucket_us_east_1
|
|
164
|
+
|
|
165
|
+
versioning = {
|
|
166
|
+
enabled = false
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
server_side_encryption_configuration = {
|
|
170
|
+
rule = {
|
|
171
|
+
apply_server_side_encryption_by_default = {
|
|
172
|
+
sse_algorithm = "AES256"
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
attach_policy = true
|
|
178
|
+
policy = data.aws_iam_policy_document.lambda_artifacts_immutability[local.lambda_artifacts_bucket_us_east_1].json
|
|
179
|
+
|
|
180
|
+
block_public_acls = true
|
|
181
|
+
block_public_policy = true
|
|
182
|
+
ignore_public_acls = true
|
|
183
|
+
restrict_public_buckets = true
|
|
184
|
+
|
|
185
|
+
force_destroy = false
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
# OIDC role assumed by GitHub Actions in source repos to upload artifacts.
|
|
189
|
+
module "gha_lambda_builder_role" {
|
|
190
|
+
source = "terraform-aws-modules/iam/aws//modules/iam-role"
|
|
191
|
+
version = "~> 6.0"
|
|
192
|
+
|
|
193
|
+
name = "gha-lambda-builder-${local.env}"
|
|
194
|
+
|
|
195
|
+
enable_github_oidc = true
|
|
196
|
+
oidc_wildcard_subjects = local.lambda_builder_oidc_subjects
|
|
197
|
+
|
|
198
|
+
create_inline_policy = true
|
|
199
|
+
inline_policy_permissions = {
|
|
200
|
+
WriteLambdaArtifacts = {
|
|
201
|
+
actions = [
|
|
202
|
+
"s3:PutObject",
|
|
203
|
+
"s3:GetObject",
|
|
204
|
+
"s3:HeadObject",
|
|
205
|
+
"s3:ListBucket",
|
|
206
|
+
]
|
|
207
|
+
resources = [
|
|
208
|
+
module.lambda_artifacts_bucket_primary.s3_bucket_arn,
|
|
209
|
+
"${module.lambda_artifacts_bucket_primary.s3_bucket_arn}/*",
|
|
210
|
+
module.lambda_artifacts_bucket_us_east_1.s3_bucket_arn,
|
|
211
|
+
"${module.lambda_artifacts_bucket_us_east_1.s3_bucket_arn}/*",
|
|
212
|
+
]
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
output "lambda_artifacts_bucket_primary_arn" {
|
|
218
|
+
value = module.lambda_artifacts_bucket_primary.s3_bucket_arn
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
output "lambda_artifacts_bucket_us_east_1_arn" {
|
|
222
|
+
value = module.lambda_artifacts_bucket_us_east_1.s3_bucket_arn
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
output "gha_lambda_builder_role_arn" {
|
|
226
|
+
value = module.gha_lambda_builder_role.arn
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
> Note: if you only use a single region (no Lambda@Edge), remove every
|
|
231
|
+
> us-east-1 reference together, or `terraform plan` fails on dangling
|
|
232
|
+
> references: the `lambda_artifacts_bucket_us_east_1` module, the `us_east_1`
|
|
233
|
+
> provider alias, the `local.lambda_artifacts_bucket_us_east_1` entry in the
|
|
234
|
+
> immutability policy `for_each`, and the two
|
|
235
|
+
> `module.lambda_artifacts_bucket_us_east_1` ARNs in the OIDC role's
|
|
236
|
+
> `WriteLambdaArtifacts` resources.
|
|
237
|
+
|
|
238
|
+
Apply the Terraform once per environment. The bucket names and role ARN are
|
|
239
|
+
referenced by your source repos' CI workflows in [USAGE.md](./USAGE.md).
|
|
240
|
+
|
|
241
|
+
## GitHub OIDC provider
|
|
242
|
+
|
|
243
|
+
If your account doesn't already have the GitHub Actions OIDC provider, the
|
|
244
|
+
`terraform-aws-modules/iam` collection includes a module for it:
|
|
245
|
+
|
|
246
|
+
```hcl
|
|
247
|
+
module "iam_github_oidc_provider" {
|
|
248
|
+
source = "terraform-aws-modules/iam/aws//modules/iam-oidc-provider"
|
|
249
|
+
version = "~> 6.0"
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
The provider is shared per AWS account - declare it once, not per environment.
|
|
254
|
+
|
|
255
|
+
## Next steps
|
|
256
|
+
|
|
257
|
+
The AWS infrastructure is now provisioned. To use it day-to-day:
|
|
258
|
+
|
|
259
|
+
- [USAGE.md](./USAGE.md) - the source-repo CI workflow, the per-Lambda manifest,
|
|
260
|
+
declarative sources, consuming artifacts from Terraform, and troubleshooting.
|
|
261
|
+
- [examples/complete/](./examples/complete/) - a runnable example that wires the
|
|
262
|
+
pieces together.
|