From a5d8b4c4d3691b5ca879ba835c5954077dd7a8da Mon Sep 17 00:00:00 2001 From: Olivier Benz Date: Wed, 13 Sep 2023 07:36:53 +0200 Subject: [PATCH 01/12] Add Dev Container Configuration Files --- .devcontainer/.gitignore | 31 +++++ .devcontainer/GHC.Dockerfile | 112 ++++++++++++++++++ .devcontainer/LICENSE | 35 ++++++ .devcontainer/README.md | 87 ++++++++++++++ .devcontainer/assets/cradles/cabal/hie.yaml | 19 +++ .devcontainer/assets/cradles/stack/hie.yaml | 19 +++ .../assets/screenshots/manageHLS.png | Bin 0 -> 55571 bytes .devcontainer/bind-mounts/.keep | 0 .devcontainer/conf/etc/stack/config.yaml | 4 + .devcontainer/devcontainer.json | 61 ++++++++++ .devcontainer/ghc-9.6.2/devcontainer.json | 59 +++++++++ .../scripts/usr/local/bin/fix-chsh.sh | 18 +++ .../scripts/usr/local/bin/onCreateCommand.sh | 41 +++++++ 13 files changed, 486 insertions(+) create mode 100644 .devcontainer/.gitignore create mode 100644 .devcontainer/GHC.Dockerfile create mode 100644 .devcontainer/LICENSE create mode 100644 .devcontainer/README.md create mode 100644 .devcontainer/assets/cradles/cabal/hie.yaml create mode 100644 .devcontainer/assets/cradles/stack/hie.yaml create mode 100644 .devcontainer/assets/screenshots/manageHLS.png create mode 100644 .devcontainer/bind-mounts/.keep create mode 100644 .devcontainer/conf/etc/stack/config.yaml create mode 100644 .devcontainer/devcontainer.json create mode 100644 .devcontainer/ghc-9.6.2/devcontainer.json create mode 100755 .devcontainer/scripts/usr/local/bin/fix-chsh.sh create mode 100755 .devcontainer/scripts/usr/local/bin/onCreateCommand.sh diff --git a/.devcontainer/.gitignore b/.devcontainer/.gitignore new file mode 100644 index 0000000000..b2b871603f --- /dev/null +++ b/.devcontainer/.gitignore @@ -0,0 +1,31 @@ +* + +!/assets/ +!/assets/cradles/ +!/assets/cradles/cabal/ +!/assets/cradles/stack/ +!/assets/screenshots/ +!/bind-mounts/ +!/conf/ +!/conf/etc/ +!/conf/etc/stack/ +!/ghc-*/ +!/scripts/ +!/scripts/usr/ +!/scripts/usr/local/ +!/scripts/usr/local/bin/ + +!/assets/cradles/cabal/hie.yaml +!/assets/cradles/stack/hie.yaml +!/assets/screenshots/manageHLS.png +!/conf/etc/stack/config.yaml +!/ghc-*/devcontainer.json +!/scripts/usr/local/bin/*.sh + +!/devcontainer.json +!/GHC.Dockerfile +!/LICENSE +!/README.md + +!.gitignore +!.keep diff --git a/.devcontainer/GHC.Dockerfile b/.devcontainer/GHC.Dockerfile new file mode 100644 index 0000000000..e2f4bd29fb --- /dev/null +++ b/.devcontainer/GHC.Dockerfile @@ -0,0 +1,112 @@ +ARG BUILD_ON_IMAGE=glcr.b-data.ch/ghc/ghc-musl +ARG GHC_VERSION=latest +ARG HLS_VERSION +ARG STACK_VERSION + +ARG HLS_GHC_VERSION=${HLS_VERSION:+$GHC_VERSION} +ARG HLS_SFX=/${HLS_GHC_VERSION:-all}/hls:${HLS_VERSION:-none} + +ARG STACK_VERSION_OVERRIDE=${STACK_VERSION:-none} + +FROM ${BUILD_ON_IMAGE}:${GHC_VERSION} as files + +RUN mkdir /files + +COPY conf /files +COPY scripts /files + +## Ensure file modes are correct +RUN find /files -type d -exec chmod 755 {} \; \ + && find /files -type f -exec chmod 644 {} \; \ + && find /files/usr/local/bin -type f -exec chmod 755 {} \; + +FROM glcr.b-data.ch/commercialhaskell/ssi:${STACK_VERSION_OVERRIDE} as ssi + +FROM ${BUILD_ON_IMAGE}${HLS_SFX} as hls + +FROM glcr.b-data.ch/ndmitchell/hlsi:latest as hlsi + +FROM docker.io/koalaman/shellcheck:stable as sci + +FROM ${BUILD_ON_IMAGE}:${GHC_VERSION} + +COPY --from=files /files / + +RUN sysArch="$(uname -m)" \ + ## Ensure that common CA certificates + ## and OpenSSL libraries are up to date + && apk upgrade --no-cache ca-certificates openssl-dev \ + ## Install yamllint + && apk add --no-cache yamllint \ + ## Install hadolint + && case "$sysArch" in \ + x86_64) tarArch="x86_64" ;; \ + aarch64) tarArch="arm64" ;; \ + *) echo "error: Architecture $sysArch unsupported"; exit 1 ;; \ + esac \ + && apiResponse="$(curl -sSL \ + https://api.github.com/repos/hadolint/hadolint/releases/latest)" \ + && downloadUrl="$(echo "$apiResponse" | grep -e \ + "browser_download_url.*Linux-$tarArch\"" | cut -d : -f 2,3 | tr -d \")" \ + && echo "$downloadUrl" | xargs curl -sSLo /usr/local/bin/hadolint \ + && chmod 755 /usr/local/bin/hadolint \ + ## Create folders in root directory + && mkdir -p /root/.local/bin \ + ## Create folders in skeleton directory + && mkdir -p /etc/skel/.local/bin + +## Update environment +ARG USE_ZSH_FOR_ROOT +ARG SET_LANG +ARG SET_TZ + +ENV TZ=${SET_TZ:-$TZ} \ + LANG=${SET_LANG:-$LANG} + + ## Change root's shell to ZSH +RUN if [ -n "$USE_ZSH_FOR_ROOT" ]; then \ + apk add --no-cache zsh shadow; \ + fix-chsh.sh; \ + chsh -s /bin/zsh; \ + fi \ + ## Update timezone if needed + && if [ "$TZ" != "" ]; then \ + apk add --no-cache tzdata; \ + echo "Setting TZ to $TZ"; \ + ln -snf "/usr/share/zoneinfo/$TZ" /etc/localtime \ + && echo "$TZ" > /etc/timezone; \ + fi \ + ## Add/Update locale if needed + && if [ "$LANG" != "C.UTF-8" ]; then \ + if [ -n "$LANG" ]; then \ + apk add --no-cache musl-locales musl-locales-lang; \ + fi; \ + sed -i "s/LANG=C.UTF-8/LANG=$LANG/" /etc/profile.d/*locale.sh; \ + sed -i "s/LANG:-C.UTF-8/LANG:-$LANG/" /etc/profile.d/*locale.sh; \ + sed -i "s/LC_COLLATE=C/LC_COLLATE=$LANG/" /etc/profile.d/*locale.sh; \ + sed -i "s/LC_COLLATE:-C/LC_COLLATE:-$LANG/" /etc/profile.d/*locale.sh; \ + fi + +## Copy binaries as late as possible to avoid cache busting +## Install Stack +COPY --from=ssi /usr/local /usr/local +## Install HLS +COPY --from=hls /usr/local /usr/local +## Install HLint +COPY --from=hlsi /usr/local /usr/local +## Install ShellCheck +COPY --from=sci --chown=root:root /bin/shellcheck /usr/local/bin + +ARG HLS_VERSION +ARG STACK_VERSION + +ARG STACK_VERSION_OVERRIDE=${STACK_VERSION} + +ENV HLS_VERSION=${HLS_VERSION} \ + STACK_VERSION=${STACK_VERSION_OVERRIDE:-$STACK_VERSION} + +RUN if [ "${GHC_VERSION%.*}" = "9.2" ]; then \ + if [ -f /usr/local/bin/stack ]; then \ + mv -f /usr/local/bin/stack /usr/bin/; \ + fi \ + fi diff --git a/.devcontainer/LICENSE b/.devcontainer/LICENSE new file mode 100644 index 0000000000..74e3bbd1bb --- /dev/null +++ b/.devcontainer/LICENSE @@ -0,0 +1,35 @@ +Copyright (c) 2023 Olivier Benz + +The code in this directory is not part of Stack (the software) and, with the +exceptions noted below, is distributed under the terms of the MIT License: + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + +This directory also contains code with other copyrights. The affected files, +their copyrights and license statements are listed below. + +-------------------------------------------------------------------------------- +scripts/fix-chsh.sh +Copyright (c) Microsoft Corporation. All rights reserved. + +Licensed under the MIT License. See +https://github.com/devcontainers/features/blob/main/LICENSE for +license information. + +-------------------------------------------------------------------------------- diff --git a/.devcontainer/README.md b/.devcontainer/README.md new file mode 100644 index 0000000000..428b126915 --- /dev/null +++ b/.devcontainer/README.md @@ -0,0 +1,87 @@ +# Dev Containers + +These Dev Containers are based on the same docker images that are used to build +the *statically linked* Linux amd64 and arm64 binary releases of Stack. + +Those multi-arch (`linux/amd64`, `linux/arm64/v8`) docker images themselves are +based on Alpine Linux and contain *unofficial* builds of GHC. + +Only use the GHC available in the Dev Containers, because + +1. the *official* GHC bindists for Alpine Linux (`x86_64`) are just too buggy. +2. there are currently (2023-09-05) no bindists for Alpine Linux (`AArch64`). + +Therefore, flags `--system-ghc` and `--no-install-ghc` are set system-wide in +`/etc/stack/config.yaml`. + +## Usage + +For use with Github Codespaces, please follow the instruction at +[Creating a codespace for a repository](https://docs.github.com/en/codespaces/developing-in-codespaces/creating-a-codespace-for-a-repository#creating-a-codespace-for-a-repository). + +For local/'remote host' usage with VS Code, please follow the instructions at +[Developing inside a Container](https://code.visualstudio.com/docs/devcontainers/containers). + +### Persistence + +Data in the following locations is persisted: + +1. The user's home directory (`/home/vscode`)[^1] +2. The Dev Container's workspace (`/workspaces`) + +[^1]: Alternatively for the root user (`/root`). Use with Docker/Podman in +*rootless mode*. + +This is accomplished either via a *volume* or *bind mount* (or *loop device* on +Codespaces) and is preconfigured. + +## Build Stack + +### Using cabal + +:information_source: Default Dev Container only. + +Command `cabal build` to build the `stack` executable. + +Append `--flag=static` to build a *statically linked* `stack` executable that +can run on any Linux machine of the same architecture. + +### Using Stack + +Command `stack build` to build the `stack` executable. + +Append `--flag=stack:static` to build a *statically linked* `stack` executable +that can run on any Linux machine of the same architecture. + +Append `--stack-yaml stack-ghc-$GHC_VERSION.yaml` if you want to use an +experimental project-level configuration with the appropriate Dev Container. + +## Haskell Language Server (HLS) + +The +[Haskell Language Server](https://github.com/haskell/haskell-language-server) +and the +[Haskell](https://marketplace.visualstudio.com/items?itemName=haskell.haskell) +extension are only available in the default Dev Container. + +In order to use the Haskell extension, you must first configure the project for +the build tool of your choice: + +**Stack** + +Place the cradle ([hie.yaml](assets/cradles/stack/hie.yaml)) for Stack in the +root of the workspace: `cp -f .devcontainer/assets/cradles/stack/hie.yaml .` + +**Cabal** + +Place the cradle ([hie.yaml](assets/cradles/cabal/hie.yaml)) for Cabal in the +root of the workspace: `cp -f .devcontainer/assets/cradles/cabal/hie.yaml .` + +This should suffice to configure the HLS explicitly for `./Setup.hs` and each +of the buildable components in Stack's Cabal file. + +### Haskell extension + +Choose `Manually via PATH` when asked the following question: + +manageHLS diff --git a/.devcontainer/assets/cradles/cabal/hie.yaml b/.devcontainer/assets/cradles/cabal/hie.yaml new file mode 100644 index 0000000000..cb6b06a9cb --- /dev/null +++ b/.devcontainer/assets/cradles/cabal/hie.yaml @@ -0,0 +1,19 @@ +cradle: + multi: + - path: "./Setup.hs" + config: + cradle: + direct: + arguments: [] + - path: "./" + config: + cradle: + cabal: + - path: "./src" + component: "lib:stack" + - path: "./app" + component: "stack:exe:stack" + - path: "./tests/integration" + component: "stack:exe:stack-integration-test" + - path: "./tests/unit" + component: "stack:test:stack-unit-test" diff --git a/.devcontainer/assets/cradles/stack/hie.yaml b/.devcontainer/assets/cradles/stack/hie.yaml new file mode 100644 index 0000000000..bcc8ccd9c4 --- /dev/null +++ b/.devcontainer/assets/cradles/stack/hie.yaml @@ -0,0 +1,19 @@ +cradle: + multi: + - path: "./Setup.hs" + config: + cradle: + direct: + arguments: [] + - path: "./" + config: + cradle: + stack: + - path: "./src" + component: "stack:lib" + - path: "./app" + component: "stack:exe:stack" + - path: "./tests/integration" + component: "stack:exe:stack-integration-test" + - path: "./tests/unit" + component: "stack:test:stack-unit-test" diff --git a/.devcontainer/assets/screenshots/manageHLS.png b/.devcontainer/assets/screenshots/manageHLS.png new file mode 100644 index 0000000000000000000000000000000000000000..d6589d593a822050d212da83d18e4c340fd2a31a GIT binary patch literal 55571 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 /etc/pam.d/chsh +elif [[ -n "$(awk '/^auth(.*)pam_rootok\.so$/ && !/^auth[[:blank:]]+sufficient[[:blank:]]+pam_rootok\.so$/' /etc/pam.d/chsh)" ]]; then + awk '/^auth(.*)pam_rootok\.so$/ { $2 = "sufficient" } { print }' /etc/pam.d/chsh > /tmp/chsh.tmp && mv /tmp/chsh.tmp /etc/pam.d/chsh +fi diff --git a/.devcontainer/scripts/usr/local/bin/onCreateCommand.sh b/.devcontainer/scripts/usr/local/bin/onCreateCommand.sh new file mode 100755 index 0000000000..6c53c127ad --- /dev/null +++ b/.devcontainer/scripts/usr/local/bin/onCreateCommand.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +# Copyright (c) 2023 b-data GmbH. +# Distributed under the terms of the MIT License. + +set -e + +mkdir -p "$HOME/.cabal/bin" +mkdir -p "$HOME/.local/bin" + +# Copy Zsh-related files and folders from the untouched home directory +if [ "$(id -un)" == "root" ]; then + if [ ! -d /root/.oh-my-zsh ]; then + cp -R /home/*/.oh-my-zsh /root; + fi + if [ ! -f /root/.zshrc ]; then + cp /home/*/.zshrc /root; + fi +else + if [ ! -d "$HOME/.oh-my-zsh" ]; then + sudo cp -R /root/.oh-my-zsh "$HOME"; + sudo chown -R "$(id -u)":"$(id -g)" "$HOME/.oh-my-zsh"; + fi + if [ ! -f "$HOME/.zshrc" ]; then + sudo cp /root/.zshrc "$HOME"; + sudo chown "$(id -u)":"$(id -g)" "$HOME/.zshrc"; + fi +fi + +# Set PATH so it includes user's private bin if it exists +if ! grep -q "user's private bin" "$HOME/.zshrc"; then + echo -e "\n# set PATH so it includes user's private bin if it exists\nif [ -d \"\$HOME/bin\" ] && [[ \"\$PATH\" != *\"\$HOME/bin\"* ]] ; then\n PATH=\"\$HOME/bin:\$PATH\"\nfi" >> "$HOME/.zshrc"; + echo -e "\n# set PATH so it includes user's private bin if it exists\nif [ -d \"\$HOME/.local/bin\" ] && [[ \"\$PATH\" != *\"\$HOME/.local/bin\"* ]] ; then\n PATH=\"\$HOME/.local/bin:\$PATH\"\nfi" >> "$HOME/.zshrc"; +fi + +# Set PATH so it includes cabal's bin if it exists +if ! grep -q "cabal's bin" "$HOME/.zshrc"; then + echo -e "\n# set PATH so it includes cabal's bin if it exists\nif [ -d \"\$HOME/.cabal/bin\" ] && [[ \"\$PATH\" != *\"\$HOME/.cabal/bin\"* ]] ; then\n PATH=\"\$HOME/.cabal/bin:\$PATH\"\nfi" >> "$HOME/.zshrc"; +fi + +# Remove old .zcompdump files +rm -f "$HOME"/.zcompdump* From f287ec05c9d478189be029faa388b41c5a17c22c Mon Sep 17 00:00:00 2001 From: Olivier Benz Date: Wed, 13 Sep 2023 13:45:20 +0200 Subject: [PATCH 02/12] Update README.md - A 'Full Rebuild Container' resets the home directory on Codespaces --- .devcontainer/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.devcontainer/README.md b/.devcontainer/README.md index 428b126915..0dc00502a2 100644 --- a/.devcontainer/README.md +++ b/.devcontainer/README.md @@ -35,6 +35,9 @@ Data in the following locations is persisted: This is accomplished either via a *volume* or *bind mount* (or *loop device* on Codespaces) and is preconfigured. +| **Codespaces: A 'Full Rebuild Container' resets the home directory!**
:information_source: This is never necessary unless you want exactly that. | +|:----------------------------------------------------------------------------------------------------------------------------------------------------| + ## Build Stack ### Using cabal From 0e6e664df868430c25e60da597fba84d329788f3 Mon Sep 17 00:00:00 2001 From: Olivier Benz Date: Wed, 13 Sep 2023 16:34:03 +0200 Subject: [PATCH 03/12] Install package pip; Remove Prettier extension - Pip may be used to install MkDocs --- .devcontainer/GHC.Dockerfile | 2 ++ .devcontainer/devcontainer.json | 10 +++++++--- .devcontainer/ghc-9.6.2/devcontainer.json | 10 +++++++--- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/.devcontainer/GHC.Dockerfile b/.devcontainer/GHC.Dockerfile index e2f4bd29fb..9d2067eef7 100644 --- a/.devcontainer/GHC.Dockerfile +++ b/.devcontainer/GHC.Dockerfile @@ -38,6 +38,8 @@ RUN sysArch="$(uname -m)" \ && apk upgrade --no-cache ca-certificates openssl-dev \ ## Install yamllint && apk add --no-cache yamllint \ + ## Install pip + && apk add --no-cache py3-pip \ ## Install hadolint && case "$sysArch" in \ x86_64) tarArch="x86_64" ;; \ diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 997416a5e1..4db825f12d 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -33,7 +33,6 @@ "haskell.haskell", "mhutchie.git-graph", "mutantdino.resourcemonitor", - "esbenp.prettier-vscode", "timonwong.shellcheck" ], "settings": { @@ -51,11 +50,16 @@ "mounts": [ "source=stack-default-home-vscode,target=/home/vscode,type=volume" // "source=${localWorkspaceFolder}/.devcontainer/bind-mounts/stack-default-home-vscode,target=/home/vscode,type=bind" - ] + ], // "remoteUser": "root", // "mounts": [ // "source=stack-default-root,target=/root,type=volume" // // "source=${localWorkspaceFolder}/.devcontainer/bind-mounts/stack-default-root,target=/root,type=bind" - // ] + // ], + + // Pip: Install packages to the user site + "remoteEnv": { + "PIP_USER": "1" + } } diff --git a/.devcontainer/ghc-9.6.2/devcontainer.json b/.devcontainer/ghc-9.6.2/devcontainer.json index 8a260e8df3..83d0f9fd30 100644 --- a/.devcontainer/ghc-9.6.2/devcontainer.json +++ b/.devcontainer/ghc-9.6.2/devcontainer.json @@ -32,7 +32,6 @@ "GitHub.vscode-pull-request-github", "mhutchie.git-graph", "mutantdino.resourcemonitor", - "esbenp.prettier-vscode", "timonwong.shellcheck" ], "settings": { @@ -49,11 +48,16 @@ "mounts": [ "source=stack-ghc-9.6.2-home-vscode,target=/home/vscode,type=volume" // "source=${localWorkspaceFolder}/.devcontainer/bind-mounts/stack-ghc-9.6.2-home-vscode,target=/home/vscode,type=bind" - ] + ], // "remoteUser": "root", // "mounts": [ // "source=stack-ghc-9.6.2-root,target=/root,type=volume" // // "source=${localWorkspaceFolder}/.devcontainer/bind-mounts/stack-ghc-9.6.2-root,target=/root,type=bind" - // ] + // ], + + // Pip: Install packages to the user site + "remoteEnv": { + "PIP_USER": "1" + } } From 849efa526e7f155b45ee2afa888148f02c520908 Mon Sep 17 00:00:00 2001 From: Olivier Benz Date: Wed, 13 Sep 2023 16:34:11 +0200 Subject: [PATCH 04/12] Update README.md --- .devcontainer/README.md | 58 ++++++++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 13 deletions(-) diff --git a/.devcontainer/README.md b/.devcontainer/README.md index 0dc00502a2..37a7b36d99 100644 --- a/.devcontainer/README.md +++ b/.devcontainer/README.md @@ -1,19 +1,51 @@ # Dev Containers -These Dev Containers are based on the same docker images that are used to build -the *statically linked* Linux amd64 and arm64 binary releases of Stack. - -Those multi-arch (`linux/amd64`, `linux/arm64/v8`) docker images themselves are -based on Alpine Linux and contain *unofficial* builds of GHC. - -Only use the GHC available in the Dev Containers, because +Dev Containers provide all the required tools to contribute to a project. For +Stack these are: + +1. The [**Haskell Toolchain**](https://www.haskell.org/ghcup/install/#supported-tools) + * [GHC](https://www.haskell.org/ghc) + * [Cabal](https://cabal.readthedocs.io) + * [Stack](https://docs.haskellstack.org) + * [HLS](https://haskell-language-server.readthedocs.io) + :exclamation: Default Dev Container only +1. In addition + * [Git](https://git-scm.com) + * [HLint](https://hackage.haskell.org/package/hlint) + * [yamllint](https://yamllint.readthedocs.io) + * [ShellCheck](https://www.shellcheck.net) + * [hadolint](https://github.com/hadolint/hadolint) + +[VS Code](https://code.visualstudio.com) is used as IDE, with the following +extensions pre-installed: + +* [Haskell](https://marketplace.visualstudio.com/items?itemName=haskell.haskell) + :exclamation: Default Dev Container only +* [GitHub Pull Requests and Issues](https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-pull-request-github) +* [GitLens — Git supercharged](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens) + :information_source: Pinned to version 11.7.0 due to unsolicited AI content + in recent versions +* [Git Graph](https://marketplace.visualstudio.com/items?itemName=mhutchie.git-graph) +* [ShellCheck](https://marketplace.visualstudio.com/items?itemName=timonwong.shellcheck) +* [hadolint](https://marketplace.visualstudio.com/items?itemName=exiasr.hadolint) +* [Resource Monitor](https://marketplace.visualstudio.com/items?itemName=mutantdino.resourcemonitor) + +## Parent images + +Stack's Dev Containers are derived from the same docker images used to build +the *statically linked* Linux amd64 and arm64 binary releases of Stack itself. + +Those multi-arch (`linux/amd64`, `linux/arm64/v8`) *ghc-musl* images are based +on Alpine Linux (i.e. [musl libc](https://musl.libc.org) and +[BusyBox](https://www.busybox.net)) and contain *unofficial* builds of GHC. + +To make sure only the GHC available in the Dev Containers is used, flags +`--system-ghc` and `--no-install-ghc` are set system-wide in +`/etc/stack/config.yaml`. Reason: 1. the *official* GHC bindists for Alpine Linux (`x86_64`) are just too buggy. 2. there are currently (2023-09-05) no bindists for Alpine Linux (`AArch64`). -Therefore, flags `--system-ghc` and `--no-install-ghc` are set system-wide in -`/etc/stack/config.yaml`. - ## Usage For use with Github Codespaces, please follow the instruction at @@ -42,7 +74,7 @@ Codespaces) and is preconfigured. ### Using cabal -:information_source: Default Dev Container only. +:exclamation: Default Dev Container only. Command `cabal build` to build the `stack` executable. @@ -64,8 +96,8 @@ experimental project-level configuration with the appropriate Dev Container. The [Haskell Language Server](https://github.com/haskell/haskell-language-server) and the -[Haskell](https://marketplace.visualstudio.com/items?itemName=haskell.haskell) -extension are only available in the default Dev Container. +[Haskell extension](https://marketplace.visualstudio.com/items?itemName=haskell.haskell) +are only available in the default Dev Container. In order to use the Haskell extension, you must first configure the project for the build tool of your choice: From 0b5faf409c77ae44d3c547ad986ba3dca588bc20 Mon Sep 17 00:00:00 2001 From: Olivier Benz Date: Thu, 14 Sep 2023 09:30:30 +0200 Subject: [PATCH 05/12] Enable Oh My Zsh plugins --- .devcontainer/scripts/usr/local/bin/onCreateCommand.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.devcontainer/scripts/usr/local/bin/onCreateCommand.sh b/.devcontainer/scripts/usr/local/bin/onCreateCommand.sh index 6c53c127ad..bcc422df0b 100755 --- a/.devcontainer/scripts/usr/local/bin/onCreateCommand.sh +++ b/.devcontainer/scripts/usr/local/bin/onCreateCommand.sh @@ -37,5 +37,8 @@ if ! grep -q "cabal's bin" "$HOME/.zshrc"; then echo -e "\n# set PATH so it includes cabal's bin if it exists\nif [ -d \"\$HOME/.cabal/bin\" ] && [[ \"\$PATH\" != *\"\$HOME/.cabal/bin\"* ]] ; then\n PATH=\"\$HOME/.cabal/bin:\$PATH\"\nfi" >> "$HOME/.zshrc"; fi +# Enable Oh My Zsh plugins +sed -i "s/plugins=(git)/plugins=(cabal git pip stack)/g" "$HOME/.zshrc" + # Remove old .zcompdump files rm -f "$HOME"/.zcompdump* From 7bc03e4d9473726ddf758a4eac8a603b8ca40f0e Mon Sep 17 00:00:00 2001 From: Olivier Benz Date: Thu, 14 Sep 2023 09:32:30 +0200 Subject: [PATCH 06/12] Move README.md to doc/maintainers/devcontainers.md - Update mkdocs.yml --- .devcontainer/README.md | 126 ++-------------- .../screenshots => doc/img}/manageHLS.png | Bin doc/maintainers/devcontainers.md | 134 ++++++++++++++++++ mkdocs.yml | 2 + 4 files changed, 144 insertions(+), 118 deletions(-) rename {.devcontainer/assets/screenshots => doc/img}/manageHLS.png (100%) create mode 100644 doc/maintainers/devcontainers.md diff --git a/.devcontainer/README.md b/.devcontainer/README.md index 37a7b36d99..18cd829019 100644 --- a/.devcontainer/README.md +++ b/.devcontainer/README.md @@ -1,122 +1,12 @@ # Dev Containers -Dev Containers provide all the required tools to contribute to a project. For -Stack these are: +See [haskellstack.org](https://docs.haskellstack.org) \> Stack's code +(advanced) \> Maintainers \> Dev Containers or +[../doc/maintainers/devcontainers.md](../doc/maintainers/devcontainers.md) +for more information. -1. The [**Haskell Toolchain**](https://www.haskell.org/ghcup/install/#supported-tools) - * [GHC](https://www.haskell.org/ghc) - * [Cabal](https://cabal.readthedocs.io) - * [Stack](https://docs.haskellstack.org) - * [HLS](https://haskell-language-server.readthedocs.io) - :exclamation: Default Dev Container only -1. In addition - * [Git](https://git-scm.com) - * [HLint](https://hackage.haskell.org/package/hlint) - * [yamllint](https://yamllint.readthedocs.io) - * [ShellCheck](https://www.shellcheck.net) - * [hadolint](https://github.com/hadolint/hadolint) +## License -[VS Code](https://code.visualstudio.com) is used as IDE, with the following -extensions pre-installed: - -* [Haskell](https://marketplace.visualstudio.com/items?itemName=haskell.haskell) - :exclamation: Default Dev Container only -* [GitHub Pull Requests and Issues](https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-pull-request-github) -* [GitLens — Git supercharged](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens) - :information_source: Pinned to version 11.7.0 due to unsolicited AI content - in recent versions -* [Git Graph](https://marketplace.visualstudio.com/items?itemName=mhutchie.git-graph) -* [ShellCheck](https://marketplace.visualstudio.com/items?itemName=timonwong.shellcheck) -* [hadolint](https://marketplace.visualstudio.com/items?itemName=exiasr.hadolint) -* [Resource Monitor](https://marketplace.visualstudio.com/items?itemName=mutantdino.resourcemonitor) - -## Parent images - -Stack's Dev Containers are derived from the same docker images used to build -the *statically linked* Linux amd64 and arm64 binary releases of Stack itself. - -Those multi-arch (`linux/amd64`, `linux/arm64/v8`) *ghc-musl* images are based -on Alpine Linux (i.e. [musl libc](https://musl.libc.org) and -[BusyBox](https://www.busybox.net)) and contain *unofficial* builds of GHC. - -To make sure only the GHC available in the Dev Containers is used, flags -`--system-ghc` and `--no-install-ghc` are set system-wide in -`/etc/stack/config.yaml`. Reason: - -1. the *official* GHC bindists for Alpine Linux (`x86_64`) are just too buggy. -2. there are currently (2023-09-05) no bindists for Alpine Linux (`AArch64`). - -## Usage - -For use with Github Codespaces, please follow the instruction at -[Creating a codespace for a repository](https://docs.github.com/en/codespaces/developing-in-codespaces/creating-a-codespace-for-a-repository#creating-a-codespace-for-a-repository). - -For local/'remote host' usage with VS Code, please follow the instructions at -[Developing inside a Container](https://code.visualstudio.com/docs/devcontainers/containers). - -### Persistence - -Data in the following locations is persisted: - -1. The user's home directory (`/home/vscode`)[^1] -2. The Dev Container's workspace (`/workspaces`) - -[^1]: Alternatively for the root user (`/root`). Use with Docker/Podman in -*rootless mode*. - -This is accomplished either via a *volume* or *bind mount* (or *loop device* on -Codespaces) and is preconfigured. - -| **Codespaces: A 'Full Rebuild Container' resets the home directory!**
:information_source: This is never necessary unless you want exactly that. | -|:----------------------------------------------------------------------------------------------------------------------------------------------------| - -## Build Stack - -### Using cabal - -:exclamation: Default Dev Container only. - -Command `cabal build` to build the `stack` executable. - -Append `--flag=static` to build a *statically linked* `stack` executable that -can run on any Linux machine of the same architecture. - -### Using Stack - -Command `stack build` to build the `stack` executable. - -Append `--flag=stack:static` to build a *statically linked* `stack` executable -that can run on any Linux machine of the same architecture. - -Append `--stack-yaml stack-ghc-$GHC_VERSION.yaml` if you want to use an -experimental project-level configuration with the appropriate Dev Container. - -## Haskell Language Server (HLS) - -The -[Haskell Language Server](https://github.com/haskell/haskell-language-server) -and the -[Haskell extension](https://marketplace.visualstudio.com/items?itemName=haskell.haskell) -are only available in the default Dev Container. - -In order to use the Haskell extension, you must first configure the project for -the build tool of your choice: - -**Stack** - -Place the cradle ([hie.yaml](assets/cradles/stack/hie.yaml)) for Stack in the -root of the workspace: `cp -f .devcontainer/assets/cradles/stack/hie.yaml .` - -**Cabal** - -Place the cradle ([hie.yaml](assets/cradles/cabal/hie.yaml)) for Cabal in the -root of the workspace: `cp -f .devcontainer/assets/cradles/cabal/hie.yaml .` - -This should suffice to configure the HLS explicitly for `./Setup.hs` and each -of the buildable components in Stack's Cabal file. - -### Haskell extension - -Choose `Manually via PATH` when asked the following question: - -manageHLS +The code in this directory is not part of Stack (the software) and, with the +exceptions noted in [LICENSE](LICENSE), is distributed under the terms of the +MIT License. diff --git a/.devcontainer/assets/screenshots/manageHLS.png b/doc/img/manageHLS.png similarity index 100% rename from .devcontainer/assets/screenshots/manageHLS.png rename to doc/img/manageHLS.png diff --git a/doc/maintainers/devcontainers.md b/doc/maintainers/devcontainers.md new file mode 100644 index 0000000000..fc88955167 --- /dev/null +++ b/doc/maintainers/devcontainers.md @@ -0,0 +1,134 @@ +
+ +# Dev Containers + +Stack's Dev Containers provide the following tools: + +1. The [**Haskell Toolchain**](https://www.haskell.org/ghcup/install/#supported-tools): + * [GHC](https://www.haskell.org/ghc) + * [Cabal](https://cabal.readthedocs.io) + * Stack + * [HLS](https://haskell-language-server.readthedocs.io) (Default Dev Container only) +1. Additionally: + * [Git](https://git-scm.com) + * [HLint](https://hackage.haskell.org/package/hlint) + * [yamllint](https://yamllint.readthedocs.io) + * [ShellCheck](https://www.shellcheck.net) + * [hadolint](https://github.com/hadolint/hadolint) + +[VS Code](https://code.visualstudio.com) is used as IDE, with the following +extensions pre‑installed: + +* [Haskell](https://marketplace.visualstudio.com/items?itemName=haskell.haskell) + (Default Dev Container only) +* [GitHub Pull Requests and Issues](https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-pull-request-github) +* [GitLens — Git supercharged](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens) + * Pinned to version 11.7.0 due to unsolicited AI content + in recent versions +* [Git Graph](https://marketplace.visualstudio.com/items?itemName=mhutchie.git-graph) +* [ShellCheck](https://marketplace.visualstudio.com/items?itemName=timonwong.shellcheck) +* [hadolint](https://marketplace.visualstudio.com/items?itemName=exiasr.hadolint) +* [Resource Monitor](https://marketplace.visualstudio.com/items?itemName=mutantdino.resourcemonitor) + +## Parent images + +These Dev Containers are derived from the same docker images used to build the +*statically linked* Linux amd64 and arm64 binary releases of Stack itself. + +Those multi‑arch (`linux/amd64`, `linux/arm64/v8`) *ghc‑musl* images are based +on Alpine Linux (i.e. [musl libc](https://musl.libc.org) and +[BusyBox](https://www.busybox.net)) and contain *unofficial* builds of GHC. + +To make sure only the GHC available in the Dev Containers is used, flags +`--system-ghc` and `--no-install-ghc` are set +system‑wide in `/etc/stack/config.yaml`. Reason: + +1. the *official* GHC bindists for Alpine Linux (`x86_64`) are just too buggy. +2. there are currently (2023‑09‑05) no bindists for Alpine Linux (`AArch64`). + +## Usage + +For local/remote usage with VS Code, please follow the instructions at +[Developing inside a Container](https://code.visualstudio.com/docs/devcontainers/containers). + +For use with Github Codespaces, please follow the instruction at +[Creating a codespace for a repository](https://docs.github.com/en/codespaces/developing-in-codespaces/creating-a-codespace-for-a-repository#creating-a-codespace-for-a-repository). + +### Persistence + +Data in the following locations is persisted: + +1. The user's home directory (`/home/vscode`)[^1] +2. The Dev Container's workspace (`/workspaces`) + +[^1]: Alternatively for the root user (`/root`). Use with Docker/Podman in +*rootless mode*. + +This is accomplished either via a *volume* or *bind mount* (or *loop device* +on Codespaces) and is preconfigured. + +!!! info "Codespaces: A 'Full Rebuild Container' resets the home directory" + + This is never necessary unless you want exactly that. + +## Build Stack + +### Using cabal + +!!! info "Default Dev Container only" + +Command `cabal build` to build the `stack` executable. + +Append `--flag=static` to build a *statically linked* `stack` +executable that can run on any Linux machine of the same architecture. + +### Using Stack + +Command `stack build` to build the `stack` executable. + +Append `--flag=stack:static` to build a *statically linked* +`stack` executable that can run on any Linux machine of the same architecture. + +Append `--stack-yaml stack-ghc-$GHC_VERSION.yaml` if you want to +use an experimental project‑level configuration with the appropriate Dev +Container. + +## Haskell Language Server (HLS) + +The +[Haskell Language Server](https://github.com/haskell/haskell-language-server) +and the +[Haskell extension](https://marketplace.visualstudio.com/items?itemName=haskell.haskell) +are only available in the default Dev Container. In order to use the Haskell +extension, you must first configure the project for the build tool of your +choice. + + + +See [Stack's code (advanced) > Contributors > Contributor's guide: Haskell Language Server](../../CONTRIBUTING/#haskell-language-server) +for cradles (`hie.yaml` files) that should suffice to configure the HLS +explicitly for `./Setup.hs` and each of the buildable components in Stack's +Cabal file. + +### Haskell extension + +Choose `Manually via PATH` when asked the following question: + +Manage HLS> + +## Issues + +If there is a problem with a Dev Container, please +[open an issue](https://github.com/benz0li/ghc-musl/issues/new) at its +[parent images](#parent-images)' repository at +[https://github.com/benz0li/ghc-musl](https://github.com/benz0li/ghc-musl). diff --git a/mkdocs.yml b/mkdocs.yml index 196871cf0d..c22571c99c 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -98,6 +98,7 @@ nav: - Maintainer team process: maintainers/team_process.md - Add GHC version: maintainers/ghc.md - Docker images: maintainers/docker.md + - Dev Containers: maintainers/devcontainers.md - Upgrading MSYS2: maintainers/msys.md - Upgrading 7-Zip: maintainers/7zip.md - HaskellStack.org: maintainers/haskellstack.org.md @@ -108,6 +109,7 @@ nav: markdown_extensions: - admonition - attr_list +- footnotes - pymdownx.details - pymdownx.emoji: emoji_index: !!python/name:materialx.emoji.twemoji From 7ab34d1fe2741f34dd14a119272b0204d5444a6b Mon Sep 17 00:00:00 2001 From: Olivier Benz Date: Thu, 14 Sep 2023 09:33:03 +0200 Subject: [PATCH 07/12] Update CONTRIBUTING.md --- CONTRIBUTING.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1b1219df5f..a229375e42 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -692,6 +692,22 @@ Stack can be built with Stack (which is recommended) or with Cabal (the tool). A cradle is not committed to Stack's repository because it imposes a choice of build tool. +## Dev Containers + +A [Development Container](https://containers.dev) (or Dev Container for short) +allows you to use a container as a full‑featured development environment. + +You can run Dev Containers locally/remotely (with VS Code) or create a +[Codespace](https://github.com/features/codespaces) for a branch in a +repository to develop online. + +Stack's default Dev Container is intended for use with its default +project‑level configuration (`stack.yaml`). But there are also Dev Containers +for the experimental project‑level configurations. + +See [Stack's code (advanced) > Maintainers > Dev Containers](../maintainers/devcontainers) +for more information. + ## Slack channel If you're making deep changes and real-time communication with the Stack team From 25698009de651a01f1c42f9a1c6ca98a771ef37e Mon Sep 17 00:00:00 2001 From: Olivier Benz Date: Thu, 14 Sep 2023 09:34:27 +0200 Subject: [PATCH 08/12] Fix doc/maintainers/devcontainers.md --- doc/maintainers/devcontainers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/maintainers/devcontainers.md b/doc/maintainers/devcontainers.md index fc88955167..bf03aebecc 100644 --- a/doc/maintainers/devcontainers.md +++ b/doc/maintainers/devcontainers.md @@ -124,7 +124,7 @@ Cabal file. Choose `Manually via PATH` when asked the following question: -Manage HLS> +Manage HLS ## Issues From 564662e52b5baeeaab7f55200b210c5000eef7a7 Mon Sep 17 00:00:00 2001 From: Olivier Benz Date: Thu, 14 Sep 2023 09:39:10 +0200 Subject: [PATCH 09/12] Update ChangeLog.md --- ChangeLog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index 67679f438d..d16d3b38c0 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -7,6 +7,8 @@ Release notes: * Further to the release notes for Stack 2.3.1, the `-static` suffix has been removed from the statically linked Linux/x86_64 binaries. * The binaries for Linux/Aarch64 are now statically linked. +* Dev Containers are now available for this repository. See + [#6228](https://github.com/commercialhaskell/stack/pull/6228). **Changes since v2.11.1:** From 7ebb38196408ccf2111b8d9b44f4a8920298eccb Mon Sep 17 00:00:00 2001 From: Olivier Benz Date: Thu, 14 Sep 2023 10:07:02 +0200 Subject: [PATCH 10/12] Minor formatting changes --- .devcontainer/README.md | 4 ++-- doc/maintainers/devcontainers.md | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.devcontainer/README.md b/.devcontainer/README.md index 18cd829019..d8704bfde8 100644 --- a/.devcontainer/README.md +++ b/.devcontainer/README.md @@ -1,7 +1,7 @@ # Dev Containers -See [haskellstack.org](https://docs.haskellstack.org) \> Stack's code -(advanced) \> Maintainers \> Dev Containers or +See [haskellstack.org](https://docs.haskellstack.org): Stack's code (advanced) +\> Maintainers \> Dev Containers or [../doc/maintainers/devcontainers.md](../doc/maintainers/devcontainers.md) for more information. diff --git a/doc/maintainers/devcontainers.md b/doc/maintainers/devcontainers.md index bf03aebecc..e16f975b2f 100644 --- a/doc/maintainers/devcontainers.md +++ b/doc/maintainers/devcontainers.md @@ -67,15 +67,18 @@ Data in the following locations is persisted: This is accomplished either via a *volume* or *bind mount* (or *loop device* on Codespaces) and is preconfigured. -!!! info "Codespaces: A 'Full Rebuild Container' resets the home directory" +!!! info + **Codespaces: A 'Full Rebuild Container' resets the home directory.** This is never necessary unless you want exactly that. ## Build Stack ### Using cabal -!!! info "Default Dev Container only" +!!! info + + Default Dev Container only. Command `cabal build` to build the `stack` executable. From fec4bfb772e311da50c3f89d194f40d3fe90d354 Mon Sep 17 00:00:00 2001 From: Olivier Benz Date: Thu, 14 Sep 2023 10:49:09 +0200 Subject: [PATCH 11/12] Update doc/maintainers/devcontainers.md - Add info about PATH and precedence of executables --- doc/maintainers/devcontainers.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/doc/maintainers/devcontainers.md b/doc/maintainers/devcontainers.md index e16f975b2f..675433bf58 100644 --- a/doc/maintainers/devcontainers.md +++ b/doc/maintainers/devcontainers.md @@ -4,7 +4,8 @@ Stack's Dev Containers provide the following tools: -1. The [**Haskell Toolchain**](https://www.haskell.org/ghcup/install/#supported-tools): +1. The [**Haskell Toolchain**](https://www.haskell.org/ghcup/install/#supported-tools): + (installed at `/usr/local/bin`[^1]) * [GHC](https://www.haskell.org/ghc) * [Cabal](https://cabal.readthedocs.io) * Stack @@ -16,6 +17,14 @@ Stack's Dev Containers provide the following tools: * [ShellCheck](https://www.shellcheck.net) * [hadolint](https://github.com/hadolint/hadolint) +[^1]: `PATH=$HOME/.cabal/bin:$HOME/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin` + +!!! info + + Executables installed with Cabal (at `$HOME/.cabal/bin`) or Stack/Pip (at + `$HOME/.local/bin`) take precedence over the same executable installed at + `/usr/local/sbin`, `/usr/local/bin`, etc. + [VS Code](https://code.visualstudio.com) is used as IDE, with the following extensions pre‑installed: @@ -58,10 +67,10 @@ For use with Github Codespaces, please follow the instruction at Data in the following locations is persisted: -1. The user's home directory (`/home/vscode`)[^1] +1. The user's home directory (`/home/vscode`)[^2] 2. The Dev Container's workspace (`/workspaces`) -[^1]: Alternatively for the root user (`/root`). Use with Docker/Podman in +[^2]: Alternatively for the root user (`/root`). Use with Docker/Podman in *rootless mode*. This is accomplished either via a *volume* or *bind mount* (or *loop device* From 20b14c0df2dd6612e07316393407f5687ff29b4b Mon Sep 17 00:00:00 2001 From: Olivier Benz Date: Thu, 14 Sep 2023 11:49:45 +0200 Subject: [PATCH 12/12] Install terminal multiplexers screen and tmux - Enable Oh My Zsh plugins screen tmux vscode --- .devcontainer/GHC.Dockerfile | 2 ++ .devcontainer/scripts/usr/local/bin/onCreateCommand.sh | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.devcontainer/GHC.Dockerfile b/.devcontainer/GHC.Dockerfile index 9d2067eef7..3dbe9c627f 100644 --- a/.devcontainer/GHC.Dockerfile +++ b/.devcontainer/GHC.Dockerfile @@ -40,6 +40,8 @@ RUN sysArch="$(uname -m)" \ && apk add --no-cache yamllint \ ## Install pip && apk add --no-cache py3-pip \ + ## Install terminal multiplexers + && apk add --no-cache screen tmux \ ## Install hadolint && case "$sysArch" in \ x86_64) tarArch="x86_64" ;; \ diff --git a/.devcontainer/scripts/usr/local/bin/onCreateCommand.sh b/.devcontainer/scripts/usr/local/bin/onCreateCommand.sh index bcc422df0b..88d76094d0 100755 --- a/.devcontainer/scripts/usr/local/bin/onCreateCommand.sh +++ b/.devcontainer/scripts/usr/local/bin/onCreateCommand.sh @@ -38,7 +38,7 @@ if ! grep -q "cabal's bin" "$HOME/.zshrc"; then fi # Enable Oh My Zsh plugins -sed -i "s/plugins=(git)/plugins=(cabal git pip stack)/g" "$HOME/.zshrc" +sed -i "s/plugins=(git)/plugins=(cabal git pip stack screen tmux vscode)/g" "$HOME/.zshrc" # Remove old .zcompdump files rm -f "$HOME"/.zcompdump*