diff --git a/CLAUDE.md b/CLAUDE.md index 39d0c48..cd69c70 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -129,12 +129,20 @@ COPY --from=python-builder /opt/python /opt/python Python installs to `/opt/python/cpython-{version}-linux-x86_64-gnu/`. ### Installing R +Register the Posit Open apt repository once, then install from it — the +same repository Quarto uses: ```dockerfile -RUN RUN_UNATTENDED=1 R_VERSION=4.5.1 bash -c "$(curl -fsSL https://rstd.io/r-install)" && \ - find . -type f -name '[rR]-4.5.1.*\.(deb|rpm)' -delete +RUN bash -c "$(curl -1fsSL 'https://dl.posit.co/public/open/setup.deb.sh')" +RUN apt-get update -yqq && \ + apt-get install -yqq --no-install-recommends r-4.5.3 && \ + apt-get clean -yqq && \ + rm -rf /var/lib/apt/lists/* ``` -R installs to `/opt/R/{version}/`. +R installs to `/opt/R/{version}/`. The Open repo only keeps the latest +patch per minor line (unlike the retired `rstd.io/r-install` script's CDN, +which never pruned) — check the repo has the version you want before +pinning it, e.g. `apt-cache policy r-{version}` after registering the repo. ### Package installation patterns @@ -162,13 +170,12 @@ RUN apt-get update -yqq && \ ### Cleanup requirements - Always clean apt caches: `apt-get clean -yqq && rm -rf /var/lib/apt/lists/*` -- Delete R installer artifacts: `find . -type f -name '[rR]-{version}.*\.(deb|rpm)' -delete` - Use `--no-cache-dir` with pip - Use `clean = TRUE` with R `install.packages()` ## Key resources - [Posit Public Package Manager](https://p3m.dev/) - Package repositories for R and Python -- [R installer script](https://rstd.io/r-install) - Automated R installation +- [Posit Open apt/dnf repository setup](https://cloudsmith.io/~posit/repos/open/setup/) - R and Quarto package repository - [Goss](https://github.com/goss-org/goss) - Container testing framework used by Bakery - [GitHub Discussions](https://github.com/posit-dev/images/discussions) - Feedback and questions diff --git a/bakery/05-images-with-managed-dependencies/bakery.yaml b/bakery/05-images-with-managed-dependencies/bakery.yaml index ba2fa96..b93641f 100644 --- a/bakery/05-images-with-managed-dependencies/bakery.yaml +++ b/bakery/05-images-with-managed-dependencies/bakery.yaml @@ -34,6 +34,6 @@ images: latest: true dependencies: - dependency: R - version: "4.5.2" + version: "4.6.1" - dependency: python versions: ["3.14.3", "3.13.12"] diff --git a/bakery/05-images-with-managed-dependencies/example-image/1.0.0/Containerfile b/bakery/05-images-with-managed-dependencies/example-image/1.0.0/Containerfile index b716884..b27ba34 100644 --- a/bakery/05-images-with-managed-dependencies/example-image/1.0.0/Containerfile +++ b/bakery/05-images-with-managed-dependencies/example-image/1.0.0/Containerfile @@ -1,5 +1,5 @@ # Build Python using uv in a separate stage -FROM ghcr.io/astral-sh/uv:bookworm-slim AS python-builder +FROM ghcr.io/astral-sh/uv:debian-slim AS python-builder ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy ENV UV_PYTHON_INSTALL_DIR=/opt/python @@ -16,7 +16,8 @@ LABEL org.opencontainers.image.base.name="docker.io/library/ubuntu:24.04" ARG DEBIAN_FRONTEND=noninteractive ### Setup environment ### -RUN apt-get update -yqq --fix-missing && \ +RUN echo 'Acquire::Retries "3"; Acquire::http::Timeout "30"; Acquire::https::Timeout "30";' > /etc/apt/apt.conf.d/99-retries && \ + apt-get update -yqq --fix-missing && \ apt-get upgrade -yqq && \ apt-get dist-upgrade -yqq && \ apt-get autoremove -yqq --purge && \ @@ -25,13 +26,21 @@ RUN apt-get update -yqq --fix-missing && \ ca-certificates \ gnupg \ tar && \ - bash -c "$(curl -1fsSL 'https://dl.posit.co/public/pro/setup.deb.sh')" && \ + apt-get install -yqq --no-install-recommends locales && \ + localedef -i en_US -f UTF-8 en_US.UTF-8 && \ + bash -c "$(curl -1fsSL 'https://dl.posit.co/public/open/setup.deb.sh')" && \ apt-get clean -yqq && \ rm -rf /var/lib/apt/lists/* +ENV LANG=en_US.UTF-8 +ENV LANGUAGE=en_US:en +ENV LC_ALL=en_US.UTF-8 ### Install Python from previous stage ### COPY --from=python-builder /opt/python /opt/python ### Install R ### -RUN RUN_UNATTENDED=1 R_VERSION=4.5.2 bash -c "$(curl -fsSL https://rstd.io/r-install)" && \ - find . -type f -name "[rR]-4.5.2.*\.(deb|rpm)" -delete +RUN apt-get update -yqq && \ + apt-get install -yqq --no-install-recommends \ + r-4.6.1 && \ + apt-get clean -yqq && \ + rm -rf /var/lib/apt/lists/* diff --git a/bakery/05-images-with-managed-dependencies/example-image/1.0.0/test/goss.yaml b/bakery/05-images-with-managed-dependencies/example-image/1.0.0/test/goss.yaml index df99290..00d60b0 100644 --- a/bakery/05-images-with-managed-dependencies/example-image/1.0.0/test/goss.yaml +++ b/bakery/05-images-with-managed-dependencies/example-image/1.0.0/test/goss.yaml @@ -1,5 +1,5 @@ package: - r-4.5.2: + r-4.6.1: installed: true @@ -11,11 +11,11 @@ file: command: - "Verify R installation is version 4.5.2": - exec: /opt/R/4.5.2/bin/R --version + "Verify R installation is version 4.6.1": + exec: /opt/R/4.6.1/bin/R --version exit-status: 0 stdout: - - "R version 4.5.2" + - "R version 4.6.1" "Verify Python installation is version 3.14.3": exec: /opt/python/3.14.3/bin/python --version exit-status: 0 diff --git a/bakery/05-images-with-managed-dependencies/example-image/template/Containerfile.jinja2 b/bakery/05-images-with-managed-dependencies/example-image/template/Containerfile.jinja2 index c4b03a0..b2c3e5e 100644 --- a/bakery/05-images-with-managed-dependencies/example-image/template/Containerfile.jinja2 +++ b/bakery/05-images-with-managed-dependencies/example-image/template/Containerfile.jinja2 @@ -16,7 +16,7 @@ LABEL org.opencontainers.image.base.name="docker.io/library/ubuntu:24.04" ARG DEBIAN_FRONTEND=noninteractive ### Setup environment ### -{{ apt.run_setup() }} +{{ apt.run_setup(pro_repo=False, open_repo=True) }} ### Install Python from previous stage ### {{ python.copy_from_build_stage() }} diff --git a/bakery/06-matrix-images/README.md b/bakery/06-matrix-images/README.md index aef939c..4e14315 100644 --- a/bakery/06-matrix-images/README.md +++ b/bakery/06-matrix-images/README.md @@ -140,14 +140,13 @@ Matrix templates use build argument macros instead of direct version references FROM docker.io/library/ubuntu:24.04 ### ARG declarations ### -{{ r.declare_build_arg() }} -{{ python.declare_build_arg() }} -{{ quarto.declare_build_arg() }} # Install R using build arg +{{ r.declare_build_arg() }} {{ r.run_install(r.build_arg()) }} # Install Quarto using build arg +{{ quarto.declare_build_arg() }} {{ quarto.run_install(quarto.build_arg(), True) }} ``` diff --git a/bakery/06-matrix-images/test-matrix/matrix/Containerfile.ubuntu2404 b/bakery/06-matrix-images/test-matrix/matrix/Containerfile.ubuntu2404 index 412a7ab..ad72c2a 100644 --- a/bakery/06-matrix-images/test-matrix/matrix/Containerfile.ubuntu2404 +++ b/bakery/06-matrix-images/test-matrix/matrix/Containerfile.ubuntu2404 @@ -15,12 +15,9 @@ LABEL org.opencontainers.image.base.name="docker.io/library/ubuntu:24.04" ### ARG declarations ### ARG DEBIAN_FRONTEND=noninteractive -ARG R_VERSION -ARG PYTHON_VERSION -ARG QUARTO_VERSION - ### Install Apt Packages and set up the Posit Pro apt repo ### -RUN apt-get update -yqq --fix-missing && \ +RUN echo 'Acquire::Retries "3"; Acquire::http::Timeout "30"; Acquire::https::Timeout "30";' > /etc/apt/apt.conf.d/99-retries && \ + apt-get update -yqq --fix-missing && \ apt-get upgrade -yqq && \ apt-get dist-upgrade -yqq && \ apt-get autoremove -yqq --purge && \ @@ -29,9 +26,13 @@ RUN apt-get update -yqq --fix-missing && \ ca-certificates \ gnupg \ tar && \ - bash -c "$(curl -1fsSL 'https://dl.posit.co/public/pro/setup.deb.sh')" && \ + apt-get install -yqq --no-install-recommends locales && \ + localedef -i en_US -f UTF-8 en_US.UTF-8 && \ apt-get clean -yqq && \ rm -rf /var/lib/apt/lists/* +ENV LANG=en_US.UTF-8 +ENV LANGUAGE=en_US:en +ENV LC_ALL=en_US.UTF-8 ### Set up the Posit Open apt repo and install tools needed for Quarto ### # The Open repo hosts the `quarto` deb package (see next RUN). `xz-utils` is @@ -46,10 +47,15 @@ RUN bash -c "$(curl -1fsSL 'https://dl.posit.co/public/open/setup.deb.sh')" && \ COPY --from=python-builder /opt/python /opt/python # Install R -RUN RUN_UNATTENDED=1 R_VERSION=$R_VERSION bash -c "$(curl -fsSL https://rstd.io/r-install)" && \ - find . -type f -name '[rR]-$R_VERSION.*\.(deb|rpm)' -delete +ARG R_VERSION +RUN apt-get update -yqq && \ + apt-get install -yqq --no-install-recommends \ + r-$R_VERSION && \ + apt-get clean -yqq && \ + rm -rf /var/lib/apt/lists/* ### Install Quarto from the Posit Open apt repo ### +ARG QUARTO_VERSION # The `quarto` deb installs to /opt/quarto/bin/quarto and symlinks the binary # into /usr/local/bin/quarto via its postinst script. RUN apt-get update -yqq && \ @@ -62,4 +68,4 @@ RUN apt-get update -yqq && \ # Installing TinyTeX via Quarto yields a fuller set of LaTeX packages than # the standalone TinyTeX installer. RUN --mount=type=secret,id=github_token,required=false \ - GH_TOKEN="$([ -s /run/secrets/github_token ] && cat /run/secrets/github_token)" HOME="/opt" /opt/quarto/bin/quarto install tinytex --no-prompt --update-path + GH_TOKEN="$([ -s /run/secrets/github_token ] && cat /run/secrets/github_token)" HOME="/opt" CI=true /opt/quarto/bin/quarto install tinytex --no-prompt --update-path diff --git a/bakery/06-matrix-images/test-matrix/template/Containerfile.ubuntu2404.jinja2 b/bakery/06-matrix-images/test-matrix/template/Containerfile.ubuntu2404.jinja2 index 2091fae..4f936f3 100644 --- a/bakery/06-matrix-images/test-matrix/template/Containerfile.ubuntu2404.jinja2 +++ b/bakery/06-matrix-images/test-matrix/template/Containerfile.ubuntu2404.jinja2 @@ -15,12 +15,8 @@ LABEL org.opencontainers.image.base.name="docker.io/library/ubuntu:24.04" ### ARG declarations ### ARG DEBIAN_FRONTEND=noninteractive -{{ r.declare_build_arg() }} -{{ python.declare_build_arg() }} -{{ quarto.declare_build_arg() }} - ### Install Apt Packages and set up the Posit Pro apt repo ### -{{ apt.run_setup() }} +{{ apt.run_setup(pro_repo=False) }} ### Set up the Posit Open apt repo and install tools needed for Quarto ### # The Open repo hosts the `quarto` deb package (see next RUN). `xz-utils` is @@ -34,9 +30,11 @@ RUN bash -c "$(curl -1fsSL 'https://dl.posit.co/public/open/setup.deb.sh')" && \ {{ python.copy_from_build_stage() }} # Install R +{{ r.declare_build_arg() }} {{ r.run_install(r.build_arg()) }} ### Install Quarto from the Posit Open apt repo ### +{{ quarto.declare_build_arg() }} # The `quarto` deb installs to /opt/quarto/bin/quarto and symlinks the binary # into /usr/local/bin/quarto via its postinst script. RUN apt-get update -yqq && \ diff --git a/extending/common/R/Containerfile b/extending/common/R/Containerfile index c19a9ed..2cbb2be 100644 --- a/extending/common/R/Containerfile +++ b/extending/common/R/Containerfile @@ -16,16 +16,27 @@ ARG PRODUCT_VERSION="2026.05.0" FROM ghcr.io/posit-dev/workbench:${PRODUCT_VERSION}-min +### Register the Posit Open apt repository ### +# R installs from here, the same repository Quarto uses. Register it once, +# before installing any R version, so this setup script does not run once +# per version below. +RUN bash -c "$(curl -1fsSL 'https://dl.posit.co/public/open/setup.deb.sh')" + + ### Install R versions ### -RUN RUN_UNATTENDED=1 R_VERSION=4.5.1 bash -c "$(curl -fsSL https://rstd.io/r-install)" && \ - find . -type f -name '[rR]-4.5.1.*\.(deb|rpm)' -delete -RUN RUN_UNATTENDED=1 R_VERSION=4.4.3 bash -c "$(curl -fsSL https://rstd.io/r-install)" && \ - find . -type f -name '[rR]-4.4.3.*\.(deb|rpm)' -delete +RUN apt-get update -yqq && \ + apt-get install -yqq --no-install-recommends r-4.5.3 && \ + apt-get clean -yqq && \ + rm -rf /var/lib/apt/lists/* +RUN apt-get update -yqq && \ + apt-get install -yqq --no-install-recommends r-4.4.3 && \ + apt-get clean -yqq && \ + rm -rf /var/lib/apt/lists/* ### Install R packages from Posit Public Package Manager ### COPY packages.txt /tmp/packages.txt -RUN /opt/R/4.5.1/bin/R --vanilla -e \ +RUN /opt/R/4.5.3/bin/R --vanilla -e \ 'install.packages(readLines("/tmp/packages.txt"), repos="https://p3m.dev/cran/__linux__/noble/latest", clean = TRUE)' RUN /opt/R/4.4.3/bin/R --vanilla -e \ 'install.packages(readLines("/tmp/packages.txt"), repos="https://p3m.dev/cran/__linux__/noble/latest", clean = TRUE)'