Merge pull request #1252 from brandon-leapyear/chinn/docker

Rewrite Docker image
This commit is contained in:
Vaibhav Sagar 2021-08-21 04:27:36 +10:00 committed by GitHub
commit 39f18ffa3d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 126 additions and 48 deletions

View File

@ -94,3 +94,16 @@ jobs:
stack install
stack exec -- ihaskell install --stack
test/acceptance.nbconvert.sh stack exec -- jupyter nbconvert
- name: Check Dockerfile GHC version matches
run: |
if [[ '${{ matrix.versions.stack-yaml }}' != 'stack.yaml' ]]; then
exit 0
fi
set -e
STACK_GHC_VERSION=$(stack exec -- ghc --version | awk '{ print $NF }')
DOCKER_GHC_VERSION=$(sed -n 's/ARG GHC_VERSION=\(.*\)/\1/p' Dockerfile)
if [[ ${STACK_GHC_VERSION} != ${DOCKER_GHC_VERSION} ]]; then
echo 'GHC_VERSION in Dockerfile does not match stack resolver'
exit 1
fi

View File

@ -1,58 +1,103 @@
FROM ubuntu:18.04
# should match the GHC version of the stack.yaml resolver
# checked in CI
ARG GHC_VERSION=8.10.4
ARG STACK_VERSION=2.5.1
ARG RESOLVER=lts-17.4
FROM haskell:${GHC_VERSION} AS ihaskell_base
# Install all necessary Ubuntu packages
RUN apt-get update && apt-get install -y python3-pip libgmp-dev libmagic-dev libtinfo-dev libzmq3-dev libcairo2-dev libpango1.0-dev libblas-dev liblapack-dev gcc g++ wget && \
# Install Ubuntu packages needed for IHaskell runtime
RUN apt-get update && \
apt-get install -y \
libblas3 \
libcairo2 \
liblapack3 \
libmagic1 \
libpango-1.0-0 \
libzmq5 \
&& \
rm -rf /var/lib/apt/lists/*
FROM ihaskell_base AS builder
# Install Ubuntu packages needed for IHaskell build
RUN apt-get update && \
apt-get install -y \
libblas-dev \
libcairo2-dev \
liblapack-dev \
libmagic-dev \
libpango1.0-dev \
libzmq3-dev \
&& \
rm -rf /var/lib/apt/lists/*
WORKDIR /build
# Build snapshot
COPY stack.yaml stack.yaml
COPY ihaskell.cabal ihaskell.cabal
COPY ipython-kernel ipython-kernel
COPY ghc-parser ghc-parser
COPY ihaskell-display ihaskell-display
RUN stack setup
RUN stack build --only-snapshot
# Build IHaskell itself.
# Don't just `COPY .` so that changes in e.g. README.md don't trigger rebuild.
COPY src src
COPY html html
COPY main main
COPY LICENSE LICENSE
RUN stack install --local-bin-path ./bin/
# Save resolver used to build IHaskell
RUN sed -n 's/resolver: \(.*\)/\1/p' stack.yaml | tee resolver.txt
# Save third-party data files
RUN mkdir /data && \
snapshot_install_root=$(stack path --snapshot-install-root) && \
cp $(find ${snapshot_install_root} -name hlint.yaml) /data
FROM ihaskell_base AS ihaskell
# Install Jupyter notebook
RUN apt-get update && \
apt-get install -y python3-pip && \
rm -rf /var/lib/apt/lists/*
RUN pip3 install -U jupyter
ENV LANG C.UTF-8
ENV LC_ALL C.UTF-8
# Create runtime user
ENV NB_USER jovyan
ENV NB_UID 1000
ENV HOME /home/${NB_USER}
RUN adduser --disabled-password \
--gecos "Default user" \
--uid ${NB_UID} \
${NB_USER}
RUN wget -qO- https://github.com/commercialhaskell/stack/releases/download/v$STACK_VERSION/stack-$STACK_VERSION-linux-x86_64.tar.gz | tar xz --wildcards --strip-components=1 -C /usr/bin '*/stack'
# Create directory for storing ihaskell files
ENV IHASKELL_DATA_DIR /usr/local/lib/ihaskell
RUN mkdir -p ${IHASKELL_DATA_DIR} && chown ${NB_UID} ${IHASKELL_DATA_DIR}
# Set up a working directory for IHaskell
RUN install -d -o ${NB_UID} -g ${NB_UID} ${HOME} ${HOME}/ihaskell
WORKDIR ${HOME}/ihaskell
# Set up + set hlint data directory
ENV HLINT_DATA_DIR /usr/local/lib/hlint
COPY --from=builder --chown=${NB_UID} /data/hlint.yaml ${HLINT_DATA_DIR}/
ENV hlint_datadir ${HLINT_DATA_DIR}
# Set current user + directory
USER ${NB_UID}
WORKDIR /home/${NB_USER}/src
# Install dependencies for IHaskell
COPY --chown=${NB_UID}:${NB_UID} stack.yaml stack.yaml
COPY --chown=${NB_UID}:${NB_UID} ihaskell.cabal ihaskell.cabal
COPY --chown=${NB_UID}:${NB_UID} ipython-kernel ipython-kernel
COPY --chown=${NB_UID}:${NB_UID} ghc-parser ghc-parser
COPY --chown=${NB_UID}:${NB_UID} ihaskell-display ihaskell-display
# Set up global project
COPY --from=builder --chown=${NB_UID} /build/resolver.txt /tmp/
RUN stack setup --resolver=$(cat /tmp/resolver.txt) --system-ghc
RUN stack setup
RUN stack build --only-snapshot
# Set up env file
RUN stack exec env --system-ghc > ${IHASKELL_DATA_DIR}/env
# Install IHaskell itself. Don't just COPY . so that
# changes in e.g. README.md don't trigger rebuild.
COPY --chown=${NB_UID}:${NB_UID} src ${HOME}/ihaskell/src
COPY --chown=${NB_UID}:${NB_UID} html ${HOME}/ihaskell/html
COPY --chown=${NB_UID}:${NB_UID} main ${HOME}/ihaskell/main
COPY --chown=${NB_UID}:${NB_UID} LICENSE ${HOME}/ihaskell/LICENSE
RUN stack build && stack install
RUN mkdir -p ${HOME}/.stack/global-project && \
echo "packages: []\nresolver: ${RESOLVER}\n" > ${HOME}/.stack/global-project/stack.yaml
# Run the notebook
ENV PATH $(stack path --local-install-root)/bin:$(stack path --snapshot-install-root)/bin:$(stack path --compiler-bin):/home/${NB_USER}/.local/bin:${PATH}
RUN ihaskell install --stack
WORKDIR ${HOME}
# Install + setup IHaskell
COPY --from=builder --chown=${NB_UID} /build/bin/ihaskell /usr/local/bin/
COPY --from=builder --chown=${NB_UID} /build/html ${IHASKELL_DATA_DIR}/html
RUN export ihaskell_datadir=${IHASKELL_DATA_DIR} && \
ihaskell install --env-file ${IHASKELL_DATA_DIR}/env
RUN jupyter notebook --generate-config
CMD ["jupyter", "notebook", "--ip", "0.0.0.0"]

View File

@ -123,7 +123,7 @@ in the top directory.
```bash
docker build -t ihaskell:latest .
docker run --rm -it -p8888:8888 ihaskell:latest
docker run --rm -p 8888:8888 ihaskell:latest
```
Or use the continuously updated Docker image
@ -137,7 +137,7 @@ In order to mount your own local files into the Docker container
use following command:
```sh
docker run --rm -p 8888:8888 -v "$PWD":/home/jovyan/work gibiansky/ihaskell
docker run --rm -p 8888:8888 -v "$PWD":/home/jovyan/src gibiansky/ihaskell
```
Be aware that the directory you're mounting must contain
@ -154,6 +154,7 @@ It's recommended to use the same LTS version as the IHaskell image is using itse
This guarantees that stack doesn't have to first perform
a lengthy installation of GHC before running your notebook.
You can also use the following script to run IHaskell in Docker: https://gist.github.com/brandonchinn178/928d6137bfd17961b9584a8f96c18827
## Stack and Docker

View File

@ -127,7 +127,7 @@ executable ihaskell
hs-source-dirs: main
other-modules:
Paths_ihaskell
ghc-options: -threaded -rtsopts -Wall -dynamic
ghc-options: -threaded -rtsopts -Wall
if os(darwin)
ghc-options: -optP-Wno-nonportable-include-path

View File

@ -98,6 +98,8 @@ parseKernelArgs = foldl' addFlag defaultKernelSpecOptions
kernelSpecOpts { kernelSpecInstallPrefix = Just prefix }
addFlag kernelSpecOpts KernelspecUseStack =
kernelSpecOpts { kernelSpecUseStack = True }
addFlag kernelSpecOpts (KernelspecEnvFile fp) =
kernelSpecOpts { kernelSpecEnvFile = Just fp }
addFlag _kernelSpecOpts flag = error $ "Unknown flag" ++ show flag
-- | Run the IHaskell language kernel.
@ -127,13 +129,12 @@ runKernel kOpts profileSrc = do
-- If we're in a stack directory, use `stack` to set the environment
-- We can't do this with base <= 4.6 because setEnv doesn't exist.
when stack $ do
stackEnv <- lines <$> readProcess "stack" ["exec", "env"] ""
forM_ stackEnv $ \line ->
let (var, val) = break (== '=') line
in case tailMay val of
Nothing -> return ()
Just val' -> setEnv var val'
when stack $
readProcess "stack" ["exec", "env"] "" >>= parseAndSetEnv
case kernelSpecEnvFile kOpts of
Nothing -> return ()
Just envFile -> readFile envFile >>= parseAndSetEnv
-- Serve on all sockets and ports defined in the profile.
interface <- serveProfile profile debug
@ -210,6 +211,12 @@ runKernel kOpts profileSrc = do
isCommMessage req = mhMsgType (header req) `elem` [CommDataMessage, CommCloseMessage]
parseAndSetEnv envLines =
forM_ (lines envLines) $ \line -> do
case break (== '=') line of
(_, []) -> return ()
(key, _:val) -> setEnv key val
-- Initial kernel state.
initialKernelState :: IO (MVar KernelState)
initialKernelState = newMVar defaultKernelState

View File

@ -39,6 +39,7 @@ data Argument = ConfFile String -- ^ A file with commands to load at startup
| ConvertLhsStyle (LhsStyle String)
| KernelspecInstallPrefix String
| KernelspecUseStack
| KernelspecEnvFile FilePath
deriving (Eq, Show)
data LhsStyle string =
@ -124,6 +125,14 @@ kernelStackFlag = flagNone ["stack"] addStack
where
addStack (Args md prev) = Args md (KernelspecUseStack : prev)
kernelEnvFileFlag :: Flag Args
kernelEnvFileFlag =
flagReq
["env-file"]
(store KernelspecEnvFile)
"<file>"
"Load environment from this file when kernel is installed"
confFlag :: Flag Args
confFlag = flagReq ["conf", "c"] (store ConfFile) "<rc.hs>"
"File with commands to execute at start; replaces ~/.ihaskell/rc.hs."
@ -144,11 +153,11 @@ store constructor str (Args md prev) = Right $ Args md $ constructor str : prev
installKernelSpec :: Mode Args
installKernelSpec =
mode "install" (Args InstallKernelSpec []) "Install the Jupyter kernelspec." noArgs
[ghcLibFlag, ghcRTSFlag, kernelDebugFlag, confFlag, installPrefixFlag, helpFlag, kernelStackFlag]
[ghcLibFlag, ghcRTSFlag, kernelDebugFlag, confFlag, installPrefixFlag, helpFlag, kernelStackFlag, kernelEnvFileFlag]
kernel :: Mode Args
kernel = mode "kernel" (Args (Kernel Nothing) []) "Invoke the IHaskell kernel." kernelArg
[ghcLibFlag, kernelDebugFlag, confFlag, kernelStackFlag, kernelCodeMirrorFlag]
[ghcLibFlag, kernelDebugFlag, confFlag, kernelStackFlag, kernelEnvFileFlag, kernelCodeMirrorFlag]
where
kernelArg = flagArg update "<json-kernel-file>"
update filename (Args _ flags) = Right $ Args (Kernel $ Just filename) flags

View File

@ -43,6 +43,7 @@ data KernelSpecOptions =
, kernelSpecConfFile :: IO (Maybe String) -- ^ Filename of profile JSON file.
, kernelSpecInstallPrefix :: Maybe String
, kernelSpecUseStack :: Bool -- ^ Whether to use @stack@ environments.
, kernelSpecEnvFile :: Maybe FilePath
}
defaultKernelSpecOptions :: KernelSpecOptions
@ -55,6 +56,7 @@ defaultKernelSpecOptions = KernelSpecOptions
, kernelSpecConfFile = defaultConfFile
, kernelSpecInstallPrefix = Nothing
, kernelSpecUseStack = False
, kernelSpecEnvFile = Nothing
}
-- | The IPython kernel name.

View File

@ -1,3 +1,4 @@
# the GHC version of this resolver needs to match the GHC version in Dockerfile
resolver: lts-18.5
allow-newer: true