Skip to content

Commit 97d67e7

Browse files
PGijsbersmfeurer
andauthored
Add a Docker Image for testing and doc building in an isolated environment (#1075)
* Initial structure * Add doc and test functionality for mounted repo * Add branch support and safeguards * Update docker usage and name, add structure * Improved formatting * Add reference to docker image from main docs * Add Workflow to build and push docker image * Use environment variable directly * Try other formatting for SHA tag * Try format as string * Only push latest * Explicitly make context relative * Checkout repository * Install wheel and setuptools before other packages * Rename master to main * Add information about Docker PR * Make 'note' italtics instead of content Co-authored-by: Matthias Feurer <feurerm@informatik.uni-freiburg.de> Co-authored-by: Matthias Feurer <feurerm@informatik.uni-freiburg.de>
1 parent b0e944d commit 97d67e7

7 files changed

Lines changed: 229 additions & 1 deletion

File tree

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: release-docker
2+
3+
on:
4+
push:
5+
branches:
6+
- 'develop'
7+
- 'docker'
8+
9+
jobs:
10+
docker:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- name: Set up QEMU
14+
uses: docker/setup-qemu-action@v1
15+
- name: Set up Docker Buildx
16+
uses: docker/setup-buildx-action@v1
17+
- name: Login to DockerHub
18+
uses: docker/login-action@v1
19+
with:
20+
username: ${{ secrets.DOCKERHUB_USERNAME }}
21+
password: ${{ secrets.DOCKERHUB_TOKEN }}
22+
- uses: actions/checkout@v2
23+
- name: Build and push
24+
id: docker_build
25+
uses: docker/build-push-action@v2
26+
with:
27+
context: ./docker/
28+
push: true
29+
tags: openml/openml-python:latest
30+
- name: Image digest
31+
run: echo ${{ steps.docker_build.outputs.digest }}

CONTRIBUTING.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,10 @@ following rules before you submit a pull request:
178178
- If any source file is being added to the repository, please add the BSD 3-Clause license to it.
179179

180180

181+
*Note*: We recommend to follow the instructions below to install all requirements locally.
182+
However it is also possible to use the [openml-python docker image](https://github.com/openml/openml-python/blob/main/docker/readme.md) for testing and building documentation.
183+
This can be useful for one-off contributions or when you are experiencing installation issues.
184+
181185
First install openml with its test dependencies by running
182186
```bash
183187
$ pip install -e .[test]

doc/progress.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Changelog
88

99
0.12.2
1010
~~~~~~
11-
11+
* ADD #1075: A docker image is now automatically built on a push to develop. It can be used to build docs or run tests in an isolated environment.
1212
* DOC: Fixes a few broken links in the documentation.
1313
* MAINT/DOC: Automatically check for broken external links when building the documentation.
1414
* MAINT/DOC: Fail documentation building on warnings. This will make the documentation building

doc/usage.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,19 @@ This file is easily configurable by the ``openml`` command line interface.
6565
To see where the file is stored, and what its values are, use `openml configure none`.
6666
Set any field with ``openml configure FIELD`` or even all fields with just ``openml configure``.
6767

68+
~~~~~~
69+
Docker
70+
~~~~~~
71+
72+
It is also possible to try out the latest development version of ``openml-python`` with docker:
73+
74+
```
75+
docker run -it openml/openml-python
76+
```
77+
78+
79+
See the `openml-python docker documentation <https://github.com/openml/openml-python/blob/main/docker/readme.md>`_ for more information.
80+
6881
~~~~~~~~~~~~
6982
Key concepts
7083
~~~~~~~~~~~~

docker/Dockerfile

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Dockerfile to build an image with preinstalled dependencies
2+
# Useful building docs or running unix tests from a Windows host.
3+
FROM python:3
4+
5+
RUN git clone https://github.com/openml/openml-python.git omlp
6+
WORKDIR omlp
7+
RUN python -m venv venv
8+
RUN venv/bin/pip install wheel setuptools
9+
RUN venv/bin/pip install -e .[test,examples,docs,examples_unix]
10+
11+
WORKDIR /
12+
RUN mkdir scripts
13+
ADD startup.sh scripts/
14+
# Due to the nature of the Docker container it might often be built from Windows.
15+
# It is typical to have the files with \r\n line-ending, we want to remove it for the unix image.
16+
RUN sed -i 's/\r//g' scripts/startup.sh
17+
18+
# overwrite the default `python` entrypoint
19+
ENTRYPOINT ["/bin/bash", "/scripts/startup.sh"]

docker/readme.md

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# OpenML Python Container
2+
3+
This docker container has the latest development version of openml-python downloaded and pre-installed.
4+
It can be used to run the unit tests or build the docs in a fresh and/or isolated unix environment.
5+
Instructions only tested on a Windows host machine.
6+
7+
First pull the docker image:
8+
9+
docker pull openml/openml-python
10+
11+
## Usage
12+
13+
14+
docker run -it openml/openml-python [DOC,TEST] [BRANCH]
15+
16+
The image is designed to work with two specified directories which may be mounted ([`docker --mount documentation`](https://docs.docker.com/storage/bind-mounts/#start-a-container-with-a-bind-mount)).
17+
You can mount your openml-python folder to the `/code` directory to run tests or build docs on your local files.
18+
You can mount an `/output` directory to which the container will write output (currently only used for docs).
19+
Each can be mounted by adding a `--mount type=bind,source=SOURCE,destination=/DESTINATION` where `SOURCE` is the absolute path to your code or output directory, and `DESTINATION` is either `code` or `output`.
20+
21+
E.g. mounting a code directory:
22+
23+
docker run -i --mount type=bind,source="E:\\repositories/openml-python",destination="/code" -t openml/openml-python
24+
25+
E.g. mounting an output directory:
26+
27+
docker run -i --mount type=bind,source="E:\\files/output",destination="/output" -t openml/openml-python
28+
29+
You can mount both at the same time.
30+
31+
### Bash (default)
32+
By default bash is invoked, you should also use the `-i` flag when starting the container so it processes input:
33+
34+
docker run -it openml/openml-python
35+
36+
### Building Documentation
37+
There are two ways to build documentation, either directly from the `HEAD` of a branch on Github or from your local directory.
38+
39+
#### Building from a local repository
40+
Building from a local directory requires you to mount it to the ``/code`` directory:
41+
42+
docker run --mount type=bind,source=PATH_TO_REPOSITORY,destination=/code -t openml/openml-python doc
43+
44+
The produced documentation will be in your repository's ``doc/build`` folder.
45+
If an `/output` folder is mounted, the documentation will *also* be copied there.
46+
47+
#### Building from an online repository
48+
Building from a remote repository requires you to specify a branch.
49+
The branch may be specified by name directly if it exists on the original repository (https://github.com/openml/openml-python/):
50+
51+
docker run --mount type=bind,source=PATH_TO_OUTPUT,destination=/output -t openml/openml-python doc BRANCH
52+
53+
Where `BRANCH` is the name of the branch for which to generate the documentation.
54+
It is also possible to build the documentation from the branch on a fork, in this case the `BRANCH` should be specified as `GITHUB_NAME#BRANCH` (e.g. `PGijsbers#my_feature`) and the name of the forked repository should be `openml-python`.
55+
56+
### Running tests
57+
There are two ways to run tests, either directly from the `HEAD` of a branch on Github or from your local directory.
58+
It works similar to building docs, but should specify `test` as mode.
59+
For example, to run tests on your local repository:
60+
61+
docker run --mount type=bind,source=PATH_TO_REPOSITORY,destination=/code -t openml/openml-python test
62+
63+
Running tests from the state of an online repository is supported similar to building documentation (i.e. specify `BRANCH` instead of mounting `/code`).
64+
65+
## Troubleshooting
66+
67+
When you are mounting a directory you can check that it is mounted correctly by running the image in bash mode.
68+
Navigate to the `/code` and `/output` directories and see if the expected files are there.
69+
If e.g. there is no code in your mounted `/code`, you should double-check the provided path to your host directory.
70+
71+
## Notes for developers
72+
This section contains some notes about the structure of the image, intended for those who want to work on it.
73+
74+
### Added Directories
75+
The `openml/openml-python` image is built on a vanilla `python:3` image.
76+
Additionally it contains the following files are directories:
77+
78+
- `/omlp`: contains the openml-python repository in the state with which the image was built by default.
79+
If working with a `BRANCH`, this repository will be set to the `HEAD` of `BRANCH`.
80+
- `/omlp/venv/`: contains the used virtual environment for `doc` and `test`. It has `openml-python` dependencies pre-installed.
81+
When invoked with `doc` or `test`, the dependencies will be updated based on the `setup.py` of the `BRANCH` or mounted `/code`.
82+
- `/scripts/startup.sh`: the entrypoint of the image. Takes care of the automated features (e.g. `doc` and `test`).
83+
84+
## Building the image
85+
To build the image yourself, execute `docker build -f Dockerfile .` from this directory.
86+
It will use the `startup.sh` as is, so any local changes will be present in the image.

docker/startup.sh

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# Entry script to allow docker to be ran for bash, tests and docs.
2+
# The script assumes a code repository can be mounted to ``/code`` and an output directory to ``/output``.
3+
# Executes ``mode`` on ``branch`` or the provided ``code`` directory.
4+
# $1: Mode, optional. Options:
5+
# - test: execute unit tests
6+
# - doc: build documentation, requires a mounted ``output`` directory if built from a branch.
7+
# - if not provided: execute bash.
8+
# $2: Branch, optional.
9+
# Mutually exclusive with mounting a ``code`` directory.
10+
# Can be a branch on a Github fork, specified with the USERNAME#BRANCH format.
11+
# The test or doc build is executed on this branch.
12+
13+
if [ -z "$1" ]; then
14+
echo "Executing in BASH mode."
15+
bash
16+
exit
17+
fi
18+
19+
# doc and test modes require mounted directories and/or specified branches
20+
if ! [ -d "/code" ] && [ -z "$2" ]; then
21+
echo "To perform $1 a code repository must be mounted to '/code' or a branch must be specified." >> /dev/stderr
22+
exit 1
23+
fi
24+
if [ -d "/code" ] && [ -n "$2" ]; then
25+
# We want to avoid switching the git environment from within the docker container
26+
echo "You can not specify a branch for a mounted code repository." >> /dev/stderr
27+
exit 1
28+
fi
29+
if [ "$1" == "doc" ] && [ -n "$2" ] && ! [ -d "/output" ]; then
30+
echo "To build docs from an online repository, you need to mount an output directory." >> /dev/stderr
31+
exit 1
32+
fi
33+
34+
if [ -n "$2" ]; then
35+
# if a branch is provided, we will pull it into the `omlp` local repository that was created with the image.
36+
cd omlp
37+
if [[ $2 == *#* ]]; then
38+
# If a branch is specified on a fork (with NAME#BRANCH format), we have to construct the url before pulling
39+
# We add a trailing '#' delimiter so the second element doesn't get the trailing newline from <<<
40+
readarray -d '#' -t fork_name_and_branch<<<"$2#"
41+
fork_url="https://github.com/${fork_name_and_branch[0]}/openml-python.git"
42+
fork_branch="${fork_name_and_branch[1]}"
43+
echo git fetch "$fork_url" "$fork_branch":branch_from_fork
44+
git fetch "$fork_url" "$fork_branch":branch_from_fork
45+
branch=branch_from_fork
46+
else
47+
branch=$2
48+
fi
49+
if ! git checkout "$branch" ; then
50+
echo "Could not checkout $branch. If the branch lives on a fork, specify it as USER#BRANCH. Make sure to push the branch." >> /dev/stderr
51+
exit 1
52+
fi
53+
git pull
54+
code_dir="/omlp"
55+
else
56+
code_dir="/code"
57+
fi
58+
59+
source /omlp/venv/bin/activate
60+
cd $code_dir
61+
# The most recent ``master`` is already installed, but we want to update any outdated dependencies
62+
pip install -e .[test,examples,docs,examples_unix]
63+
64+
if [ "$1" == "test" ]; then
65+
pytest -n 4 --durations=20 --timeout=600 --timeout-method=thread --dist load -sv
66+
fi
67+
68+
if [ "$1" == "doc" ]; then
69+
cd doc
70+
make html
71+
make linkcheck
72+
if [ -d "/output" ]; then
73+
cp -r /omlp/doc/build /output
74+
fi
75+
fi

0 commit comments

Comments
 (0)