Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
33 changes: 8 additions & 25 deletions scripts/repro_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import argparse
import atexit
import contextlib
import os
import re
import shutil
import sys
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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

Expand All @@ -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)

Expand Down