diff --git a/posit-bakery/posit_bakery/config/templating/macros/r.j2 b/posit-bakery/posit_bakery/config/templating/macros/r.j2 index d38f411f0..02dd7aa77 100644 --- a/posit-bakery/posit_bakery/config/templating/macros/r.j2 +++ b/posit-bakery/posit_bakery/config/templating/macros/r.j2 @@ -26,6 +26,40 @@ RUN_UNATTENDED=1 R_VERSION={{ version | trim }} bash -c "$(curl -fsSL https://rs find . -type f -name '[rR]-{{ version | trim }}.*\.(deb|rpm)' -delete {%- endmacro %} +{# Downloads and extracts a manylinux R tarball from cdn.posit.co/r directly to +/opt/R/{version}/. Use in place of run_install when the platform cannot satisfy +the RPM/deb dependencies. + +:param version: The R version to install, e.g. "4.4.3" +:param glibc_version: The manylinux glibc version string, e.g. "2_34" (default). + Requires the host glibc to be >= the chosen version. +#} +{% macro install_manylinux(version, glibc_version="2_34") -%} +{%- set v = version | trim -%} +ARCH=$(uname -m) && \ +ARCH_SUFFIX=$([ "$ARCH" = "aarch64" ] && echo "-arm64" || echo "") && \ +curl -fsSL "https://cdn.posit.co/r/manylinux_{{ glibc_version }}/R-{{ v }}-manylinux_{{ glibc_version }}${ARCH_SUFFIX}.tar.gz" \ + | tar -xz -C /opt/R/ +{%- endmacro %} + +{# Installs one or more versions of R from manylinux tarballs as separate RUN statements + +:param versions: A list of R versions or a comma separated string of R versions to install +:param glibc_version: The manylinux glibc version string, e.g. "2_34" (default). + Passed through to install_manylinux. +#} +{% macro run_install_manylinux(versions, glibc_version="2_34") -%} +{%- if versions is string -%} +{%- set versions = versions | split(",") | map('trim') -%} +{%- endif -%} +RUN mkdir -p /opt/R +{% for version in versions -%} +RUN {{ install_manylinux(version, glibc_version) | indent(4) }} +{%- if not loop.last %} +{% endif %} +{%- endfor %} +{%- endmacro %} + {# Installs one or more versions of R as separate RUN statements :param versions: A list of R versions or a comma separated string of R versions to install diff --git a/posit-bakery/test/config/templating/test_macros.py b/posit-bakery/test/config/templating/test_macros.py index ce71d07c5..641f2c90b 100644 --- a/posit-bakery/test/config/templating/test_macros.py +++ b/posit-bakery/test/config/templating/test_macros.py @@ -2385,6 +2385,97 @@ def test_run_install(self, environment_with_macros, input, expected): rendered = environment_with_macros.from_string(template).render() assert rendered == expected + def test_install_manylinux_default(self, environment_with_macros): + template = '{%- import "r.j2" as r -%}\n{{ r.install_manylinux("4.6.0") }}' + expected = textwrap.dedent( + """\ + ARCH=$(uname -m) && \\ + ARCH_SUFFIX=$([ "$ARCH" = "aarch64" ] && echo "-arm64" || echo "") && \\ + curl -fsSL "https://cdn.posit.co/r/manylinux_2_34/R-4.6.0-manylinux_2_34${ARCH_SUFFIX}.tar.gz" \\ + | tar -xz -C /opt/R/""" + ) + rendered = environment_with_macros.from_string(template).render() + assert rendered == expected + + def test_install_manylinux_explicit_version(self, environment_with_macros): + template = '{%- import "r.j2" as r -%}\n{{ r.install_manylinux("4.6.0", glibc_version="2_28") }}' + expected = textwrap.dedent( + """\ + ARCH=$(uname -m) && \\ + ARCH_SUFFIX=$([ "$ARCH" = "aarch64" ] && echo "-arm64" || echo "") && \\ + curl -fsSL "https://cdn.posit.co/r/manylinux_2_28/R-4.6.0-manylinux_2_28${ARCH_SUFFIX}.tar.gz" \\ + | tar -xz -C /opt/R/""" + ) + rendered = environment_with_macros.from_string(template).render() + assert rendered == expected + + @pytest.mark.parametrize( + "input,expected", + [ + pytest.param( + ["4.6.0"], + textwrap.dedent( + """\ + RUN mkdir -p /opt/R + RUN ARCH=$(uname -m) && \\ + ARCH_SUFFIX=$([ "$ARCH" = "aarch64" ] && echo "-arm64" || echo "") && \\ + curl -fsSL "https://cdn.posit.co/r/manylinux_2_34/R-4.6.0-manylinux_2_34${ARCH_SUFFIX}.tar.gz" \\ + | tar -xz -C /opt/R/""" + ), + id="single-version", + ), + pytest.param( + ["4.6.0", "4.4.3"], + textwrap.dedent( + """\ + RUN mkdir -p /opt/R + RUN ARCH=$(uname -m) && \\ + ARCH_SUFFIX=$([ "$ARCH" = "aarch64" ] && echo "-arm64" || echo "") && \\ + curl -fsSL "https://cdn.posit.co/r/manylinux_2_34/R-4.6.0-manylinux_2_34${ARCH_SUFFIX}.tar.gz" \\ + | tar -xz -C /opt/R/ + RUN ARCH=$(uname -m) && \\ + ARCH_SUFFIX=$([ "$ARCH" = "aarch64" ] && echo "-arm64" || echo "") && \\ + curl -fsSL "https://cdn.posit.co/r/manylinux_2_34/R-4.4.3-manylinux_2_34${ARCH_SUFFIX}.tar.gz" \\ + | tar -xz -C /opt/R/""" + ), + id="multiple-versions", + ), + pytest.param( + "'4.6.0,4.4.3'", + textwrap.dedent( + """\ + RUN mkdir -p /opt/R + RUN ARCH=$(uname -m) && \\ + ARCH_SUFFIX=$([ "$ARCH" = "aarch64" ] && echo "-arm64" || echo "") && \\ + curl -fsSL "https://cdn.posit.co/r/manylinux_2_34/R-4.6.0-manylinux_2_34${ARCH_SUFFIX}.tar.gz" \\ + | tar -xz -C /opt/R/ + RUN ARCH=$(uname -m) && \\ + ARCH_SUFFIX=$([ "$ARCH" = "aarch64" ] && echo "-arm64" || echo "") && \\ + curl -fsSL "https://cdn.posit.co/r/manylinux_2_34/R-4.4.3-manylinux_2_34${ARCH_SUFFIX}.tar.gz" \\ + | tar -xz -C /opt/R/""" + ), + id="string-versions", + ), + ], + ) + def test_run_install_manylinux(self, environment_with_macros, input, expected): + template = '{%- import "r.j2" as r -%}\n{{ r.run_install_manylinux(' + str(input) + ") }}" + rendered = environment_with_macros.from_string(template).render() + assert rendered == expected + + def test_run_install_manylinux_explicit_glibc_version(self, environment_with_macros): + template = '{%- import "r.j2" as r -%}\n{{ r.run_install_manylinux(["4.6.0"], "2_28") }}' + expected = textwrap.dedent( + """\ + RUN mkdir -p /opt/R + RUN ARCH=$(uname -m) && \\ + ARCH_SUFFIX=$([ "$ARCH" = "aarch64" ] && echo "-arm64" || echo "") && \\ + curl -fsSL "https://cdn.posit.co/r/manylinux_2_28/R-4.6.0-manylinux_2_28${ARCH_SUFFIX}.tar.gz" \\ + | tar -xz -C /opt/R/""" + ) + rendered = environment_with_macros.from_string(template).render() + assert rendered == expected + @pytest.mark.parametrize( "_os,expected", [