repro-lambda 0.5.2__tar.gz → 0.6.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.5.2 → repro_lambda-0.6.1}/.github/workflows/build.yml +93 -1
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/.github/workflows/ci.yml +1 -1
- repro_lambda-0.6.1/.github/workflows/release.yml +85 -0
- repro_lambda-0.6.1/.release-please-manifest.json +3 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/CHANGELOG.md +7 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/PKG-INFO +1 -1
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/pyproject.toml +1 -1
- repro_lambda-0.6.1/release-please-config.json +26 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/src/repro_lambda/__init__.py +1 -1
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/src/repro_lambda/cli.py +33 -4
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/tests/test_promote.py +71 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/uv.lock +1 -1
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/.github/workflows/move-major-tag.yml +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/.github/workflows/promote.yml +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/.github/workflows/publish.yml +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/.gitignore +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/.pre-commit-config.yaml +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/LICENSE +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/README.md +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/SETUP.md +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/src/repro_lambda/__main__.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/src/repro_lambda/build.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/src/repro_lambda/catalog.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/src/repro_lambda/docker_runner.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/src/repro_lambda/git_guard.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/src/repro_lambda/hasher.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/src/repro_lambda/manifest.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/src/repro_lambda/promote.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/src/repro_lambda/s3_uploader.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/src/repro_lambda/source_locker.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/src/repro_lambda/source_stager.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/src/repro_lambda/sources.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/src/repro_lambda/verify.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/src/repro_lambda/zip_packager.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/tests/__init__.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/tests/conftest.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/tests/fixtures/sample_nodejs_lambda/handler/index.js +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/tests/fixtures/sample_nodejs_lambda/handler/package-lock.json +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/tests/fixtures/sample_nodejs_lambda/handler/package.json +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/tests/fixtures/sample_nodejs_lambda/lambdas.toml +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/tests/fixtures/sample_python_lambda/handler/app.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/tests/fixtures/sample_python_lambda/handler/requirements.arm64.lock +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/tests/fixtures/sample_python_lambda/handler/requirements.in +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/tests/fixtures/sample_python_lambda/lambdas.toml +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/tests/test_build_integration.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/tests/test_build_nodejs.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/tests/test_catalog.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/tests/test_cli_build.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/tests/test_cli_lock.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/tests/test_cli_smoke.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/tests/test_docker_runner.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/tests/test_docker_runner_nodejs.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/tests/test_e2e_nodejs_lambda.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/tests/test_e2e_python_lambda.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/tests/test_extra_files.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/tests/test_git_guard.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/tests/test_hasher.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/tests/test_manifest.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/tests/test_per_lambda_builder.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/tests/test_python_byte_compat_regression.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/tests/test_s3_uploader.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/tests/test_source_locker.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/tests/test_source_stager.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/tests/test_sources.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/tests/test_sources_hash.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/tests/test_sources_schema.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/tests/test_verify.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/tests/test_zip_excludes.py +0 -0
- {repro_lambda-0.5.2 → repro_lambda-0.6.1}/tests/test_zip_packager.py +0 -0
|
@@ -25,6 +25,27 @@ on:
|
|
|
25
25
|
required: false
|
|
26
26
|
default: ""
|
|
27
27
|
description: S3 bucket name for prod Lambda artifacts (master push only).
|
|
28
|
+
promotion_repo:
|
|
29
|
+
type: string
|
|
30
|
+
required: false
|
|
31
|
+
default: ""
|
|
32
|
+
description: >-
|
|
33
|
+
owner/repo of the centralized promotion workflow. When set, the build Step
|
|
34
|
+
Summary renders a ready-to-run `gh workflow run` command per function. Generic
|
|
35
|
+
by design - the reusable workflow never hardcodes a promotion target; the caller
|
|
36
|
+
supplies it (keep this empty for public callers).
|
|
37
|
+
promotion_workflow:
|
|
38
|
+
type: string
|
|
39
|
+
required: false
|
|
40
|
+
default: promote-lambda.yml
|
|
41
|
+
description: Workflow file name dispatched by the promotion command in the Step Summary.
|
|
42
|
+
project:
|
|
43
|
+
type: string
|
|
44
|
+
required: false
|
|
45
|
+
default: ""
|
|
46
|
+
description: >-
|
|
47
|
+
Project key the promotion workflow expects (-f project=...). Defaults to the
|
|
48
|
+
caller repo name when empty.
|
|
28
49
|
secrets:
|
|
29
50
|
sources_token:
|
|
30
51
|
required: false
|
|
@@ -92,7 +113,78 @@ jobs:
|
|
|
92
113
|
env:
|
|
93
114
|
REPRO_LAMBDA_BUCKET: ${{ inputs.dev_bucket }}
|
|
94
115
|
REPRO_LAMBDA_SOURCES_TOKEN: ${{ secrets.sources_token }}
|
|
95
|
-
run: uvx --from repro-lambda repro-lambda build --manifest "${{ inputs.manifest_path }}" --arch "${{ matrix.arch }}"
|
|
116
|
+
run: uvx --from repro-lambda repro-lambda build --manifest "${{ inputs.manifest_path }}" --arch "${{ matrix.arch }}" --json-out "$RUNNER_TEMP/build-summary.json"
|
|
117
|
+
|
|
118
|
+
# Render the dev build's content shas + a copy-paste promotion command per function
|
|
119
|
+
# into the run's Step Summary (the build JSON is otherwise buried in step logs).
|
|
120
|
+
# NB: the JSON lives in RUNNER_TEMP (outside the workspace), so hashFiles() can't guard
|
|
121
|
+
# it - run on success() and skip inside the script if the file is somehow absent.
|
|
122
|
+
- name: Build summary (content shas + promote commands)
|
|
123
|
+
if: success()
|
|
124
|
+
env:
|
|
125
|
+
SUMMARY_JSON: ${{ runner.temp }}/build-summary.json
|
|
126
|
+
PROMOTION_REPO: ${{ inputs.promotion_repo }}
|
|
127
|
+
PROMOTION_WORKFLOW: ${{ inputs.promotion_workflow }}
|
|
128
|
+
PROJECT: ${{ inputs.project != '' && inputs.project || github.event.repository.name }}
|
|
129
|
+
ARCH: ${{ matrix.arch }}
|
|
130
|
+
SRC_REPO: ${{ github.repository }}
|
|
131
|
+
SRC_COMMIT: ${{ github.sha }}
|
|
132
|
+
run: |
|
|
133
|
+
python3 - <<'PY'
|
|
134
|
+
import json, os, sys
|
|
135
|
+
|
|
136
|
+
path = os.environ["SUMMARY_JSON"]
|
|
137
|
+
if not os.path.exists(path):
|
|
138
|
+
print(f"::notice::no build summary JSON at {path}; skipping summary.")
|
|
139
|
+
sys.exit(0)
|
|
140
|
+
rows = json.load(open(path))
|
|
141
|
+
repo = os.environ["PROMOTION_REPO"]
|
|
142
|
+
wf = os.environ["PROMOTION_WORKFLOW"]
|
|
143
|
+
project = os.environ["PROJECT"]
|
|
144
|
+
arch = os.environ["ARCH"]
|
|
145
|
+
src = os.environ["SRC_REPO"]
|
|
146
|
+
commit = os.environ["SRC_COMMIT"]
|
|
147
|
+
|
|
148
|
+
out = [
|
|
149
|
+
f"## Lambda build ({arch})",
|
|
150
|
+
"",
|
|
151
|
+
f"Source: `{src}` @ `{commit[:12]}`",
|
|
152
|
+
"",
|
|
153
|
+
"| Function | Outcome | Content SHA | Dev key |",
|
|
154
|
+
"| --- | --- | --- | --- |",
|
|
155
|
+
]
|
|
156
|
+
for r in rows:
|
|
157
|
+
out.append(
|
|
158
|
+
f"| `{r['logical_name']}` | {r['outcome']} | `{r['sha256']}` | `{r['bucket_key']}` |"
|
|
159
|
+
)
|
|
160
|
+
out += [
|
|
161
|
+
"",
|
|
162
|
+
"### Pipeline",
|
|
163
|
+
"",
|
|
164
|
+
"1. **Build** (this run) - artifacts uploaded to the dev bucket, keyed by content sha.",
|
|
165
|
+
"2. **Pin dev** - point the dev stack's lambda pin at the sha below, then apply dev and test.",
|
|
166
|
+
"3. **Promote to prod** - run the promote command below (copies dev -> prod by content"
|
|
167
|
+
" sha + opens the prod-pin PR), then apply prod.",
|
|
168
|
+
"",
|
|
169
|
+
"### Per-function",
|
|
170
|
+
"",
|
|
171
|
+
]
|
|
172
|
+
for r in rows:
|
|
173
|
+
fn, sha = r["logical_name"], r["sha256"]
|
|
174
|
+
out += [f"**`{fn}`** - content sha `{sha}`", "", f"- dev pin: `{fn} = \"{sha}\"`"]
|
|
175
|
+
if repo:
|
|
176
|
+
out += [
|
|
177
|
+
"- promote to prod:",
|
|
178
|
+
"```bash",
|
|
179
|
+
f"gh workflow run {wf} --repo {repo} "
|
|
180
|
+
f"-f project={project} -f function={fn} -f sha={sha}",
|
|
181
|
+
"```",
|
|
182
|
+
]
|
|
183
|
+
out += [""]
|
|
184
|
+
|
|
185
|
+
with open(os.environ["GITHUB_STEP_SUMMARY"], "a") as f:
|
|
186
|
+
f.write("\n".join(out) + "\n")
|
|
187
|
+
PY
|
|
96
188
|
|
|
97
189
|
- name: Verify reproducible (PR only)
|
|
98
190
|
if: github.event_name == 'pull_request'
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
|
|
3
|
+
# Automated releases via release-please. On every push to master, release-please
|
|
4
|
+
# maintains a "release PR" that bumps the version (pyproject.toml + __init__.py) and
|
|
5
|
+
# the CHANGELOG. Merging that PR creates the vX.Y.Z tag + GitHub Release, then this
|
|
6
|
+
# same run publishes to PyPI (OIDC) and force-moves the sliding v0 major tag.
|
|
7
|
+
#
|
|
8
|
+
# Why publish here instead of relying on publish.yml's tag trigger: release-please
|
|
9
|
+
# creates the tag via the GitHub API using GITHUB_TOKEN, and a GITHUB_TOKEN-created
|
|
10
|
+
# tag does NOT trigger other workflows. So the publish + major-tag steps run as
|
|
11
|
+
# release_created-gated jobs in this workflow. publish.yml / move-major-tag.yml stay
|
|
12
|
+
# as the manual-tag fallback (a human can still `git tag vX.Y.Z && git push`).
|
|
13
|
+
|
|
14
|
+
on:
|
|
15
|
+
push:
|
|
16
|
+
branches: [master]
|
|
17
|
+
workflow_dispatch:
|
|
18
|
+
|
|
19
|
+
permissions: {}
|
|
20
|
+
|
|
21
|
+
jobs:
|
|
22
|
+
release-please:
|
|
23
|
+
runs-on: ubuntu-24.04
|
|
24
|
+
permissions:
|
|
25
|
+
contents: write # create the release commit, tag, and GitHub Release
|
|
26
|
+
pull-requests: write # open/update the release PR
|
|
27
|
+
issues: write # manage the autorelease:* labels
|
|
28
|
+
outputs:
|
|
29
|
+
release_created: ${{ steps.release.outputs.release_created }}
|
|
30
|
+
tag_name: ${{ steps.release.outputs.tag_name }}
|
|
31
|
+
steps:
|
|
32
|
+
- uses: googleapis/release-please-action@v5
|
|
33
|
+
id: release
|
|
34
|
+
with:
|
|
35
|
+
token: ${{ secrets.GITHUB_TOKEN }}
|
|
36
|
+
target-branch: master
|
|
37
|
+
config-file: release-please-config.json
|
|
38
|
+
manifest-file: .release-please-manifest.json
|
|
39
|
+
|
|
40
|
+
publish:
|
|
41
|
+
needs: release-please
|
|
42
|
+
if: ${{ needs.release-please.outputs.release_created == 'true' }}
|
|
43
|
+
runs-on: ubuntu-24.04
|
|
44
|
+
permissions:
|
|
45
|
+
id-token: write # PyPI OIDC Trusted Publishing
|
|
46
|
+
contents: read
|
|
47
|
+
steps:
|
|
48
|
+
- uses: actions/checkout@v7
|
|
49
|
+
- uses: astral-sh/setup-uv@v8.2.0
|
|
50
|
+
- run: uv sync --all-extras
|
|
51
|
+
- name: Verify tag matches package version
|
|
52
|
+
env:
|
|
53
|
+
TAG_NAME: ${{ needs.release-please.outputs.tag_name }}
|
|
54
|
+
run: |
|
|
55
|
+
TAG="${TAG_NAME#v}"
|
|
56
|
+
PKG=$(uv run python -c "import repro_lambda; print(repro_lambda.__version__)")
|
|
57
|
+
if [[ "$TAG" != "$PKG" ]]; then
|
|
58
|
+
echo "tag $TAG != package version $PKG" >&2
|
|
59
|
+
exit 1
|
|
60
|
+
fi
|
|
61
|
+
- name: Build sdist + wheel
|
|
62
|
+
run: uv build
|
|
63
|
+
- name: Publish to PyPI
|
|
64
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
65
|
+
with:
|
|
66
|
+
packages-dir: dist/
|
|
67
|
+
|
|
68
|
+
major-tag:
|
|
69
|
+
needs: release-please
|
|
70
|
+
if: ${{ needs.release-please.outputs.release_created == 'true' }}
|
|
71
|
+
runs-on: ubuntu-24.04
|
|
72
|
+
permissions:
|
|
73
|
+
contents: write
|
|
74
|
+
steps:
|
|
75
|
+
- uses: actions/checkout@v7
|
|
76
|
+
with:
|
|
77
|
+
fetch-depth: 0
|
|
78
|
+
fetch-tags: true
|
|
79
|
+
- name: Force-move sliding major tag
|
|
80
|
+
env:
|
|
81
|
+
TAG: ${{ needs.release-please.outputs.tag_name }}
|
|
82
|
+
run: |
|
|
83
|
+
MAJOR="${TAG%%.*}" # v0.4.1 -> v0
|
|
84
|
+
git tag -f "$MAJOR" "$TAG"
|
|
85
|
+
git push -f origin "refs/tags/$MAJOR"
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.6.1](https://github.com/antonbabenko/repro-lambda/compare/v0.6.0...v0.6.1) (2026-06-22)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **ci:** render the build Step Summary (hashFiles cannot guard a RUNNER_TEMP file) ([#5](https://github.com/antonbabenko/repro-lambda/issues/5)) ([4fadb30](https://github.com/antonbabenko/repro-lambda/commit/4fadb307136f4dffd8c7e6bf5303d79f1a641f5a))
|
|
9
|
+
|
|
3
10
|
## v0.5.2 - 2026-06-21
|
|
4
11
|
|
|
5
12
|
### Fixed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: repro-lambda
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.1
|
|
4
4
|
Summary: Build reproducible AWS Lambda packages outside Terraform, optimized for terraform-aws-lambda by serverless.tf.
|
|
5
5
|
Project-URL: Homepage, https://github.com/antonbabenko/repro-lambda
|
|
6
6
|
Project-URL: Repository, https://github.com/antonbabenko/repro-lambda
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
|
|
3
|
+
"packages": {
|
|
4
|
+
".": {
|
|
5
|
+
"release-type": "python",
|
|
6
|
+
"package-name": "repro-lambda",
|
|
7
|
+
"include-component-in-tag": false,
|
|
8
|
+
"bump-minor-pre-major": true,
|
|
9
|
+
"bump-patch-for-minor-pre-major": false,
|
|
10
|
+
"changelog-path": "CHANGELOG.md",
|
|
11
|
+
"extra-files": [
|
|
12
|
+
"src/repro_lambda/__init__.py"
|
|
13
|
+
],
|
|
14
|
+
"changelog-sections": [
|
|
15
|
+
{ "type": "feat", "section": "Features" },
|
|
16
|
+
{ "type": "fix", "section": "Bug Fixes" },
|
|
17
|
+
{ "type": "perf", "section": "Performance Improvements" },
|
|
18
|
+
{ "type": "refactor", "section": "Code Refactoring" },
|
|
19
|
+
{ "type": "docs", "section": "Documentation" },
|
|
20
|
+
{ "type": "test", "section": "Tests", "hidden": true },
|
|
21
|
+
{ "type": "chore", "section": "Miscellaneous", "hidden": true },
|
|
22
|
+
{ "type": "ci", "section": "Continuous Integration", "hidden": true }
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -70,6 +70,13 @@ def build(
|
|
|
70
70
|
"--arch", help="Only build lambdas with this arch (e.g. arm64, x86_64). Empty = all."
|
|
71
71
|
),
|
|
72
72
|
] = "",
|
|
73
|
+
json_out: Annotated[
|
|
74
|
+
Path | None,
|
|
75
|
+
typer.Option(
|
|
76
|
+
"--json-out",
|
|
77
|
+
help="Also write the build summary JSON array to this file (for CI Step Summary).",
|
|
78
|
+
),
|
|
79
|
+
] = None,
|
|
73
80
|
) -> None:
|
|
74
81
|
"""Build one lambda (or all) per manifest and upload to S3."""
|
|
75
82
|
import json
|
|
@@ -170,6 +177,9 @@ def build(
|
|
|
170
177
|
if not dry_run:
|
|
171
178
|
catalog.save(catalog_path)
|
|
172
179
|
|
|
180
|
+
if json_out is not None:
|
|
181
|
+
json_out.write_text(json.dumps(summary, indent=2))
|
|
182
|
+
|
|
173
183
|
typer.echo(json.dumps(summary, indent=2))
|
|
174
184
|
|
|
175
185
|
|
|
@@ -196,14 +206,25 @@ def promote(
|
|
|
196
206
|
help="Destination (prod) base bucket; us-east-1 variant auto-derived.",
|
|
197
207
|
),
|
|
198
208
|
] = "",
|
|
209
|
+
sha: Annotated[
|
|
210
|
+
str,
|
|
211
|
+
typer.Option(
|
|
212
|
+
"--sha",
|
|
213
|
+
help="Promote this exact 64-hex content sha instead of the catalog's current. "
|
|
214
|
+
"Requires a single lambda target (not 'all').",
|
|
215
|
+
),
|
|
216
|
+
] = "",
|
|
199
217
|
dry_run: Annotated[bool, typer.Option("--dry-run")] = False,
|
|
200
218
|
) -> None:
|
|
201
219
|
"""Copy built artifacts dev -> prod by content hash (no rebuild).
|
|
202
220
|
|
|
203
|
-
|
|
204
|
-
is exactly the one the source commit built and tested in dev.
|
|
221
|
+
By default the sha per lambda is read from builds/catalog.json, so the artifact
|
|
222
|
+
promoted is exactly the one the source commit built and tested in dev. Pass
|
|
223
|
+
``--sha`` to promote one explicit content sha (e.g. copy-pasted from a build run's
|
|
224
|
+
Step Summary), bypassing the catalog.
|
|
205
225
|
"""
|
|
206
226
|
import json
|
|
227
|
+
import re
|
|
207
228
|
|
|
208
229
|
from repro_lambda.manifest import load_manifest
|
|
209
230
|
from repro_lambda.promote import (
|
|
@@ -225,6 +246,14 @@ def promote(
|
|
|
225
246
|
typer.echo(f"no lambda named {target!r} in {manifest}", err=True)
|
|
226
247
|
raise typer.Exit(2)
|
|
227
248
|
|
|
249
|
+
if sha:
|
|
250
|
+
if target == "all" or len(selected) != 1:
|
|
251
|
+
typer.echo("--sha requires a single lambda target (not 'all')", err=True)
|
|
252
|
+
raise typer.Exit(2)
|
|
253
|
+
if not re.fullmatch(r"[a-f0-9]{64}", sha):
|
|
254
|
+
typer.echo(f"--sha must be 64 lowercase hex chars, got {sha!r}", err=True)
|
|
255
|
+
raise typer.Exit(2)
|
|
256
|
+
|
|
228
257
|
if not dry_run and (not dev_bucket or not prod_bucket):
|
|
229
258
|
typer.echo(
|
|
230
259
|
"--dev-bucket and --prod-bucket (or REPRO_LAMBDA_DEV_BUCKET / "
|
|
@@ -237,10 +266,10 @@ def promote(
|
|
|
237
266
|
summary = []
|
|
238
267
|
for spec in selected:
|
|
239
268
|
try:
|
|
240
|
-
|
|
269
|
+
resolved_sha = sha or sha_from_catalog(catalog_path, spec.logical_name)
|
|
241
270
|
outcome = promote_one(
|
|
242
271
|
spec=spec,
|
|
243
|
-
sha=
|
|
272
|
+
sha=resolved_sha,
|
|
244
273
|
dev_bucket=dev_bucket,
|
|
245
274
|
prod_bucket=prod_bucket,
|
|
246
275
|
dry_run=dry_run,
|
|
@@ -226,3 +226,74 @@ def test_cli_promote_unknown_target_exits_2(tmp_path: Path):
|
|
|
226
226
|
],
|
|
227
227
|
)
|
|
228
228
|
assert result.exit_code == 2
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
_EXPLICIT_SHA = "a" * 64 # 64-hex, deliberately NOT the catalog's "cafef00d"
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def test_cli_promote_explicit_sha_bypasses_catalog(tmp_path: Path):
|
|
235
|
+
repo = _consumer(tmp_path)
|
|
236
|
+
with mock_aws():
|
|
237
|
+
c = boto3.client("s3", region_name="eu-west-1")
|
|
238
|
+
_make_bucket(c, DEV, "eu-west-1")
|
|
239
|
+
_make_bucket(c, PROD, "eu-west-1")
|
|
240
|
+
# Only the explicit-sha object exists; the catalog's current sha does not.
|
|
241
|
+
c.put_object(Bucket=DEV, Key=f"lambdas/app/{_EXPLICIT_SHA}.zip", Body=b"z")
|
|
242
|
+
|
|
243
|
+
result = runner.invoke(
|
|
244
|
+
app,
|
|
245
|
+
[
|
|
246
|
+
"promote",
|
|
247
|
+
"app",
|
|
248
|
+
"--manifest",
|
|
249
|
+
str(repo / "lambdas.toml"),
|
|
250
|
+
"--dev-bucket",
|
|
251
|
+
DEV,
|
|
252
|
+
"--prod-bucket",
|
|
253
|
+
PROD,
|
|
254
|
+
"--sha",
|
|
255
|
+
_EXPLICIT_SHA,
|
|
256
|
+
],
|
|
257
|
+
)
|
|
258
|
+
assert result.exit_code == 0, result.stdout
|
|
259
|
+
assert c.head_object(Bucket=PROD, Key=f"lambdas/app/{_EXPLICIT_SHA}.zip")
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def test_cli_promote_sha_rejects_all_target(tmp_path: Path):
|
|
263
|
+
repo = _consumer(tmp_path)
|
|
264
|
+
result = runner.invoke(
|
|
265
|
+
app,
|
|
266
|
+
[
|
|
267
|
+
"promote",
|
|
268
|
+
"all",
|
|
269
|
+
"--manifest",
|
|
270
|
+
str(repo / "lambdas.toml"),
|
|
271
|
+
"--dev-bucket",
|
|
272
|
+
DEV,
|
|
273
|
+
"--prod-bucket",
|
|
274
|
+
PROD,
|
|
275
|
+
"--sha",
|
|
276
|
+
_EXPLICIT_SHA,
|
|
277
|
+
],
|
|
278
|
+
)
|
|
279
|
+
assert result.exit_code == 2 # --sha forbidden with 'all'
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def test_cli_promote_sha_invalid_hex_exits_2(tmp_path: Path):
|
|
283
|
+
repo = _consumer(tmp_path)
|
|
284
|
+
result = runner.invoke(
|
|
285
|
+
app,
|
|
286
|
+
[
|
|
287
|
+
"promote",
|
|
288
|
+
"app",
|
|
289
|
+
"--manifest",
|
|
290
|
+
str(repo / "lambdas.toml"),
|
|
291
|
+
"--dev-bucket",
|
|
292
|
+
DEV,
|
|
293
|
+
"--prod-bucket",
|
|
294
|
+
PROD,
|
|
295
|
+
"--sha",
|
|
296
|
+
"not-a-valid-sha",
|
|
297
|
+
],
|
|
298
|
+
)
|
|
299
|
+
assert result.exit_code == 2 # not 64-hex
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{repro_lambda-0.5.2 → repro_lambda-0.6.1}/tests/fixtures/sample_nodejs_lambda/handler/index.js
RENAMED
|
File without changes
|
|
File without changes
|
{repro_lambda-0.5.2 → repro_lambda-0.6.1}/tests/fixtures/sample_nodejs_lambda/handler/package.json
RENAMED
|
File without changes
|
|
File without changes
|
{repro_lambda-0.5.2 → repro_lambda-0.6.1}/tests/fixtures/sample_python_lambda/handler/app.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|