repro-lambda 0.7.2__tar.gz → 0.8.0__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.
Files changed (82) hide show
  1. repro_lambda-0.8.0/.release-please-manifest.json +3 -0
  2. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/CHANGELOG.md +11 -0
  3. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/PKG-INFO +2 -2
  4. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/USAGE.md +12 -7
  5. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/pyproject.toml +1 -1
  6. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/src/repro_lambda/__init__.py +1 -1
  7. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/src/repro_lambda/manifest.py +21 -5
  8. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/src/repro_lambda/source_locker.py +51 -6
  9. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/tests/test_source_locker.py +44 -8
  10. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/tests/test_sources_schema.py +34 -2
  11. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/uv.lock +1 -1
  12. repro_lambda-0.7.2/.release-please-manifest.json +0 -3
  13. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/.github/workflows/build.yml +0 -0
  14. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/.github/workflows/ci.yml +0 -0
  15. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/.github/workflows/move-major-tag.yml +0 -0
  16. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/.github/workflows/promote.yml +0 -0
  17. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/.github/workflows/publish.yml +0 -0
  18. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/.github/workflows/release.yml +0 -0
  19. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/.gitignore +0 -0
  20. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/.pre-commit-config.yaml +0 -0
  21. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/LICENSE +0 -0
  22. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/README.md +0 -0
  23. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/SETUP.md +0 -0
  24. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/assets/social-preview.html +0 -0
  25. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/assets/social-preview.png +0 -0
  26. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/examples/complete/README.md +0 -0
  27. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/examples/complete/builds/catalog.json +0 -0
  28. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/examples/complete/lambdas.toml +0 -0
  29. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/examples/complete/main.tf +0 -0
  30. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/examples/complete/outputs.tf +0 -0
  31. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/examples/complete/src/app/app.py +0 -0
  32. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/examples/complete/src/app/requirements.arm64.lock +0 -0
  33. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/examples/complete/variables.tf +0 -0
  34. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/examples/complete/versions.tf +0 -0
  35. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/release-please-config.json +0 -0
  36. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/src/repro_lambda/__main__.py +0 -0
  37. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/src/repro_lambda/build.py +0 -0
  38. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/src/repro_lambda/catalog.py +0 -0
  39. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/src/repro_lambda/cli.py +0 -0
  40. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/src/repro_lambda/docker_runner.py +0 -0
  41. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/src/repro_lambda/git_guard.py +0 -0
  42. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/src/repro_lambda/hasher.py +0 -0
  43. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/src/repro_lambda/promote.py +0 -0
  44. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/src/repro_lambda/s3_uploader.py +0 -0
  45. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/src/repro_lambda/source_stager.py +0 -0
  46. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/src/repro_lambda/sources.py +0 -0
  47. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/src/repro_lambda/verify.py +0 -0
  48. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/src/repro_lambda/zip_packager.py +0 -0
  49. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/tests/__init__.py +0 -0
  50. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/tests/conftest.py +0 -0
  51. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/tests/fixtures/sample_nodejs_lambda/handler/index.js +0 -0
  52. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/tests/fixtures/sample_nodejs_lambda/handler/package-lock.json +0 -0
  53. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/tests/fixtures/sample_nodejs_lambda/handler/package.json +0 -0
  54. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/tests/fixtures/sample_nodejs_lambda/lambdas.toml +0 -0
  55. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/tests/fixtures/sample_python_lambda/handler/app.py +0 -0
  56. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/tests/fixtures/sample_python_lambda/handler/requirements.arm64.lock +0 -0
  57. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/tests/fixtures/sample_python_lambda/handler/requirements.in +0 -0
  58. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/tests/fixtures/sample_python_lambda/lambdas.toml +0 -0
  59. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/tests/test_build_integration.py +0 -0
  60. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/tests/test_build_nodejs.py +0 -0
  61. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/tests/test_catalog.py +0 -0
  62. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/tests/test_cli_build.py +0 -0
  63. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/tests/test_cli_lock.py +0 -0
  64. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/tests/test_cli_smoke.py +0 -0
  65. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/tests/test_docker_runner.py +0 -0
  66. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/tests/test_docker_runner_nodejs.py +0 -0
  67. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/tests/test_e2e_nodejs_lambda.py +0 -0
  68. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/tests/test_e2e_python_lambda.py +0 -0
  69. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/tests/test_extra_files.py +0 -0
  70. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/tests/test_git_guard.py +0 -0
  71. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/tests/test_hasher.py +0 -0
  72. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/tests/test_manifest.py +0 -0
  73. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/tests/test_per_lambda_builder.py +0 -0
  74. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/tests/test_promote.py +0 -0
  75. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/tests/test_python_byte_compat_regression.py +0 -0
  76. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/tests/test_s3_uploader.py +0 -0
  77. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/tests/test_source_stager.py +0 -0
  78. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/tests/test_sources.py +0 -0
  79. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/tests/test_sources_hash.py +0 -0
  80. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/tests/test_verify.py +0 -0
  81. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/tests/test_zip_excludes.py +0 -0
  82. {repro_lambda-0.7.2 → repro_lambda-0.8.0}/tests/test_zip_packager.py +0 -0
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "0.8.0"
3
+ }
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.8.0](https://github.com/antonbabenko/repro-lambda/compare/v0.7.2...v0.8.0) (2026-09-18)
4
+
5
+
6
+ ### ⚠ BREAKING CHANGES
7
+
8
+ * **lock:** `version_from` defaults to `format = "mise"`. A manifest that reads a `.tool-versions` file must now declare `format = "asdf"`.
9
+
10
+ ### Features
11
+
12
+ * **lock:** read version_from pins from mise.toml by default ([#15](https://github.com/antonbabenko/repro-lambda/issues/15)) ([a9258af](https://github.com/antonbabenko/repro-lambda/commit/a9258af92fa29227b3c040cc8d7867e8739c3648))
13
+
3
14
  ## [0.7.2](https://github.com/antonbabenko/repro-lambda/compare/v0.7.1...v0.7.2) (2026-06-23)
4
15
 
5
16
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: repro-lambda
3
- Version: 0.7.2
3
+ Version: 0.8.0
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
@@ -119,7 +119,7 @@ member = "vendor-{version}" # map the versioned top dir to dest
119
119
  dest = "vendor"
120
120
  version = "1.4.0" # bump this one line; lock re-pins everything
121
121
 
122
- # A public binary whose version is derived from the vendor release's .tool-versions.
122
+ # A public binary whose version is derived from the vendor release's mise.toml.
123
123
  [[lambda.source]]
124
124
  name = "terraform"
125
125
  type = "https"
@@ -131,18 +131,23 @@ dest = "bin/terraform"
131
131
  executable = true
132
132
  version = "1.9.0"
133
133
  [lambda.source.version_from]
134
- source = "vendor" # read from the vendor source's extracted tree
135
- file = ".tool-versions" # relative to its member-stripped root
136
- key = "terraform"
134
+ source = "vendor" # read from the vendor source's extracted tree
135
+ file = "mise.toml" # relative to its member-stripped root
136
+ key = "terraform" # full tool key or its short name
137
+ # format = "asdf" # optional; default "mise"
137
138
  ```
138
139
 
139
140
  - **Pinning.** `sha256` is verified before the archive is opened. `extract` is
140
141
  `zip` / `tar.gz` / `none`. `member` extracts one file or a directory subtree to
141
142
  `dest`; omit it to extract the whole archive under `dest`. Source names are
142
143
  unique per lambda and dests may not overlap each other or the staged source.
143
- - **`version_from`** (single-level) derives a source's `version` from an asdf-style
144
- `key value` line in another source's file, so bumping the root `version`
145
- cascades to dependents. It is a lock input - it never affects the artifact hash.
144
+ - **`version_from`** (single-level) derives a source's `version` from a pin file in
145
+ another source's extracted tree, so bumping the root `version` cascades to
146
+ dependents. It is a lock input - it never affects the artifact hash. `format`
147
+ picks the reader: the default `mise` parses a mise config's `[tools]` table and
148
+ matches `key` against the full tool key or its short name
149
+ (`aqua:hashicorp/terraform` resolves as `terraform`); `asdf` reads a
150
+ `key value` line from a `.tool-versions` file.
146
151
  - **`repro-lambda lock`** re-resolves `version_from`, re-downloads, recomputes each
147
152
  `sha256`, and rewrites this file (comment-preserving, atomic, idempotent). Run it
148
153
  after bumping a `version`. Pass `REPRO_LAMBDA_SOURCES_TOKEN` for private
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "repro-lambda"
3
- version = "0.7.2"
3
+ version = "0.8.0"
4
4
  description = "Build reproducible AWS Lambda packages outside Terraform, optimized for terraform-aws-lambda by serverless.tf."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -1,3 +1,3 @@
1
1
  """repro-lambda — reproducible AWS Lambda packaging outside Terraform."""
2
2
 
3
- __version__ = "0.7.2" # x-release-please-version
3
+ __version__ = "0.8.0" # x-release-please-version
@@ -38,6 +38,7 @@ class ExtraFile:
38
38
 
39
39
  SUPPORTED_SOURCE_TYPES = {"github_release", "https"}
40
40
  SUPPORTED_EXTRACT = {"zip", "tar.gz", "none"}
41
+ SUPPORTED_VERSION_FORMATS = {"mise", "asdf"}
41
42
 
42
43
 
43
44
  @dataclass(frozen=True)
@@ -45,15 +46,21 @@ class VersionFrom:
45
46
  """Lock-time version resolution rule for a source (never hashed).
46
47
 
47
48
  At `lock` time the referenced source (`source`, by name) is fetched + extracted,
48
- the asdf-style `<key> <value>` line is read from `file` (relative to that source's
49
- extracted tree), and the value re-pins this source's `version`. `build` never
50
- resolves this - it substitutes the already-locked `version` into the url/tag/asset
51
- templates. Single-level only: the referenced source may not itself use version_from.
49
+ `file` is read from that source's extracted tree, and the version it gives for
50
+ `key` re-pins this source's `version`. `build` never resolves this - it substitutes
51
+ the already-locked `version` into the url/tag/asset templates. Single-level only:
52
+ the referenced source may not itself use version_from.
53
+
54
+ `format` selects the reader. The default `mise` parses a mise config's `[tools]`
55
+ table, matching `key` against either the full tool key or its short name
56
+ ("aqua:hashicorp/terraform" -> "terraform"). `asdf` reads a `<key> <value>` line
57
+ from a .tool-versions file.
52
58
  """
53
59
 
54
60
  source: str
55
61
  file: str
56
62
  key: str
63
+ format: str = "mise"
57
64
 
58
65
 
59
66
  @dataclass(frozen=True)
@@ -323,8 +330,17 @@ def _parse_sources(path: Path, entry: dict) -> tuple[Source, ...]:
323
330
  f"{path}: source {name!r} version_from.source cannot reference itself"
324
331
  )
325
332
  _validate_relpath(path, "version_from.file", vf_raw["file"], where=f"source {name!r}")
333
+ vf_format = vf_raw.get("format", "mise")
334
+ if vf_format not in SUPPORTED_VERSION_FORMATS:
335
+ raise ValueError(
336
+ f"{path}: source {name!r} version_from.format={vf_format!r} is not one of "
337
+ f"{sorted(SUPPORTED_VERSION_FORMATS)}"
338
+ )
326
339
  version_from = VersionFrom(
327
- source=vf_raw["source"], file=vf_raw["file"], key=vf_raw["key"]
340
+ source=vf_raw["source"],
341
+ file=vf_raw["file"],
342
+ key=vf_raw["key"],
343
+ format=vf_format,
328
344
  )
329
345
 
330
346
  uses_template = any("{version}" in v for v in (url, tag, asset, member or ""))
@@ -10,6 +10,7 @@ from __future__ import annotations
10
10
 
11
11
  import os
12
12
  import tempfile
13
+ import tomllib
13
14
  from dataclasses import dataclass, replace
14
15
  from pathlib import Path
15
16
 
@@ -22,7 +23,7 @@ from repro_lambda.sources import (
22
23
  extract_to_temp,
23
24
  )
24
25
 
25
- _MAX_VERSION_FILE_BYTES = 1024 * 1024 # an asdf .tool-versions is tiny; cap to be safe
26
+ _MAX_VERSION_FILE_BYTES = 1024 * 1024 # a pin file is tiny; cap to be safe
26
27
 
27
28
 
28
29
  @dataclass
@@ -35,13 +36,18 @@ class SourcePin:
35
36
  changed: bool
36
37
 
37
38
 
38
- def _read_asdf_version(path: Path, key: str) -> str:
39
- """Read `<key> <value>` from an asdf-style file (e.g. .tool-versions)."""
39
+ def _read_pin_file(path: Path) -> str:
40
+ """Read a version_from pin file, with the guards both readers share."""
40
41
  if not path.is_file():
41
42
  raise SourceFetchError(f"version_from file not found: {path}")
42
43
  if path.stat().st_size > _MAX_VERSION_FILE_BYTES:
43
44
  raise SourceFetchError(f"version_from file too large: {path}")
44
- for raw in path.read_text(encoding="utf-8").splitlines():
45
+ return path.read_text(encoding="utf-8")
46
+
47
+
48
+ def _read_asdf_version(path: Path, key: str) -> str:
49
+ """Read `<key> <value>` from an asdf-style file (e.g. .tool-versions)."""
50
+ for raw in _read_pin_file(path).splitlines():
45
51
  line = raw.strip()
46
52
  if not line or line.startswith("#"):
47
53
  continue
@@ -51,6 +57,44 @@ def _read_asdf_version(path: Path, key: str) -> str:
51
57
  raise SourceFetchError(f"key {key!r} not found in {path.name}")
52
58
 
53
59
 
60
+ def _mise_version(spec: object) -> str:
61
+ """The pinned version of one `[tools]` entry, or "" when it pins none.
62
+
63
+ mise accepts a bare string, a table/inline table with `version`, or a list of
64
+ versions (first wins). An entry with only e.g. `bin_path` pins nothing.
65
+ """
66
+ if isinstance(spec, str):
67
+ return spec
68
+ if isinstance(spec, dict):
69
+ version = spec.get("version")
70
+ return version if isinstance(version, str) else ""
71
+ if isinstance(spec, list):
72
+ return next((v for v in spec if isinstance(v, str)), "")
73
+ return ""
74
+
75
+
76
+ def _read_mise_version(path: Path, key: str) -> str:
77
+ """Read a tool version from a mise config's `[tools]` table.
78
+
79
+ `key` matches either the full tool key or its short name, so
80
+ "aqua:hashicorp/terraform" and "http:hcledit" both resolve as "terraform"/"hcledit".
81
+ """
82
+ try:
83
+ tools = tomllib.loads(_read_pin_file(path)).get("tools", {})
84
+ except tomllib.TOMLDecodeError as exc:
85
+ raise SourceFetchError(f"version_from file is not valid TOML: {path} ({exc})") from exc
86
+ for raw_key, spec in tools.items():
87
+ if key not in (raw_key, raw_key.split(":", 1)[-1].split("/")[-1]):
88
+ continue
89
+ version = _mise_version(spec)
90
+ if version:
91
+ return version
92
+ raise SourceFetchError(f"key {key!r} not found in {path.name}")
93
+
94
+
95
+ _VERSION_READERS = {"mise": _read_mise_version, "asdf": _read_asdf_version}
96
+
97
+
54
98
  def _ordered(sources: tuple[Source, ...]) -> list[Source]:
55
99
  """Non-version_from sources first (the referenced roots), then dependents.
56
100
 
@@ -80,12 +124,13 @@ def _lock_lambda_sources(
80
124
  f"(it must declare an archive extract)"
81
125
  )
82
126
  # Read relative to the referenced source's member-stripped tree, so file= can
83
- # be e.g. ".tool-versions" rather than the version-dependent "pofix-9.9/...".
127
+ # be e.g. "mise.toml" rather than the version-dependent "pofix-9.9/...".
84
128
  base = extracted[ref]
85
129
  ref_member = src_by_name[ref].resolved_member
86
130
  if ref_member:
87
131
  base = base / ref_member
88
- version = _read_asdf_version(base / src.version_from.file, src.version_from.key)
132
+ read = _VERSION_READERS[src.version_from.format]
133
+ version = read(base / src.version_from.file, src.version_from.key)
89
134
  resolved = replace(src, version=version)
90
135
 
91
136
  raw = tmp / f"lock-{src.name}"
@@ -6,9 +6,12 @@ import tarfile
6
6
  import zipfile
7
7
  from pathlib import Path
8
8
 
9
+ import pytest
10
+
9
11
  from repro_lambda import source_locker
10
12
  from repro_lambda.manifest import load_manifest
11
13
  from repro_lambda.source_locker import lock_sources
14
+ from repro_lambda.sources import SourceFetchError
12
15
 
13
16
  OLD_SHA = "0" * 64
14
17
 
@@ -29,7 +32,7 @@ def _zip(path: Path, entries: list[tuple[str, bytes]]) -> bytes:
29
32
  return path.read_bytes()
30
33
 
31
34
 
32
- def _manifest(tmp_path: Path) -> Path:
35
+ def _manifest(tmp_path: Path, pin_file: str = ".tool-versions", pin_format: str = "asdf") -> Path:
33
36
  p = tmp_path / "lambdas.toml"
34
37
  p.write_text(
35
38
  "# keep this comment\n"
@@ -59,10 +62,11 @@ def _manifest(tmp_path: Path) -> Path:
59
62
  'extract = "zip"\n'
60
63
  'member = "terraform"\n'
61
64
  'dest = "bin/terraform"\n'
62
- 'version = "0.0.0"\n' # stale; lock derives it from pofix's .tool-versions
65
+ 'version = "0.0.0"\n' # stale; lock derives it from the pofix pin file
63
66
  "[lambda.source.version_from]\n"
64
67
  'source = "pofix"\n'
65
- 'file = ".tool-versions"\n'
68
+ f'file = "{pin_file}"\n'
69
+ f'format = "{pin_format}"\n'
66
70
  'key = "terraform"\n'
67
71
  "\n[builder]\n"
68
72
  f'base_image_python = "public.ecr.aws/lambda/python:3.13@sha256:{"0" * 64}"\n'
@@ -70,11 +74,24 @@ def _manifest(tmp_path: Path) -> Path:
70
74
  return p
71
75
 
72
76
 
73
- def _fixtures(tmp_path: Path) -> dict[str, bytes]:
74
- pofix = _targz(
75
- tmp_path / "pofix.tgz",
76
- [("pofix-9.9/.tool-versions", b"terraform 1.9.0\nhcledit 0.2.17\n")],
77
- )
77
+ ASDF_PINS = b"terraform 1.9.0\nhcledit 0.2.17\n"
78
+
79
+ # A string pin, a table pin, and an entry that pins no version at all.
80
+ MISE_PINS = b"""[tools]
81
+ "aqua:hashicorp/terraform" = "1.9.0"
82
+
83
+ [tools."http:hcledit"]
84
+ version = "0.2.17"
85
+
86
+ [tools."http:nopin"]
87
+ bin_path = "bin"
88
+ """
89
+
90
+
91
+ def _fixtures(
92
+ tmp_path: Path, pin_name: str = ".tool-versions", pin_body: bytes = ASDF_PINS
93
+ ) -> dict[str, bytes]:
94
+ pofix = _targz(tmp_path / "pofix.tgz", [(f"pofix-9.9/{pin_name}", pin_body)])
78
95
  tf = _zip(tmp_path / "tf.zip", [("terraform", b"TFBINARY")])
79
96
  return {"pofix": pofix, "terraform": tf}
80
97
 
@@ -135,3 +152,22 @@ def test_lock_no_sources_returns_false(tmp_path):
135
152
  f'base_image_python = "public.ecr.aws/lambda/python:3.13@sha256:{"0" * 64}"\n'
136
153
  )
137
154
  assert lock_sources(p, None) is False
155
+
156
+
157
+ def test_lock_resolves_version_from_mise(tmp_path, monkeypatch):
158
+ manifest = _manifest(tmp_path, pin_file="mise.toml", pin_format="mise")
159
+ _install_fake_download(monkeypatch, _fixtures(tmp_path, "mise.toml", MISE_PINS))
160
+
161
+ assert lock_sources(manifest, None) is True
162
+
163
+ by_name = {s.name: s for s in load_manifest(manifest).lambdas[0].sources}
164
+ assert by_name["terraform"].version == "1.9.0" # short name of "aqua:hashicorp/terraform"
165
+
166
+
167
+ def test_lock_mise_missing_key_raises(tmp_path, monkeypatch):
168
+ manifest = _manifest(tmp_path, pin_file="mise.toml", pin_format="mise")
169
+ pins = MISE_PINS.replace(b'"aqua:hashicorp/terraform" = "1.9.0"', b"")
170
+ _install_fake_download(monkeypatch, _fixtures(tmp_path, "mise.toml", pins))
171
+
172
+ with pytest.raises(SourceFetchError, match="key 'terraform' not found"):
173
+ lock_sources(manifest, None)
@@ -92,13 +92,45 @@ def test_parse_version_from_and_substitution(tmp_path: Path):
92
92
  'url="https://releases.example.com/terraform_{version}_linux_arm64.zip"\n'
93
93
  f'sha256="{SHA2}"\nextract="zip"\nmember="terraform"\ndest="bin/terraform"\n'
94
94
  'version="1.9.0"\n'
95
- '[lambda.source.version_from]\nsource="pofix"\nfile=".tool-versions"\nkey="terraform"\n',
95
+ '[lambda.source.version_from]\nsource="pofix"\nfile="mise.toml"\nkey="terraform"\n',
96
96
  )
97
97
  tf = sources[1]
98
- assert tf.version_from == VersionFrom(source="pofix", file=".tool-versions", key="terraform")
98
+ # format defaults to mise.
99
+ assert tf.version_from == VersionFrom(source="pofix", file="mise.toml", key="terraform")
100
+ assert tf.version_from.format == "mise"
99
101
  assert tf.resolved_url == "https://releases.example.com/terraform_1.9.0_linux_arm64.zip"
100
102
 
101
103
 
104
+ def test_version_from_asdf_format_opt_in(tmp_path: Path):
105
+ sources = _load(
106
+ tmp_path,
107
+ "[[lambda.source]]\n"
108
+ 'name="pofix"\ntype="github_release"\nrepo="o/pofix"\ntag="pofix-v0.10.0"\n'
109
+ f'asset="pofix-0.10.0.tar.gz"\nsha256="{SHA}"\nextract="tar.gz"\ndest="pofix"\n'
110
+ "[[lambda.source]]\n"
111
+ 'name="tf"\ntype="https"\nurl="https://releases.example.com/tf.zip"\n'
112
+ f'sha256="{SHA2}"\nextract="zip"\nmember="terraform"\ndest="bin/terraform"\n'
113
+ "[lambda.source.version_from]\n"
114
+ 'source="pofix"\nfile=".tool-versions"\nformat="asdf"\nkey="terraform"\n',
115
+ )
116
+ assert sources[1].version_from.format == "asdf"
117
+
118
+
119
+ def test_version_from_rejects_unknown_format(tmp_path: Path):
120
+ with pytest.raises(ValueError, match="version_from.format"):
121
+ _load(
122
+ tmp_path,
123
+ "[[lambda.source]]\n"
124
+ 'name="pofix"\ntype="github_release"\nrepo="o/pofix"\ntag="pofix-v0.10.0"\n'
125
+ f'asset="pofix-0.10.0.tar.gz"\nsha256="{SHA}"\nextract="tar.gz"\ndest="pofix"\n'
126
+ "[[lambda.source]]\n"
127
+ 'name="tf"\ntype="https"\nurl="https://releases.example.com/tf.zip"\n'
128
+ f'sha256="{SHA2}"\nextract="zip"\nmember="terraform"\ndest="bin/terraform"\n'
129
+ "[lambda.source.version_from]\n"
130
+ 'source="pofix"\nfile="tool.json"\nformat="json"\nkey="terraform"\n',
131
+ )
132
+
133
+
102
134
  def test_sources_default_empty(tmp_path: Path):
103
135
  assert _load(tmp_path, "") == ()
104
136
 
@@ -646,7 +646,7 @@ wheels = [
646
646
 
647
647
  [[package]]
648
648
  name = "repro-lambda"
649
- version = "0.7.2"
649
+ version = "0.8.0"
650
650
  source = { editable = "." }
651
651
  dependencies = [
652
652
  { name = "boto3" },
@@ -1,3 +0,0 @@
1
- {
2
- ".": "0.7.2"
3
- }
File without changes
File without changes
File without changes
File without changes