Unverified Commit 39f18ffa authored by Vaibhav Sagar's avatar Vaibhav Sagar Committed by GitHub

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

Rewrite Docker image
parents 11c47158 3867595c
...@@ -94,3 +94,16 @@ jobs: ...@@ -94,3 +94,16 @@ jobs:
stack install stack install
stack exec -- ihaskell install --stack stack exec -- ihaskell install --stack
test/acceptance.nbconvert.sh stack exec -- jupyter nbconvert 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
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 FROM haskell:${GHC_VERSION} AS ihaskell_base
ARG RESOLVER=lts-17.4
# Install all necessary Ubuntu packages # Install Ubuntu packages needed for IHaskell runtime
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 && \ RUN apt-get update && \
apt-get install -y \
libblas3 \
libcairo2 \
liblapack3 \
libmagic1 \
libpango-1.0-0 \
libzmq5 \
&& \
rm -rf /var/lib/apt/lists/* 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 # Install Jupyter notebook
RUN apt-get update && \
apt-get install -y python3-pip && \
rm -rf /var/lib/apt/lists/*
RUN pip3 install -U jupyter RUN pip3 install -U jupyter
ENV LANG C.UTF-8 # Create runtime user
ENV LC_ALL C.UTF-8
ENV NB_USER jovyan ENV NB_USER jovyan
ENV NB_UID 1000 ENV NB_UID 1000
ENV HOME /home/${NB_USER}
RUN adduser --disabled-password \ RUN adduser --disabled-password \
--gecos "Default user" \ --gecos "Default user" \
--uid ${NB_UID} \ --uid ${NB_UID} \
${NB_USER} ${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 # Set up + set hlint data directory
RUN install -d -o ${NB_UID} -g ${NB_UID} ${HOME} ${HOME}/ihaskell ENV HLINT_DATA_DIR /usr/local/lib/hlint
WORKDIR ${HOME}/ihaskell 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} USER ${NB_UID}
WORKDIR /home/${NB_USER}/src
# Install dependencies for IHaskell # Set up global project
COPY --chown=${NB_UID}:${NB_UID} stack.yaml stack.yaml COPY --from=builder --chown=${NB_UID} /build/resolver.txt /tmp/
COPY --chown=${NB_UID}:${NB_UID} ihaskell.cabal ihaskell.cabal RUN stack setup --resolver=$(cat /tmp/resolver.txt) --system-ghc
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
RUN stack setup # Set up env file
RUN stack build --only-snapshot RUN stack exec env --system-ghc > ${IHASKELL_DATA_DIR}/env
# Install IHaskell itself. Don't just COPY . so that # Install + setup IHaskell
# changes in e.g. README.md don't trigger rebuild. COPY --from=builder --chown=${NB_UID} /build/bin/ihaskell /usr/local/bin/
COPY --chown=${NB_UID}:${NB_UID} src ${HOME}/ihaskell/src COPY --from=builder --chown=${NB_UID} /build/html ${IHASKELL_DATA_DIR}/html
COPY --chown=${NB_UID}:${NB_UID} html ${HOME}/ihaskell/html RUN export ihaskell_datadir=${IHASKELL_DATA_DIR} && \
COPY --chown=${NB_UID}:${NB_UID} main ${HOME}/ihaskell/main ihaskell install --env-file ${IHASKELL_DATA_DIR}/env
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}
RUN jupyter notebook --generate-config RUN jupyter notebook --generate-config
CMD ["jupyter", "notebook", "--ip", "0.0.0.0"] CMD ["jupyter", "notebook", "--ip", "0.0.0.0"]
...@@ -123,7 +123,7 @@ in the top directory. ...@@ -123,7 +123,7 @@ in the top directory.
```bash ```bash
docker build -t ihaskell:latest . 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 Or use the continuously updated Docker image
...@@ -137,7 +137,7 @@ In order to mount your own local files into the Docker container ...@@ -137,7 +137,7 @@ In order to mount your own local files into the Docker container
use following command: use following command:
```sh ```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 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 ...@@ -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 This guarantees that stack doesn't have to first perform
a lengthy installation of GHC before running your notebook. 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 ## Stack and Docker
......
...@@ -127,7 +127,7 @@ executable ihaskell ...@@ -127,7 +127,7 @@ executable ihaskell
hs-source-dirs: main hs-source-dirs: main
other-modules: other-modules:
Paths_ihaskell Paths_ihaskell
ghc-options: -threaded -rtsopts -Wall -dynamic ghc-options: -threaded -rtsopts -Wall
if os(darwin) if os(darwin)
ghc-options: -optP-Wno-nonportable-include-path ghc-options: -optP-Wno-nonportable-include-path
......
...@@ -98,6 +98,8 @@ parseKernelArgs = foldl' addFlag defaultKernelSpecOptions ...@@ -98,6 +98,8 @@ parseKernelArgs = foldl' addFlag defaultKernelSpecOptions
kernelSpecOpts { kernelSpecInstallPrefix = Just prefix } kernelSpecOpts { kernelSpecInstallPrefix = Just prefix }
addFlag kernelSpecOpts KernelspecUseStack = addFlag kernelSpecOpts KernelspecUseStack =
kernelSpecOpts { kernelSpecUseStack = True } kernelSpecOpts { kernelSpecUseStack = True }
addFlag kernelSpecOpts (KernelspecEnvFile fp) =
kernelSpecOpts { kernelSpecEnvFile = Just fp }
addFlag _kernelSpecOpts flag = error $ "Unknown flag" ++ show flag addFlag _kernelSpecOpts flag = error $ "Unknown flag" ++ show flag
-- | Run the IHaskell language kernel. -- | Run the IHaskell language kernel.
...@@ -127,13 +129,12 @@ runKernel kOpts profileSrc = do ...@@ -127,13 +129,12 @@ runKernel kOpts profileSrc = do
-- If we're in a stack directory, use `stack` to set the environment -- 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. -- We can't do this with base <= 4.6 because setEnv doesn't exist.
when stack $ do when stack $
stackEnv <- lines <$> readProcess "stack" ["exec", "env"] "" readProcess "stack" ["exec", "env"] "" >>= parseAndSetEnv
forM_ stackEnv $ \line ->
let (var, val) = break (== '=') line case kernelSpecEnvFile kOpts of
in case tailMay val of Nothing -> return ()
Nothing -> return () Just envFile -> readFile envFile >>= parseAndSetEnv
Just val' -> setEnv var val'
-- Serve on all sockets and ports defined in the profile. -- Serve on all sockets and ports defined in the profile.
interface <- serveProfile profile debug interface <- serveProfile profile debug
...@@ -210,6 +211,12 @@ runKernel kOpts profileSrc = do ...@@ -210,6 +211,12 @@ runKernel kOpts profileSrc = do
isCommMessage req = mhMsgType (header req) `elem` [CommDataMessage, CommCloseMessage] 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. -- Initial kernel state.
initialKernelState :: IO (MVar KernelState) initialKernelState :: IO (MVar KernelState)
initialKernelState = newMVar defaultKernelState initialKernelState = newMVar defaultKernelState
......
...@@ -39,6 +39,7 @@ data Argument = ConfFile String -- ^ A file with commands to load at startup ...@@ -39,6 +39,7 @@ data Argument = ConfFile String -- ^ A file with commands to load at startup
| ConvertLhsStyle (LhsStyle String) | ConvertLhsStyle (LhsStyle String)
| KernelspecInstallPrefix String | KernelspecInstallPrefix String
| KernelspecUseStack | KernelspecUseStack
| KernelspecEnvFile FilePath
deriving (Eq, Show) deriving (Eq, Show)
data LhsStyle string = data LhsStyle string =
...@@ -124,6 +125,14 @@ kernelStackFlag = flagNone ["stack"] addStack ...@@ -124,6 +125,14 @@ kernelStackFlag = flagNone ["stack"] addStack
where where
addStack (Args md prev) = Args md (KernelspecUseStack : prev) 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 :: Flag Args
confFlag = flagReq ["conf", "c"] (store ConfFile) "<rc.hs>" confFlag = flagReq ["conf", "c"] (store ConfFile) "<rc.hs>"
"File with commands to execute at start; replaces ~/.ihaskell/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 ...@@ -144,11 +153,11 @@ store constructor str (Args md prev) = Right $ Args md $ constructor str : prev
installKernelSpec :: Mode Args installKernelSpec :: Mode Args
installKernelSpec = installKernelSpec =
mode "install" (Args InstallKernelSpec []) "Install the Jupyter kernelspec." noArgs 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 Args
kernel = mode "kernel" (Args (Kernel Nothing) []) "Invoke the IHaskell kernel." kernelArg kernel = mode "kernel" (Args (Kernel Nothing) []) "Invoke the IHaskell kernel." kernelArg
[ghcLibFlag, kernelDebugFlag, confFlag, kernelStackFlag, kernelCodeMirrorFlag] [ghcLibFlag, kernelDebugFlag, confFlag, kernelStackFlag, kernelEnvFileFlag, kernelCodeMirrorFlag]
where where
kernelArg = flagArg update "<json-kernel-file>" kernelArg = flagArg update "<json-kernel-file>"
update filename (Args _ flags) = Right $ Args (Kernel $ Just filename) flags update filename (Args _ flags) = Right $ Args (Kernel $ Just filename) flags
......
...@@ -43,6 +43,7 @@ data KernelSpecOptions = ...@@ -43,6 +43,7 @@ data KernelSpecOptions =
, kernelSpecConfFile :: IO (Maybe String) -- ^ Filename of profile JSON file. , kernelSpecConfFile :: IO (Maybe String) -- ^ Filename of profile JSON file.
, kernelSpecInstallPrefix :: Maybe String , kernelSpecInstallPrefix :: Maybe String
, kernelSpecUseStack :: Bool -- ^ Whether to use @stack@ environments. , kernelSpecUseStack :: Bool -- ^ Whether to use @stack@ environments.
, kernelSpecEnvFile :: Maybe FilePath
} }
defaultKernelSpecOptions :: KernelSpecOptions defaultKernelSpecOptions :: KernelSpecOptions
...@@ -55,6 +56,7 @@ defaultKernelSpecOptions = KernelSpecOptions ...@@ -55,6 +56,7 @@ defaultKernelSpecOptions = KernelSpecOptions
, kernelSpecConfFile = defaultConfFile , kernelSpecConfFile = defaultConfFile
, kernelSpecInstallPrefix = Nothing , kernelSpecInstallPrefix = Nothing
, kernelSpecUseStack = False , kernelSpecUseStack = False
, kernelSpecEnvFile = Nothing
} }
-- | The IPython kernel name. -- | The IPython kernel name.
......
# the GHC version of this resolver needs to match the GHC version in Dockerfile
resolver: lts-18.5 resolver: lts-18.5
allow-newer: true allow-newer: true
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment