diff --git a/README.md b/README.md index 7e63e33..71821fc 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ You can build Open Container Initiative (OCI) container images from the definiti Each Containerfile uses the root of the repository as the build context. ```shell -PWB_VERSION="2026.04" +PWB_VERSION="2026.05" # Build the standard Workbench image using docker docker buildx build \ diff --git a/bakery.yaml b/bakery.yaml index ac3f896..961b62a 100644 --- a/bakery.yaml +++ b/bakery.yaml @@ -69,9 +69,20 @@ images: namespace: "posit-dev" repository: "workbench-preview" versions: + - name: 2026.05.0+218.pro1 + subpath: '2026.05' + latest: true + os: + - name: Ubuntu 22.04 + - name: Ubuntu 24.04 + primary: true + dependencies: + - dependency: R + version: 4.6.0 + - dependency: python + version: 3.14.5 - name: 2026.04.0+526.pro2 subpath: '2026.04' - latest: true os: - name: Ubuntu 22.04 - name: Ubuntu 24.04 @@ -122,9 +133,14 @@ images: namespace: "posit-dev" repository: "workbench-session-init-preview" versions: + - name: 2026.05.0+218.pro1 + subpath: '2026.05' + latest: true + os: + - name: Ubuntu 24.04 + primary: true - name: 2026.04.0+526.pro2 subpath: '2026.04' - latest: true os: - name: Ubuntu 24.04 primary: true diff --git a/workbench-session-init/2026.05/Containerfile.ubuntu2404 b/workbench-session-init/2026.05/Containerfile.ubuntu2404 new file mode 100644 index 0000000..9792843 --- /dev/null +++ b/workbench-session-init/2026.05/Containerfile.ubuntu2404 @@ -0,0 +1,40 @@ +FROM docker.io/library/ubuntu:24.04 AS builder + +ARG DEBIAN_FRONTEND=noninteractive +ARG BUILDARCH +ARG TARGETARCH=${BUILDARCH} + +SHELL ["/bin/bash", "-euo", "pipefail", "-c"] + +### Initialize PTI in container ### +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 && \ + apt-get install -yqq --no-install-recommends \ + curl \ + ca-certificates \ + gnupg \ + tar && \ + bash -c "$(curl -1fsSL 'https://dl.posit.co/public/pro/setup.deb.sh')" && \ + apt-get clean -yqq && \ + rm -rf /var/lib/apt/lists/* + +# Download the RStudio Workbench session components +RUN case ${TARGETARCH} in \ + amd64) CDN_ARCH=x86_64 ;; \ + arm64) CDN_ARCH=arm64 ;; \ + *) echo "Unsupported architecture: ${TARGETARCH}" && exit 1 ;; \ + esac && \ + mkdir -p /pwb-staging && \ + curl -fsSL -o /pwb-staging/rsp-session-multi-linux.tar.gz "https://s3.amazonaws.com/rstudio-ide-build/session/multi/$CDN_ARCH/rsp-session-multi-linux-2026.05.0-218.pro1-$CDN_ARCH.tar.gz" && \ + mkdir -p /opt/session-components && \ + tar -C /opt/session-components -xpf /pwb-staging/rsp-session-multi-linux.tar.gz && \ + chmod 755 /opt/session-components && \ + rm -rf /pwb-staging + +FROM docker.io/library/ubuntu:24.04 +LABEL org.opencontainers.image.base.name="docker.io/library/ubuntu:24.04" +COPY --from=builder /opt/session-components /opt/session-components +CMD ["/opt/session-components/bin/session-init"] diff --git a/workbench-session-init/2026.05/test/goss.yaml b/workbench-session-init/2026.05/test/goss.yaml new file mode 100644 index 0000000..12541cd --- /dev/null +++ b/workbench-session-init/2026.05/test/goss.yaml @@ -0,0 +1,105 @@ +file: + /opt/session-components/: + exists: true + mode: "0755" + filetype: directory + /opt/session-components/bin/session-init: + exists: true + filetype: file + mode: "0755" + /opt/session-components/bin/git-credential-pwb: + exists: true + filetype: file + mode: "0755" + /opt/session-components/bin/jammy: + exists: true + filetype: directory + mode: "0755" + /opt/session-components/bin/noble: + exists: true + filetype: directory + mode: "0755" + /opt/session-components/bin/opensuse15: + exists: true + filetype: directory + mode: "0755" + /opt/session-components/bin/postback: + exists: true + filetype: directory + mode: "0755" + /opt/session-components/bin/pwb-supervisor: + exists: true + filetype: file + mode: "0755" + /opt/session-components/bin/quarto: + exists: true + filetype: directory + mode: "0755" + /opt/session-components/bin/r-ldpath: + exists: true + filetype: file + mode: "0755" + /opt/session-components/bin/rhel8: + exists: true + filetype: directory + mode: "0755" + /opt/session-components/bin/rhel9: + exists: true + filetype: directory + mode: "0755" + /opt/session-components/bin/shared-run: + exists: true + filetype: file + mode: "0755" + /opt/session-components/R: + exists: true + filetype: directory + mode: "0755" + /opt/session-components/resources: + exists: true + filetype: directory + mode: "0755" + /opt/session-components/www: + exists: true + filetype: directory + mode: "0755" + /opt/session-components/www-symbolmaps: + exists: true + filetype: directory + mode: "0755" + /opt/session-components/bin/jupyter-session-run: + exists: true + filetype: file + mode: "0755" + /opt/session-components/extras: + exists: true + filetype: directory + mode: "0755" + /opt/session-components/bin/positron-server: + exists: true + filetype: directory + mode: "0755" + /opt/session-components/bin/positron-session-run: + exists: true + filetype: file + mode: "0755" + /opt/session-components/bin/rsession-run: + exists: true + filetype: file + mode: "0755" + /opt/session-components/bin/pwb-code-server: + exists: true + filetype: directory + mode: "0755" + /opt/session-components/bin/vscode-session-run: + exists: true + filetype: file + mode: "0755" + +command: + session-components-files-other-read: + exec: 'find /opt/session-components -type f -not -perm -004 -print 2>&1 | head -c1 | (! grep -q .)' + exit-status: 0 + session-components-dirs-other-rx: + exec: 'find /opt/session-components -type d -not -perm -005 -print 2>&1 | head -c1 | (! grep -q .)' + exit-status: 0 diff --git a/workbench-session-init/README.md b/workbench-session-init/README.md index 242f143..b473ab0 100644 --- a/workbench-session-init/README.md +++ b/workbench-session-init/README.md @@ -105,7 +105,7 @@ The container starts as `root` so the session container can read the session com You can pull the session components into a custom session image at build time using a multi-stage build: ```dockerfile -FROM ghcr.io/posit-dev/workbench-session-init:2026.04.0 AS session-init +FROM ghcr.io/posit-dev/workbench-session-init:2026.05.0 AS session-init FROM your-custom-base COPY --from=session-init /opt/session-components /opt/session-components diff --git a/workbench/2026.05/Containerfile.ubuntu2204.min b/workbench/2026.05/Containerfile.ubuntu2204.min new file mode 100644 index 0000000..81a5393 --- /dev/null +++ b/workbench/2026.05/Containerfile.ubuntu2204.min @@ -0,0 +1,79 @@ +FROM docker.io/library/ubuntu:22.04 +LABEL org.opencontainers.image.base.name="docker.io/library/ubuntu:22.04" + +### ARG declarations ### +ARG DEBIAN_FRONTEND=noninteractive +ARG BUILDARCH +ARG TARGETARCH=${BUILDARCH} +ARG WORKBENCH_VERSION="2026.05.0+218.pro1" +ARG RSTUDIO_INSTALL_NO_LICENSE_INITIALIZATION=1 + +ENV PWB_STARTUP_DEBUG=0 +ENV PWB_LICENSE="" +ENV PWB_LICENSE_SERVER="" +ENV PWB_TESTUSER="" +ENV PWB_TESTUSER_PASSWD="" +ENV PWB_TESTUSER_UID="" +ENV PWB_LAUNCHER=true +ENV PWB_LAUNCHER_TIMEOUT=10 +ENV PWB_DIAGNOSTIC_DIR=/var/log/rstudio +ENV PWB_DIAGNOSTIC_ENABLE=false +ENV PWB_EXIT_AFTER_VERIFY=false + +### Setup environment ### +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 && \ + apt-get install -yqq --no-install-recommends \ + curl \ + ca-certificates \ + gnupg \ + tar && \ + bash -c "$(curl -1fsSL 'https://dl.posit.co/public/pro/setup.deb.sh')" && \ + apt-get clean -yqq && \ + rm -rf /var/lib/apt/lists/* + +## Install Apt Packages ### +COPY workbench/2026.05/deps/ubuntu-22.04_packages.txt /tmp/packages.txt +RUN apt-get update -yqq && \ + xargs -a /tmp/packages.txt apt-get install -yqq --no-install-recommends && \ + apt-get clean -yqq && \ + rm -rf /var/lib/apt/lists/* + +### Install wait-for-it ### +COPY --chmod=0755 workbench/scripts/wait-for-it.sh /usr/local/bin/wait-for-it.sh + +### Install Workbench 2026.05.0+218.pro1 ### +COPY --chmod=0755 workbench/2026.05/scripts/install_workbench.sh /tmp/install_workbench.sh +RUN \ + /tmp/install_workbench.sh && \ + rm -f /tmp/install_workbench.sh + +### Install Quarto to PATH ### +RUN ln -s /lib/rstudio-server/bin/quarto/bin/quarto /usr/local/bin/quarto + + + +### Copy startup scripts ### +COPY --chmod=0775 "workbench/2026.05/scripts/startup.sh" /usr/local/bin/startup.sh +COPY "workbench/2026.05/startup/launcher" /startup/launcher +COPY "workbench/2026.05/startup/base" /startup/base +COPY "workbench/2026.05/startup/supervisord.conf" /etc/supervisor/supervisord.conf +COPY "workbench/2026.05/conf/jupyter/*" "workbench/2026.05/conf/launcher/*" "workbench/2026.05/conf/rstudio/*" "workbench/2026.05/conf/positron/*" "workbench/2026.05/conf/vscode/*" /etc/rstudio + +### Configure Workbench ### +RUN mkdir -p /var/lib/rstudio-server/monitor/log \ + && chown -R rstudio-server:rstudio-server /var/lib/rstudio-server/monitor \ + && mkdir -p /startup/custom/ \ + && mkdir -p /startup/user-provisioning/ \ + && printf '\n# allow home directory creation\nsession required pam_mkhomedir.so skel=/etc/skel umask=0077\n' >> /etc/pam.d/common-session \ + && echo "RSPM=https://p3m.dev/cran/__linux__/jammy/latest" > /etc/rstudio/repos.conf \ + && echo "CRAN=https://p3m.dev/cran/__linux__/jammy/latest" >> /etc/rstudio/repos.conf + +EXPOSE 8787/tcp +EXPOSE 5559/tcp +HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \ + CMD curl -fsS http://localhost:8787/health-check || exit 1 +CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"] diff --git a/workbench/2026.05/Containerfile.ubuntu2204.std b/workbench/2026.05/Containerfile.ubuntu2204.std new file mode 100644 index 0000000..eba895f --- /dev/null +++ b/workbench/2026.05/Containerfile.ubuntu2204.std @@ -0,0 +1,135 @@ +# Build Python using uv in a separate stage +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 +ENV UV_PYTHON_PREFERENCE=only-managed +RUN uv python install 3.14.5 +RUN mv /opt/python/cpython-3.14.5-linux-*/ /opt/python/3.14.5 + + +FROM docker.io/library/ubuntu:22.04 +LABEL org.opencontainers.image.base.name="docker.io/library/ubuntu:22.04" + +### ARG declarations ### +ARG DEBIAN_FRONTEND=noninteractive +ARG BUILDARCH +ARG TARGETARCH=${BUILDARCH} +ARG WORKBENCH_VERSION="2026.05.0+218.pro1" +ARG RSTUDIO_INSTALL_NO_LICENSE_INITIALIZATION=1 + +ENV PWB_STARTUP_DEBUG=0 +ENV PWB_LICENSE="" +ENV PWB_LICENSE_SERVER="" +ENV PWB_TESTUSER="" +ENV PWB_TESTUSER_PASSWD="" +ENV PWB_TESTUSER_UID="" +ENV PWB_LAUNCHER=true +ENV PWB_LAUNCHER_TIMEOUT=10 +ENV PWB_DIAGNOSTIC_DIR=/var/log/rstudio +ENV PWB_DIAGNOSTIC_ENABLE=false +ENV PWB_EXIT_AFTER_VERIFY=false + +### Setup environment ### +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 && \ + apt-get install -yqq --no-install-recommends \ + curl \ + ca-certificates \ + gnupg \ + tar && \ + bash -c "$(curl -1fsSL 'https://dl.posit.co/public/pro/setup.deb.sh')" && \ + apt-get clean -yqq && \ + rm -rf /var/lib/apt/lists/* + +## Install Apt Packages ### +COPY workbench/2026.05/deps/ubuntu-22.04_packages.txt /tmp/packages.txt +COPY workbench/2026.05/deps/ubuntu-22.04_optional_packages.txt /tmp/optional_packages.txt +RUN apt-get update -yqq && \ + xargs -a /tmp/packages.txt apt-get install -yqq --no-install-recommends && \ + xargs -a /tmp/optional_packages.txt apt-get install -yqq --no-install-recommends && \ + apt-get clean -yqq && \ + rm -rf /var/lib/apt/lists/* + + +### Install wait-for-it ### +COPY --chmod=0755 workbench/scripts/wait-for-it.sh /usr/local/bin/wait-for-it.sh + +### Install Workbench 2026.05.0+218.pro1 ### +COPY --chmod=0755 workbench/2026.05/scripts/install_workbench.sh /tmp/install_workbench.sh +RUN \ + /tmp/install_workbench.sh && \ + rm -f /tmp/install_workbench.sh + +### Install Quarto to PATH ### +RUN ln -s /lib/rstudio-server/bin/quarto/bin/quarto /usr/local/bin/quarto + +### Install R ### +RUN RUN_UNATTENDED=1 R_VERSION=4.6.0 bash -c "$(curl -fsSL https://rstd.io/r-install)" && \ + find . -type f -name '[rR]-4.6.0.*\.(deb|rpm)' -delete + +### Install Python from previous stage ### +COPY --from=python-builder /opt/python /opt/python + +### Install Jupyter ### +RUN /opt/python/3.14.5/bin/python -m venv /opt/python/jupyter && \ + /opt/python/jupyter/bin/pip install --no-cache-dir --upgrade pip setuptools wheel && \ + /opt/python/jupyter/bin/pip install --no-cache-dir --upgrade \ + "jupyterlab<5" \ + notebook \ + "pwb_jupyterlab<2" && \ + rm -rf /opt/python/jupyter/lib/python*/site-packages/jupyterlab/tests && \ + rm -rf /opt/python/jupyter/lib/python*/site-packages/jupyterlab/galata && \ + rm -f /opt/python/jupyter/lib/python*/site-packages/jupyterlab/staging/yarn.lock && \ + ln -s /opt/python/jupyter/bin/jupyter /usr/local/bin/jupyter + +### Install Jupyter kernels ### +RUN /opt/python/3.14.5/bin/pip install --no-cache-dir --upgrade --break-system-packages \ + ipykernel && \ + /opt/python/3.14.5/bin/python -m ipykernel install --user --name python3.14.5 --display-name "Python 3.14.5" + +### Install Pro Drivers ### +RUN apt-get install -yq rstudio-drivers && \ + apt-get clean -yqq && \ +rm -rf /var/lib/apt/lists/* + +### Copy startup scripts ### +COPY --chmod=0775 "workbench/2026.05/scripts/startup.sh" /usr/local/bin/startup.sh +COPY "workbench/2026.05/startup/launcher" /startup/launcher +COPY "workbench/2026.05/startup/base" /startup/base +COPY "workbench/2026.05/startup/supervisord.conf" /etc/supervisor/supervisord.conf +COPY "workbench/2026.05/startup/user-provisioning" /startup/user-provisioning +COPY --chmod=600 "workbench/2026.05/conf/sssd/sssd.conf" /etc/sssd/sssd.conf +COPY "workbench/2026.05/conf/jupyter/*" "workbench/2026.05/conf/launcher/*" "workbench/2026.05/conf/rstudio/*" "workbench/2026.05/conf/positron/*" "workbench/2026.05/conf/vscode/*" /etc/rstudio + +### Configure Workbench ### +RUN mkdir -p /var/lib/rstudio-server/monitor/log \ + && chown -R rstudio-server:rstudio-server /var/lib/rstudio-server/monitor \ + && mkdir -p /startup/custom/ \ + && mkdir -p /startup/user-provisioning/ \ + && printf '\n# allow home directory creation\nsession required pam_mkhomedir.so skel=/etc/skel umask=0077\n' >> /etc/pam.d/common-session \ + && echo "RSPM=https://p3m.dev/cran/__linux__/jammy/latest" > /etc/rstudio/repos.conf \ + && echo "CRAN=https://p3m.dev/cran/__linux__/jammy/latest" >> /etc/rstudio/repos.conf + +### Install TinyTeX using Quarto ### +# Caches won't invalidate correctly on new releases for TinyTeX installs. This ADD instruction is a workaround to bust +# the cache on new releases. +# +# TinyTeX is installed under HOME="/opt" so the install lands at /opt/.TinyTeX, +# which is readable by non-root runtime users. `--update-path` makes tlmgr +# symlink the TinyTeX binaries into /usr/local/bin. +# TODO: Remove `HOME="/opt"` once Quarto supports custom install locations +# for TinyTeX, see https://github.com/quarto-dev/quarto-cli/issues/11800. +ADD https://github.com/rstudio/tinytex-releases/releases/latest /tmp/tinytex-release.json +RUN --mount=type=secret,id=github_token,required=false \ + GH_TOKEN="$([ -s /run/secrets/github_token ] && cat /run/secrets/github_token)" HOME="/opt" /lib/rstudio-server/bin/quarto/bin/quarto install tinytex --no-prompt --update-path \ + && rm -f /tmp/tinytex-release.json + +EXPOSE 8787/tcp +EXPOSE 5559/tcp +HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \ + CMD curl -fsS http://localhost:8787/health-check || exit 1 +CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"] diff --git a/workbench/2026.05/Containerfile.ubuntu2404.min b/workbench/2026.05/Containerfile.ubuntu2404.min new file mode 100644 index 0000000..e225040 --- /dev/null +++ b/workbench/2026.05/Containerfile.ubuntu2404.min @@ -0,0 +1,79 @@ +FROM docker.io/library/ubuntu:24.04 +LABEL org.opencontainers.image.base.name="docker.io/library/ubuntu:24.04" + +### ARG declarations ### +ARG DEBIAN_FRONTEND=noninteractive +ARG BUILDARCH +ARG TARGETARCH=${BUILDARCH} +ARG WORKBENCH_VERSION="2026.05.0+218.pro1" +ARG RSTUDIO_INSTALL_NO_LICENSE_INITIALIZATION=1 + +ENV PWB_STARTUP_DEBUG=0 +ENV PWB_LICENSE="" +ENV PWB_LICENSE_SERVER="" +ENV PWB_TESTUSER="" +ENV PWB_TESTUSER_PASSWD="" +ENV PWB_TESTUSER_UID="" +ENV PWB_LAUNCHER=true +ENV PWB_LAUNCHER_TIMEOUT=10 +ENV PWB_DIAGNOSTIC_DIR=/var/log/rstudio +ENV PWB_DIAGNOSTIC_ENABLE=false +ENV PWB_EXIT_AFTER_VERIFY=false + +### Setup environment ### +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 && \ + apt-get install -yqq --no-install-recommends \ + curl \ + ca-certificates \ + gnupg \ + tar && \ + bash -c "$(curl -1fsSL 'https://dl.posit.co/public/pro/setup.deb.sh')" && \ + apt-get clean -yqq && \ + rm -rf /var/lib/apt/lists/* + +## Install Apt Packages ### +COPY workbench/2026.05/deps/ubuntu-24.04_packages.txt /tmp/packages.txt +RUN apt-get update -yqq && \ + xargs -a /tmp/packages.txt apt-get install -yqq --no-install-recommends && \ + apt-get clean -yqq && \ + rm -rf /var/lib/apt/lists/* + +### Install wait-for-it ### +COPY --chmod=0755 workbench/scripts/wait-for-it.sh /usr/local/bin/wait-for-it.sh + +### Install Workbench 2026.05.0+218.pro1 ### +COPY --chmod=0755 workbench/2026.05/scripts/install_workbench.sh /tmp/install_workbench.sh +RUN \ + /tmp/install_workbench.sh && \ + rm -f /tmp/install_workbench.sh + +### Install Quarto to PATH ### +RUN ln -s /lib/rstudio-server/bin/quarto/bin/quarto /usr/local/bin/quarto + + + +### Copy startup scripts ### +COPY --chmod=0775 "workbench/2026.05/scripts/startup.sh" /usr/local/bin/startup.sh +COPY "workbench/2026.05/startup/launcher" /startup/launcher +COPY "workbench/2026.05/startup/base" /startup/base +COPY "workbench/2026.05/startup/supervisord.conf" /etc/supervisor/supervisord.conf +COPY "workbench/2026.05/conf/jupyter/*" "workbench/2026.05/conf/launcher/*" "workbench/2026.05/conf/rstudio/*" "workbench/2026.05/conf/positron/*" "workbench/2026.05/conf/vscode/*" /etc/rstudio + +### Configure Workbench ### +RUN mkdir -p /var/lib/rstudio-server/monitor/log \ + && chown -R rstudio-server:rstudio-server /var/lib/rstudio-server/monitor \ + && mkdir -p /startup/custom/ \ + && mkdir -p /startup/user-provisioning/ \ + && printf '\n# allow home directory creation\nsession required pam_mkhomedir.so skel=/etc/skel umask=0077\n' >> /etc/pam.d/common-session \ + && echo "RSPM=https://p3m.dev/cran/__linux__/noble/latest" > /etc/rstudio/repos.conf \ + && echo "CRAN=https://p3m.dev/cran/__linux__/noble/latest" >> /etc/rstudio/repos.conf + +EXPOSE 8787/tcp +EXPOSE 5559/tcp +HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \ + CMD curl -fsS http://localhost:8787/health-check || exit 1 +CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"] diff --git a/workbench/2026.05/Containerfile.ubuntu2404.std b/workbench/2026.05/Containerfile.ubuntu2404.std new file mode 100644 index 0000000..b700af8 --- /dev/null +++ b/workbench/2026.05/Containerfile.ubuntu2404.std @@ -0,0 +1,138 @@ +# Build Python using uv in a separate stage +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 +ENV UV_PYTHON_PREFERENCE=only-managed +RUN uv python install 3.14.5 +RUN mv /opt/python/cpython-3.14.5-linux-*/ /opt/python/3.14.5 + + +FROM docker.io/library/ubuntu:24.04 +LABEL org.opencontainers.image.base.name="docker.io/library/ubuntu:24.04" + +### ARG declarations ### +ARG DEBIAN_FRONTEND=noninteractive +ARG BUILDARCH +ARG TARGETARCH=${BUILDARCH} +ARG WORKBENCH_VERSION="2026.05.0+218.pro1" +ARG RSTUDIO_INSTALL_NO_LICENSE_INITIALIZATION=1 + +ENV PWB_STARTUP_DEBUG=0 +ENV PWB_LICENSE="" +ENV PWB_LICENSE_SERVER="" +ENV PWB_TESTUSER="" +ENV PWB_TESTUSER_PASSWD="" +ENV PWB_TESTUSER_UID="" +ENV PWB_LAUNCHER=true +ENV PWB_LAUNCHER_TIMEOUT=10 +ENV PWB_DIAGNOSTIC_DIR=/var/log/rstudio +ENV PWB_DIAGNOSTIC_ENABLE=false +ENV PWB_EXIT_AFTER_VERIFY=false + +### Setup environment ### +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 && \ + apt-get install -yqq --no-install-recommends \ + curl \ + ca-certificates \ + gnupg \ + tar && \ + bash -c "$(curl -1fsSL 'https://dl.posit.co/public/pro/setup.deb.sh')" && \ + apt-get clean -yqq && \ + rm -rf /var/lib/apt/lists/* + +## Install Apt Packages ### +COPY workbench/2026.05/deps/ubuntu-24.04_packages.txt /tmp/packages.txt +COPY workbench/2026.05/deps/ubuntu-24.04_optional_packages.txt /tmp/optional_packages.txt +RUN apt-get update -yqq && \ + xargs -a /tmp/packages.txt apt-get install -yqq --no-install-recommends && \ + xargs -a /tmp/optional_packages.txt apt-get install -yqq --no-install-recommends && \ + apt-get clean -yqq && \ + rm -rf /var/lib/apt/lists/* + + +### Install wait-for-it ### +COPY --chmod=0755 workbench/scripts/wait-for-it.sh /usr/local/bin/wait-for-it.sh + +### Install Workbench 2026.05.0+218.pro1 ### +COPY --chmod=0755 workbench/2026.05/scripts/install_workbench.sh /tmp/install_workbench.sh +RUN \ + /tmp/install_workbench.sh && \ + rm -f /tmp/install_workbench.sh + +### Install Quarto to PATH ### +RUN ln -s /lib/rstudio-server/bin/quarto/bin/quarto /usr/local/bin/quarto + +### Install R ### +RUN RUN_UNATTENDED=1 R_VERSION=4.6.0 bash -c "$(curl -fsSL https://rstd.io/r-install)" && \ + find . -type f -name '[rR]-4.6.0.*\.(deb|rpm)' -delete + +### Install Python from previous stage ### +COPY --from=python-builder /opt/python /opt/python + +### Install Jupyter ### +RUN /opt/python/3.14.5/bin/python -m venv /opt/python/jupyter && \ + /opt/python/jupyter/bin/pip install --no-cache-dir --upgrade pip setuptools wheel && \ + /opt/python/jupyter/bin/pip install --no-cache-dir --upgrade \ + "jupyterlab<5" \ + notebook \ + "pwb_jupyterlab<2" && \ + rm -rf /opt/python/jupyter/lib/python*/site-packages/jupyterlab/tests && \ + rm -rf /opt/python/jupyter/lib/python*/site-packages/jupyterlab/galata && \ + rm -f /opt/python/jupyter/lib/python*/site-packages/jupyterlab/staging/yarn.lock && \ + ln -s /opt/python/jupyter/bin/jupyter /usr/local/bin/jupyter + +### Install Jupyter kernels ### +RUN /opt/python/3.14.5/bin/pip install --no-cache-dir --upgrade --break-system-packages \ + ipykernel && \ + /opt/python/3.14.5/bin/python -m ipykernel install --user --name python3.14.5 --display-name "Python 3.14.5" + +### Install Pro Drivers ### +RUN apt-get update -yqq && \ + apt-get install -yqq --no-install-recommends \ + rstudio-drivers && \ + apt-get clean -yqq && \ + rm -rf /var/lib/apt/lists/* +RUN cp /opt/rstudio-drivers/odbcinst.ini.sample /etc/odbcinst.ini + +### Copy startup scripts ### +COPY --chmod=0775 "workbench/2026.05/scripts/startup.sh" /usr/local/bin/startup.sh +COPY "workbench/2026.05/startup/launcher" /startup/launcher +COPY "workbench/2026.05/startup/base" /startup/base +COPY "workbench/2026.05/startup/supervisord.conf" /etc/supervisor/supervisord.conf +COPY "workbench/2026.05/startup/user-provisioning" /startup/user-provisioning +COPY --chmod=600 "workbench/2026.05/conf/sssd/sssd.conf" /etc/sssd/sssd.conf +COPY "workbench/2026.05/conf/jupyter/*" "workbench/2026.05/conf/launcher/*" "workbench/2026.05/conf/rstudio/*" "workbench/2026.05/conf/positron/*" "workbench/2026.05/conf/vscode/*" /etc/rstudio + +### Configure Workbench ### +RUN mkdir -p /var/lib/rstudio-server/monitor/log \ + && chown -R rstudio-server:rstudio-server /var/lib/rstudio-server/monitor \ + && mkdir -p /startup/custom/ \ + && mkdir -p /startup/user-provisioning/ \ + && printf '\n# allow home directory creation\nsession required pam_mkhomedir.so skel=/etc/skel umask=0077\n' >> /etc/pam.d/common-session \ + && echo "RSPM=https://p3m.dev/cran/__linux__/noble/latest" > /etc/rstudio/repos.conf \ + && echo "CRAN=https://p3m.dev/cran/__linux__/noble/latest" >> /etc/rstudio/repos.conf + +### Install TinyTeX using Quarto ### +# Caches won't invalidate correctly on new releases for TinyTeX installs. This ADD instruction is a workaround to bust +# the cache on new releases. +# +# TinyTeX is installed under HOME="/opt" so the install lands at /opt/.TinyTeX, +# which is readable by non-root runtime users. `--update-path` makes tlmgr +# symlink the TinyTeX binaries into /usr/local/bin. +# TODO: Remove `HOME="/opt"` once Quarto supports custom install locations +# for TinyTeX, see https://github.com/quarto-dev/quarto-cli/issues/11800. +ADD https://github.com/rstudio/tinytex-releases/releases/latest /tmp/tinytex-release.json +RUN --mount=type=secret,id=github_token,required=false \ + GH_TOKEN="$([ -s /run/secrets/github_token ] && cat /run/secrets/github_token)" HOME="/opt" /lib/rstudio-server/bin/quarto/bin/quarto install tinytex --no-prompt --update-path \ + && rm -f /tmp/tinytex-release.json + +EXPOSE 8787/tcp +EXPOSE 5559/tcp +HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \ + CMD curl -fsS http://localhost:8787/health-check || exit 1 +CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"] diff --git a/workbench/2026.05/conf/jupyter/jupyter.conf b/workbench/2026.05/conf/jupyter/jupyter.conf new file mode 100644 index 0000000..aa124e8 --- /dev/null +++ b/workbench/2026.05/conf/jupyter/jupyter.conf @@ -0,0 +1,2 @@ +labs-enabled=1 +default-session-cluster=Local diff --git a/workbench/2026.05/conf/launcher/launcher-env b/workbench/2026.05/conf/launcher/launcher-env new file mode 100644 index 0000000..d8127a5 --- /dev/null +++ b/workbench/2026.05/conf/launcher/launcher-env @@ -0,0 +1,6 @@ +JobType: session +Environment: LANG=en_US.UTF-8 + LANGUAGE=en_US:en + LC_ALL=en_US.UTF-8 +JobType: any +Environment: PATH=/opt/python/default/bin:$PATH diff --git a/workbench/2026.05/conf/launcher/launcher.conf b/workbench/2026.05/conf/launcher/launcher.conf new file mode 100644 index 0000000..c73ef78 --- /dev/null +++ b/workbench/2026.05/conf/launcher/launcher.conf @@ -0,0 +1,12 @@ +[server] +address=127.0.0.1 +port=5559 +server-user=rstudio-server +admin-group=rstudio-server +authorization-enabled=1 +thread-pool-size=4 +enable-debug-logging=1 + +[cluster] +name=Local +type=Local diff --git a/workbench/2026.05/conf/launcher/launcher.local.conf b/workbench/2026.05/conf/launcher/launcher.local.conf new file mode 100644 index 0000000..051857e --- /dev/null +++ b/workbench/2026.05/conf/launcher/launcher.local.conf @@ -0,0 +1 @@ +unprivileged=1 diff --git a/workbench/2026.05/conf/positron/positron-user-settings.json b/workbench/2026.05/conf/positron/positron-user-settings.json new file mode 100644 index 0000000..544c7f2 --- /dev/null +++ b/workbench/2026.05/conf/positron/positron-user-settings.json @@ -0,0 +1,6 @@ +{ + "extensions.autoCheckUpdates": false, + "extensions.autoUpdate": false, + "quarto.path": "/usr/lib/rstudio-server/bin/quarto/bin/quarto", + "terminal.integrated.defaultProfile.linux": "bash" +} diff --git a/workbench/2026.05/conf/positron/positron.extensions.conf b/workbench/2026.05/conf/positron/positron.extensions.conf new file mode 100644 index 0000000..42191cc --- /dev/null +++ b/workbench/2026.05/conf/positron/positron.extensions.conf @@ -0,0 +1,2 @@ +posit.shiny +posit.publisher diff --git a/workbench/2026.05/conf/rstudio/logging.conf b/workbench/2026.05/conf/rstudio/logging.conf new file mode 100644 index 0000000..08a0917 --- /dev/null +++ b/workbench/2026.05/conf/rstudio/logging.conf @@ -0,0 +1,3 @@ +[*] +logger-type=stderr +log-level=info diff --git a/workbench/2026.05/conf/rstudio/rserver-float.conf b/workbench/2026.05/conf/rstudio/rserver-float.conf new file mode 100644 index 0000000..f04dd8c --- /dev/null +++ b/workbench/2026.05/conf/rstudio/rserver-float.conf @@ -0,0 +1 @@ +server-license-type=remote diff --git a/workbench/2026.05/conf/rstudio/rserver.conf b/workbench/2026.05/conf/rstudio/rserver.conf new file mode 100644 index 0000000..29ba018 --- /dev/null +++ b/workbench/2026.05/conf/rstudio/rserver.conf @@ -0,0 +1,13 @@ +server-health-check-enabled=1 +admin-enabled=1 + +www-port=8787 +server-project-sharing=0 +auth-pam-sessions-enabled=1 + +# Launcher Config +launcher-address=127.0.0.1 +launcher-port=5559 +launcher-sessions-enabled=1 +launcher-default-cluster=Local +launcher-sessions-callback-address=http://127.0.0.1:8787 diff --git a/workbench/2026.05/conf/sssd/sssd.conf b/workbench/2026.05/conf/sssd/sssd.conf new file mode 100644 index 0000000..7bbba4a --- /dev/null +++ b/workbench/2026.05/conf/sssd/sssd.conf @@ -0,0 +1,9 @@ +[sssd] +config_file_version = 2 +domains = placeholder + +[domain/placeholder] +id_provider = none +auth_provider = none +chpass_provider = none +sudo_provider = none diff --git a/workbench/2026.05/conf/vscode/vscode-user-settings.json b/workbench/2026.05/conf/vscode/vscode-user-settings.json new file mode 100644 index 0000000..347ab85 --- /dev/null +++ b/workbench/2026.05/conf/vscode/vscode-user-settings.json @@ -0,0 +1,5 @@ +{ + "extensions.autoCheckUpdates": false, + "extensions.autoUpdate": false, + "terminal.integrated.shell.linux": "bash" +} diff --git a/workbench/2026.05/conf/vscode/vscode.conf b/workbench/2026.05/conf/vscode/vscode.conf new file mode 100644 index 0000000..886a529 --- /dev/null +++ b/workbench/2026.05/conf/vscode/vscode.conf @@ -0,0 +1,2 @@ +enabled=1 +args=--verbose --host=0.0.0.0 diff --git a/workbench/2026.05/conf/vscode/vscode.extensions.conf b/workbench/2026.05/conf/vscode/vscode.extensions.conf new file mode 100644 index 0000000..4fe33b5 --- /dev/null +++ b/workbench/2026.05/conf/vscode/vscode.extensions.conf @@ -0,0 +1,5 @@ +quarto.quarto +REditorSupport.r +ms-python.python +posit.shiny +ms-toolsai.jupyter diff --git a/workbench/2026.05/deps/ubuntu-22.04_optional_packages.txt b/workbench/2026.05/deps/ubuntu-22.04_optional_packages.txt new file mode 100644 index 0000000..47fa26b --- /dev/null +++ b/workbench/2026.05/deps/ubuntu-22.04_optional_packages.txt @@ -0,0 +1,45 @@ +cmake +default-jdk +gdal-bin +gdb +git +gsfonts +imagemagick +libapparmor1 +libcairo2-dev +libcurl4-openssl-dev +libedit2 +libfontconfig1-dev +libfreetype6-dev +libfribidi-dev +libgdal-dev +libgeos-dev +libgl1-mesa-dev +libglpk-dev +libglu1-mesa-dev +libharfbuzz-dev +libicu-dev +libjpeg-dev +libmagick++-dev +libmysqlclient-dev +libnode-dev +libpng-dev +libproj-dev +libsodium-dev +libsqlite3-dev +libssh2-1-dev +libtiff-dev +libudunits2-dev +libxml2-dev +make +oddjob-mkhomedir +python3 +strace +sssd +tcl +tk +tk-dev +tk-table +unixodbc-dev +xz-utils +zlib1g-dev diff --git a/workbench/2026.05/deps/ubuntu-22.04_packages.txt b/workbench/2026.05/deps/ubuntu-22.04_packages.txt new file mode 100644 index 0000000..7586568 --- /dev/null +++ b/workbench/2026.05/deps/ubuntu-22.04_packages.txt @@ -0,0 +1,12 @@ +build-essential +libc6 +libclang-dev +libpq5 +libssl-dev +libsqlite3-0 +libxkbcommon-x11-0 +lsb-release +psmisc +rrdtool +sudo +supervisor diff --git a/workbench/2026.05/deps/ubuntu-24.04_optional_packages.txt b/workbench/2026.05/deps/ubuntu-24.04_optional_packages.txt new file mode 100644 index 0000000..a76a840 --- /dev/null +++ b/workbench/2026.05/deps/ubuntu-24.04_optional_packages.txt @@ -0,0 +1,45 @@ +cmake +default-jdk +gdal-bin +gdb +git +gsfonts +imagemagick +libapparmor1 +libcairo2-dev +libcurl4-openssl-dev +libedit2 +libfontconfig1-dev +libfreetype-dev +libfribidi-dev +libgdal-dev +libgeos-dev +libgl1-mesa-dev +libglpk-dev +libglu1-mesa-dev +libharfbuzz-dev +libicu-dev +libjpeg-dev +libmagick++-dev +libmysqlclient-dev +libnode-dev +libpng-dev +libproj-dev +libsodium-dev +libsqlite3-dev +libssh2-1-dev +libtiff-dev +libudunits2-dev +libxml2-dev +make +oddjob-mkhomedir +python3 +strace +sssd +tcl +tk +tk-dev +tk-table +unixodbc-dev +xz-utils +zlib1g-dev diff --git a/workbench/2026.05/deps/ubuntu-24.04_packages.txt b/workbench/2026.05/deps/ubuntu-24.04_packages.txt new file mode 100644 index 0000000..7586568 --- /dev/null +++ b/workbench/2026.05/deps/ubuntu-24.04_packages.txt @@ -0,0 +1,12 @@ +build-essential +libc6 +libclang-dev +libpq5 +libssl-dev +libsqlite3-0 +libxkbcommon-x11-0 +lsb-release +psmisc +rrdtool +sudo +supervisor diff --git a/workbench/2026.05/scripts/install_workbench.sh b/workbench/2026.05/scripts/install_workbench.sh new file mode 100755 index 0000000..7d56dae --- /dev/null +++ b/workbench/2026.05/scripts/install_workbench.sh @@ -0,0 +1,39 @@ +#!/bin/bash +set -euo pipefail + +# Output delimiter +d="====" +package_name="rstudio-server" + +# Update apt repositories +apt-get update -yq + +echo "$d Fetching Posit Workbench 2026.05.0+218.pro1 $d" +# For non-development versions, download the deb package using apt-get +apt-get download "${package_name}=2026.05.0+218.pro1" +deb_file="$(pwd)/$(ls ${package_name}*.deb)" + +# Install dependencies +# shellcheck disable=SC2046 +apt-get install -yq $(dpkg -I "$deb_file" | grep '^ Depends:' | sed 's/^ Depends: //' | tr ',' '\n' | awk '{print $1}' | tr -d '(') + +# Patch the installer to not activate the service +echo "$d Patching ${deb_file} $d" +dpkg --unpack "${deb_file}" +sed -i 's/^rstudio-server force-suspend-all/# rstudio-server force-suspend-all/' /var/lib/dpkg/info/rstudio-server.postinst +sed -i 's/systemctl enable rstudio-server.service/# systemctl enable rstudio-server.service/g' /var/lib/dpkg/info/rstudio-server.postinst +sed -i 's/systemctl enable rstudio-launcher.service/# systemctl enable rstudio-launcher.service/g' /var/lib/dpkg/info/rstudio-server.postinst +awk '/if test "\$RSTUDIO_INSTALL_NO_LICENSE_INITIALIZATION" != "1"/ { skip=1 } + skip { if (/fi/) { skip=0 } next } + { print } +' "/var/lib/dpkg/info/rstudio-server.postinst" > "/var/lib/dpkg/info/rstudio-server.postinst.tmp" && mv "/var/lib/dpkg/info/rstudio-server.postinst.tmp" "/var/lib/dpkg/info/rstudio-server.postinst" + +# Install Workbench +echo "$d Install Posit Workbench 2026.05.0+218.pro1 $d" +dpkg --configure "${package_name}" +apt-get install -yf +rm -f "${deb_file}" + +# Clean up +apt-get clean -yqq && \ +rm -rf /var/lib/apt/lists/* diff --git a/workbench/2026.05/scripts/startup.sh b/workbench/2026.05/scripts/startup.sh new file mode 100755 index 0000000..465fb69 --- /dev/null +++ b/workbench/2026.05/scripts/startup.sh @@ -0,0 +1,110 @@ +#!/bin/bash + +set -e +if [[ "${PWB_STARTUP_DEBUG:-0}" -eq 1 ]]; then + set -x +fi + +# Deactivate license when the process exits +deactivate() { + echo "== Exiting ==" + rstudio-server stop + + echo "Deactivating license ..." + is_deactivated=0 + retries=0 + while [[ $is_deactivated -ne 1 ]] && [[ $retries -le 3 ]]; do + /usr/lib/rstudio-server/bin/license-manager deactivate >/dev/null 2>&1 + is_deactivated=1 + ((retries+=1)) + # shellcheck disable=SC2045 + for file in $(ls -A /var/lib/.local); do + if [ -s "/var/lib/.local/$file" ]; then + if [[ $retries -lt 3 ]]; then + echo "License did not deactivate, retry ${retries}..." + is_deactivated=0 + else + echo "Unable to deactivate license. If you encounter issues activating your product in the future, please contact Posit support." + fi + continue + fi + done + done +} +trap deactivate EXIT + +# Backward compatibility for RSW_ prefix +PWB_TESTUSER=${PWB_TESTUSER:-${RSW_TESTUSER}} +PWB_TESTUSER_UID=${PWB_TESTUSER_UID:-${RSW_TESTUSER_UID}} +PWB_TESTUSER_PASSWD=${PWB_TESTUSER_PASSWD:-${RSW_TESTUSER_PASSWD}} + +verify_installation(){ + echo "==VERIFY INSTALLATION=="; + mkdir -p "$PWB_DIAGNOSTIC_DIR" + chmod 777 "$PWB_DIAGNOSTIC_DIR" + rstudio-server verify-installation --verify-user="$PWB_TESTUSER" | tee "$PWB_DIAGNOSTIC_DIR/verify.log" +} + +# Backward compatibility for RSW_ and RSP_ prefixes +PWB_LICENSE=${PWB_LICENSE:-${RSW_LICENSE:-${RSP_LICENSE}}} +PWB_LICENSE_SERVER=${PWB_LICENSE_SERVER:-${RSW_LICENSE_SERVER:-${RSP_LICENSE_SERVER}}} +PWB_LICENSE_FILE_PATH=${PWB_LICENSE_FILE_PATH:-${RSW_LICENSE_FILE_PATH}} + +# Activate License +PWB_LICENSE_FILE_PATH=${PWB_LICENSE_FILE_PATH:-/etc/rstudio-server/license.lic} +if [ -n "$PWB_LICENSE" ]; then + /usr/lib/rstudio-server/bin/license-manager activate "$PWB_LICENSE" +elif [ -n "$PWB_LICENSE_SERVER" ]; then + /usr/lib/rstudio-server/bin/license-manager license-server "$PWB_LICENSE_SERVER" +elif test -f "$PWB_LICENSE_FILE_PATH"; then + /usr/lib/rstudio-server/bin/license-manager activate-file "$PWB_LICENSE_FILE_PATH" +fi + +# ensure these cannot be inherited by child processes +unset PWB_LICENSE +unset PWB_LICENSE_SERVER +unset RSP_LICENSE +unset RSP_LICENSE_SERVER +unset RSW_LICENSE +unset RSW_LICENSE_SERVER + +# Create one user +if [ "$(getent passwd "$PWB_TESTUSER_UID")" ] ; then + echo "UID $PWB_TESTUSER_UID already exists, not creating $PWB_TESTUSER test user"; +else + if [ -z "$PWB_TESTUSER" ]; then + echo "Empty 'PWB_TESTUSER' variables, not creating test user"; + else + if [ -z "$PWB_TESTUSER_UID" ]; then + PWB_TESTUSER_UID=10000 + fi + useradd -m -s /bin/bash -u "$PWB_TESTUSER_UID" -U "$PWB_TESTUSER" + echo "$PWB_TESTUSER:$PWB_TESTUSER_PASSWD" | sudo chpasswd + fi +fi + +# Backward compatibility for RSW_ prefix +PWB_LAUNCHER=${PWB_LAUNCHER:-${RSW_LAUNCHER}} +PWB_LAUNCHER_TIMEOUT=${PWB_LAUNCHER_TIMEOUT:-${RSW_LAUNCHER_TIMEOUT}} + +# Start Launcher +if [ "$PWB_LAUNCHER" == "true" ]; then + echo "Waiting for launcher to startup... to disable set PWB_LAUNCHER=false" + wait-for-it.sh localhost:5559 -t "$PWB_LAUNCHER_TIMEOUT" +fi + +# Check diagnostic configurations +if [ "$PWB_DIAGNOSTIC_ENABLE" == "true" ]; then + verify_installation + if [ "$PWB_EXIT_AFTER_VERIFY" == "true" ]; then + echo "$(<"$PWB_DIAGNOSTIC_DIR"/verify.log)"; + echo "Exiting script because PWB_EXIT_AFTER_VERIFY=${PWB_EXIT_AFTER_VERIFY}"; + exit 0 + fi; +else + echo "not running verify installation because PWB_DIAGNOSTIC_ENABLE=${PWB_DIAGNOSTIC_ENABLE}"; +fi + +# the main container process +# cannot use "exec" or the "trap" will be lost +/usr/lib/rstudio-server/bin/rserver --server-daemonize 0 > /dev/stderr diff --git a/workbench/2026.05/startup/base/rstudio-workbench.conf b/workbench/2026.05/startup/base/rstudio-workbench.conf new file mode 100644 index 0000000..0acac70 --- /dev/null +++ b/workbench/2026.05/startup/base/rstudio-workbench.conf @@ -0,0 +1,8 @@ +[program:rstudio-workbench] +command=/usr/local/bin/startup.sh +autorestart=false +numprocs=1 +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 diff --git a/workbench/2026.05/startup/launcher/rstudio-launcher.conf b/workbench/2026.05/startup/launcher/rstudio-launcher.conf new file mode 100644 index 0000000..5fd8540 --- /dev/null +++ b/workbench/2026.05/startup/launcher/rstudio-launcher.conf @@ -0,0 +1,8 @@ +[program:rstudio-launcher] +command=/usr/lib/rstudio-server/bin/rstudio-launcher +autorestart=false +numprocs=1 +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 diff --git a/workbench/2026.05/startup/supervisord.conf b/workbench/2026.05/startup/supervisord.conf new file mode 100644 index 0000000..7180a82 --- /dev/null +++ b/workbench/2026.05/startup/supervisord.conf @@ -0,0 +1,47 @@ +; supervisor config file + +[unix_http_server] +file=/var/run/supervisor.sock ; (the path to the socket file) +chmod=0700 ; sockef file mode (default 0700) + +[supervisord] +logfile=/dev/stdout ; (main log file;default $CWD/supervisord.log) +user=root +pidfile=/var/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid) +; should configure each program to use stdout/stderr +; childlogdir=/var/log/supervisor ; ('AUTO' child log dir, default $TEMP) +logfile_maxbytes=0 +logfile_backups=0 +loglevel=info +nodaemon=true + +; the below section must remain in the config file for RPC +; (supervisorctl/web interface) to work, additional interfaces may be +; added by defining them in separate rpcinterface: sections +[rpcinterface:supervisor] +supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface + +[supervisorctl] +serverurl=unix:///var/run/supervisor.sock ; use a unix:// URL for a unix socket + +; The [include] section can just contain the "files" setting. This +; setting can list multiple files (separated by whitespace or +; newlines). It can also contain wildcards. The filenames are +; interpreted as relative to this file. Included files *cannot* +; include files themselves. + +[eventlistener:process-monitor] +command=bash -c "printf 'READY\n' && while read line; do kill -SIGQUIT $PPID; done < /dev/stdin" +events=PROCESS_STATE_STOPPED,PROCESS_STATE_EXITED,PROCESS_STATE_FATAL +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stdout_logfile_backups=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 +stderr_logfile_backups=0 + +; beware possible race condition +; if one of these services exit before the process-monitor is up + +[include] +files = /startup/base/*.conf /startup/launcher/*.conf /startup/user-provisioning/*.conf /startup/custom/*.conf diff --git a/workbench/2026.05/startup/user-provisioning/sssd.conf b/workbench/2026.05/startup/user-provisioning/sssd.conf new file mode 100644 index 0000000..387913e --- /dev/null +++ b/workbench/2026.05/startup/user-provisioning/sssd.conf @@ -0,0 +1,11 @@ +[program:sssd] +# TODO: a way to disable this easily...? +command=/usr/sbin/sssd -i -c /etc/sssd/sssd.conf --logger=stderr +autorestart=false +numprocs=1 +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stdout_logfile_backups=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 +stderr_logfile_backups=0 diff --git a/workbench/2026.05/test/goss.yaml b/workbench/2026.05/test/goss.yaml new file mode 100644 index 0000000..1841f52 --- /dev/null +++ b/workbench/2026.05/test/goss.yaml @@ -0,0 +1,238 @@ +user: + rstudio-server: + exists: true + uid: 999 + gid: {{ if and (eq .Env.IMAGE_VARIANT "Standard") (and (eq .Env.IMAGE_OS_NAME "ubuntu") (eq .Env.IMAGE_OS_VERSION "24.04")) }}997{{ else }}999{{ end }} + groups: + - rstudio-server + +group: + rstudio-server: + exists: true + gid: {{ if and (eq .Env.IMAGE_VARIANT "Standard") (and (eq .Env.IMAGE_OS_NAME "ubuntu") (eq .Env.IMAGE_OS_VERSION "24.04")) }}997{{ else }}999{{ end }} + +package: + rstudio-server: + installed: true + + r-4.6.0: + installed: true + skip: {{ if eq .Env.IMAGE_VARIANT "Minimal" }}true{{ else }}false{{ end }} + + {{ $apt_package_list_path := printf "/tmp/version/deps/%s-%s_packages.txt" .Env.IMAGE_OS_NAME .Env.IMAGE_OS_VERSION }} + {{ $apt_package_list := readFile $apt_package_list_path | splitList "\n" }} + {{- range $apt_package_list }} + {{.}}: + installed: true + {{end}} + {{ if eq .Env.IMAGE_VARIANT "Standard" }} + + {{ $apt_optional_package_list_path := printf "/tmp/version/deps/%s-%s_optional_packages.txt" .Env.IMAGE_OS_NAME .Env.IMAGE_OS_VERSION }} + {{ $apt_optional_package_list := readFile $apt_optional_package_list_path | splitList "\n" }} + {{- range $apt_optional_package_list }} + {{.}}: + installed: true + {{end}} + {{ end }} + +port: + # WARNING: This test may fail depending on how ipv6 is configured on the host + # Known Issue with goss: https://github.com/goss-org/goss/issues/149 + tcp:8787: + listening: true + ip: + - 0.0.0.0 + skip: {{ if eq .Env.IMAGE_VARIANT "Minimal" }}true{{ else }}false{{ end }} + tcp:5559: + listening: true + ip: + - 127.0.0.1 + skip: {{ if eq .Env.IMAGE_VARIANT "Minimal" }}true{{ else }}false{{ end }} + +process: + rserver: + running: true + skip: {{ if eq .Env.IMAGE_VARIANT "Minimal" }}true{{ else }}false{{ end }} + # The output from `ps` is trucated to 16 characters, so we check for `rstudio-launche` instead of `rstudio-launcher` + rstudio-launche: + running: true + skip: {{ if eq .Env.IMAGE_VARIANT "Minimal" }}true{{ else }}false{{ end }} + +file: + # Check that license files were not accidentally included in build + /etc/rstudio-server/license.lic: + exists: false + # Check that the RStudio Server installation directory exists + /usr/lib/rstudio-server: + exists: true + # Check for rserver executable + /usr/lib/rstudio-server/bin/rserver: + exists: true + # Check for rstudio-server executable + /usr/lib/rstudio-server/bin/rstudio-server: + exists: true + # Check for rstudio-launcher executable + /usr/lib/rstudio-server/bin/rstudio-launcher: + exists: true + # Check for rstudio-server monitor log directory + /var/lib/rstudio-server/monitor/log: + exists: true + owner: rstudio-server + group: rstudio-server + # Check for code-server executable (path varies based on RStudio Workbench version) + {{ $version_split := split "." "2026.05.0+218.pro1" }} + {{ if or (ge ($version_split._0 | atoi) 2025) (and (ge ($version_split._0 | atoi) 2024) (ge ($version_split._1 | atoi) 7)) }} + /usr/lib/rstudio-server/bin/pwb-code-server/bin/code-server: + exists: true + {{ else }} + /usr/lib/rstudio-server/bin/code-server/bin/code-server: + exists: true + {{ end }} + # Check for vscode.conf and ensure VS Code is enabled + /etc/rstudio/vscode.conf: + exists: true + contents: + - "enabled=1" + # Check pam configuration for mkhomedir + /etc/pam.d/common-session: + exists: true + contents: + - "session required pam_mkhomedir.so skel=/etc/skel umask=0077" + # Check for sssd.conf + /etc/sssd/sssd.conf: + exists: {{ if eq .Env.IMAGE_VARIANT "Standard" }}true{{ else }}false{{ end }} + owner: root + group: root + mode: "0600" + # Check for repos.conf + /etc/rstudio/repos.conf: + exists: true + contents: + - "RSPM=https://p3m.dev/cran/__linux__/{{ .Env.IMAGE_OS_CODENAME }}/latest" + - "CRAN=https://p3m.dev/cran/__linux__/{{ .Env.IMAGE_OS_CODENAME }}/latest" + # Check for symlinked Quarto executable + /usr/local/bin/quarto: + exists: true + filetype: symlink + # TinyTeX is installed under /opt/.TinyTeX (via HOME="/opt") so non-root runtime + # users can read the install; tlmgr path add symlinks the binaries into + # /usr/local/bin so they are on PATH. + /opt/.TinyTeX: + exists: {{ if eq .Env.IMAGE_VARIANT "Standard" }}true{{ else }}false{{ end }} + filetype: directory + /usr/local/bin/tlmgr: + exists: {{ if eq .Env.IMAGE_VARIANT "Standard" }}true{{ else }}false{{ end }} + filetype: symlink + /usr/local/bin/pdflatex: + exists: {{ if eq .Env.IMAGE_VARIANT "Standard" }}true{{ else }}false{{ end }} + filetype: symlink + # Check for R installation + + /opt/R/4.6.0/bin/R: + exists: {{ if eq .Env.IMAGE_VARIANT "Standard" }}true{{ else }}false{{ end }} + filetype: file + + # Check for Python installation + + /opt/python/3.14.5/bin/python: + exists: {{ if eq .Env.IMAGE_VARIANT "Standard" }}true{{ else }}false{{ end }} + + /opt/python/jupyter/bin/python: + exists: {{ if eq .Env.IMAGE_VARIANT "Standard" }}true{{ else }}false{{ end }} + /opt/python/jupyter/bin/jupyter: + exists: {{ if eq .Env.IMAGE_VARIANT "Standard" }}true{{ else }}false{{ end }} + /usr/local/bin/jupyter: + exists: {{ if eq .Env.IMAGE_VARIANT "Standard" }}true{{ else }}false{{ end }} + filetype: symlink + + # Check that job nodes are not created for the builder + /var/lib/rstudio-launcher/Local/jobs/buildkitsystem: + exists: false + /var/lib/rstudio-launcher/Local/jobs/buildkitsandbox: + exists: false + + # Check that startup files were not accidentally included in build + # These can only be reliably checked for minimal variant for now, see #372 + + {{ if eq .Env.IMAGE_VARIANT "Minimal" }} + /etc/rstudio/launcher.pem: + exists: false + /etc/rstudio/launcher.pub: + exists: false + /var/lib/rstudio-server/secure-cookie-key: + exists: false + /var/lib/rstudio-server/session-rpc-key: + exists: false + /etc/rstudio/secure-cookie-key: + exists: false + /etc/rstudio/session-rpc-key: + exists: false + /var/lib/rstudio-server/rstudio.sqlite: + exists: false + {{ end }} + + +command: + "Ensure rstudio-server has permissions to log directory": + exec: su rstudio-server -c 'touch /var/lib/rstudio-server/monitor/log/rstudio-server.log' + exit-status: 0 + "Ensure server log can be created": + exec: touch /var/log/rstudio-server.log + exit-status: 0 + "Verify Posit Workbench version": + exec: /usr/lib/rstudio-server/bin/rstudio-server version + exit-status: 0 + stdout: + - "2026.05.0+218.pro1 Workbench" + "Verify Jupyter Notebook works": + exec: "echo '{ \"cells\": [], \"metadata\": {}, \"nbformat\": 4, \"nbformat_minor\": 2}' | /opt/python/jupyter/bin/jupyter nbconvert --to notebook --stdin --stdout" + timeout: 60000 + exit-status: 0 + skip: {{ if eq .Env.IMAGE_VARIANT "Minimal" }}true{{ else }}false{{ end }} + "Verify R installation is version 4.6.0": + exec: /opt/R/4.6.0/bin/R --version + exit-status: 0 + stdout: + - "R version 4.6.0" + skip: {{ if eq .Env.IMAGE_VARIANT "Minimal" }}true{{ else }}false{{ end }} + + "Verify Python installation is version 3.14.5": + exec: /opt/python/3.14.5/bin/python --version + exit-status: 0 + stdout: + - "3.14.5" + skip: {{ if eq .Env.IMAGE_VARIANT "Minimal" }}true{{ else }}false{{ end }} + + "Verify Jupyter installation Python version": + exec: /opt/python/jupyter/bin/python --version + exit-status: 0 + stdout: + - "3.14.5" + skip: {{ if eq .Env.IMAGE_VARIANT "Minimal" }}true{{ else }}false{{ end }} + "Verify system OpenSSL": + exec: which openssl + exit-status: 0 + stdout: + - "/usr/bin/openssl" + "Verify Quarto": + exec: /usr/local/bin/quarto check --quiet + timeout: 120000 + exit-status: 0 + "Verify Quarto has TinyTeX installed": + exec: "HOME='/opt' quarto list tools" + exit-status: 0 + stderr: + - "/tinytex\\s+Up to date/" + skip: {{ if eq .Env.IMAGE_VARIANT "Minimal" }}true{{ else }}false{{ end }} + "Verify TinyTeX is readable and executable by a non-root user": + # Runs tlmgr and pdflatex as `nobody` to confirm the /opt/.TinyTeX install + # is accessible to non-root. `command -v` also forces PATH resolution, + # not just direct invocation — proves the /usr/local/bin symlinks are on + # the user's PATH. Guards against regressions in placement, permissions, + # or PATH setup. + exec: su -s /bin/sh -c 'command -v tlmgr && command -v pdflatex && tlmgr --version && pdflatex --version' nobody + exit-status: 0 + stdout: + - "tlmgr" + - "pdfTeX" + skip: {{ if eq .Env.IMAGE_VARIANT "Minimal" }}true{{ else }}false{{ end }} diff --git a/workbench/README.md b/workbench/README.md index 0063512..548e95c 100644 --- a/workbench/README.md +++ b/workbench/README.md @@ -45,7 +45,7 @@ For Kubernetes deployments, Workbench uses these images together. See the [repos ### Quick start ```bash -PWB_VERSION="latest" # or a specific version like "2026.04.0" +PWB_VERSION="latest" # or a specific version like "2026.05.0" PWB_IMAGE="ghcr.io/posit-dev/workbench" # or docker.io/posit/workbench PWB_LICENSE_FILE_HOST_PATH="/path/to/license.lic" PWB_LICENSE_FILE_PATH="/etc/rstudio-server/license.lic" # this is the default path for the `PWB_LICENSE_FILE_PATH` container environment variable, included for illustrative purposes @@ -68,7 +68,7 @@ Access Workbench at `http://localhost:8787`. Log in with username `posit` and pa ### With a custom configuration file ```bash -PWB_VERSION="latest" # or a specific version like "2026.04.0" +PWB_VERSION="latest" # or a specific version like "2026.05.0" PWB_IMAGE="ghcr.io/posit-dev/workbench" # or docker.io/posit/workbench PWB_LICENSE_FILE_HOST_PATH="/path/to/license.lic" PWB_CONFIG_HOST_PATH="/path/to/rstudio"