mirror of
https://github.com/IHaskell/IHaskell.git
synced 2025-04-16 11:26:08 +00:00
Merge pull request #1252 from brandon-leapyear/chinn/docker
Rewrite Docker image
This commit is contained in:
commit
39f18ffa3d
13
.github/workflows/ci.yml
vendored
13
.github/workflows/ci.yml
vendored
@ -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
|
||||
|
117
Dockerfile
117
Dockerfile
@ -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"]
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
21
main/Main.hs
21
main/Main.hs
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user