1
0
Fork 0
mirror of https://github.com/imjasonh/terraform-playground synced 2026-07-08 07:44:57 +00:00
Commit graph

39 commits

Author SHA1 Message Date
Cursor Agent
80ff593fc9
wheel: conformance test vs uv pip install (oracle) — same site-packages paths + console scripts, no RCE (wheel install runs no code)
Co-authored-by: Jason Hall <imjasonh@users.noreply.github.com>
2026-06-11 02:37:49 +00:00
Cursor Agent
23503b5534
lock: delegate uv.lock resolution to uv export (closure/extras/groups/workspace), evaluate emitted markers per target, attach URLs from lock; built-in walker kept as fallback when uv/pyproject absent
Co-authored-by: Jason Hall <imjasonh@users.noreply.github.com>
2026-06-11 02:35:24 +00:00
Cursor Agent
5267cc9cab
docs: AI/GPU image comparison — CUDA ships in wheels (~98% of image); one-dep bump re-uploads ~11MB vs ~2.9GB for a uv sync Dockerfile
Co-authored-by: Jason Hall <imjasonh@users.noreply.github.com>
2026-06-11 02:17:01 +00:00
Cursor Agent
0cc8bc424f
address Copilot review: normalize SBOM names, reject wheel '..' segments, Windows-safe cache/wheel rename, fix lock-not-found message
- sbom: normalize component names (PEP 503) for canonical pkg:pypi PURLs and stable ordering
- wheel: reject any member with a '..' path segment (catches escapes that resolve back under the prefix but out of site-packages)
- cache/wheelhouse: replace file atomically across platforms (os.Rename fails on existing dest on Windows)
- project: include requirements.lock in the no-lock-found error
- tests for each

Co-authored-by: Jason Hall <imjasonh@users.noreply.github.com>
2026-06-11 02:01:37 +00:00
Cursor Agent
971e955c35
wheelhouse: don't build sdists; fail fast and direct to --find-links pre-built wheels
Building sdists runs dependency build code on the host (RCE surface, no
sandbox), is non-hermetic/non-reproducible, needs a toolchain, and is
host-arch-only for compiled packages -- all at odds with pymage's guarantees.
Remove the builder and --build-sdists opt-in; keep sdist parsing only so the
error can explain the package is sdist-only and point at the --find-links
pre-build workflow.

Co-authored-by: Jason Hall <imjasonh@users.noreply.github.com>
2026-06-11 01:50:41 +00:00
Cursor Agent
bf565be0ce
docs: document sdists as opt-in with security (host RCE) and single-arch caveats
Co-authored-by: Jason Hall <imjasonh@users.noreply.github.com>
2026-06-11 01:42:12 +00:00
Cursor Agent
872abd1ef7
wheelhouse: gate sdist building behind --build-sdists opt-in (RCE risk, single-arch)
Co-authored-by: Jason Hall <imjasonh@users.noreply.github.com>
2026-06-11 01:42:11 +00:00
Cursor Agent
f0ba6e3f41
wheelhouse: test sdist built-wheel selection and cache path
Co-authored-by: Jason Hall <imjasonh@users.noreply.github.com>
2026-06-11 01:33:41 +00:00
Cursor Agent
5f7012b8c9
docs: document sdist building, --extra/--package, marker eval, base OS-package requirement, and incremental-rebuild comparison
Co-authored-by: Jason Hall <imjasonh@users.noreply.github.com>
2026-06-11 01:32:56 +00:00
Cursor Agent
d9a25a373d
wheelhouse: build wheels from sdists when no compatible wheel is available
Co-authored-by: Jason Hall <imjasonh@users.noreply.github.com>
2026-06-11 01:30:46 +00:00
Cursor Agent
7e8e2261a7
lock: evaluate PEP 508 markers and support --extra/--package in uv.lock closure
Co-authored-by: Jason Hall <imjasonh@users.noreply.github.com>
2026-06-11 01:30:45 +00:00
Cursor Agent
b484b02113
docs: real-world comparison study (uv Dockerfiles vs pymage) + migration gaps
Co-authored-by: Jason Hall <imjasonh@users.noreply.github.com>
2026-06-11 01:05:27 +00:00
Cursor Agent
89e64cb3d2
uv.lock: install only the runtime dependency closure (exclude dev groups)
Previously pymage installed every package in uv.lock, including dev-dependency
groups and the whole resolution universe. On real projects this bloated images
badly: ruff alone was 50% of deps in astral-sh/uv-docker-example, and dev/CI
tools (ruff, mypy, ty, zizmor, ...) were 63% of deps in
fastapi/full-stack-fastapi-template.

ParseUVLockFile now computes the runtime closure from the local/project
packages' dependencies, transitively following dependencies and expanding
requested extras via optional-dependencies, never following dev-dependencies
(matching 'uv sync --no-dev'). Locks with no project package still install
everything. sdist-only errors now only trigger for packages actually in the
closure. Cuts deps 50-63% on the studied projects.

Co-authored-by: Jason Hall <imjasonh@users.noreply.github.com>
2026-06-11 01:05:26 +00:00
Cursor Agent
b721e31fd6
build: annotate dependency layers with their wheels (dev.pymage.wheels)
Each dependency layer descriptor now carries a dev.pymage.wheels annotation
listing the wheel(s) it installs (comma-separated name==version), mirroring the
config-history description and correctly listing all wheels in packed
multi-wheel layers. Adds a test covering per-wheel and packed layers.

Co-authored-by: Jason Hall <imjasonh@users.noreply.github.com>
2026-06-11 00:48:33 +00:00
Cursor Agent
1f0ade9788
cache: make caching opt-out (default on) with --no-cache
Caching (layer blobs, downloaded wheels, base interpreter detection) is now
enabled by default at the per-user cache dir (or --cache-dir / $PYMAGE_CACHE_DIR).
--no-cache disables it: layer/meta caches off and wheels downloaded into an
ephemeral temp dir that's cleaned up after the build.

- setupCaches centralizes cache wiring (one cache.Cache serves layers + meta);
  --no-cache uses a temp wheel dir with cleanup
- DefaultCacheDir honors $PYMAGE_CACHE_DIR; cli tests point it at a temp dir so
  the on-by-default cache doesn't touch the user's cache
- tests: setupCaches (default + --no-cache cleanup); README/config updated

Co-authored-by: Jason Hall <imjasonh@users.noreply.github.com>
2026-06-11 00:42:36 +00:00
Cursor Agent
4100e12b20
perf: speed up no-op builds (resolve base once, cache interpreter detection)
A no-op build's ~2-3s was almost all registry I/O:
- the base index was fetched 3x (BasePlatforms + remote.Image per platform)
- the base's top layer was downloaded once per platform every build to read
  /etc/apko.json for interpreter detection (Chainguard has no PYTHON_VERSION)

Changes:
- build.BaseSet resolves the base reference once and serves per-platform child
  images memoized (index fetched once; verified by TestBaseSet)
- cache the detected interpreter version by base digest in a default per-user
  metadata cache (cache.GetText/PutText), so repeat builds skip the apko layer
  download
- lazy wheel-cache dir creation; wheelhouse.Resolve defaults to the per-user
  cache dir

Tests: TestBaseSet (index fetched once + per-platform memoized), TestCachedInterpreter
(write + cache-hit short-circuit), cache text round-trip.

Co-authored-by: Jason Hall <imjasonh@users.noreply.github.com>
2026-06-11 00:34:16 +00:00
Cursor Agent
a62051afb8
Address Copilot review: workdir-derived PYTHONPATH, wheel cache default, accurate messages
- src-layout PYTHONPATH is now derived from --workdir (project exposes SrcLayout;
  CLI computes <workdir>/src) instead of hard-coding /app/src
- wheelhouse.Resolve defaults to the per-user wheel cache dir so lock-based
  downloads work; cache dir creation is lazy so local-only resolution is hermetic
- --max-layers error message matches the accepted range (>= 0)
- DiscoverLock error lists requirements.lock too
- tests for the workdir-derived PYTHONPATH and python-prefix

Co-authored-by: Jason Hall <imjasonh@users.noreply.github.com>
2026-06-11 00:16:30 +00:00
Cursor Agent
74982d6dc6
Address Copilot review: require python prefix; robust entry_points parsing
- parsePythonTag now rejects tags without the 'python' prefix (e.g. '3.12'),
  which would otherwise produce a bogus .../lib/3.12/site-packages layout
- parseConsoleScripts iterates with strings.Split instead of bufio.Scanner, so
  a long line/large entry_points.txt can't silently truncate the launcher set
- tests for both

Co-authored-by: Jason Hall <imjasonh@users.noreply.github.com>
2026-06-11 00:01:44 +00:00
Cursor Agent
90030346c6
push: use a shared remote.Pusher across tags
Replace per-tag remote.Write/WriteIndex with a single remote.NewPusher reused
for every tag. The pusher caches per-repo upload state, so identical blobs are
checked/uploaded only once even when pushing the same digest to multiple tags
(previously each tag re-HEADed every blob). Still uploads layers/index children
concurrently via WithJobs.

Co-authored-by: Jason Hall <imjasonh@users.noreply.github.com>
2026-06-10 23:58:03 +00:00
Cursor Agent
1df0e423c5
push: set explicit, tunable concurrent layer uploads
go-containerregistry already uploads layers (and index children) concurrently,
but only at its default of 4. Set remote.WithJobs explicitly with an auto
default that scales with CPU count (floor 4), plus a --push-concurrency flag and
[tool.pymage] push-concurrency config to tune it.

Co-authored-by: Jason Hall <imjasonh@users.noreply.github.com>
2026-06-10 23:55:43 +00:00
Cursor Agent
8fa761a6b3
cli/config: --max-layers and --max-wheel-layers (TOML [tool.pymage]); docs
Default --layer-strategy is now 'auto'. Adds max-layers (default 127) and
max-wheel-layers config keys/flags with validation. Documents the layer budget
and reuse-stable bin packing in README and DESIGN.

Co-authored-by: Jason Hall <imjasonh@users.noreply.github.com>
2026-06-10 23:47:57 +00:00
Cursor Agent
68d12457ba
build: budget-aware layer packing (default auto, max 127 layers)
The new default 'auto' strategy keeps one layer per wheel while the total image
layer count (base + deps + app) fits a budget (default 127), and otherwise
bin-packs wheels into the budget by hashing each distribution's normalized name
to a stable bucket. A wheel's bucket depends only on its name, so adding,
removing, or version-bumping one dependency changes only that bucket's layer —
every other layer keeps its digest and is reused.

- MaxLayers / MaxWheelLayers options; base layer count + app layer reserved
- group layers built in parallel and content-addressed in the cache
- tests: packing, stable bucketing, budget from max-layers (base/app reserved),
  clamp when base exceeds cap, and the reuse guarantees (add dep / version bump
  change <= 1 layer), reproducibility

Co-authored-by: Jason Hall <imjasonh@users.noreply.github.com>
2026-06-10 23:47:57 +00:00
Cursor Agent
298f317209
test: make find-links resolution test cross-platform (Windows abs paths)
/abs/wheels isn't absolute on Windows (no drive letter), so it was joined to
the root and the assertion failed. Use a real absolute temp dir and a TOML
literal string so backslashes survive.

Co-authored-by: Jason Hall <imjasonh@users.noreply.github.com>
2026-06-10 21:59:42 +00:00
Cursor Agent
341914e488
fix: runnable entrypoint for app console scripts + keep deps importable
- project: a [project.scripts] entry becomes a direct 'python -c "from mod
  import fn; fn()"' (or 'python -m') invocation instead of the bare script
  name, which pymage never installs as a launcher (fixes 'exec: "example":
  executable file not found')
- build: PYTHONPATH is now additive (site-packages + base + app /app/src) so an
  app PYTHONPATH no longer clobbers the venv site-packages (deps stayed
  unimportable)
- tests for script-target translation and PYTHONPATH accumulation

Co-authored-by: Jason Hall <imjasonh@users.noreply.github.com>
2026-06-10 21:53:36 +00:00
Cursor Agent
bec24b79ca
cli: default --platform to the base image's supported platforms
When no platform is given, inspect the base reference: a multi-arch base index
yields a multi-arch build (filtering out attestation/unknown entries), a
single-arch base yields one image. Explicit --platform still overrides. Adds
build.BasePlatforms with tests + a CLI default-platforms test; documents it.

Co-authored-by: Jason Hall <imjasonh@users.noreply.github.com>
2026-06-10 21:39:40 +00:00
Cursor Agent
389bfca56b
cli: stdout is only the image ref; log blob push/skip to stderr
- output() writes the single by-digest reference (repo@sha256:...) to stdout so
  'docker run "$(pymage build)"' works; per-tag pointers and ggcr's per-blob
  progress (pushed/existing/mounted blob) go to stderr via logs.Progress
- adds TestBuildStdoutIsImageRef asserting the stdout contract and that blob
  logs land on stderr; documents it in the README
- completes and removes todo.txt

Co-authored-by: Jason Hall <imjasonh@users.noreply.github.com>
2026-06-10 21:28:10 +00:00
Jason Hall
d587263759 lots of changes
Signed-off-by: Jason Hall <jason.hall@anysphere.co>
2026-06-10 17:05:36 -04:00
Cursor Agent
d670295074
cli: make --python optional, auto-detect from base when omitted
If --python is given it must match the base's detected Python version; if
omitted, the version is auto-detected (PYTHON_VERSION env, else apko.json) and
used for both wheel selection and the site-packages layout. Errors clearly when
the base version can't be determined and --python is unset. CI now relies on
auto-detection (drops --python). Adds resolveInterpreter tests and asserts the
auto-detected version flows into the image.

Co-authored-by: Jason Hall <imjasonh@users.noreply.github.com>
2026-06-10 16:24:07 +00:00
Cursor Agent
a8e0c59657
build: detect Chainguard base Python version from apko.json
When the base doesn't set PYTHON_VERSION (Chainguard/Wolfi images), fall back to
reading /etc/apko.json from the top-most layer and parsing the python-X.Y
package, so --python validation works for those bases too. Env still takes
precedence. Adds tests for the apko fallback and env precedence.

Co-authored-by: Jason Hall <imjasonh@users.noreply.github.com>
2026-06-10 16:16:20 +00:00
Cursor Agent
f17ec18a0c
build: validate base interpreter against --python to catch floating-tag drift
Reads the base image's advertised PYTHON_VERSION (config env, no layer
download) and fails the build when it doesn't match --python, so a base tag
that slides to a new Python version can't silently produce a broken image.
Documents base-pinning tradeoffs in the README. Adds unit + CLI tests.

Co-authored-by: Jason Hall <imjasonh@users.noreply.github.com>
2026-06-10 16:02:33 +00:00
Cursor Agent
c15f3e350c
cli: build multi-arch image index from multiple --platform values
- --platform is now repeatable/comma-separated; >1 platform builds one image
  per platform and assembles them into an OCI image index (remote.WriteIndex /
  OCI layout). SBOM aggregates wheels across platforms.
- adds TestBuildMultiArchIndex (2-platform index, reproducible) against an
  in-process registry; documents multi-arch in the README

Co-authored-by: Jason Hall <imjasonh@users.noreply.github.com>
2026-06-10 15:45:36 +00:00
Cursor Agent
7290806232
wheel: strip extras from console-script entry points; lock: strip inline comments from first '#'
- console-script targets like 'pkg.mod:main [extra]' no longer leak the
  '[extra]' suffix into the generated launcher; module-less entry points are
  skipped instead of producing invalid Python
- stripComment now strips from the first '#' (not only ' #') and trims trailing
  whitespace, matching its docstring
- adds tests demonstrating both behaviors

Co-authored-by: Jason Hall <imjasonh@users.noreply.github.com>
2026-06-10 15:25:47 +00:00
Cursor Agent
f83222fbd9
cli: reject empty env/label key; testwheel: deterministic map iteration
Co-authored-by: Jason Hall <imjasonh@users.noreply.github.com>
2026-06-10 14:35:37 +00:00
Cursor Agent
c571602058
wheel: parse build tag and prefer highest build when selecting wheels
Co-authored-by: Jason Hall <imjasonh@users.noreply.github.com>
2026-06-10 14:35:37 +00:00
Cursor Agent
9a7dc02643
lock: accept name[extras]==version; error on dependency-bearing options (-e/-r/-c)
Co-authored-by: Jason Hall <imjasonh@users.noreply.github.com>
2026-06-10 14:35:36 +00:00
Cursor Agent
5830434434
build/cli: layer cache + parallel wheel layers, secret deny-list, env validation, docs
- add internal/cache content-addressed layer store; build per-wheel layers in
  parallel and reuse cached compressed blobs across rebuilds (--cache-dir)
- thread platform/python target into wheel resolution; validate --env
- add common secret patterns to the default source ignore list
- README: copy/paste-safe usage example + new flags; DESIGN status updated

Co-authored-by: Jason Hall <imjasonh@users.noreply.github.com>
2026-06-10 14:07:25 +00:00
Cursor Agent
7c5e8608d3
wheel/lock: platform-aware wheel selection, size limits, prefix containment, strict lock parsing
- wheelhouse now filters candidates by target platform/ABI/python tag and
  prefers the most specific compatible wheel (fixes wrong-arch selection); also
  streams sha256 and uses an index map instead of an O(n*m) scan
- wheel: add tag parser + compatibility, cap per-member size (zip-bomb guard),
  reject members that escape the install prefix, stable sort
- lock: error on non-pinned/URL/editable lines instead of silently dropping

Co-authored-by: Jason Hall <imjasonh@users.noreply.github.com>
2026-06-10 14:07:10 +00:00
Cursor Agent
87e36eb41d
ptar: reject path traversal and duplicate paths in layers
Co-authored-by: Jason Hall <imjasonh@users.noreply.github.com>
2026-06-10 14:06:54 +00:00
Cursor Agent
1f2fd93117
Rename py-image-builder to pymage (module, imports, CLI, workflow, docs)
Co-authored-by: Jason Hall <imjasonh@users.noreply.github.com>
2026-06-10 13:30:05 +00:00