diff --git a/Dockerfile b/Dockerfile index f757986..5aa1320 100644 --- a/Dockerfile +++ b/Dockerfile @@ -83,11 +83,19 @@ RUN apt-get update \ libudev1 \ && rm -rf /var/lib/apt/lists/* RUN rustup target add wasm32v1-none -COPY --from=builder /out/bin/stellar /usr/local/bin/stellar -RUN chmod +x /usr/local/bin/stellar -ENV STELLAR_CONFIG_HOME=/config \ - STELLAR_DATA_HOME=/data +RUN useradd --create-home --home-dir /stellar --uid 1000 --shell /bin/bash stellar \ + && mkdir -p /source /config /data \ + && chown stellar:stellar /source /config /data + +COPY --from=builder --chown=root:root --chmod=0755 /out/bin/stellar /usr/local/bin/stellar + +ENV CARGO_HOME=/stellar/.cargo \ + HOME=/stellar \ + STELLAR_CONFIG_HOME=/config \ + STELLAR_DATA_HOME=/data \ + STELLAR_NO_UPDATE_CHECK=1 +USER stellar WORKDIR /source ENTRYPOINT ["stellar"] diff --git a/README.md b/README.md index 61d3b1c..34419a3 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,33 @@ Build a contract by mounting the contract directory at `/source`: docker run --rm -v "$PWD:/source" docker.io/stellar/stellar-cli:latest contract build --locked ``` +The image exposes four well-known paths: + +| Path | What | +| ---------- | --------------------------------------------------------------------------------- | +| `/source` | `WORKDIR`. Bind-mount your contract here. | +| `/config` | `STELLAR_CONFIG_HOME`. Mount to persist network and identity configuration. | +| `/data` | `STELLAR_DATA_HOME`. Mount to persist CLI data. | +| `/stellar` | Home for user `stellar` (UID 1000). Mount to persist the cargo cache (see below). | + +The image runs as user `stellar` (UID 1000) with `/stellar` as the home +directory. `CARGO_HOME` resolves to `/stellar/.cargo` inside the +container, which is wiped on exit by default. + +To reuse cargo's registry index, git checkouts, and crate sources across +runs — and to make the image work under `--user "$(id -u):$(id -g)"` on +Linux hosts whose UID is not 1000 — mount a writable host directory at +`/stellar`: + +```sh +mkdir -p /tmp/myproject +docker run --rm \ + --user "$(id -u):$(id -g)" \ + -v /tmp/myproject:/stellar \ + -v "$PWD:/source" \ + docker.io/stellar/stellar-cli:latest contract build --locked +``` + ## Verifiable builds ([SEP-58](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0058.md)) For verifiable references, **always pin to a per-arch single-architecture diff --git a/scripts/repro_test.py b/scripts/repro_test.py index c1cf29b..b9db093 100755 --- a/scripts/repro_test.py +++ b/scripts/repro_test.py @@ -8,7 +8,7 @@ import argparse import atexit -import contextlib +import os import re import shutil import sys @@ -46,6 +46,10 @@ def build_and_hash(image: str, contract_dir: Path) -> str: "docker", "run", "--rm", + "--user", + f"{os.getuid()}:{os.getgid()}", + "-e", + "CARGO_HOME=/tmp/cargo", "--entrypoint", "bash", "-v", @@ -103,35 +107,14 @@ def clone(repo: str, rev: str, workdir: Path) -> None: runner.run(["git", "-C", str(workdir), "checkout", "-q", "FETCH_HEAD"]) -def make_cleanup(image: str, workdir: Path, keep: bool): +def make_cleanup(workdir: Path, keep: bool): def cleanup() -> None: if keep: common.log(f"keeping workdir on exit: {workdir}") return if not workdir.exists(): return - # Files inside may be root-owned by the container builds. Wipe via - # docker so this works on Linux CI; fall back to host rm. - wipe = runner.run( - [ - "docker", - "run", - "--rm", - "--entrypoint", - "sh", - "-v", - f"{workdir}:/work", - image, - "-c", - "find /work -mindepth 1 -delete", - ], - check=False, - capture_output=True, - ) - if wipe.returncode != 0: - shutil.rmtree(workdir, ignore_errors=True) - with contextlib.suppress(OSError): - workdir.rmdir() + shutil.rmtree(workdir, ignore_errors=True) return cleanup @@ -142,7 +125,7 @@ def main(argv: list[str] | None = None) -> int: contracts = args.contracts or list(DEFAULT_CONTRACTS) workdir = Path(tempfile.mkdtemp(prefix="repro-test.")) - atexit.register(make_cleanup(args.image, workdir, args.keep_workdir)) + atexit.register(make_cleanup(workdir, args.keep_workdir)) clone(args.repo, args.rev, workdir)