From 8edfa530bfd831a0585d54604721b0c34af44643 Mon Sep 17 00:00:00 2001 From: Hugo Lecomte Date: Thu, 10 Jun 2021 18:03:43 +0200 Subject: [PATCH 1/2] Add Guix buildpack --- docs/source/config_files.rst | 21 +++++ repo2docker/app.py | 2 + repo2docker/buildpacks/__init__.py | 1 + repo2docker/buildpacks/base.py | 1 + repo2docker/buildpacks/guix/__init__.py | 83 +++++++++++++++++++ repo2docker/buildpacks/guix/guix-install.bash | 14 ++++ tests/guix/binder-dir/README.rst | 4 + tests/guix/binder-dir/binder/manifest.scm | 3 + tests/guix/binder-dir/verify | 3 + tests/guix/ignore-outside/README.rst | 4 + tests/guix/ignore-outside/binder/manifest.scm | 4 + tests/guix/ignore-outside/manifest.scm | 2 + tests/guix/ignore-outside/verify | 3 + tests/guix/simple-channels/README.rst | 4 + tests/guix/simple-channels/channels.scm | 20 +++++ tests/guix/simple-channels/manifest.scm | 3 + tests/guix/simple-channels/verify | 3 + tests/guix/simple/README.rst | 5 ++ tests/guix/simple/manifest.scm | 3 + tests/guix/simple/verify | 3 + 20 files changed, 186 insertions(+) create mode 100644 repo2docker/buildpacks/guix/__init__.py create mode 100755 repo2docker/buildpacks/guix/guix-install.bash create mode 100644 tests/guix/binder-dir/README.rst create mode 100644 tests/guix/binder-dir/binder/manifest.scm create mode 100755 tests/guix/binder-dir/verify create mode 100644 tests/guix/ignore-outside/README.rst create mode 100644 tests/guix/ignore-outside/binder/manifest.scm create mode 100644 tests/guix/ignore-outside/manifest.scm create mode 100755 tests/guix/ignore-outside/verify create mode 100644 tests/guix/simple-channels/README.rst create mode 100644 tests/guix/simple-channels/channels.scm create mode 100644 tests/guix/simple-channels/manifest.scm create mode 100755 tests/guix/simple-channels/verify create mode 100644 tests/guix/simple/README.rst create mode 100644 tests/guix/simple/manifest.scm create mode 100755 tests/guix/simple/verify diff --git a/docs/source/config_files.rst b/docs/source/config_files.rst index 4b71980cf..a29614503 100644 --- a/docs/source/config_files.rst +++ b/docs/source/config_files.rst @@ -231,6 +231,27 @@ to produce a reproducible environment. To see an example repository visit `nix binder example `_. +.. _manifest.scm: + +``manifest.scm`` - the Guix package manager +=========================================== + +Specify packages to be installed by the `Guix package manager `_. +All packages specified in |manifest|_ will be installed in a container using |guix_package|_.If you do not install Jupyter explicitly, Repo2docker will no be able to start your container. In addition, you can use different `channels `_ rather +than the ones available by default (official channels of GNU Guix 1.3.0). +You must describe such channels in a ``channels.scm`` file which will be used +alongside ``manifest.scm`` with the |guix_time-machine|_ command. Furthermore, using a ``channels.scm`` file lets you `pin a specific revision `_ of Guix, allowing you to unambiguously specific the software environment to reproduce. + +For more information about Guix please read the `manual `_. + +.. |manifest| replace:: ``manifest.scm`` +.. _manifest: https://guix.gnu.org/manual/en/html_node/Invoking-guix-package.html#index-profile-manifesthy + +.. |guix_package| replace:: ``guix package`` +.. _guix_package: https://guix.gnu.org/manual/en/html_node/Invoking-guix-package.html#Invoking-guix-package + +.. |guix_time-machine| replace:: ``guix time-machine`` +.. _guix_time-machine: https://guix.gnu.org/manual/en/html_node/Invoking-guix-time_002dmachine.html ``Dockerfile`` - Advanced environments ====================================== diff --git a/repo2docker/app.py b/repo2docker/app.py index 8a804905c..fc2f97d78 100755 --- a/repo2docker/app.py +++ b/repo2docker/app.py @@ -37,6 +37,7 @@ PipfileBuildPack, PythonBuildPack, RBuildPack, + GuixBuildPack, ) from . import contentproviders from .utils import ByteSpecification, chdir @@ -95,6 +96,7 @@ def _default_log_level(self): JuliaProjectTomlBuildPack, JuliaRequireBuildPack, NixBuildPack, + GuixBuildPack, RBuildPack, CondaBuildPack, PipfileBuildPack, diff --git a/repo2docker/buildpacks/__init__.py b/repo2docker/buildpacks/__init__.py index 10fe89a4e..cde1fb21c 100644 --- a/repo2docker/buildpacks/__init__.py +++ b/repo2docker/buildpacks/__init__.py @@ -8,3 +8,4 @@ from .legacy import LegacyBinderDockerBuildPack from .r import RBuildPack from .nix import NixBuildPack +from .guix import GuixBuildPack diff --git a/repo2docker/buildpacks/base.py b/repo2docker/buildpacks/base.py index 145d29648..1889bc7ef 100644 --- a/repo2docker/buildpacks/base.py +++ b/repo2docker/buildpacks/base.py @@ -718,3 +718,4 @@ def get_start_script(self): # the only path evaluated at container start time rather than build time return os.path.join("${REPO_DIR}", start) return None + diff --git a/repo2docker/buildpacks/guix/__init__.py b/repo2docker/buildpacks/guix/__init__.py new file mode 100644 index 000000000..0b371c2db --- /dev/null +++ b/repo2docker/buildpacks/guix/__init__.py @@ -0,0 +1,83 @@ +"""BuildPack for guix environments""" +import os + +from ..base import BuildPack, BaseImage + + +class GuixBuildPack(BaseImage): + """A Guix Package Manager BuildPack""" + + def get_build_env(self): + """""" + env = super().get_build_env() + [ + ("GUIX_DIR", "${APP_BASE}/guix") + ] + return env + + def get_path(self): + """Return paths to be added to PATH environment variable""" + path = super().get_path() + path.insert(0, "${GUIX_DIR}/bin") + return path + + + + def get_build_scripts(self): + """ + Install a pinned version of Guix package manager, + precised in guix-install.bash. + """ + return super().get_build_scripts() + [ + ( + "root", + """ + bash /tmp/.local/bin/guix-install.bash + """, + ), + + ] + + def get_build_script_files(self): + + """Copying guix installation script on the image""" + return { + "guix/guix-install.bash": + "/tmp/.local/bin/guix-install.bash", + } + + def get_assemble_scripts(self): + """ + Launch Guix daemon with --disable-chroot to avoid the need + of root privileges for the user. + Make sure we never use Debian's python by error by renaming it + then, as an user install packages listed in manifest.scm, + use guix time-machine if channels.scm file exists. + Finally set Guix environment variables. + """ + assemble_script =""" + /var/guix/profiles/per-user/root/current-guix/bin/guix-daemon \ + --build-users-group=guixbuild --disable-chroot & \ + su - $NB_USER -c '{}' && \ + echo 'GUIX_PROFILE="/var/guix/profiles/per-user/'$NB_USER'/guix-profile" ; \ + source "$GUIX_PROFILE/etc/profile"'>> /etc/profile.d/99-guix.sh + """ + + if os.path.exists(self.binder_path("channels.scm")): + assemble_script = assemble_script.format( + "guix time-machine -C " + self.binder_path("channels.scm") + + " -- package -m " + self.binder_path("manifest.scm") + ) + else: + assemble_script = assemble_script.format( + "guix package -m " + self.binder_path("manifest.scm") + ) + return super().get_assemble_scripts() + [ + ( "root", + assemble_script, + ) + ] + + def detect(self): + """Check if current repo should be built with the guix BuildPack""" + return os.path.exists(self.binder_path("manifest.scm")) + diff --git a/repo2docker/buildpacks/guix/guix-install.bash b/repo2docker/buildpacks/guix/guix-install.bash new file mode 100755 index 000000000..26a664299 --- /dev/null +++ b/repo2docker/buildpacks/guix/guix-install.bash @@ -0,0 +1,14 @@ +#!/bin/bash +# This downloads and installs a pinned version of Guix using a pinned version of the installation script. +# Here is the commit associated with Guix version 1.3.0 the latest version when this script was written. +set -euxo pipefail + +GUIX_COMMIT="a0178d34f582b50e9bdbb0403943129ae5b560ff" +BIN_VER="1.3.0x86_64-linux" +GUIX_SHA256="bcdeaa757cd42d2c9de4791272737e9ee0d518398403955f113611f4a893380a" + +wget "https://git.savannah.gnu.org/cgit/guix.git/plain/etc/guix-install.sh?id=$GUIX_COMMIT" + +echo "$GUIX_SHA256 guix-install.sh?id=$GUIX_COMMIT" | sha256sum -c + +(yes || true) | BIN_VER=$BIN_VER bash guix-install.sh?id=$GUIX_COMMIT diff --git a/tests/guix/binder-dir/README.rst b/tests/guix/binder-dir/README.rst new file mode 100644 index 000000000..ac0bbd5e5 --- /dev/null +++ b/tests/guix/binder-dir/README.rst @@ -0,0 +1,4 @@ +`manifest.scm` in a binder/ directory +------------------------------------- + +Check if we can find and use `manifest.scm` when it is ina `binder/` sub-directory. diff --git a/tests/guix/binder-dir/binder/manifest.scm b/tests/guix/binder-dir/binder/manifest.scm new file mode 100644 index 000000000..c9fdbeb25 --- /dev/null +++ b/tests/guix/binder-dir/binder/manifest.scm @@ -0,0 +1,3 @@ +(specifications->manifest + '("jupyter" + "hello")) diff --git a/tests/guix/binder-dir/verify b/tests/guix/binder-dir/verify new file mode 100755 index 000000000..1d4239a7a --- /dev/null +++ b/tests/guix/binder-dir/verify @@ -0,0 +1,3 @@ +#!/bin/sh + +hello diff --git a/tests/guix/ignore-outside/README.rst b/tests/guix/ignore-outside/README.rst new file mode 100644 index 000000000..b0bb62edc --- /dev/null +++ b/tests/guix/ignore-outside/README.rst @@ -0,0 +1,4 @@ +`manifest.scm` in main directory and in `binder/` +------------------------------------------------- + +Check if `manifest.scm` located in the `binder/` sub-directory is prefered to the one in the main directory. diff --git a/tests/guix/ignore-outside/binder/manifest.scm b/tests/guix/ignore-outside/binder/manifest.scm new file mode 100644 index 000000000..a262c0505 --- /dev/null +++ b/tests/guix/ignore-outside/binder/manifest.scm @@ -0,0 +1,4 @@ +(specifications->manifest + '("jupyter" + "python" + "python-numpy")) diff --git a/tests/guix/ignore-outside/manifest.scm b/tests/guix/ignore-outside/manifest.scm new file mode 100644 index 000000000..452b8ff0d --- /dev/null +++ b/tests/guix/ignore-outside/manifest.scm @@ -0,0 +1,2 @@ +(specifications->manifest + '("jupyter")) diff --git a/tests/guix/ignore-outside/verify b/tests/guix/ignore-outside/verify new file mode 100755 index 000000000..7fa832bdc --- /dev/null +++ b/tests/guix/ignore-outside/verify @@ -0,0 +1,3 @@ +#!/usr/bin/env python3 + +import numpy diff --git a/tests/guix/simple-channels/README.rst b/tests/guix/simple-channels/README.rst new file mode 100644 index 000000000..5a8949352 --- /dev/null +++ b/tests/guix/simple-channels/README.rst @@ -0,0 +1,4 @@ +`manifest.scm` and `channels.scm` in main directory +--------------------------------------------------- + +CHeck if we can use `manifest.scm` alongside a `channels.scm` file using `guix time-machine ...` diff --git a/tests/guix/simple-channels/channels.scm b/tests/guix/simple-channels/channels.scm new file mode 100644 index 000000000..3ae7063ad --- /dev/null +++ b/tests/guix/simple-channels/channels.scm @@ -0,0 +1,20 @@ +(list (channel + (name 'guix) + (url "https://git.savannah.gnu.org/git/guix.git") + (commit + "f1bfd9f1948a5ff336d737c0614b9a30c2bb3097") + (introduction + (make-channel-introduction + "9edb3f66fd807b096b48283debdcddccfea34bad" + (openpgp-fingerprint + "BBB0 2DDF 2CEA F6A8 0D1D E643 A2A0 6DF2 A33A 54FA")))) + (channel + (name 'nonguix) + (url "https://gitlab.com/nonguix/nonguix") + (commit + "46c1d8bcca674d3a71cd077c52dde9552a89873d") + (introduction + (make-channel-introduction + "897c1a470da759236cc11798f4e0a5f7d4d59fbc" + (openpgp-fingerprint + "2A39 3FFF 68F4 EF7A 3D29 12AF 6F51 20A0 22FB B2D5"))))) diff --git a/tests/guix/simple-channels/manifest.scm b/tests/guix/simple-channels/manifest.scm new file mode 100644 index 000000000..c9fdbeb25 --- /dev/null +++ b/tests/guix/simple-channels/manifest.scm @@ -0,0 +1,3 @@ +(specifications->manifest + '("jupyter" + "hello")) diff --git a/tests/guix/simple-channels/verify b/tests/guix/simple-channels/verify new file mode 100755 index 000000000..1d4239a7a --- /dev/null +++ b/tests/guix/simple-channels/verify @@ -0,0 +1,3 @@ +#!/bin/sh + +hello diff --git a/tests/guix/simple/README.rst b/tests/guix/simple/README.rst new file mode 100644 index 000000000..b0c266935 --- /dev/null +++ b/tests/guix/simple/README.rst @@ -0,0 +1,5 @@ + +`manifest.scm` in the main directory +---------------------------------- + +Check if we can find and use `manifest.scm` when it is in the main directory. diff --git a/tests/guix/simple/manifest.scm b/tests/guix/simple/manifest.scm new file mode 100644 index 000000000..c9fdbeb25 --- /dev/null +++ b/tests/guix/simple/manifest.scm @@ -0,0 +1,3 @@ +(specifications->manifest + '("jupyter" + "hello")) diff --git a/tests/guix/simple/verify b/tests/guix/simple/verify new file mode 100755 index 000000000..1d4239a7a --- /dev/null +++ b/tests/guix/simple/verify @@ -0,0 +1,3 @@ +#!/bin/sh + +hello From 54c193fafd20269cdb05cb381a7995f66d31d008 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 31 Oct 2022 13:58:38 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- repo2docker/buildpacks/base.py | 1 - repo2docker/buildpacks/guix/__init__.py | 26 +++++++++++-------------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/repo2docker/buildpacks/base.py b/repo2docker/buildpacks/base.py index 1889bc7ef..145d29648 100644 --- a/repo2docker/buildpacks/base.py +++ b/repo2docker/buildpacks/base.py @@ -718,4 +718,3 @@ def get_start_script(self): # the only path evaluated at container start time rather than build time return os.path.join("${REPO_DIR}", start) return None - diff --git a/repo2docker/buildpacks/guix/__init__.py b/repo2docker/buildpacks/guix/__init__.py index 0b371c2db..82237e095 100644 --- a/repo2docker/buildpacks/guix/__init__.py +++ b/repo2docker/buildpacks/guix/__init__.py @@ -9,9 +9,7 @@ class GuixBuildPack(BaseImage): def get_build_env(self): """""" - env = super().get_build_env() + [ - ("GUIX_DIR", "${APP_BASE}/guix") - ] + env = super().get_build_env() + [("GUIX_DIR", "${APP_BASE}/guix")] return env def get_path(self): @@ -20,8 +18,6 @@ def get_path(self): path.insert(0, "${GUIX_DIR}/bin") return path - - def get_build_scripts(self): """ Install a pinned version of Guix package manager, @@ -34,15 +30,13 @@ def get_build_scripts(self): bash /tmp/.local/bin/guix-install.bash """, ), - ] def get_build_script_files(self): """Copying guix installation script on the image""" return { - "guix/guix-install.bash": - "/tmp/.local/bin/guix-install.bash", + "guix/guix-install.bash": "/tmp/.local/bin/guix-install.bash", } def get_assemble_scripts(self): @@ -54,7 +48,7 @@ def get_assemble_scripts(self): use guix time-machine if channels.scm file exists. Finally set Guix environment variables. """ - assemble_script =""" + assemble_script = """ /var/guix/profiles/per-user/root/current-guix/bin/guix-daemon \ --build-users-group=guixbuild --disable-chroot & \ su - $NB_USER -c '{}' && \ @@ -64,20 +58,22 @@ def get_assemble_scripts(self): if os.path.exists(self.binder_path("channels.scm")): assemble_script = assemble_script.format( - "guix time-machine -C " + self.binder_path("channels.scm") + - " -- package -m " + self.binder_path("manifest.scm") + "guix time-machine -C " + + self.binder_path("channels.scm") + + " -- package -m " + + self.binder_path("manifest.scm") ) else: assemble_script = assemble_script.format( "guix package -m " + self.binder_path("manifest.scm") ) return super().get_assemble_scripts() + [ - ( "root", - assemble_script, - ) + ( + "root", + assemble_script, + ) ] def detect(self): """Check if current repo should be built with the guix BuildPack""" return os.path.exists(self.binder_path("manifest.scm")) -