From 1e3630cc1095708148ca7e253b23fb1197c658ea Mon Sep 17 00:00:00 2001 From: Chris Ostrouchov Date: Mon, 17 Sep 2018 12:00:23 -0400 Subject: [PATCH 01/37] Feature: Adding nix support for repo2docker test url: https://gitlab.com/costrouc/nix-binder-example --- .travis.yml | 1 + docs/source/config_files.rst | 24 ++++++- repo2docker/app.py | 4 +- repo2docker/buildpacks/__init__.py | 1 + repo2docker/buildpacks/nix/__init__.py | 70 ++++++++++++++++++++ repo2docker/buildpacks/nix/install-nix.bash | 12 ++++ repo2docker/buildpacks/nix/nix-shell-wrapper | 15 +++++ tests/nix/simple/README.rst | 6 ++ tests/nix/simple/default.nix | 21 ++++++ tests/nix/simple/verify | 3 + 10 files changed, 155 insertions(+), 2 deletions(-) create mode 100644 repo2docker/buildpacks/nix/__init__.py create mode 100644 repo2docker/buildpacks/nix/install-nix.bash create mode 100644 repo2docker/buildpacks/nix/nix-shell-wrapper create mode 100644 tests/nix/simple/README.rst create mode 100644 tests/nix/simple/default.nix create mode 100755 tests/nix/simple/verify diff --git a/.travis.yml b/.travis.yml index 219aba75..6cb94ab8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -49,6 +49,7 @@ env: - REPO_TYPE=stencila - REPO_TYPE=julia - REPO_TYPE=r + - REPO_TYPE=nix - REPO_TYPE=dockerfile - REPO_TYPE=external/* - REPO_TYPE=**/*.py diff --git a/docs/source/config_files.rst b/docs/source/config_files.rst index fe3fb02e..efc0c848 100644 --- a/docs/source/config_files.rst +++ b/docs/source/config_files.rst @@ -164,7 +164,6 @@ used for installing libraries. To see an example R repository, visit our `R example in binder-examples `_. - ``Dockerfile`` - Advanced environments ====================================== @@ -179,3 +178,26 @@ With Dockerfiles, a regular Docker build will be performed. See the `Advanced Binder Documentation `_ for best-practices with Dockerfiles. + +.. _default.nix: + +``default.nix`` +~~~~~~~~~~~~~~~ + +This allows you to use the `nix package manager `_. It is hard to explain what nix +is to new users and why it is usefull. If you are inclined please read +more at the `nix homepage `_. It is currently +the largest package repository, offers reproducible builds, multiple +versions of same package coexisting, source and binary based, and +packages many languages such as python, R, go, javascript, haskell, +ruby, etc. . + +A ``default.nix`` file allows you to use `nix-shell `_ +to evaluate a ``nix`` expression to define a reproducible nix environment. +The only requirement is that you expose a ``jupyter`` command within the shell +(since jupyterlab is currently what ``repo2docker`` is designed +around). While the ``nix`` environment does have ``NIX_PATH`` set with +``nixpkgs=...`` you should not rely on it and make sure to +`pin your nixpkgs `_. +By doing this you are truley producing a reproducible environment. To see an +example repository visit a `nix binder example `_. diff --git a/repo2docker/app.py b/repo2docker/app.py index 17f1b39a..ea403813 100644 --- a/repo2docker/app.py +++ b/repo2docker/app.py @@ -30,7 +30,8 @@ from traitlets.config import Application from . import __version__ from .buildpacks import ( PythonBuildPack, DockerBuildPack, LegacyBinderDockerBuildPack, - CondaBuildPack, JuliaBuildPack, RBuildPack + CondaBuildPack, JuliaBuildPack, BaseImage, + RBuildPack, NixBuildPack ) from . import contentproviders from .utils import ( @@ -77,6 +78,7 @@ class Repo2Docker(Application): LegacyBinderDockerBuildPack, DockerBuildPack, JuliaBuildPack, + NixBuildPack, RBuildPack, CondaBuildPack, PythonBuildPack, diff --git a/repo2docker/buildpacks/__init__.py b/repo2docker/buildpacks/__init__.py index 5ccc0c0e..1900a449 100644 --- a/repo2docker/buildpacks/__init__.py +++ b/repo2docker/buildpacks/__init__.py @@ -5,3 +5,4 @@ from .julia import JuliaBuildPack from .docker import DockerBuildPack from .legacy import LegacyBinderDockerBuildPack from .r import RBuildPack +from .nix import NixBuildPack diff --git a/repo2docker/buildpacks/nix/__init__.py b/repo2docker/buildpacks/nix/__init__.py new file mode 100644 index 00000000..a4841ebf --- /dev/null +++ b/repo2docker/buildpacks/nix/__init__.py @@ -0,0 +1,70 @@ +"""BuildPack for nixpkgs environments""" +import os + +from ..base import BuildPack + + +class NixBuildPack(BuildPack): + """A nix Package Manager BuildPack""" + + def get_path(self): + """Return paths to be added to PATH environemnt variable + """ + return super().get_path() + [ + '/home/${NB_USER}/.nix-profile/bin' + ] + + def get_env(self): + """Ordered list of environment variables to be set for this image""" + return super().get_env() + [ + ('NIX_PATH', "nixpkgs=/home/${NB_USER}/.nix-defexpr/channels/nixpkgs"), + ('NIX_SSL_CERT_FILE', '/etc/ssl/certs/ca-certificates.crt'), + ('GIT_SSL_CAINFO', '/etc/ssl/certs/ca-certificates.crt') + ] + + def get_build_scripts(self): + """ + Return series of build-steps common to all nix repositories. + Notice how only root privileges are needed for creating nix + directory. + + - create nix directory for user nix installation + - install nix package manager for user + """ + return super().get_build_scripts() + [ + ("root", """ + mkdir -m 0755 /nix && \ + chown -R ${NB_USER}:${NB_USER} /nix /usr/local/bin/nix-shell-wrapper /home/${NB_USER} + """), + ("${NB_USER}", """ + bash /home/${NB_USER}/.local/bin/install-nix.bash && \ + rm /home/${NB_USER}/.local/bin/install-nix.bash + """) + ] + + def get_build_script_files(self): + """Dict of files to be copied to the container image for use in building + """ + return { + "nix/install-nix.bash": "/home/${NB_USER}/.local/bin/install-nix.bash", + "nix/nix-shell-wrapper": "/usr/local/bin/nix-shell-wrapper" + } + + def get_assemble_scripts(self): + """Return series of build-steps specific to this source repository. + """ + return super().get_assemble_scripts() + [ + ('${NB_USER}', """ + nix-channel --add https://nixos.org/channels/nixpkgs-unstable nixpkgs && \ + nix-channel --update && \ + nix-shell default.nix --command "command -v jupyter" + """) + ] + + def get_start_script(self): + """The path to a script to be executed as ENTRYPOINT""" + return "/usr/local/bin/nix-shell-wrapper" + + def detect(self): + """Check if current repo should be built with the nix BuildPack""" + return os.path.exists(self.binder_path('default.nix')) diff --git a/repo2docker/buildpacks/nix/install-nix.bash b/repo2docker/buildpacks/nix/install-nix.bash new file mode 100644 index 00000000..c4bf70e1 --- /dev/null +++ b/repo2docker/buildpacks/nix/install-nix.bash @@ -0,0 +1,12 @@ +#!/bin/bash +# This downloads and installs a pinned version of nix +set -ex + +NIX_VERSION="2.1.1" +NIX_SHA256="ad10b4da69035a585fe89d7330037c4a5d867a372bb0e52a1542ab95aec67999" + +wget https://nixos.org/releases/nix/nix-$NIX_VERSION/nix-$NIX_VERSION-x86_64-linux.tar.bz2 +echo "$NIX_SHA256 nix-2.1.1-x86_64-linux.tar.bz2" | sha256sum -c +tar xjf nix-*-x86_64-linux.tar.bz2 +sh nix-*-x86_64-linux/install +rm -r nix-*-x86_64-linux* diff --git a/repo2docker/buildpacks/nix/nix-shell-wrapper b/repo2docker/buildpacks/nix/nix-shell-wrapper new file mode 100644 index 00000000..be7a3bfb --- /dev/null +++ b/repo2docker/buildpacks/nix/nix-shell-wrapper @@ -0,0 +1,15 @@ +#!/bin/bash + +_term() { + echo "Caught SIGTERM signal!" + # kill -TERM "$PID" 2>/dev/null + exit 0 +} + +trap _term SIGTERM + +echo "$*" +nix-shell default.nix --command "$*" & + +PID=$! +wait "$PID" diff --git a/tests/nix/simple/README.rst b/tests/nix/simple/README.rst new file mode 100644 index 00000000..fc1bbe72 --- /dev/null +++ b/tests/nix/simple/README.rst @@ -0,0 +1,6 @@ +Nix environment - default.nix +----------------------------- + +You can install a nix shell environment using the traditional default.nix. + +Documentation on the syntax and typical setup of a ``nix-shell`` environment can be found `here `_. diff --git a/tests/nix/simple/default.nix b/tests/nix/simple/default.nix new file mode 100644 index 00000000..7b2f7f59 --- /dev/null +++ b/tests/nix/simple/default.nix @@ -0,0 +1,21 @@ +let + # Pinning nixpkgs to specific release + # To get sha256 use "nix-prefetch-git --rev " + commitRev="5574b6a152b1b3ae5f93ba37c4ffd1981f62bf5a"; + nixpkgs = builtins.fetchTarball { + url = "https://github.com/NixOS/nixpkgs/archive/${commitRev}.tar.gz"; + sha256 = "1pqdddp4aiz726c7qs1dwyfzixi14shp0mbzi1jhapl9hrajfsjg"; + }; + pkgs = import nixpkgs { config = { allowUnfree = true; }; }; +in +pkgs.mkShell { + buildInputs = with pkgs; [ + python36Packages.numpy + python36Packages.scipy + python36Packages.jupyterlab + ]; + + shellHook = '' + export NIX_PATH="nixpkgs=${nixpkgs}:." + ''; +} diff --git a/tests/nix/simple/verify b/tests/nix/simple/verify new file mode 100755 index 00000000..4f794e84 --- /dev/null +++ b/tests/nix/simple/verify @@ -0,0 +1,3 @@ +#!/usr/bin/env python +import numpy +import scipy From ed590002db757af0eec6845577875fe96006883d Mon Sep 17 00:00:00 2001 From: Chris Holdgraf Date: Fri, 19 Oct 2018 16:11:48 -0700 Subject: [PATCH 02/37] adding back sidebars --- docs/source/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index cc48fea0..2c20fc35 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -110,7 +110,7 @@ html_theme_path = [alabaster_jupyterhub.get_html_theme_path()] # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] - +html_sidebars = { '**': ['globaltoc.html', 'relations.html', 'sourcelink.html', 'searchbox.html'] } # -- Options for HTMLHelp output ------------------------------------------ From 417157fb83b76d0cb69b82c3de405750c56ffd58 Mon Sep 17 00:00:00 2001 From: Chris Holdgraf Date: Mon, 22 Oct 2018 10:19:55 -0700 Subject: [PATCH 03/37] moving contributing into the docs --- CONTRIBUTING.md | 260 +----------------- docs/source/architecture.md | 2 +- docs/source/conf.py | 7 + .../buildpack.md} | 0 docs/source/contributing/contributing.md | 119 ++++++++ docs/source/contributing/tasks.md | 151 ++++++++++ docs/source/design.md | 2 +- docs/source/{ => howto}/deploy.rst | 0 docs/source/index.rst | 14 +- 9 files changed, 290 insertions(+), 265 deletions(-) rename docs/source/{dev_newbuildpack.md => contributing/buildpack.md} (100%) create mode 100644 docs/source/contributing/contributing.md create mode 100644 docs/source/contributing/tasks.md rename docs/source/{ => howto}/deploy.rst (100%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b8544366..b78dfa91 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,260 +1,6 @@ # Contributing to repo2docker development -This document covers: +The repo2docker developer documentation can be found on these pages: -- Process for making a code contribution -- Setting up for Local Development -- Running Tests -- Updating and Freezing BuildPack Dependencies -- Updating the change log -- Creating a Release - - -## Process for making a code contribution - -This outlines the process for getting changes to the code of -repo2docker merged. This serves as information on when a PR is "done". - -Contributions should follow these guidelines: - -* all changes by pull request (PR); -* please prefix the title of your pull request with `[MRG]` if the contribution - is complete and should be subjected to a detailed review; -* create a PR as early as possible, marking it with `[WIP]` while you work on - it (good to avoid duplicated work, get broad review of functionality or API, - or seek collaborators); -* a PR solves one problem (do not mix problems together in one PR) with the - minimal set of changes; -* describe why you are proposing the changes you are proposing; -* try to not rush changes (the definition of rush depends on how big your - changes are); -* someone else has to merge your PR; -* new code needs to come with a test; -* apply [PEP8](https://www.python.org/dev/peps/pep-0008/) as much - as possible, but not too much; -* no merging if travis is red; -* do use merge commits instead of merge-by-squashing/-rebasing. This makes it - easier to find all changes since the last deployment `git log --merges --pretty=format:"%h %<(10,trunc)%an %<(15)%ar %s" ..` -* [when you merge do deploy to mybinder.org](http://mybinder-sre.readthedocs.io/en/latest/deployment/how.html) - -These are not hard rules to be enforced by :police_car: but instead guidelines. - - -## Setting up for Local Development - -To develop & test repo2docker locally, you need: - -1. Familiarity with using a command line terminal -2. A computer running macOS / Linux -3. Some knowledge of git -4. At least python 3.6 -5. Your favorite text editor -6. A recent version of [Docker Community Edition](https://www.docker.com/community-edition) - -### Clone the repository - -First, you need to get a copy of the repo2docker git repository on your local -disk. - -```bash -git clone https://github.com/jupyter/repo2docker -``` - -This will clone repo2docker into a directory called `repo2docker`. You can -make that your current directory with `cd repo2docker`. - -### Set up a local virtual environment - -After cloning the repository (or your fork of the repository), you should set up an -isolated environment to install libraries required for running / developing -repo2docker. There are many ways to do this, and a `virtual environment` is -one of them. - -```bash -python3 -m venv . -source bin/activate -pip3 install -e . -pip3 install -r dev-requirements.txt -pip3 install -r docs/doc-requirements.txt -``` - -This should install all the libraries required for testing & running repo2docker! - -### Verify that docker is installed and running - -If you do not already have [Docker](https://www.docker.com/), you should be able -to download and install it for your operating system using the links from the -[official website](https://www.docker.com/community-edition). After you have -installed it, you can verify that it is working by running the following commands: - -```bash -docker version -``` - -It should output something like: - -``` -Client: - Version: 17.09.0-ce - API version: 1.32 - Go version: go1.8.3 - Git commit: afdb6d4 - Built: Tue Sep 26 22:42:45 2017 - OS/Arch: linux/amd64 - -Server: - Version: 17.09.0-ce - API version: 1.32 (minimum version 1.12) - Go version: go1.8.3 - Git commit: afdb6d4 - Built: Tue Sep 26 22:41:24 2017 - OS/Arch: linux/amd64 - Experimental: false -``` - -Then you are good to go! - - -## Running tests - -We have a lot of tests for various cases supported by repo2docker in the `tests/` -subdirectory. If you fix a bug or add new functionality consider adding a new -test to prevent the bug from coming back. These use -[py.test](https://docs.pytest.org/). - -You can run all the tests with: - -```bash -py.test -s tests/* -``` - -If you want to run a specific test, you can do so with: - -```bash -py.test -s tests/ -``` - -## Update and Freeze BuildPack Dependencies - -### Updating libraries installed for all repositories - -For both the `conda` and `virtualenv` (`pip`) base environments in the **Conda BuildPack** and **Python BuildPack**, -we install specific pinned versions of all dependencies. We explicitly list the dependencies -we want, then *freeze* them at commit time to explicitly list all the -transitive dependencies at current versions. This way, we know that -all dependencies will have the exact same version installed at all times. - -To update one of the dependencies shared across all `repo2docker` builds, you -must follow these steps (with more detailed information in the sections below): - -* Make sure you have [Docker](https://www.docker.com/) running on your computer -* Bump the version numbers of the dependencies you want to update in the `conda` environment ([link](https://github.com/jupyter/repo2docker/blob/master/CONTRIBUTING.md#conda-dependencies)) -* Make a pull request with your changes ([link](https://github.com/jupyter/repo2docker/blob/master/CONTRIBUTING.md#make-a-pull-request)) - -See the subsections below for more detailed instructions. - - -### Conda dependencies - -1. There are two files related to conda dependencies. Edit as needed. - - - `repo2docker/buildpacks/conda/environment.yml` - - Contains list of packages to install in Python3 conda environments, - which are the default. **This is where all Notebook versions & - notebook extensions (such as JupyterLab / nteract) go**. - - - `repo2docker/buildpacks/conda/environment.py-2.7.yml` - - Contains list of packages to install in Python2 conda environments, which - can be specifically requested by users. **This only needs `IPyKernel` - and kernel related libraries**. Notebook / Notebook Extension need - not be installed here. - -2. Once you edit either of these files to add a new package / bump version on - an existing package, you should then run: - - ```bash - cd ./repo2docker/buildpacks/conda/ - python freeze.py - ``` - - This script will resolve dependencies and write them to the respective `.frozen.yml` - files. You will need `docker` installed to run this script. - -3. After the freeze script finishes, a number of files will have been created. - Commit the following subset of files to git: - - ``` - repo2docker/buildpacks/conda/environment.yml - repo2docker/buildpacks/conda/environment.frozen.yml - repo2docker/buildpacks/conda/environment.py-2.7.yml - repo2docker/buildpacks/conda/environment.py-2.7.frozen.yml - repo2docker/buildpacks/conda/environment.py-3.5.frozen.yml - repo2docker/buildpacks/conda/environment.py-3.6.frozen.yml - ``` - -5. Make a pull request; see details below. - -6. Once the pull request is approved (but not yet merged), Update the - change log (details below) and commit the change log, then update - the pull request. - - -### Change log - -To add your change to the change log, find the relevant Feature/Bug -fix/API change section for the next release near the top of the file; -then add one or two sentences as a new bullet point about your -changes. Include the pull request or issue number between square -brackets at the end. - -Some details: - -- versioning follows the x.y.z, major.minor.bugfix numbering - -- bug fixes go into the next bugfix release. If there isn't any, you - can create a new section (see point below). Don't worry if you're - not sure about that, and think it should go into a next major or - minor release: an admin will let you know, or move the change later - to the appropriate section - -- API changes should preferably go into the next major release, unless - they are backward compatible (for example, a deprecated function - keyword): then they can go into the next minor release. For release - with major release 0, non-backward compatible breaking changes are - also fine for the next minor release. - -- new features should go into the next minor release. - -- if there is no section for the appropriate release, you can add one: - - follow the versioning scheme, by simply increasing the relevant - number for one of the major /minor/bugfix numbers, appropriate for - your change (see the above bullet points); add the release - section. Then add three subsections: new features, api changes, and - bug fixes. Leave out the sections that are not appropriate for the - newlye added release section. - -Release candidate versions in the change log are only temporary, and -should be superseded by either a next release candidate, or the final -release for that version (bugfix version 0). - - -### Make a Pull Request - -Once you've made the commit, please make a Pull Request to the `jupyterhub/repo2docker` -repository, with a description of what versions were bumped / what new packages were -added and why. If you fix a bug or add new functionality consider adding a new -test to prevent the bug from coming back/the feature breaking in the future. - - -## Creating a Release - -We try to make a release of repo2docker every few months if possible. - -We follow semantic versioning. - -Check hat the Change log is ready and then tag a new release on GitHub. - -When the travis run completes check that the new release is available on PyPI. +* [Contributing to repo2docker](https://repo2docker.readthedocs.io/en/latest/contributing/contributing.html) +* [Common developer tasks and how-tos](https://repo2docker.readthedocs.io/en/latest/contributing/tasks.html) diff --git a/docs/source/architecture.md b/docs/source/architecture.md index 93437f17..d810013c 100644 --- a/docs/source/architecture.md +++ b/docs/source/architecture.md @@ -1,4 +1,4 @@ -# Architecture +# Architecture of repo2docker This is a living document talking about the architecture of repo2docker from various perspectives. diff --git a/docs/source/conf.py b/docs/source/conf.py index 2c20fc35..dce1d5c2 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -43,8 +43,15 @@ source_parsers = { '.md': 'recommonmark.parser.CommonMarkParser', } +from recommonmark.transform import AutoStructify + def setup(app): app.add_stylesheet('custom.css') # may also be a URL + app.add_config_value('recommonmark_config', { + 'auto_toc_tree_section': 'Contents', + }, True) + app.add_transform(AutoStructify) + # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: diff --git a/docs/source/dev_newbuildpack.md b/docs/source/contributing/buildpack.md similarity index 100% rename from docs/source/dev_newbuildpack.md rename to docs/source/contributing/buildpack.md diff --git a/docs/source/contributing/contributing.md b/docs/source/contributing/contributing.md new file mode 100644 index 00000000..40d67136 --- /dev/null +++ b/docs/source/contributing/contributing.md @@ -0,0 +1,119 @@ +# Contributing to repo2docker development + +## Process for making a code contribution + +This outlines the process for getting changes to the code of +repo2docker merged. + +* If your change is relatively significant, **open an issue to discuss** + before spending a lot of time writing code. Getting consensus with the + community is a great way to save time later. +* Make edits in your fork of the repo2docker repository +* Submit a pull request (this is how all changes are made) +* Edit [the changelog](https://github.com/jupyter/repo2docker/blob/master/CHANGES.rst) + by appending your feature / bug fix to the development version. +* Wait for a community member to merge your changes +* (optional) Deploy a new version of repo2docker to mybinder.org by [following these steps](http://mybinder-sre.readthedocs.io/en/latest/deployment/how.html) + + +## Guidelines to getting a Pull Request merged + +These are not hard rules to be enforced by :police_car: but instead guidelines +to help you make the most effictive / efficient contribution. + +* prefix the title of your pull request with `[MRG]` if the contribution + is complete and should be subjected to a detailed review; +* create a PR as early as possible, marking it with `[WIP]` while you work on + it (good to avoid duplicated work, get broad review of functionality or API, + or seek collaborators); +* a PR solves one problem (do not mix problems together in one PR) with the + minimal set of changes; +* describe why you are proposing the changes you are proposing; +* try to not rush changes (the definition of rush depends on how big your + changes are); +* someone else has to merge your PR; +* new code needs to come with a test; +* apply [PEP8](https://www.python.org/dev/peps/pep-0008/) as much + as possible, but not too much; +* no merging if travis is red; +* do use merge commits instead of merge-by-squashing/-rebasing. This makes it + easier to find all changes since the last deployment `git log --merges --pretty=format:"%h %<(10,trunc)%an %<(15)%ar %s" ..` + + +## Setting up for Local Development + +To develop & test repo2docker locally, you need: + +1. Familiarity with using a command line terminal +2. A computer running macOS / Linux +3. Some knowledge of git +4. At least python 3.6 +5. Your favorite text editor +6. A recent version of [Docker Community Edition](https://www.docker.com/community-edition) + +### Clone the repository + +First, you need to get a copy of the repo2docker git repository on your local +disk. + +```bash +git clone https://github.com/jupyter/repo2docker +``` + +This will clone repo2docker into a directory called `repo2docker`. You can +make that your current directory with `cd repo2docker`. + +### Set up a local virtual environment + +After cloning the repository (or your fork of the repository), you should set up an +isolated environment to install libraries required for running / developing +repo2docker. There are many ways to do this, and a `virtual environment` is +one of them. + +```bash +python3 -m venv . +source bin/activate +pip3 install -e . +pip3 install -r dev-requirements.txt +pip3 install -r docs/doc-requirements.txt +``` + +This should install all the libraries required for testing & running repo2docker! + +### Verify that docker is installed and running + +If you do not already have [Docker](https://www.docker.com/), you should be able +to download and install it for your operating system using the links from the +[official website](https://www.docker.com/community-edition). After you have +installed it, you can verify that it is working by running the following commands: + +```bash +docker version +``` + +It should output something like: + +``` +Client: + Version: 17.09.0-ce + API version: 1.32 + Go version: go1.8.3 + Git commit: afdb6d4 + Built: Tue Sep 26 22:42:45 2017 + OS/Arch: linux/amd64 + +Server: + Version: 17.09.0-ce + API version: 1.32 (minimum version 1.12) + Go version: go1.8.3 + Git commit: afdb6d4 + Built: Tue Sep 26 22:41:24 2017 + OS/Arch: linux/amd64 + Experimental: false +``` + +Then you are good to go! + + + + diff --git a/docs/source/contributing/tasks.md b/docs/source/contributing/tasks.md new file mode 100644 index 00000000..f15b1a15 --- /dev/null +++ b/docs/source/contributing/tasks.md @@ -0,0 +1,151 @@ +# Common tasks + +These are some common tasks to be done as a part of developing +and maintaining repo2docker. If you'd like more guidance for how +to do these things, reach out in the [JupyterHub Gitter channel](https://gitter.im/jupyterhub/jupyterhub). + +## Running tests + +We have a lot of tests for various cases supported by repo2docker in the `tests/` +subdirectory. If you fix a bug or add new functionality consider adding a new +test to prevent the bug from coming back. These use +[py.test](https://docs.pytest.org/). + +You can run all the tests with: + +```bash +py.test -s tests/* +``` + +If you want to run a specific test, you can do so with: + +```bash +py.test -s tests/ +``` + +## Update and Freeze BuildPack Dependencies + +This section covers the process by which repo2docker defines and updates the +dependencies that are installed by default for several buildpacks. + +For both the `conda` and `virtualenv` (`pip`) base environments in the **Conda BuildPack** and **Python BuildPack**, +we install specific pinned versions of all dependencies. We explicitly list the dependencies +we want, then *freeze* them at commit time to explicitly list all the +transitive dependencies at current versions. This way, we know that +all dependencies will have the exact same version installed at all times. + +To update one of the dependencies shared across all `repo2docker` builds, you +must follow these steps (with more detailed information in the sections below): + +1. Make sure you have [Docker](https://www.docker.com/) running on your computer +2. Bump the version numbers of the dependencies you want to update in the `conda` environment ([link](https://github.com/jupyter/repo2docker/blob/master/CONTRIBUTING.md#conda-dependencies)) +3. Make a pull request with your changes ([link](https://github.com/jupyter/repo2docker/blob/master/CONTRIBUTING.md#make-a-pull-request)) + +See the subsections below for more detailed instructions. + + +### Conda dependencies + +1. There are two files related to conda dependencies. Edit as needed. + + - `repo2docker/buildpacks/conda/environment.yml` + + Contains list of packages to install in Python3 conda environments, + which are the default. **This is where all Notebook versions & + notebook extensions (such as JupyterLab / nteract) go**. + + - `repo2docker/buildpacks/conda/environment.py-2.7.yml` + + Contains list of packages to install in Python2 conda environments, which + can be specifically requested by users. **This only needs `IPyKernel` + and kernel related libraries**. Notebook / Notebook Extension need + not be installed here. + +2. Once you edit either of these files to add a new package / bump version on + an existing package, you should then run: + + ```bash + cd ./repo2docker/buildpacks/conda/ + python freeze.py + ``` + + This script will resolve dependencies and write them to the respective `.frozen.yml` + files. You will need `docker` installed to run this script. + +3. After the freeze script finishes, a number of files will have been created. + Commit the following subset of files to git: + + ``` + repo2docker/buildpacks/conda/environment.yml + repo2docker/buildpacks/conda/environment.frozen.yml + repo2docker/buildpacks/conda/environment.py-2.7.yml + repo2docker/buildpacks/conda/environment.py-2.7.frozen.yml + repo2docker/buildpacks/conda/environment.py-3.5.frozen.yml + repo2docker/buildpacks/conda/environment.py-3.6.frozen.yml + ``` + +5. Make a pull request; see details below. + +6. Once the pull request is approved (but not yet merged), Update the + change log (details below) and commit the change log, then update + the pull request. + + +### Make a Pull Request + +Once you've made the commit, please make a Pull Request to the `jupyterhub/repo2docker` +repository, with a description of what versions were bumped / what new packages were +added and why. If you fix a bug or add new functionality consider adding a new +test to prevent the bug from coming back/the feature breaking in the future. + + + +## Creating a Release + +We try to make a release of repo2docker every few months if possible. + +We follow semantic versioning. + +Check that the Change log is ready and then tag a new release on GitHub. + +When the travis run completes check that the new release is available on PyPI. + + +### Update the change log + +To add your change to the change log, find the relevant Feature/Bug +fix/API change section for the next release near the top of the file; +then add one or two sentences as a new bullet point about your +changes. Include the pull request or issue number between square +brackets at the end. + +Some details: + +- versioning follows the x.y.z, major.minor.bugfix numbering + +- bug fixes go into the next bugfix release. If there isn't any, you + can create a new section (see point below). Don't worry if you're + not sure about that, and think it should go into a next major or + minor release: an admin will let you know, or move the change later + to the appropriate section + +- API changes should preferably go into the next major release, unless + they are backward compatible (for example, a deprecated function + keyword): then they can go into the next minor release. For release + with major release 0, non-backward compatible breaking changes are + also fine for the next minor release. + +- new features should go into the next minor release. + +- if there is no section for the appropriate release, you can add one: + + follow the versioning scheme, by simply increasing the relevant + number for one of the major /minor/bugfix numbers, appropriate for + your change (see the above bullet points); add the release + section. Then add three subsections: new features, api changes, and + bug fixes. Leave out the sections that are not appropriate for the + newlye added release section. + +Release candidate versions in the change log are only temporary, and +should be superseded by either a next release candidate, or the final +release for that version (bugfix version 0). diff --git a/docs/source/design.md b/docs/source/design.md index 8826ecd2..1fa40aa0 100644 --- a/docs/source/design.md +++ b/docs/source/design.md @@ -1,4 +1,4 @@ -# Design +# Design of repo2docker The repo2docker buildpacks are inspired by [Heroku's Build Packs](https://devcenter.heroku.com/articles/buildpacks). diff --git a/docs/source/deploy.rst b/docs/source/howto/deploy.rst similarity index 100% rename from docs/source/deploy.rst rename to docs/source/howto/deploy.rst diff --git a/docs/source/index.rst b/docs/source/index.rst index 61930917..b1d8fa46 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -21,6 +21,7 @@ Please report `Bugs `_, install usage + faq .. toctree:: :maxdepth: 1 @@ -29,6 +30,7 @@ Please report `Bugs `_, howto/user_interface howto/languages howto/jupyterhub_images + howto/deploy .. toctree:: :maxdepth: 2 @@ -37,11 +39,11 @@ Please report `Bugs `_, config_files .. toctree:: - :maxdepth: 1 - :caption: Advanced and developer information + :maxdepth: 2 + :caption: Contribute to repo2docker - faq - deploy - design + contributing/contributing architecture - dev_newbuildpack + design + contributing/tasks + contributing/buildpack From 057d5edbaf41b01bb2e51c98174c168977548904 Mon Sep 17 00:00:00 2001 From: Chris Holdgraf Date: Tue, 23 Oct 2018 10:22:07 -0700 Subject: [PATCH 04/37] clone from user repo in docs --- docs/source/contributing/contributing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/contributing/contributing.md b/docs/source/contributing/contributing.md index 40d67136..c620c937 100644 --- a/docs/source/contributing/contributing.md +++ b/docs/source/contributing/contributing.md @@ -54,10 +54,10 @@ To develop & test repo2docker locally, you need: ### Clone the repository First, you need to get a copy of the repo2docker git repository on your local -disk. +disk. Fork the repository on GitHub, then clone it to your computer: ```bash -git clone https://github.com/jupyter/repo2docker +git clone https://github.com//repo2docker ``` This will clone repo2docker into a directory called `repo2docker`. You can From e050a43d2a5f710e052ecabaf278e69e4ad2b273 Mon Sep 17 00:00:00 2001 From: Tim Head Date: Wed, 24 Oct 2018 08:42:38 +0200 Subject: [PATCH 05/37] Update changelog --- CHANGES.rst | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index c12a307b..dbbd8d46 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -6,14 +6,20 @@ Release date: unknown New features ------------ -- Editable mode: allows editing a local repository from a live container [#421] - -- Change log added [#426] +- Build from sub-directory: build the image based on a sub-directory of a + repository `#413`_ by `@dsludwig`_. +- Editable mode: allows editing a local repository from a live container + `#421`_ by `@evertrol`_. +- Change log added `#426`_ by `@evertrol`_. +- Documentation: improved the documentation for contributors `#453`_ by + `@choldgraf`_. API changes ----------- +- Add content provider abstraction `#421`_ by `@betatim`_. + Bug fixes --------- @@ -54,3 +60,14 @@ Version 0.1 =========== Released 2017-04-14 + +.. _#453: https://github.com/jupyter/repo2docker/pull/453 +.. _#413: https://github.com/jupyter/repo2docker/pull/413 +.. _#421: https://github.com/jupyter/repo2docker/pull/421 +.. _#426: https://github.com/jupyter/repo2docker/pull/426 +.. _#242: https://github.com/jupyter/repo2docker/pull/242 + +.. _@betatim: https://github.com/betatim +.. _@choldgraf: https://github.com/choldgraf +.. _@dsludwig: https://github.com/dsludwig +.. _@evertrol: https://github.com/evertrol From 849b3f9bf957216730cb625531fe0509d69810e2 Mon Sep 17 00:00:00 2001 From: nuest Date: Mon, 29 Oct 2018 13:38:52 +0100 Subject: [PATCH 06/37] fix some typos --- .gitignore | 2 ++ repo2docker/buildpacks/base.py | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index b88fd663..166646d1 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,5 @@ test_file_text.txt # Untracked artifacts from the conda script repo2docker/buildpacks/conda/environment.py-3.5.yml repo2docker/buildpacks/conda/environment.py-3.6.yml + +\.vscode/ diff --git a/repo2docker/buildpacks/base.py b/repo2docker/buildpacks/base.py index 8d5991af..a2325411 100644 --- a/repo2docker/buildpacks/base.py +++ b/repo2docker/buildpacks/base.py @@ -332,7 +332,7 @@ class BuildPack: An ordered list of executable scripts to execute after build. Is run as a non-root user, and must be executable. Used for performing - build time steps that can not be perfomed with standard tools. + build time steps that can not be performed with standard tools. The scripts should be as deterministic as possible - running it twice should not produce different results! @@ -341,12 +341,12 @@ class BuildPack: def get_start_script(self): """ - The path to a script to be executated at container start up. + The path to a script to be executed at container start up. This script is added as the `ENTRYPOINT` to the container. It is run as a non-root user, and must be executable. Used for performing - run time steps that can not be perfomed with standard tools. For example + run time steps that can not be performed with standard tools. For example setting environment variables for your repository. The script should be as deterministic as possible - running it twice From 24def164edd2b7ff6e03746a9180bee15abbe818 Mon Sep 17 00:00:00 2001 From: nuest Date: Mon, 29 Oct 2018 20:52:05 +0100 Subject: [PATCH 07/37] verify R installation for r-markdown with Stencila --- .../{basic/archive => pyjp}/py-jupyter/bibliography.bibtex | 0 .../stencila/{basic/archive => pyjp}/py-jupyter/manifest.xml | 0 .../{basic/archive => pyjp}/py-jupyter/py-jupyter.ipynb | 0 .../archive => pyjp}/py-jupyter/py-jupyter.ipynb.jats.xml | 0 tests/stencila/{basic => pyjp}/verify | 0 .../{basic/archive => r}/r-markdown/bibliography.bibtex | 0 tests/stencila/{basic/archive => r}/r-markdown/manifest.xml | 0 tests/stencila/{basic/archive => r}/r-markdown/rmarkdown.Rmd | 0 .../{basic/archive => r}/r-markdown/rmarkdown.Rmd.jats.xml | 0 tests/stencila/r/verify | 5 +++++ 10 files changed, 5 insertions(+) rename tests/stencila/{basic/archive => pyjp}/py-jupyter/bibliography.bibtex (100%) rename tests/stencila/{basic/archive => pyjp}/py-jupyter/manifest.xml (100%) rename tests/stencila/{basic/archive => pyjp}/py-jupyter/py-jupyter.ipynb (100%) rename tests/stencila/{basic/archive => pyjp}/py-jupyter/py-jupyter.ipynb.jats.xml (100%) rename tests/stencila/{basic => pyjp}/verify (100%) rename tests/stencila/{basic/archive => r}/r-markdown/bibliography.bibtex (100%) rename tests/stencila/{basic/archive => r}/r-markdown/manifest.xml (100%) rename tests/stencila/{basic/archive => r}/r-markdown/rmarkdown.Rmd (100%) rename tests/stencila/{basic/archive => r}/r-markdown/rmarkdown.Rmd.jats.xml (100%) create mode 100755 tests/stencila/r/verify diff --git a/tests/stencila/basic/archive/py-jupyter/bibliography.bibtex b/tests/stencila/pyjp/py-jupyter/bibliography.bibtex similarity index 100% rename from tests/stencila/basic/archive/py-jupyter/bibliography.bibtex rename to tests/stencila/pyjp/py-jupyter/bibliography.bibtex diff --git a/tests/stencila/basic/archive/py-jupyter/manifest.xml b/tests/stencila/pyjp/py-jupyter/manifest.xml similarity index 100% rename from tests/stencila/basic/archive/py-jupyter/manifest.xml rename to tests/stencila/pyjp/py-jupyter/manifest.xml diff --git a/tests/stencila/basic/archive/py-jupyter/py-jupyter.ipynb b/tests/stencila/pyjp/py-jupyter/py-jupyter.ipynb similarity index 100% rename from tests/stencila/basic/archive/py-jupyter/py-jupyter.ipynb rename to tests/stencila/pyjp/py-jupyter/py-jupyter.ipynb diff --git a/tests/stencila/basic/archive/py-jupyter/py-jupyter.ipynb.jats.xml b/tests/stencila/pyjp/py-jupyter/py-jupyter.ipynb.jats.xml similarity index 100% rename from tests/stencila/basic/archive/py-jupyter/py-jupyter.ipynb.jats.xml rename to tests/stencila/pyjp/py-jupyter/py-jupyter.ipynb.jats.xml diff --git a/tests/stencila/basic/verify b/tests/stencila/pyjp/verify similarity index 100% rename from tests/stencila/basic/verify rename to tests/stencila/pyjp/verify diff --git a/tests/stencila/basic/archive/r-markdown/bibliography.bibtex b/tests/stencila/r/r-markdown/bibliography.bibtex similarity index 100% rename from tests/stencila/basic/archive/r-markdown/bibliography.bibtex rename to tests/stencila/r/r-markdown/bibliography.bibtex diff --git a/tests/stencila/basic/archive/r-markdown/manifest.xml b/tests/stencila/r/r-markdown/manifest.xml similarity index 100% rename from tests/stencila/basic/archive/r-markdown/manifest.xml rename to tests/stencila/r/r-markdown/manifest.xml diff --git a/tests/stencila/basic/archive/r-markdown/rmarkdown.Rmd b/tests/stencila/r/r-markdown/rmarkdown.Rmd similarity index 100% rename from tests/stencila/basic/archive/r-markdown/rmarkdown.Rmd rename to tests/stencila/r/r-markdown/rmarkdown.Rmd diff --git a/tests/stencila/basic/archive/r-markdown/rmarkdown.Rmd.jats.xml b/tests/stencila/r/r-markdown/rmarkdown.Rmd.jats.xml similarity index 100% rename from tests/stencila/basic/archive/r-markdown/rmarkdown.Rmd.jats.xml rename to tests/stencila/r/r-markdown/rmarkdown.Rmd.jats.xml diff --git a/tests/stencila/r/verify b/tests/stencila/r/verify new file mode 100755 index 00000000..2bd4ad54 --- /dev/null +++ b/tests/stencila/r/verify @@ -0,0 +1,5 @@ +#!/bin/sh + +jupyter serverextension list 2>&1 | grep nbstencilaproxy +jupyter nbextension list 2>&1 | grep nbstencilaproxy +R -e "library('stencila');" From 9624f0f161da10dd6ac4e3ab900bdcf80668b790 Mon Sep 17 00:00:00 2001 From: nuest Date: Mon, 29 Oct 2018 14:48:30 +0100 Subject: [PATCH 08/37] extract contexts from stencila document --- repo2docker/buildpacks/base.py | 63 ++++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 3 deletions(-) diff --git a/repo2docker/buildpacks/base.py b/repo2docker/buildpacks/base.py index a2325411..cbb51f38 100644 --- a/repo2docker/buildpacks/base.py +++ b/repo2docker/buildpacks/base.py @@ -273,14 +273,44 @@ class BuildPack: self._stencila_manifest_dir = None for root, dirs, files in os.walk("."): if "manifest.xml" in files: + self.log.debug("Found a manifest.xml at %s", root) self._stencila_manifest_dir = root.split(os.path.sep, 1)[1] self.log.info( - "Found stencila manifest.xml in %s", + "Using stencila manifest.xml in %s", self._stencila_manifest_dir, ) break return self._stencila_manifest_dir + @property + def stencila_manifest_contexts(self): + """Find the stencila manifest contexts if it exists""" + if hasattr(self, '_stencila_manifest_contexts'): + return self._stencila_manifest_contexts + + # look at the content of the jats.xml file to extract the required execution contexts + + self._stencila_manifest_contexts = None + for root, dirs, files in os.walk("."): + for filename in files: + if filename.endswith(".test.jats.xml"): + self.log.debug("Found a .jats.xml: %s", filename) + self._stencila_manifest_contexts = set() + + # extract code languages from file + with open(os.path.join(root, filename)) as f: + for line in f: + match = re.match('.*language="(.+?)"', line) + if match: + self._stencila_manifest_contexts.add(match.group(1)) + + self.log.info( + "Using stencila executions contexts %s", + self._stencila_manifest_contexts, + ) + break + return self._stencila_manifest_contexts + def get_build_scripts(self): """ Ordered list of shell script snippets to build the base image. @@ -518,14 +548,41 @@ class BaseImage(BuildPack): )) except FileNotFoundError: pass + if self.stencila_manifest_contexts: + for context in self.stencila_manifest_contexts: + if(context == 'r'): + + # TODO: can I trigger the RBuildPack from here? + + assemble_scripts.extend( + [ + ( + "${NB_USER}", + r""" + R --quiet -e "library('devtools'); options(unzip = 'internal'); devtools::install_github('stencila/r', ref = 'f220361438432abca968d2e76a4efe7c5ddde7f1')" && \ + R --quiet -e "stencila::register()" + """, + ) + ] + ) + elif(context == 'py' or context == 'pyjp'): + assemble_scripts.extend( + [ + ( + "${NB_USER}", + r""" + ${KERNEL_PYTHON_PREFIX}/bin/pip install --no-cache https://github.com/stencila/py/archive/f6a245fd.tar.gz && \ + ${KERNEL_PYTHON_PREFIX}/bin/python -m stencila register + """, + ) + ] + ) if self.stencila_manifest_dir: assemble_scripts.extend( [ ( "${NB_USER}", r""" - ${KERNEL_PYTHON_PREFIX}/bin/pip install --no-cache https://github.com/stencila/py/archive/f6a245fd.tar.gz && \ - ${KERNEL_PYTHON_PREFIX}/bin/python -m stencila register && \ ${NB_PYTHON_PREFIX}/bin/pip install --no-cache nbstencilaproxy==0.1.1 && \ jupyter serverextension enable --sys-prefix --py nbstencilaproxy && \ jupyter nbextension install --sys-prefix --py nbstencilaproxy && \ From 8342fbd1c0ac273c64692871ccb72fecf0a85161 Mon Sep 17 00:00:00 2001 From: nuest Date: Tue, 30 Oct 2018 09:02:47 +0100 Subject: [PATCH 09/37] do not look for stencila if binder dir found --- repo2docker/buildpacks/base.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/repo2docker/buildpacks/base.py b/repo2docker/buildpacks/base.py index cbb51f38..42e9c260 100644 --- a/repo2docker/buildpacks/base.py +++ b/repo2docker/buildpacks/base.py @@ -271,6 +271,10 @@ class BuildPack: # ${STENCILA_ARCHIVE_DIR}/${STENCILA_ARCHIVE}/manifest.xml self._stencila_manifest_dir = None + + if os.path.exists('binder'): + return self._stencila_manifest_dir + for root, dirs, files in os.walk("."): if "manifest.xml" in files: self.log.debug("Found a manifest.xml at %s", root) @@ -291,6 +295,10 @@ class BuildPack: # look at the content of the jats.xml file to extract the required execution contexts self._stencila_manifest_contexts = None + + if os.path.exists('binder'): + return self._stencila_manifest_contexts + for root, dirs, files in os.walk("."): for filename in files: if filename.endswith(".test.jats.xml"): From 3a479b6deea2c400ced2134adbf5dde7319183ba Mon Sep 17 00:00:00 2001 From: nuest Date: Tue, 30 Oct 2018 10:32:42 +0100 Subject: [PATCH 10/37] install R for Stencila from R buildpack --- repo2docker/buildpacks/base.py | 21 +++------------- repo2docker/buildpacks/r.py | 46 ++++++++++++++++++++++++++-------- 2 files changed, 39 insertions(+), 28 deletions(-) diff --git a/repo2docker/buildpacks/base.py b/repo2docker/buildpacks/base.py index 42e9c260..5f6c6bcb 100644 --- a/repo2docker/buildpacks/base.py +++ b/repo2docker/buildpacks/base.py @@ -301,7 +301,7 @@ class BuildPack: for root, dirs, files in os.walk("."): for filename in files: - if filename.endswith(".test.jats.xml"): + if filename.endswith(".jats.xml"): self.log.debug("Found a .jats.xml: %s", filename) self._stencila_manifest_contexts = set() @@ -558,22 +558,7 @@ class BaseImage(BuildPack): pass if self.stencila_manifest_contexts: for context in self.stencila_manifest_contexts: - if(context == 'r'): - - # TODO: can I trigger the RBuildPack from here? - - assemble_scripts.extend( - [ - ( - "${NB_USER}", - r""" - R --quiet -e "library('devtools'); options(unzip = 'internal'); devtools::install_github('stencila/r', ref = 'f220361438432abca968d2e76a4efe7c5ddde7f1')" && \ - R --quiet -e "stencila::register()" - """, - ) - ] - ) - elif(context == 'py' or context == 'pyjp'): + if(context == 'py' or context == 'pyjp'): assemble_scripts.extend( [ ( @@ -585,6 +570,8 @@ class BaseImage(BuildPack): ) ] ) + #elif(context == 'r'): + # handled in RBuildPack if self.stencila_manifest_dir: assemble_scripts.extend( [ diff --git a/repo2docker/buildpacks/r.py b/repo2docker/buildpacks/r.py index 6be41b06..22c7715e 100644 --- a/repo2docker/buildpacks/r.py +++ b/repo2docker/buildpacks/r.py @@ -19,7 +19,14 @@ class RBuildPack(PythonBuildPack): date snapshot of https://mran.microsoft.com/timemachine from which libraries are to be installed. - 2. An optional `install.R` file that will be executed at build time, + 2. A `DESCRIPTION` file signaling an R package, or + a Stencila document (*.jats.xml) with R code chunks (i.e. language="r") + is found: + + MRAN snapshot is set to latest date that is guaranteed to exist + across timezones. + + 3. (Optional) An `install.R` file that will be executed at build time, and can be used for installing packages from both MRAN and GitHub. The `r-base` package from Ubuntu apt repositories is used to install @@ -64,21 +71,22 @@ class RBuildPack(PythonBuildPack): unless a `requirements.txt` is present and we do not want to require the presence of a `requirements.txt` to use R. - Instead we just check if runtime.txt contains a string of the form - `r---
` + Instead we check the options described in the class comment above. """ # If no date is found, then self.checkpoint_date will be False # Otherwise, it'll be a date object, which will evaluate to True if self.checkpoint_date: return True + description_R = 'DESCRIPTION' - if not os.path.exists('binder') and os.path.exists(description_R): - if not self.checkpoint_date: - # no R snapshot date set through runtime.txt - # set the R runtime to the latest date that is guaranteed to be on MRAN across timezones - self._checkpoint_date = datetime.date.today() - datetime.timedelta(days=2) - self._runtime = "r-{}".format(str(self._checkpoint_date)) - return True + if not os.path.exists('binder'): + if os.path.exists(description_R) or (self.stencila_manifest_contexts and "r" in self.stencila_manifest_contexts): + if not self.checkpoint_date: + # no R snapshot date set through runtime.txt + # set the R runtime to the latest date that is guaranteed to be on MRAN across timezones + self._checkpoint_date = datetime.date.today() - datetime.timedelta(days=2) + self._runtime = "r-{}".format(str(self._checkpoint_date)) + return True def get_path(self): """ @@ -133,6 +141,7 @@ class RBuildPack(PythonBuildPack): - R's devtools package, at a particular frozen version (determined by MRAN) - IRKernel - nbrsessionproxy (to access RStudio via Jupyter Notebook) + - stencila R package (if Stencila document with R code chunks detected) """ rstudio_url = 'https://download2.rstudio.org/rstudio-server-1.1.419-amd64.deb' # This is MD5, because that is what RStudio download page provides! @@ -148,7 +157,7 @@ class RBuildPack(PythonBuildPack): # IRKernel version - specified as a tag in the IRKernel repository irkernel_version = '0.8.11' - return super().get_build_scripts() + [ + scripts = [ ( "root", r""" @@ -226,6 +235,21 @@ class RBuildPack(PythonBuildPack): ), ] + if self.stencila_manifest_contexts: + if "r" in self.stencila_manifest_contexts: + scripts += [ + ( + "${NB_USER}", + # Install and register stencila library + r""" + R --quiet -e "devtools::install_github('stencila/r', ref = 'f220361438432abca968d2e76a4efe7c5ddde7f1')" && \ + R --quiet -e "stencila::register()" + """ + ), + ] + + return super().get_build_scripts() + scripts + def get_assemble_scripts(self): """ Return series of build-steps specific to this repository From da41a90e0464b544ee886a4b0a2fdbc597a88b67 Mon Sep 17 00:00:00 2001 From: nuest Date: Tue, 30 Oct 2018 10:33:18 +0100 Subject: [PATCH 11/37] update versions of stencila/r and stencila/py --- repo2docker/buildpacks/base.py | 2 +- repo2docker/buildpacks/r.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/repo2docker/buildpacks/base.py b/repo2docker/buildpacks/base.py index 5f6c6bcb..5c205a0d 100644 --- a/repo2docker/buildpacks/base.py +++ b/repo2docker/buildpacks/base.py @@ -564,7 +564,7 @@ class BaseImage(BuildPack): ( "${NB_USER}", r""" - ${KERNEL_PYTHON_PREFIX}/bin/pip install --no-cache https://github.com/stencila/py/archive/f6a245fd.tar.gz && \ + ${KERNEL_PYTHON_PREFIX}/bin/pip install --no-cache https://github.com/stencila/py/archive/f1260796.tar.gz && \ ${KERNEL_PYTHON_PREFIX}/bin/python -m stencila register """, ) diff --git a/repo2docker/buildpacks/r.py b/repo2docker/buildpacks/r.py index 22c7715e..e4618819 100644 --- a/repo2docker/buildpacks/r.py +++ b/repo2docker/buildpacks/r.py @@ -242,7 +242,8 @@ class RBuildPack(PythonBuildPack): "${NB_USER}", # Install and register stencila library r""" - R --quiet -e "devtools::install_github('stencila/r', ref = 'f220361438432abca968d2e76a4efe7c5ddde7f1')" && \ + R --quiet -e "source('https://bioconductor.org/biocLite.R'); biocLite('graph')" && \ + R --quiet -e "devtools::install_github('stencila/r', ref = '361bbf560f3f0561a8612349bca66cd8978f4f24')" && \ R --quiet -e "stencila::register()" """ ), From 7e8450fceeb50395f81e38ccc32f9ee80ba65881 Mon Sep 17 00:00:00 2001 From: nuest Date: Tue, 30 Oct 2018 10:33:21 +0100 Subject: [PATCH 12/37] more detailed check for Stencila + Python test --- tests/stencila/pyjp/verify | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/stencila/pyjp/verify b/tests/stencila/pyjp/verify index 0d9073c8..788ef9da 100755 --- a/tests/stencila/pyjp/verify +++ b/tests/stencila/pyjp/verify @@ -2,3 +2,4 @@ jupyter serverextension list 2>&1 | grep nbstencilaproxy jupyter nbextension list 2>&1 | grep nbstencilaproxy +python3 -c "import stencila" From 6d304174dafa90c6b5bbb818a192ecada0bdca61 Mon Sep 17 00:00:00 2001 From: nuest Date: Tue, 30 Oct 2018 11:34:27 +0100 Subject: [PATCH 13/37] add Stencila config files to docs --- docs/source/config_files.rst | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/source/config_files.rst b/docs/source/config_files.rst index fe3fb02e..3834c3fe 100644 --- a/docs/source/config_files.rst +++ b/docs/source/config_files.rst @@ -107,6 +107,22 @@ You also need to have a ``runtime.txt`` file that is formatted as ``r---
``, where YYYY-MM-DD is a snapshot of MRAN that will be used for your R installation. +``manifest.xml`` - Install Stencila +=================================== + +`Stencila `_ is an open source office suite for reproducible research. +It is powered by the open file format `Dar `_. + +If your repository contains a Stencila document, repo2docker detects it based on the file ``manifest.xml``. +The required `execution contexts ` are extracted from a Dar article (i.e. +files named ``*.jats.xml``). + +You may also have a ``runtime.txt`` and/or an ``install.R`` to manually configure the your R installation. + +To see example repositories, visit our +`Stencila with R `_ and +`Stencila with Python `_ examples. + .. _postBuild: ``postBuild`` - Run code after installing the environment From 3f4da20f7ef8c8887075b50c4811d1737f8233ba Mon Sep 17 00:00:00 2001 From: nuest Date: Tue, 30 Oct 2018 11:58:46 +0100 Subject: [PATCH 14/37] reduce calls to stencila_manifest_contexts --- repo2docker/buildpacks/base.py | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/repo2docker/buildpacks/base.py b/repo2docker/buildpacks/base.py index 5c205a0d..d8dc9bb2 100644 --- a/repo2docker/buildpacks/base.py +++ b/repo2docker/buildpacks/base.py @@ -557,21 +557,20 @@ class BaseImage(BuildPack): except FileNotFoundError: pass if self.stencila_manifest_contexts: - for context in self.stencila_manifest_contexts: - if(context == 'py' or context == 'pyjp'): - assemble_scripts.extend( - [ - ( - "${NB_USER}", - r""" - ${KERNEL_PYTHON_PREFIX}/bin/pip install --no-cache https://github.com/stencila/py/archive/f1260796.tar.gz && \ - ${KERNEL_PYTHON_PREFIX}/bin/python -m stencila register - """, - ) - ] - ) - #elif(context == 'r'): - # handled in RBuildPack + if {'py', 'pyjp'}.intersection(self.stencila_manifest_contexts): + assemble_scripts.extend( + [ + ( + "${NB_USER}", + r""" + ${KERNEL_PYTHON_PREFIX}/bin/pip install --no-cache https://github.com/stencila/py/archive/f1260796.tar.gz && \ + ${KERNEL_PYTHON_PREFIX}/bin/python -m stencila register + """, + ) + ] + ) + #if {'r'}.intersection(self.stencila_manifest_contexts): + # handled in RBuildPack if self.stencila_manifest_dir: assemble_scripts.extend( [ From a57a44391f206d18c7267aab4ef8d727105624a7 Mon Sep 17 00:00:00 2001 From: Tim Head Date: Tue, 30 Oct 2018 14:53:50 +0100 Subject: [PATCH 15/37] Fix wording in config_files.rst Co-Authored-By: nuest --- docs/source/config_files.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/config_files.rst b/docs/source/config_files.rst index 3834c3fe..2646d1cb 100644 --- a/docs/source/config_files.rst +++ b/docs/source/config_files.rst @@ -117,7 +117,7 @@ If your repository contains a Stencila document, repo2docker detects it based on The required `execution contexts ` are extracted from a Dar article (i.e. files named ``*.jats.xml``). -You may also have a ``runtime.txt`` and/or an ``install.R`` to manually configure the your R installation. +You may also have a ``runtime.txt`` and/or an ``install.R`` to manually configure your R installation. To see example repositories, visit our `Stencila with R `_ and From 8d9a48717aebef6fd85d5be7fe713c142c10fdb0 Mon Sep 17 00:00:00 2001 From: nuest Date: Tue, 30 Oct 2018 15:59:42 +0100 Subject: [PATCH 16/37] add Stencila to user interface docs --- docs/source/howto/user_interface.rst | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/docs/source/howto/user_interface.rst b/docs/source/howto/user_interface.rst index dd9d6eab..27ea633c 100644 --- a/docs/source/howto/user_interface.rst +++ b/docs/source/howto/user_interface.rst @@ -31,10 +31,29 @@ To learn more about URLs in JupyterLab and Jupyter Notebook, visit RStudio ------- -The RStudio user interface is automatically enabled a configuration file for -R is detected (an R version specified in ``runtime.txt``). If this is detected, +The RStudio user interface is automatically enabled if a configuration file for +R is detected (i.e. an R version specified in ``runtime.txt``). If this is detected, RStudio will be accessible by appending ``/rstudio`` to the URL, like so: .. code-block:: none http(s):///rstudio + +Stencila +-------- + +The Stencila user interface is automatically enabled if a Stencila document (i.e. +a file `manifest.xml`) is detected. Stencila will be accessible by appending +``/stencila`` to the URL, like so: + +.. code-block:: none + + http(s):///stencila + +The editor will open the Stencila document corresponding to the last `manifest.xml` +found in the file tree. If you want to open a different document, you can configure +the path in the URL parameter `archive`: + +.. code-block:: none + + http(s):///stencila/?archive=other-dir From d6dc570a14aa47a6376df284cc4ace96b0d5b2e8 Mon Sep 17 00:00:00 2001 From: nuest Date: Tue, 30 Oct 2018 16:02:40 +0100 Subject: [PATCH 17/37] continue Stencila detection if /binder dir is present, improve --- repo2docker/buildpacks/base.py | 8 +------- repo2docker/buildpacks/r.py | 15 +++++++-------- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/repo2docker/buildpacks/base.py b/repo2docker/buildpacks/base.py index d8dc9bb2..1e8461ed 100644 --- a/repo2docker/buildpacks/base.py +++ b/repo2docker/buildpacks/base.py @@ -272,9 +272,6 @@ class BuildPack: self._stencila_manifest_dir = None - if os.path.exists('binder'): - return self._stencila_manifest_dir - for root, dirs, files in os.walk("."): if "manifest.xml" in files: self.log.debug("Found a manifest.xml at %s", root) @@ -294,10 +291,7 @@ class BuildPack: # look at the content of the jats.xml file to extract the required execution contexts - self._stencila_manifest_contexts = None - - if os.path.exists('binder'): - return self._stencila_manifest_contexts + self._stencila_manifest_contexts = [] for root, dirs, files in os.walk("."): for filename in files: diff --git a/repo2docker/buildpacks/r.py b/repo2docker/buildpacks/r.py index e4618819..78fc194b 100644 --- a/repo2docker/buildpacks/r.py +++ b/repo2docker/buildpacks/r.py @@ -79,14 +79,13 @@ class RBuildPack(PythonBuildPack): return True description_R = 'DESCRIPTION' - if not os.path.exists('binder'): - if os.path.exists(description_R) or (self.stencila_manifest_contexts and "r" in self.stencila_manifest_contexts): - if not self.checkpoint_date: - # no R snapshot date set through runtime.txt - # set the R runtime to the latest date that is guaranteed to be on MRAN across timezones - self._checkpoint_date = datetime.date.today() - datetime.timedelta(days=2) - self._runtime = "r-{}".format(str(self._checkpoint_date)) - return True + if (not os.path.exists('binder') and os.path.exists(description_R)) or 'r' in self.stencila_manifest_contexts: + if not self.checkpoint_date: + # no R snapshot date set through runtime.txt + # set the R runtime to the latest date that is guaranteed to be on MRAN across timezones + self._checkpoint_date = datetime.date.today() - datetime.timedelta(days=2) + self._runtime = "r-{}".format(str(self._checkpoint_date)) + return True def get_path(self): """ From 12b44574375e1a6b35f1a5179d6ad2cc8cbe917d Mon Sep 17 00:00:00 2001 From: nuest Date: Tue, 30 Oct 2018 16:03:38 +0100 Subject: [PATCH 18/37] restructure R buildpack docs --- repo2docker/buildpacks/r.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/repo2docker/buildpacks/r.py b/repo2docker/buildpacks/r.py index 78fc194b..5a856d64 100644 --- a/repo2docker/buildpacks/r.py +++ b/repo2docker/buildpacks/r.py @@ -19,15 +19,22 @@ class RBuildPack(PythonBuildPack): date snapshot of https://mran.microsoft.com/timemachine from which libraries are to be installed. - 2. A `DESCRIPTION` file signaling an R package, or - a Stencila document (*.jats.xml) with R code chunks (i.e. language="r") - is found: + 2. A `DESCRIPTION` file signaling an R package - MRAN snapshot is set to latest date that is guaranteed to exist - across timezones. + 3. A Stencila document (*.jats.xml) with R code chunks (i.e. language="r") - 3. (Optional) An `install.R` file that will be executed at build time, - and can be used for installing packages from both MRAN and GitHub. + If there is no `runtime.txt`, then the MRAN snapshot is set to latest + date that is guaranteed to exist across timezones. + + R packages are installed if specified either + + - in a file `install.R`, that will be executed at build time, + and can be used for installing packages from both MRAN and GitHub + + - as dependencies in a `DESCRIPTION` file + + - are needed by a specific tool, for example the package `stencila` is + installed and configured if a Stencila document is given. The `r-base` package from Ubuntu apt repositories is used to install R itself, rather than any of the methods from https://cran.r-project.org/. @@ -70,8 +77,6 @@ class RBuildPack(PythonBuildPack): super().detect() is not called in this function - it would return false unless a `requirements.txt` is present and we do not want to require the presence of a `requirements.txt` to use R. - - Instead we check the options described in the class comment above. """ # If no date is found, then self.checkpoint_date will be False # Otherwise, it'll be a date object, which will evaluate to True From c843e25f169f0912f8b56b254137eecd0e2dc751 Mon Sep 17 00:00:00 2001 From: nuest Date: Tue, 30 Oct 2018 16:24:43 +0100 Subject: [PATCH 19/37] simplify test for contexts (is now by default an empty list) --- repo2docker/buildpacks/r.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/repo2docker/buildpacks/r.py b/repo2docker/buildpacks/r.py index 5a856d64..7dcbe30d 100644 --- a/repo2docker/buildpacks/r.py +++ b/repo2docker/buildpacks/r.py @@ -239,19 +239,18 @@ class RBuildPack(PythonBuildPack): ), ] - if self.stencila_manifest_contexts: - if "r" in self.stencila_manifest_contexts: - scripts += [ - ( - "${NB_USER}", - # Install and register stencila library - r""" - R --quiet -e "source('https://bioconductor.org/biocLite.R'); biocLite('graph')" && \ - R --quiet -e "devtools::install_github('stencila/r', ref = '361bbf560f3f0561a8612349bca66cd8978f4f24')" && \ - R --quiet -e "stencila::register()" - """ - ), - ] + if "r" in self.stencila_contexts: + scripts += [ + ( + "${NB_USER}", + # Install and register stencila library + r""" + R --quiet -e "source('https://bioconductor.org/biocLite.R'); biocLite('graph')" && \ + R --quiet -e "devtools::install_github('stencila/r', ref = '361bbf560f3f0561a8612349bca66cd8978f4f24')" && \ + R --quiet -e "stencila::register()" + """ + ), + ] return super().get_build_scripts() + scripts From 9c81c3ed19140d4ed49d9a17beccd24a98b19c58 Mon Sep 17 00:00:00 2001 From: nuest Date: Tue, 30 Oct 2018 16:25:40 +0100 Subject: [PATCH 20/37] rename stencila_manifest_contexts to stencila_contexts also remove dev comment --- repo2docker/buildpacks/base.py | 22 ++++++++++------------ repo2docker/buildpacks/r.py | 2 +- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/repo2docker/buildpacks/base.py b/repo2docker/buildpacks/base.py index 1e8461ed..79fad84f 100644 --- a/repo2docker/buildpacks/base.py +++ b/repo2docker/buildpacks/base.py @@ -284,34 +284,34 @@ class BuildPack: return self._stencila_manifest_dir @property - def stencila_manifest_contexts(self): + def stencila_contexts(self): """Find the stencila manifest contexts if it exists""" - if hasattr(self, '_stencila_manifest_contexts'): - return self._stencila_manifest_contexts + if hasattr(self, '_stencila_contexts'): + return self._stencila_contexts # look at the content of the jats.xml file to extract the required execution contexts - self._stencila_manifest_contexts = [] + self._stencila_contexts = [] for root, dirs, files in os.walk("."): for filename in files: if filename.endswith(".jats.xml"): self.log.debug("Found a .jats.xml: %s", filename) - self._stencila_manifest_contexts = set() + self._stencila_contexts = set() # extract code languages from file with open(os.path.join(root, filename)) as f: for line in f: match = re.match('.*language="(.+?)"', line) if match: - self._stencila_manifest_contexts.add(match.group(1)) + self._stencila_contexts.add(match.group(1)) self.log.info( "Using stencila executions contexts %s", - self._stencila_manifest_contexts, + self._stencila_contexts, ) break - return self._stencila_manifest_contexts + return self._stencila_contexts def get_build_scripts(self): """ @@ -550,8 +550,8 @@ class BaseImage(BuildPack): )) except FileNotFoundError: pass - if self.stencila_manifest_contexts: - if {'py', 'pyjp'}.intersection(self.stencila_manifest_contexts): + if self.stencila_contexts: + if {'py', 'pyjp'}.intersection(self.stencila_contexts): assemble_scripts.extend( [ ( @@ -563,8 +563,6 @@ class BaseImage(BuildPack): ) ] ) - #if {'r'}.intersection(self.stencila_manifest_contexts): - # handled in RBuildPack if self.stencila_manifest_dir: assemble_scripts.extend( [ diff --git a/repo2docker/buildpacks/r.py b/repo2docker/buildpacks/r.py index 7dcbe30d..220099df 100644 --- a/repo2docker/buildpacks/r.py +++ b/repo2docker/buildpacks/r.py @@ -84,7 +84,7 @@ class RBuildPack(PythonBuildPack): return True description_R = 'DESCRIPTION' - if (not os.path.exists('binder') and os.path.exists(description_R)) or 'r' in self.stencila_manifest_contexts: + if (not os.path.exists('binder') and os.path.exists(description_R)) or 'r' in self.stencila_contexts: if not self.checkpoint_date: # no R snapshot date set through runtime.txt # set the R runtime to the latest date that is guaranteed to be on MRAN across timezones From b1bff5bf5e3f372ce6964a964efd3d0a7e1c3b52 Mon Sep 17 00:00:00 2001 From: nuest Date: Tue, 30 Oct 2018 17:01:05 +0100 Subject: [PATCH 21/37] parse Stencila xml files instead of regex --- repo2docker/buildpacks/base.py | 40 +++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/repo2docker/buildpacks/base.py b/repo2docker/buildpacks/base.py index 79fad84f..6c31e9d6 100644 --- a/repo2docker/buildpacks/base.py +++ b/repo2docker/buildpacks/base.py @@ -7,6 +7,7 @@ import re import logging import docker import sys +import xml.etree.ElementTree as ET TEMPLATE = r""" FROM buildpack-deps:bionic @@ -289,28 +290,31 @@ class BuildPack: if hasattr(self, '_stencila_contexts'): return self._stencila_contexts - # look at the content of the jats.xml file to extract the required execution contexts + # look at the content of the documents in the manifest to extract the required execution contexts + self._stencila_contexts = set() - self._stencila_contexts = [] + # get paths to the article files from manifest + files = [] + if (self.stencila_manifest_dir): + manifest = ET.parse(os.path.join(self.stencila_manifest_dir, 'manifest.xml')) + files = list(map(lambda x: os.path.join(self.stencila_manifest_dir, x.get('path')), manifest.findall('./documents/document'))) + else: + return self._stencila_contexts - for root, dirs, files in os.walk("."): - for filename in files: - if filename.endswith(".jats.xml"): - self.log.debug("Found a .jats.xml: %s", filename) - self._stencila_contexts = set() + for filename in files: + self.log.debug("Extracting contexts from %s", filename) - # extract code languages from file - with open(os.path.join(root, filename)) as f: - for line in f: - match = re.match('.*language="(.+?)"', line) - if match: - self._stencila_contexts.add(match.group(1)) + # extract code languages from file + document = ET.parse(filename) + languages = list(map(lambda x: x.get('language'), document.findall('.//code[@specific-use="source"]'))) + self._stencila_contexts.update(languages) - self.log.info( - "Using stencila executions contexts %s", - self._stencila_contexts, - ) - break + self.log.info( + "Added executions contexts, now have %s", + self._stencila_contexts, + ) + break + return self._stencila_contexts def get_build_scripts(self): From d12c4b54c3c5e43717d85f481d7a5c1cce8953bf Mon Sep 17 00:00:00 2001 From: nuest Date: Wed, 31 Oct 2018 08:44:34 +0100 Subject: [PATCH 22/37] increase timeout for stencila tests on travis --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 219aba75..ca73411e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,8 +16,8 @@ script: # cd into tests so CWD being repo2docker does not hide # possible issues with MANIFEST.in - pushd tests; - if [ ${REPO_TYPE} == "r" ]; then - travis_wait pytest --cov repo2docker -v ${REPO_TYPE} || exit 1; + if [ ${REPO_TYPE} == "r" ] || [ ${REPO_TYPE} == "stencila" ]; then + travis_wait 30 pytest --cov repo2docker -v ${REPO_TYPE} || exit 1; else travis_retry pytest --cov repo2docker -v ${REPO_TYPE} || exit 1; fi; From fb500d05bce3fe5a2588da4e93c9706f5878e6ae Mon Sep 17 00:00:00 2001 From: Tim Head Date: Mon, 5 Nov 2018 15:33:51 +0100 Subject: [PATCH 23/37] Update CHANGES.rst --- CHANGES.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index dbbd8d46..f7257c86 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -13,6 +13,8 @@ New features - Change log added `#426`_ by `@evertrol`_. - Documentation: improved the documentation for contributors `#453`_ by `@choldgraf`_. +- Buildpack: added support for the nix package manager `#407`_ by + `@costrouc`_. API changes @@ -66,8 +68,10 @@ Released 2017-04-14 .. _#421: https://github.com/jupyter/repo2docker/pull/421 .. _#426: https://github.com/jupyter/repo2docker/pull/426 .. _#242: https://github.com/jupyter/repo2docker/pull/242 +.. _#407: https://github.com/jupyter/repo2docker/pull/407 .. _@betatim: https://github.com/betatim .. _@choldgraf: https://github.com/choldgraf +.. _@costrouc: https://github.com/costrouc .. _@dsludwig: https://github.com/dsludwig .. _@evertrol: https://github.com/evertrol From b032a7cd278c96b7aa29c001cb2e3a9c47cdbbb2 Mon Sep 17 00:00:00 2001 From: nuest Date: Mon, 5 Nov 2018 18:51:10 +0100 Subject: [PATCH 24/37] safe one if clause (intersection is already false-y) --- repo2docker/buildpacks/base.py | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/repo2docker/buildpacks/base.py b/repo2docker/buildpacks/base.py index 6c31e9d6..8e1c04f9 100644 --- a/repo2docker/buildpacks/base.py +++ b/repo2docker/buildpacks/base.py @@ -554,19 +554,18 @@ class BaseImage(BuildPack): )) except FileNotFoundError: pass - if self.stencila_contexts: - if {'py', 'pyjp'}.intersection(self.stencila_contexts): - assemble_scripts.extend( - [ - ( - "${NB_USER}", - r""" - ${KERNEL_PYTHON_PREFIX}/bin/pip install --no-cache https://github.com/stencila/py/archive/f1260796.tar.gz && \ - ${KERNEL_PYTHON_PREFIX}/bin/python -m stencila register - """, - ) - ] - ) + if {'py', 'pyjp'}.intersection(self.stencila_contexts): + assemble_scripts.extend( + [ + ( + "${NB_USER}", + r""" + ${KERNEL_PYTHON_PREFIX}/bin/pip install --no-cache https://github.com/stencila/py/archive/f1260796.tar.gz && \ + ${KERNEL_PYTHON_PREFIX}/bin/python -m stencila register + """, + ) + ] + ) if self.stencila_manifest_dir: assemble_scripts.extend( [ From df7c6205c0578923cc4ab354cd3b2104484a7404 Mon Sep 17 00:00:00 2001 From: nuest Date: Mon, 5 Nov 2018 19:00:39 +0100 Subject: [PATCH 25/37] do not install Stencila Python context if only python-jupyter cells found --- repo2docker/buildpacks/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repo2docker/buildpacks/base.py b/repo2docker/buildpacks/base.py index 8e1c04f9..10c7f3ea 100644 --- a/repo2docker/buildpacks/base.py +++ b/repo2docker/buildpacks/base.py @@ -554,7 +554,7 @@ class BaseImage(BuildPack): )) except FileNotFoundError: pass - if {'py', 'pyjp'}.intersection(self.stencila_contexts): + if {'py'}.intersection(self.stencila_contexts): assemble_scripts.extend( [ ( From c2b17d599eb09afb52223ee3768dcbaeff573a5d Mon Sep 17 00:00:00 2001 From: nuest Date: Mon, 5 Nov 2018 21:10:13 +0100 Subject: [PATCH 26/37] add tests pyjp vs. py for Stencila, make Stencila tests more specific --- .../py/plain-python/bibliography.bibtex | 21 ++ tests/stencila/py/plain-python/manifest.xml | 6 + tests/stencila/py/plain-python/py.ipynb | 195 ++++++++++++++++ .../py/plain-python/py.ipynb.jats.xml | 212 ++++++++++++++++++ tests/stencila/py/verify | 5 + tests/stencila/pyjp/verify | 2 +- tests/stencila/r/verify | 1 + 7 files changed, 441 insertions(+), 1 deletion(-) create mode 100644 tests/stencila/py/plain-python/bibliography.bibtex create mode 100644 tests/stencila/py/plain-python/manifest.xml create mode 100644 tests/stencila/py/plain-python/py.ipynb create mode 100644 tests/stencila/py/plain-python/py.ipynb.jats.xml create mode 100755 tests/stencila/py/verify diff --git a/tests/stencila/py/plain-python/bibliography.bibtex b/tests/stencila/py/plain-python/bibliography.bibtex new file mode 100644 index 00000000..643df1dd --- /dev/null +++ b/tests/stencila/py/plain-python/bibliography.bibtex @@ -0,0 +1,21 @@ +@article{kluyver2016jupyter, + title={Jupyter Notebooks-a publishing format for reproducible computational workflows.}, + author={Kluyver, Thomas and Ragan-Kelley, Benjamin and P{\'e}rez, Fernando and Granger, Brian E and Bussonnier, Matthias and Frederic, Jonathan and Kelley, Kyle and Hamrick, Jessica B and Grout, Jason and Corlay, Sylvain and others}, + journal={ELPUB}, + pages={87--90}, + year={2016} +} +@article{ragan2014jupyter, + title={The Jupyter/IPython architecture: a unified view of computational research, from interactive exploration to communication and publication.}, + author={Ragan-Kelley, M and Perez, F and Granger, B and Kluyver, T and Ivanov, P and Frederic, J and Bussonnier, M}, + journal={AGU Fall Meeting Abstracts}, + year={2014} +} +@article{perez2015project, + title={Project Jupyter: Computational narratives as the engine of collaborative data science}, + author={Perez, Fernando and Granger, Brian E}, + journal={Retrieved September}, + volume={11}, + pages={207}, + year={2015} +} diff --git a/tests/stencila/py/plain-python/manifest.xml b/tests/stencila/py/plain-python/manifest.xml new file mode 100644 index 00000000..ef19f7d7 --- /dev/null +++ b/tests/stencila/py/plain-python/manifest.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/tests/stencila/py/plain-python/py.ipynb b/tests/stencila/py/plain-python/py.ipynb new file mode 100644 index 00000000..2a2f1835 --- /dev/null +++ b/tests/stencila/py/plain-python/py.ipynb @@ -0,0 +1,195 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Introduction" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Jupyter notebooks ([@perez2015project; @kluyver2016jupyter; @ragan2014jupyter]) are one of the most popular platforms for doing reproducible research. Stencila supports importing of Jupyter Notebook `.ipynb` files. This allows you to work with collegues to refine a document for final publication while still retaining the code cells, and thus reprodubility of your the work. In the future we also plan to support exporting to `.ipynb` files. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Markdown cells\n", + "\n", + "Most standard Markdown should be supported by the importer including inline `code`, headings etc (although the Stencila user interface do not currently support rendering of some elements e.g. math and lists).\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Code cells\n", + "\n", + "Code cells in notebooks are imported without loss. Stencila's user interface currently differs from Jupyter in that code cells are executed on update while you are typing. This produces a very reactive user experience but is inappropriate for more compute intensive, longer running code cells. We are currently working on improving this to allowing users to decide to execute cells explicitly (e.g. using `Ctrl+Enter`)." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Hello this is Python 3.5 and it is Tue Feb 13 10:56:10 2018'" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import sys\n", + "import time\n", + "'Hello this is Python %s.%s and it is %s' % (sys.version_info[0], sys.version_info[1], time.strftime('%c'))\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Stencila also support Jupyter code cells that produce plots. The cell below produces a simple plot based on the example from [the Matplotlib website](https://matplotlib.org/examples/shapes_and_collections/scatter_demo.html). Try changing the code below (for example, the variable `N`)." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "\n", + "N = 50\n", + "N = min(N, 1000) # Prevent generation of too many numbers :)\n", + "x = np.random.rand(N)\n", + "y = np.random.rand(N)\n", + "colors = np.random.rand(N)\n", + "area = np.pi * (15 * np.random.rand(N))**2 # 0 to 15 point radii\n", + "\n", + "plt.scatter(x, y, s=area, c=colors, alpha=0.5)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We are currently working on supporting [Jupyter's magic commands](http://ipython.readthedocs.io/en/stable/interactive/magics.html) in Stencila via a bridge to Jupyter kernels." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Metadata\n", + "\n", + "To add some metadata about the document (such as authors, title, abstract and so on), In Jupyter, select `Edit -> Edit Notebook metadata` from the top menu. Add the title and abstract as JSON strings and authors and organisations metadata as [JSON arrays](https://www.w3schools.com/js/js_json_arrays.asp). Author `affiliation` identifiers (like `university-of-earth` below) must be unique and preferably use only lowercase characters and no spaces.\n", + " \n", + "For example," + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```\n", + " \"authors\": [\n", + " {\n", + " \"given-names\": \"Your first name goes here\",\n", + " \"surname\": \"Your last name goes here\",\n", + " \"email\": \"your.email@your-organisation\",\n", + " \"corresponding\": \"yes / no\",\n", + " \"affiliation\": \"university-of-earth\"\n", + " }\n", + " ],\n", + " \n", + " \"organisations\": [ \n", + " {\n", + " \"university-of-earth\": {\n", + " \"institution\": \"Your organisation name\",\n", + " \"city\": \"Your city\",\n", + " \"country\": \"Your country\" \n", + " }\n", + " ],\n", + "\n", + " \"title\": \"Your title goes here\",\n", + " \"abstract\": \"This is a paper about lots of different interesting things\",\n", + " \n", + " ```\n", + " \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Citations and references \n", + "\n", + "Stencila supports Pandoc style citations and reference lists within Jupyter notebook Markdown cells. Add a `bibliography` entry to the notebook's metadata which points to a file containing your list of references e.g.\n", + "\n", + "```json\n", + "\"bibliography\": \"my-bibliography.bibtex\"\n", + "```\n", + "\n", + "Then, within Markdown cells, you can insert citations inside square brackets and separated by semicolons. Each citation is represented using the `@` symbol followed by the citation identifier from the bibliography database e.g.\n", + "\n", + "```json\n", + "[@perez2015project; @kluyver2016jupyter]\n", + "```\n", + "\n", + "The [cite2c](https://github.com/takluyver/cite2c) Jupyter extension allows for easier, \"cite-while-you-write\" insertion of citations from a Zotero library. We're hoping to support conversion of cite2cstyle citations/references in the [future](https://github.com/stencila/convert/issues/14).\n" + ] + } + ], + "metadata": { + "anaconda-cloud": {}, + "authors": [ + { + "given-names": "Aleksandra", + "surname": "Pawlik" + } + ], + "bibliography": "bibliography.bibtex", + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.2" + }, + "title": "Jupyter and Stencila" + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/tests/stencila/py/plain-python/py.ipynb.jats.xml b/tests/stencila/py/plain-python/py.ipynb.jats.xml new file mode 100644 index 00000000..7e6e1d10 --- /dev/null +++ b/tests/stencila/py/plain-python/py.ipynb.jats.xml @@ -0,0 +1,212 @@ + + +
+ + + + Jupyter and Stencila + + + + + Pawlik + Aleksandra + + + + +

An example of a Jupyter notebook converted into a JATS document for editing in Stencila.

+
+
+
+ + + + Introduction +

Jupyter notebooks (13) are one of the most popular platforms for doing reproducible research. Stencila supports importing of Jupyter Notebook .ipynb files. This allows you to work with collegues to refine a document for final publication while still retaining the code cells, and thus reprodubility of your the work. In the future we also plan to support exporting to .ipynb files.

+
+ + Markdown cells +

Most standard Markdown should be supported by the importer including inline code, headings etc (although the Stencila user interface do not currently support rendering of some elements e.g. math and lists).

+
+ + Code cells +

Code cells in notebooks are imported without loss. Stencila’s user interface currently differs from Jupyter in that code cells are executed on update while you are typing. This produces a very reactive user experience but is inappropriate for more compute intensive, longer running code cells. We are currently working on improving this to allowing users to decide to execute cells explicitly (e.g. using Ctrl+Enter).

+ + import sys +import time +'Hello this is Python %s.%s and it is %s' % (sys.version_info[0], sys.version_info[1], time.strftime('%c')) + {} + + + +

Stencila also support Jupyter code cells that produce plots. The cell below produces a simple plot based on the example from the Matplotlib website. Try changing the code below (for example, the variable N).

+ + import numpy as np +import matplotlib.pyplot as plt + +N = 50 +N = min(N, 1000) # Prevent generation of too many numbers :) +x = np.random.rand(N) +y = np.random.rand(N) +colors = np.random.rand(N) +area = np.pi * (15 * np.random.rand(N))**2 # 0 to 15 point radii + +plt.scatter(x, y, s=area, c=colors, alpha=0.5) +plt.show() + {} + + + +

We are currently working on supporting Jupyter’s magic commands in Stencila via a bridge to Jupyter kernels.

+
+ + Metadata +

To add some metadata about the document (such as authors, title, abstract and so on), In Jupyter, select Edit -> Edit Notebook metadata from the top menu. Add the title and abstract as JSON strings and authors and organisations metadata as JSON arrays. Author affiliation identifiers (like university-of-earth below) must be unique and preferably use only lowercase characters and no spaces.

+

For example,

+ "authors": [ + { + "given-names": "Your first name goes here", + "surname": "Your last name goes here", + "email": "your.email@your-organisation", + "corresponding": "yes / no", + "affiliation": "university-of-earth" + } + ], + + "organisations": [ + { + "university-of-earth": { + "institution": "Your organisation name", + "city": "Your city", + "country": "Your country" + } + ], + + "title": "Your title goes here", + "abstract": "This is a paper about lots of different interesting things", + +
+ + Citations and references +

Stencila supports Pandoc style citations and reference lists within Jupyter notebook Markdown cells. Add a bibliography entry to the notebook’s metadata which points to a file containing your list of references e.g.

+ "bibliography": "my-bibliography.bibtex" +

Then, within Markdown cells, you can insert citations inside square brackets and separated by semicolons. Each citation is represented using the @ symbol followed by the citation identifier from the bibliography database e.g.

+ [@perez2015project; @kluyver2016jupyter] +

The cite2c Jupyter extension allows for easier, “cite-while-you-write” insertion of citations from a Zotero library. We’re hoping to support conversion of cite2cstyle citations/references in the future.

+
+ + + + + + + + Perez + Fernando + + + Granger + Brian E + + + Project jupyter: Computational narratives as the engine of collaborative data science + Retrieved September + 2015 + 11 + 207 + + + + + + + Kluyver + Thomas + + + Ragan-Kelley + Benjamin + + + Pérez + Fernando + + + Granger + Brian E + + + Bussonnier + Matthias + + + Frederic + Jonathan + + + Kelley + Kyle + + + Hamrick + Jessica B + + + Grout + Jason + + + Corlay + Sylvain + + + Others + + + Jupyter notebooks-a publishing format for reproducible computational workflows. + ELPUB + 2016 + 87 + + + + + + + Ragan-Kelley + M + + + Perez + F + + + Granger + B + + + Kluyver + T + + + Ivanov + P + + + Frederic + J + + + Bussonnier + M + + + The jupyter/ipython architecture: A unified view of computational research, from interactive exploration to communication and publication. + AGU Fall Meeting Abstracts + 2014 + + + + +
\ No newline at end of file diff --git a/tests/stencila/py/verify b/tests/stencila/py/verify new file mode 100755 index 00000000..788ef9da --- /dev/null +++ b/tests/stencila/py/verify @@ -0,0 +1,5 @@ +#!/bin/sh + +jupyter serverextension list 2>&1 | grep nbstencilaproxy +jupyter nbextension list 2>&1 | grep nbstencilaproxy +python3 -c "import stencila" diff --git a/tests/stencila/pyjp/verify b/tests/stencila/pyjp/verify index 788ef9da..218e75f4 100755 --- a/tests/stencila/pyjp/verify +++ b/tests/stencila/pyjp/verify @@ -2,4 +2,4 @@ jupyter serverextension list 2>&1 | grep nbstencilaproxy jupyter nbextension list 2>&1 | grep nbstencilaproxy -python3 -c "import stencila" +python3 -c "import stencila" 2>&1 | grep ModuleNotFoundError diff --git a/tests/stencila/r/verify b/tests/stencila/r/verify index 2bd4ad54..33db4a89 100755 --- a/tests/stencila/r/verify +++ b/tests/stencila/r/verify @@ -2,4 +2,5 @@ jupyter serverextension list 2>&1 | grep nbstencilaproxy jupyter nbextension list 2>&1 | grep nbstencilaproxy +python3 -c "import stencila" 2>&1 | grep ModuleNotFoundError R -e "library('stencila');" From e576c0344614bef59c35aed5d450a68106dc28e0 Mon Sep 17 00:00:00 2001 From: nuest Date: Tue, 6 Nov 2018 20:17:19 +0100 Subject: [PATCH 27/37] describe more complex detection mechanism used for stencila in dev docs --- docs/source/architecture.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/source/architecture.md b/docs/source/architecture.md index d810013c..5f25cace 100644 --- a/docs/source/architecture.md +++ b/docs/source/architecture.md @@ -38,8 +38,12 @@ It takes the following steps to determine this: something that it should be used for. For example, a `BuildPack` that uses `conda` to install libraries can check for presence of an `environment.yml` file and say 'yes, I can handle this repository' by returning `True`. Usually buildpacks look for presence of specific files - (`requirements.txt`, `environment.yml`, `install.R`, etc) to determine if they can handle a - repository or not. + (`requirements.txt`, `environment.yml`, `install.R`, `manifest.xml` etc) to determine if they can handle a + repository or not. Buildpacks may also look into specific files to determine specifics of the + required environment, such as the Stencila integration which extracts the required language-specific + executions contexts from an XML file (see base `BuildPack`). More than one buildpack may use such + information, as properties can be inherited (e.g. the R buildpack uses the list of required Stencila + contexts to see if R must be installed). 3. If no `BuildPack` returns true, then repo2docker will use the default `BuildPack` (defined in `Repo2Docker.default_buildpack` traitlet). From 490962e08d3fd1583623ee38b2f9f85af47daf0f Mon Sep 17 00:00:00 2001 From: nuest Date: Tue, 6 Nov 2018 20:20:34 +0100 Subject: [PATCH 28/37] split up Stencila tests to not run into timeouts on Travis CI --- .travis.yml | 5 +++-- .../py/plain-python/bibliography.bibtex | 0 tests/{stencila => stencila-py}/py/plain-python/manifest.xml | 0 tests/{stencila => stencila-py}/py/plain-python/py.ipynb | 0 .../py/plain-python/py.ipynb.jats.xml | 0 tests/{stencila => stencila-py}/py/verify | 0 .../pyjp/py-jupyter/bibliography.bibtex | 0 tests/{stencila => stencila-py}/pyjp/py-jupyter/manifest.xml | 0 .../pyjp/py-jupyter/py-jupyter.ipynb | 0 .../pyjp/py-jupyter/py-jupyter.ipynb.jats.xml | 0 tests/{stencila => stencila-py}/pyjp/verify | 0 .../r => stencila-r}/r-markdown/bibliography.bibtex | 0 tests/{stencila/r => stencila-r}/r-markdown/manifest.xml | 0 tests/{stencila/r => stencila-r}/r-markdown/rmarkdown.Rmd | 0 .../r => stencila-r}/r-markdown/rmarkdown.Rmd.jats.xml | 0 tests/{stencila/r => stencila-r}/verify | 0 16 files changed, 3 insertions(+), 2 deletions(-) rename tests/{stencila => stencila-py}/py/plain-python/bibliography.bibtex (100%) rename tests/{stencila => stencila-py}/py/plain-python/manifest.xml (100%) rename tests/{stencila => stencila-py}/py/plain-python/py.ipynb (100%) rename tests/{stencila => stencila-py}/py/plain-python/py.ipynb.jats.xml (100%) rename tests/{stencila => stencila-py}/py/verify (100%) rename tests/{stencila => stencila-py}/pyjp/py-jupyter/bibliography.bibtex (100%) rename tests/{stencila => stencila-py}/pyjp/py-jupyter/manifest.xml (100%) rename tests/{stencila => stencila-py}/pyjp/py-jupyter/py-jupyter.ipynb (100%) rename tests/{stencila => stencila-py}/pyjp/py-jupyter/py-jupyter.ipynb.jats.xml (100%) rename tests/{stencila => stencila-py}/pyjp/verify (100%) rename tests/{stencila/r => stencila-r}/r-markdown/bibliography.bibtex (100%) rename tests/{stencila/r => stencila-r}/r-markdown/manifest.xml (100%) rename tests/{stencila/r => stencila-r}/r-markdown/rmarkdown.Rmd (100%) rename tests/{stencila/r => stencila-r}/r-markdown/rmarkdown.Rmd.jats.xml (100%) rename tests/{stencila/r => stencila-r}/verify (100%) diff --git a/.travis.yml b/.travis.yml index ca73411e..afb2f360 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,7 +16,7 @@ script: # cd into tests so CWD being repo2docker does not hide # possible issues with MANIFEST.in - pushd tests; - if [ ${REPO_TYPE} == "r" ] || [ ${REPO_TYPE} == "stencila" ]; then + if [ ${REPO_TYPE} == "r" ] || [ ${REPO_TYPE} == "stencila-r" ] || [ ${REPO_TYPE} == "stencila-py" ]; then travis_wait 30 pytest --cov repo2docker -v ${REPO_TYPE} || exit 1; else travis_retry pytest --cov repo2docker -v ${REPO_TYPE} || exit 1; @@ -46,7 +46,8 @@ env: - REPO_TYPE=base - REPO_TYPE=conda - REPO_TYPE=venv - - REPO_TYPE=stencila + - REPO_TYPE=stencila-r + - REPO_TYPE=stencila-py - REPO_TYPE=julia - REPO_TYPE=r - REPO_TYPE=dockerfile diff --git a/tests/stencila/py/plain-python/bibliography.bibtex b/tests/stencila-py/py/plain-python/bibliography.bibtex similarity index 100% rename from tests/stencila/py/plain-python/bibliography.bibtex rename to tests/stencila-py/py/plain-python/bibliography.bibtex diff --git a/tests/stencila/py/plain-python/manifest.xml b/tests/stencila-py/py/plain-python/manifest.xml similarity index 100% rename from tests/stencila/py/plain-python/manifest.xml rename to tests/stencila-py/py/plain-python/manifest.xml diff --git a/tests/stencila/py/plain-python/py.ipynb b/tests/stencila-py/py/plain-python/py.ipynb similarity index 100% rename from tests/stencila/py/plain-python/py.ipynb rename to tests/stencila-py/py/plain-python/py.ipynb diff --git a/tests/stencila/py/plain-python/py.ipynb.jats.xml b/tests/stencila-py/py/plain-python/py.ipynb.jats.xml similarity index 100% rename from tests/stencila/py/plain-python/py.ipynb.jats.xml rename to tests/stencila-py/py/plain-python/py.ipynb.jats.xml diff --git a/tests/stencila/py/verify b/tests/stencila-py/py/verify similarity index 100% rename from tests/stencila/py/verify rename to tests/stencila-py/py/verify diff --git a/tests/stencila/pyjp/py-jupyter/bibliography.bibtex b/tests/stencila-py/pyjp/py-jupyter/bibliography.bibtex similarity index 100% rename from tests/stencila/pyjp/py-jupyter/bibliography.bibtex rename to tests/stencila-py/pyjp/py-jupyter/bibliography.bibtex diff --git a/tests/stencila/pyjp/py-jupyter/manifest.xml b/tests/stencila-py/pyjp/py-jupyter/manifest.xml similarity index 100% rename from tests/stencila/pyjp/py-jupyter/manifest.xml rename to tests/stencila-py/pyjp/py-jupyter/manifest.xml diff --git a/tests/stencila/pyjp/py-jupyter/py-jupyter.ipynb b/tests/stencila-py/pyjp/py-jupyter/py-jupyter.ipynb similarity index 100% rename from tests/stencila/pyjp/py-jupyter/py-jupyter.ipynb rename to tests/stencila-py/pyjp/py-jupyter/py-jupyter.ipynb diff --git a/tests/stencila/pyjp/py-jupyter/py-jupyter.ipynb.jats.xml b/tests/stencila-py/pyjp/py-jupyter/py-jupyter.ipynb.jats.xml similarity index 100% rename from tests/stencila/pyjp/py-jupyter/py-jupyter.ipynb.jats.xml rename to tests/stencila-py/pyjp/py-jupyter/py-jupyter.ipynb.jats.xml diff --git a/tests/stencila/pyjp/verify b/tests/stencila-py/pyjp/verify similarity index 100% rename from tests/stencila/pyjp/verify rename to tests/stencila-py/pyjp/verify diff --git a/tests/stencila/r/r-markdown/bibliography.bibtex b/tests/stencila-r/r-markdown/bibliography.bibtex similarity index 100% rename from tests/stencila/r/r-markdown/bibliography.bibtex rename to tests/stencila-r/r-markdown/bibliography.bibtex diff --git a/tests/stencila/r/r-markdown/manifest.xml b/tests/stencila-r/r-markdown/manifest.xml similarity index 100% rename from tests/stencila/r/r-markdown/manifest.xml rename to tests/stencila-r/r-markdown/manifest.xml diff --git a/tests/stencila/r/r-markdown/rmarkdown.Rmd b/tests/stencila-r/r-markdown/rmarkdown.Rmd similarity index 100% rename from tests/stencila/r/r-markdown/rmarkdown.Rmd rename to tests/stencila-r/r-markdown/rmarkdown.Rmd diff --git a/tests/stencila/r/r-markdown/rmarkdown.Rmd.jats.xml b/tests/stencila-r/r-markdown/rmarkdown.Rmd.jats.xml similarity index 100% rename from tests/stencila/r/r-markdown/rmarkdown.Rmd.jats.xml rename to tests/stencila-r/r-markdown/rmarkdown.Rmd.jats.xml diff --git a/tests/stencila/r/verify b/tests/stencila-r/verify similarity index 100% rename from tests/stencila/r/verify rename to tests/stencila-r/verify From b9dba98c6582d000e631bc7857388942caca9a76 Mon Sep 17 00:00:00 2001 From: Tim Head Date: Tue, 6 Nov 2018 08:50:25 +0100 Subject: [PATCH 29/37] Add a first roadmap --- CONTRIBUTING.md | 1 + README.md | 2 +- docs/source/contributing/roadmap.md | 54 +++++++++++++++++++++++++++++ docs/source/index.rst | 1 + 4 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 docs/source/contributing/roadmap.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b78dfa91..762e84b2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,4 +3,5 @@ The repo2docker developer documentation can be found on these pages: * [Contributing to repo2docker](https://repo2docker.readthedocs.io/en/latest/contributing/contributing.html) +* [Our roadmap](https://repo2docker.readthedocs.io/en/latest/contributing/roadmap.html) * [Common developer tasks and how-tos](https://repo2docker.readthedocs.io/en/latest/contributing/tasks.html) diff --git a/README.md b/README.md index e2e14b35..5f86329b 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ the configuration files found in the repository. See the [repo2docker documentation](http://repo2docker.readthedocs.io) -for more information. +for more information on using repo2docker. See the [contributing guide](CONTRIBUTING.md) for information on contributing to repo2docker. diff --git a/docs/source/contributing/roadmap.md b/docs/source/contributing/roadmap.md new file mode 100644 index 00000000..48e09ff1 --- /dev/null +++ b/docs/source/contributing/roadmap.md @@ -0,0 +1,54 @@ +# The repo2docker roadmap + +This roadmap collects "next steps" for the project. The goal is to +communicate where the current priorities are and where the project is heading. +It is not a list aimed at limiting contributions to what is listed here. If +something is not listed here please do bring it up in a new issue or start +work on it. + +Each "next step" should fit into one of the following three categories: + +* **now**, concrete/actionable step that is ready for someone to start work on. +These might be items that have a link to an issue or more abstract like +"decrease typos and dead links in the documentation" +* **soon**, less concrete/actionable step that is going to happen soon, +discussions around the topic are coming close to an end at which point it can +move into the "now" category +* **later**, abstract ideas or tasks, need a lot of discussion or +experimentation to shape the idea so that it can be discussed. Should also +contain concrete/actionable steps that have been postponed on purpose +(these are steps that could be in "now" but the decision was taken to work on +them later) + +The roadmap will get updated as time passes (next review by 1st December). +This means this list should not be exhaustive, it should only represent +the "top of the stack" of ideas. It should +not function as a wish list, collection of feature requests or todo list. +For those please create a +[new issue](https://github.com/jupyter/repo2docker/issues/new). + +The roadmap should give the reader an idea of what is happening next, what needs +input and discussion before it can happen and what has been postponed. + + +## Guiding thought for the next months +Repo2docker is a dependable tool used by humans that reduces the complexity of creating the environment in which a piece of software can be executed. + + +## Now +* reduce documentation typos and syntax errors +* add Julia Manifest support (https://docs.julialang.org/en/v1/stdlib/Pkg/index.html) +* increase test coverage (see https://codecov.io/gh/jupyter/repo2docker/tree/master/repo2docker for low coverage files) +* reduce execution time of tests +* make a new release once Pipfile and nix support have been merged + +## Soon +* create the contributor highway, define the route from newcomer to project lead +* add support for using ZIP files as the repo (`repo2docker https://example.com/an-archive.zip`) this will give us access to several archives (like Zenodo) that expose things as ZIP files. +* add support for Zenodo (`repo2docker 10.5281/zenodo.1476680`) so Zenodo software archives can be used as the source in addition to a git repository +* tooling to make it easier to produce good `requirements.txt`, `environment.yml`, etc files. They should help users create versions of these files that have a high chance of still working in a few months +* support for running with GPU acceleration + +## Later +* repo2docker in repo2docker, to reproduce an environment users need to specify the repository and the version of repo2docker to use. Add support for repo2docker inspecting a repo and then starting a different version of itself to build the image +* support execution on a remote host (with more resources than available locally) via the command-line diff --git a/docs/source/index.rst b/docs/source/index.rst index b1d8fa46..7726b257 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -43,6 +43,7 @@ Please report `Bugs `_, :caption: Contribute to repo2docker contributing/contributing + contributing/roadmap architecture design contributing/tasks From d8496648265d2c91ad9be42e69c7207d7db279c5 Mon Sep 17 00:00:00 2001 From: Tim Head Date: Wed, 7 Nov 2018 18:51:51 +0100 Subject: [PATCH 30/37] Update repo2docker/buildpacks/base.py Co-Authored-By: nuest --- repo2docker/buildpacks/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repo2docker/buildpacks/base.py b/repo2docker/buildpacks/base.py index 10c7f3ea..128056e4 100644 --- a/repo2docker/buildpacks/base.py +++ b/repo2docker/buildpacks/base.py @@ -295,7 +295,7 @@ class BuildPack: # get paths to the article files from manifest files = [] - if (self.stencila_manifest_dir): + if self.stencila_manifest_dir: manifest = ET.parse(os.path.join(self.stencila_manifest_dir, 'manifest.xml')) files = list(map(lambda x: os.path.join(self.stencila_manifest_dir, x.get('path')), manifest.findall('./documents/document'))) else: From c49694a5ea581ca51f640eff044fd01a7bbea102 Mon Sep 17 00:00:00 2001 From: Tim Head Date: Wed, 7 Nov 2018 18:51:57 +0100 Subject: [PATCH 31/37] Update repo2docker/buildpacks/base.py Co-Authored-By: nuest --- repo2docker/buildpacks/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repo2docker/buildpacks/base.py b/repo2docker/buildpacks/base.py index 128056e4..7940a569 100644 --- a/repo2docker/buildpacks/base.py +++ b/repo2docker/buildpacks/base.py @@ -554,7 +554,7 @@ class BaseImage(BuildPack): )) except FileNotFoundError: pass - if {'py'}.intersection(self.stencila_contexts): + if 'py' in self.stencila_contexts: assemble_scripts.extend( [ ( From 0ccca4983a0f5042712786c96b989cf0b9294698 Mon Sep 17 00:00:00 2001 From: Tim Head Date: Wed, 7 Nov 2018 18:52:23 +0100 Subject: [PATCH 32/37] Update docs in buildpacks/r.py Co-Authored-By: nuest --- repo2docker/buildpacks/r.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/repo2docker/buildpacks/r.py b/repo2docker/buildpacks/r.py index 220099df..50a0d5c1 100644 --- a/repo2docker/buildpacks/r.py +++ b/repo2docker/buildpacks/r.py @@ -26,7 +26,7 @@ class RBuildPack(PythonBuildPack): If there is no `runtime.txt`, then the MRAN snapshot is set to latest date that is guaranteed to exist across timezones. - R packages are installed if specified either + Additional R packages are installed if specified either - in a file `install.R`, that will be executed at build time, and can be used for installing packages from both MRAN and GitHub From 3a4e599ee9ac7235195a2030c595363211ce5353 Mon Sep 17 00:00:00 2001 From: Tim Head Date: Thu, 8 Nov 2018 19:01:31 +0100 Subject: [PATCH 33/37] Incorporate feedback on the roadmap Improves naming of sections and the description of what each step and the process around the roadmap looks like. --- docs/source/contributing/roadmap.md | 52 +++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/docs/source/contributing/roadmap.md b/docs/source/contributing/roadmap.md index 48e09ff1..1736a10e 100644 --- a/docs/source/contributing/roadmap.md +++ b/docs/source/contributing/roadmap.md @@ -1,12 +1,26 @@ # The repo2docker roadmap -This roadmap collects "next steps" for the project. The goal is to -communicate where the current priorities are and where the project is heading. -It is not a list aimed at limiting contributions to what is listed here. If -something is not listed here please do bring it up in a new issue or start -work on it. +This roadmap collects "next steps" for repo2docker. It is about creating a +shared understanding of the project's vision and direction amongst +the community of users, contributors, and maintainers. +The goal is to communicate priorities and upcoming release plans. +It is not a aimed at limiting contributions to what is listed here. -Each "next step" should fit into one of the following three categories: + +## Sharing Feedback on the Roadmap + +All of the community is encouraged to provide feedback as well as share new +ideas with the community. Please do so by submitting an issue. If you want to +have an informal conversation first use one of the other communication channels. +After submitting the issue, others from the community will probably +respond with questions or comments they have to clarify the issue. The +maintainers will help identify what a good next step is for the issue. + + +## What do we mean by "next step"? + +When submitting an issue, think about what "next step" category best describes +your issue: * **now**, concrete/actionable step that is ready for someone to start work on. These might be items that have a link to an issue or more abstract like @@ -15,12 +29,16 @@ These might be items that have a link to an issue or more abstract like discussions around the topic are coming close to an end at which point it can move into the "now" category * **later**, abstract ideas or tasks, need a lot of discussion or -experimentation to shape the idea so that it can be discussed. Should also +experimentation to shape the idea so that it can be executed. Can also contain concrete/actionable steps that have been postponed on purpose (these are steps that could be in "now" but the decision was taken to work on them later) -The roadmap will get updated as time passes (next review by 1st December). + +## Reviewing and Updating the Roadmap + +The roadmap will get updated as time passes (next review by 1st December) based +on discussions and ideas captured as issues. This means this list should not be exhaustive, it should only represent the "top of the stack" of ideas. It should not function as a wish list, collection of feature requests or todo list. @@ -31,24 +49,38 @@ The roadmap should give the reader an idea of what is happening next, what needs input and discussion before it can happen and what has been postponed. -## Guiding thought for the next months -Repo2docker is a dependable tool used by humans that reduces the complexity of creating the environment in which a piece of software can be executed. +## Project vision + +Repo2docker is a dependable tool used by humans that reduces the complexity of +creating the environment in which a piece of software can be executed. ## Now + +These "Now" items are considered active areas of focus for the project: * reduce documentation typos and syntax errors * add Julia Manifest support (https://docs.julialang.org/en/v1/stdlib/Pkg/index.html) * increase test coverage (see https://codecov.io/gh/jupyter/repo2docker/tree/master/repo2docker for low coverage files) * reduce execution time of tests * make a new release once Pipfile and nix support have been merged + ## Soon + +These "Soon" items are under discussion. Once an item reaches the point of an +actionable plan, the item will be moved to the "Now" section. Typically, +these will be moved at a future review of the roadmap. * create the contributor highway, define the route from newcomer to project lead * add support for using ZIP files as the repo (`repo2docker https://example.com/an-archive.zip`) this will give us access to several archives (like Zenodo) that expose things as ZIP files. * add support for Zenodo (`repo2docker 10.5281/zenodo.1476680`) so Zenodo software archives can be used as the source in addition to a git repository * tooling to make it easier to produce good `requirements.txt`, `environment.yml`, etc files. They should help users create versions of these files that have a high chance of still working in a few months * support for running with GPU acceleration + ## Later + +The "Later" items are things that are at the back of the project's mind. At this +time there is no active plan for an item. The project would like to find the +resources and time to discuss these ideas. * repo2docker in repo2docker, to reproduce an environment users need to specify the repository and the version of repo2docker to use. Add support for repo2docker inspecting a repo and then starting a different version of itself to build the image * support execution on a remote host (with more resources than available locally) via the command-line From dfab81ee5000303e9160195ed3a6c8ba4cb8f24b Mon Sep 17 00:00:00 2001 From: Tim Head Date: Thu, 8 Nov 2018 19:06:42 +0100 Subject: [PATCH 34/37] Link ROADMAP from the README --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 5f86329b..dc268fff 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,9 @@ for more information on using repo2docker. See the [contributing guide](CONTRIBUTING.md) for information on contributing to repo2docker. +See [our roadmap](https://repo2docker.readthedocs.io/en/latest/contributing/roadmap.html) +to learn about where the project is heading. + ## Using repo2docker ### Prerequisites From 58a5c8b4c2a6339d577970de2c2d9e642a236e72 Mon Sep 17 00:00:00 2001 From: nuest Date: Fri, 9 Nov 2018 07:40:31 +0100 Subject: [PATCH 35/37] make loops more readable --- repo2docker/buildpacks/base.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/repo2docker/buildpacks/base.py b/repo2docker/buildpacks/base.py index 7940a569..af558c14 100644 --- a/repo2docker/buildpacks/base.py +++ b/repo2docker/buildpacks/base.py @@ -286,18 +286,23 @@ class BuildPack: @property def stencila_contexts(self): - """Find the stencila manifest contexts if it exists""" + """Find the stencila manifest contexts from file path in manifest""" if hasattr(self, '_stencila_contexts'): return self._stencila_contexts - # look at the content of the documents in the manifest to extract the required execution contexts + # look at the content of the documents in the manifest + # to extract the required execution contexts self._stencila_contexts = set() # get paths to the article files from manifest files = [] if self.stencila_manifest_dir: - manifest = ET.parse(os.path.join(self.stencila_manifest_dir, 'manifest.xml')) - files = list(map(lambda x: os.path.join(self.stencila_manifest_dir, x.get('path')), manifest.findall('./documents/document'))) + manifest = ET.parse(os.path.join(self.stencila_manifest_dir, + 'manifest.xml')) + documents = manifest.findall('./documents/document') + files = [os.path.join(self.stencila_manifest_dir, x.get('path')) + for x in documents] + else: return self._stencila_contexts @@ -306,7 +311,8 @@ class BuildPack: # extract code languages from file document = ET.parse(filename) - languages = list(map(lambda x: x.get('language'), document.findall('.//code[@specific-use="source"]'))) + code_chunks = document.findall('.//code[@specific-use="source"]') + languages = [x.get('language') for x in code_chunks] self._stencila_contexts.update(languages) self.log.info( From 68194a08a296e572d97a25d3cc07d2252f994ca5 Mon Sep 17 00:00:00 2001 From: nuest Date: Fri, 9 Nov 2018 07:40:39 +0100 Subject: [PATCH 36/37] reformat some long lines --- repo2docker/buildpacks/base.py | 15 +++++++-------- repo2docker/buildpacks/r.py | 18 +++++++++++------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/repo2docker/buildpacks/base.py b/repo2docker/buildpacks/base.py index af558c14..66720eb6 100644 --- a/repo2docker/buildpacks/base.py +++ b/repo2docker/buildpacks/base.py @@ -258,7 +258,6 @@ class BuildPack: """ return {} - @property def stencila_manifest_dir(self): """Find the stencila manifest dir if it exists""" @@ -320,7 +319,7 @@ class BuildPack: self._stencila_contexts, ) break - + return self._stencila_contexts def get_build_scripts(self): @@ -387,9 +386,9 @@ class BuildPack: This script is added as the `ENTRYPOINT` to the container. - It is run as a non-root user, and must be executable. Used for performing - run time steps that can not be performed with standard tools. For example - setting environment variables for your repository. + It is run as a non-root user, and must be executable. Used for + performing run time steps that can not be performed with standard + tools. For example setting environment variables for your repository. The script should be as deterministic as possible - running it twice should not produce different results. @@ -514,9 +513,9 @@ class BaseImage(BuildPack): env = [] if self.stencila_manifest_dir: # manifest_dir is the path containing the manifest.xml - # archive_dir is the directory containing archive directories (one level up) - # default archive is the name of the directory in the archive_dir - # such that + # archive_dir is the directory containing archive directories + # (one level up) default archive is the name of the directory + # in the archive_dir such that # ${STENCILA_ARCHIVE_DIR}/${STENCILA_ARCHIVE}/manifest.xml # exists. diff --git a/repo2docker/buildpacks/r.py b/repo2docker/buildpacks/r.py index 50a0d5c1..0db0e60b 100644 --- a/repo2docker/buildpacks/r.py +++ b/repo2docker/buildpacks/r.py @@ -74,9 +74,9 @@ class RBuildPack(PythonBuildPack): """ Check if current repo should be built with the R Build pack - super().detect() is not called in this function - it would return false - unless a `requirements.txt` is present and we do not want to require the - presence of a `requirements.txt` to use R. + super().detect() is not called in this function - it would return + false unless a `requirements.txt` is present and we do not want + to require the presence of a `requirements.txt` to use R. """ # If no date is found, then self.checkpoint_date will be False # Otherwise, it'll be a date object, which will evaluate to True @@ -84,10 +84,12 @@ class RBuildPack(PythonBuildPack): return True description_R = 'DESCRIPTION' - if (not os.path.exists('binder') and os.path.exists(description_R)) or 'r' in self.stencila_contexts: + if ((not os.path.exists('binder') and os.path.exists(description_R)) + or 'r' in self.stencila_contexts): if not self.checkpoint_date: # no R snapshot date set through runtime.txt - # set the R runtime to the latest date that is guaranteed to be on MRAN across timezones + # set the R runtime to the latest date that is guaranteed to + # be on MRAN across timezones self._checkpoint_date = datetime.date.today() - datetime.timedelta(days=2) self._runtime = "r-{}".format(str(self._checkpoint_date)) return True @@ -140,9 +142,11 @@ class RBuildPack(PythonBuildPack): This sets up: - - A directory owned by non-root in ${R_LIBS_USER} for installing R packages into + - A directory owned by non-root in ${R_LIBS_USER} + for installing R packages into - RStudio - - R's devtools package, at a particular frozen version (determined by MRAN) + - R's devtools package, at a particular frozen version + (determined by MRAN) - IRKernel - nbrsessionproxy (to access RStudio via Jupyter Notebook) - stencila R package (if Stencila document with R code chunks detected) From c05f30d13c97612dae0d6d3ba37fb80ee93b011c Mon Sep 17 00:00:00 2001 From: Tim Head Date: Fri, 9 Nov 2018 13:46:18 +0100 Subject: [PATCH 37/37] Introduce extra level of headers in the roadmap --- docs/source/contributing/roadmap.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/docs/source/contributing/roadmap.md b/docs/source/contributing/roadmap.md index 1736a10e..da452b5b 100644 --- a/docs/source/contributing/roadmap.md +++ b/docs/source/contributing/roadmap.md @@ -6,8 +6,8 @@ the community of users, contributors, and maintainers. The goal is to communicate priorities and upcoming release plans. It is not a aimed at limiting contributions to what is listed here. - -## Sharing Feedback on the Roadmap +## Using the roadmap +### Sharing Feedback on the Roadmap All of the community is encouraged to provide feedback as well as share new ideas with the community. Please do so by submitting an issue. If you want to @@ -17,7 +17,7 @@ respond with questions or comments they have to clarify the issue. The maintainers will help identify what a good next step is for the issue. -## What do we mean by "next step"? +### What do we mean by "next step"? When submitting an issue, think about what "next step" category best describes your issue: @@ -35,7 +35,7 @@ contain concrete/actionable steps that have been postponed on purpose them later) -## Reviewing and Updating the Roadmap +### Reviewing and Updating the Roadmap The roadmap will get updated as time passes (next review by 1st December) based on discussions and ideas captured as issues. @@ -49,13 +49,14 @@ The roadmap should give the reader an idea of what is happening next, what needs input and discussion before it can happen and what has been postponed. -## Project vision +## The roadmap proper +### Project vision Repo2docker is a dependable tool used by humans that reduces the complexity of creating the environment in which a piece of software can be executed. -## Now +### Now These "Now" items are considered active areas of focus for the project: * reduce documentation typos and syntax errors @@ -65,7 +66,7 @@ These "Now" items are considered active areas of focus for the project: * make a new release once Pipfile and nix support have been merged -## Soon +### Soon These "Soon" items are under discussion. Once an item reaches the point of an actionable plan, the item will be moved to the "Now" section. Typically, @@ -77,7 +78,7 @@ these will be moved at a future review of the roadmap. * support for running with GPU acceleration -## Later +### Later The "Later" items are things that are at the back of the project's mind. At this time there is no active plan for an item. The project would like to find the