From 6c74b07740b13a229bba05d7368c5c1611e75d3b Mon Sep 17 00:00:00 2001 From: Ben Mares Date: Mon, 14 Mar 2022 08:08:07 +0100 Subject: [PATCH] Initial commit --- .github/workflows/publish_image.yml | 54 ++++++++++ Dockerfile | 158 ++++++++++++++++++++++++++++ LICENSE | 21 ++++ README.md | 11 ++ dev-entrypoint.sh | 99 +++++++++++++++++ 5 files changed, 343 insertions(+) create mode 100644 .github/workflows/publish_image.yml create mode 100644 Dockerfile create mode 100644 LICENSE create mode 100644 README.md create mode 100755 dev-entrypoint.sh diff --git a/.github/workflows/publish_image.yml b/.github/workflows/publish_image.yml new file mode 100644 index 0000000..2ee1b0a --- /dev/null +++ b/.github/workflows/publish_image.yml @@ -0,0 +1,54 @@ +name: Build and publish Docker image + +on: + # Run it manually for now + workflow_dispatch: + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} # user/reponame + + +jobs: + publish: + runs-on: ubuntu-latest + + # Set permissions for GitHub token + # + permissions: + contents: read + packages: write + + steps: + - name: Checkout source + uses: actions/checkout@a12a3943b4bdde767164f792f33f40b04645d846 # v3.0.0 + + - name: Setup Docker buildx + uses: docker/setup-buildx-action@94ab11c41e45d028884a99163086648e898eed25 # v1.6.0 + + - name: Prepare metadata + id: meta + uses: docker/metadata-action@e5622373a38e60fb6d795a4421e56882f2d7a681 # v3.6.2 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=sha,enable=true,prefix=git- + type=raw,value=latest + + - name: Log into registry ${{ env.REGISTRY }} + uses: docker/login-action@dd4fa0671be5250ee6f50aedf4cb05514abda2c7 # v1.14.1 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build and push Docker image + id: docker_build + uses: docker/build-push-action@7f9d37fa544684fb73bfe4835ed7214c255ce02b # v2.9.0 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest + cache-to: type=inline diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ecb8bc0 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,158 @@ +# To set up our environment, we start from Micromamba's base image. The latest tags +# can be found here: +# For reproducibility, we should pin to a particular Git tag (not a micromamba version). + +# For more info, about micromamba, see: +# . + +ARG BASE_IMAGE=mambaorg/micromamba:git-9a46999 + +# The folder to use as a workspace. The project should be mounted here. +ARG DEV_WORK_DIR=/work + +FROM ${BASE_IMAGE} + +# Grab Docker, buildx, and Docker Compose +COPY --from=docker:dind /usr/local/bin/docker /usr/local/bin/docker +COPY --from=docker/buildx-bin /buildx /usr/libexec/docker/cli-plugins/docker-buildx +COPY --from=docker/compose /usr/local/bin/docker-compose /usr/local/bin/docker-compose + +USER root + +# Reallow installing manpages +# (The Docker image is minified so manpages aren't included.) +RUN : \ + && sed -i '/path-exclude \/usr\/share\/man/d' /etc/dpkg/dpkg.cfg.d/docker \ + && sed -i '/path-exclude \/usr\/share\/groff/d' /etc/dpkg/dpkg.cfg.d/docker \ + ; + +# Install some useful OS packages +RUN apt-get update && apt-get install -y --no-install-recommends --reinstall \ + # + # manpages + man-db \ + # + # reinstall coreutils to get manpages for the standard commands (e.g. cp) + coreutils \ + # + # runs commands as superuser + sudo \ + # + # tab autocompletion for bash + bash-completion \ + # + # pagination + less \ + # + # version control + git \ + patch \ + # + # Git Large File Storage + git-lfs \ + # + # simple text editor + nano \ + # + # parses JSON on the bash command line + jq \ + # + # GNU Privacy Guard + gnupg2 \ + # + # ssh + openssh-client \ + # + # determines file types + file \ + # + # process monitor + htop \ + # + # compression + zip \ + unzip \ + p7zip-full \ + # + # downloads files + curl \ + wget \ + # + # lists open files + lsof \ + # + # ping and ip utilities + iputils-ping \ + iproute2 \ + # + # ifconfig, netstat, etc. + net-tools \ + # + # nslookup and dig (for looking up hostnames) + dnsutils \ + # + # socket cat for bidirectional byte streams + socat \ + # + # TCP terminal + telnet \ + # + # used by VS Code LiveShare extension + libicu67 \ + # + && rm -rf /var/lib/apt/lists/* + + +# Grant sudo to the user. +RUN usermod -aG sudo "${MAMBA_USER}" \ + && echo "%sudo ALL=(ALL:ALL) NOPASSWD:ALL" > /etc/sudoers.d/grant-to-sudo-group + +# Install docker-compose +RUN : \ + && COMPOSE_VERSION=$(git ls-remote https://github.com/docker/compose | grep refs/tags | grep -oE "v[0-9]+\.[0-9]+\.[0-9]+$" | sort --version-sort | tail -n 1) \ + && sh -c "curl -L https://github.com/docker/compose/releases/download/${COMPOSE_VERSION}/docker-compose-$(uname -s)-$(uname -m) > /usr/local/bin/docker-compose" \ + && chmod +x /usr/local/bin/docker-compose \ + && COMPOSE_SWITCH_VERSION=$(git ls-remote https://github.com/docker/compose-switch | grep refs/tags | grep -oE "v[0-9]+\.[0-9]+\.[0-9]+$" | sort --version-sort | tail -n 1) \ + && sh -c "curl -L https://github.com/docker/compose/releases/download/${COMPOSE_SWITCH_VERSION}/docker-compose-$(uname -s)-$(uname -m) > /usr/local/bin/compose-switch" \ + && chmod +x /usr/local/bin/compose-switch \ + ; + +# Install bash completions +RUN : \ + && mkdir -p /etc/bash_completion.d \ + && sh -c "curl -L https://raw.githubusercontent.com/docker/compose/1.29.2/contrib/completion/bash/docker-compose > /etc/bash_completion.d/docker-compose" \ + && sh -c "curl -L https://raw.githubusercontent.com/docker/cli/v20.10.13/contrib/completion/bash/docker > /etc/bash_completion.d/docker" \ + && sh -c "curl -L https://raw.githubusercontent.com/git/git/v2.35.1/contrib/completion/git-completion.bash > /etc/bash_completion.d/git" \ + ; + +# Make sure we own the working directory. +ARG DEV_WORK_DIR +RUN : \ + && mkdir -p "${DEV_WORK_DIR}" \ + && chown "$MAMBA_USER:$MAMBA_USER" "${DEV_WORK_DIR}" + +# Set the working directory. +ENV DEV_WORK_DIR="${DEV_WORK_DIR}" +WORKDIR "${DEV_WORK_DIR}" + +USER $MAMBA_USER + +# Sane defaults for Git +RUN : \ + # Switch default editor from vim to nano + && git config --global core.editor nano \ + # Prevent unintentional merges + # + && git config --global pull.ff only \ + # Use default branch name "main" instead of "master" + && git config --global init.defaultBranch main \ + # Initialize Git LFS + && git lfs install --skip-repo \ + ; + +# Symlink the .vscode-server directory to the user's home directory. +RUN ln -s "/mnt/.vscode-server" "/home/${MAMBA_USER}/.vscode-server" + +# Stack a development entrypoint after the default micromamba-docker entrypoint. +COPY dev-entrypoint.sh /usr/local/bin/_dev-entrypoint.sh +ENTRYPOINT ["/usr/local/bin/_entrypoint.sh", "/usr/local/bin/_dev-entrypoint.sh"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b77e4c0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Ben Mares + +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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..0c446f9 --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +# micromamba-devcontainer + +A micromamba-based VS Code development container. + +## Introduction + +Coming soon... + +## Usage + +See this [example](https://github.com/maresb/micromamba-devcontainer-example). diff --git a/dev-entrypoint.sh b/dev-entrypoint.sh new file mode 100755 index 0000000..9e390c6 --- /dev/null +++ b/dev-entrypoint.sh @@ -0,0 +1,99 @@ +#!/usr/bin/env bash + +# This script runs whenever the container starts before the CMD is executed. + +# Don't use strict mode so that we run through to the end. (commented out) +# set -euo pipefail + +set -x + +# For some reason, VS Code doesn't display this output when starting the dev container. +# Log it to a file so that we can check it if needed. +# Redirect stdout ( > ) into a named pipe ( >() ) running "tee" +# + +# Assign original stdout and stderr to file descriptors so that we can restore them later. +exec {stdout_alias}>&1 {stderr_alias}>&2 + +# Redirect stdout and stderr to a file. +exec 1> >(sudo tee -i /var/log/dev-entrypoint.log) +exec 2>&1 + + + +echo "Initialize the dev container..." + +# Make sure the mounted volume is fully accessible to all users. +if [[ -d /mnt/.vscode-server ]]; then + sudo chmod a+rwx /mnt/.vscode-server +fi + +# Set the user and group +# +if [[ ! -z "${NEW_MAMBA_USER}" ]] && [[ "${MAMBA_USER}" != "${NEW_MAMBA_USER}" ]]; then + NEW_MAMBA_USER_ID="$(stat --format=%u "${DEV_WORK_DIR}")" + NEW_MAMBA_USER_GID="$(stat --format=%u "${DEV_WORK_DIR}")" + sudo usermod "--login=${NEW_MAMBA_USER}" "--home=/home/${NEW_MAMBA_USER}" \ + --move-home "-u ${NEW_MAMBA_USER_ID}" "${MAMBA_USER}" + sudo groupmod "--new-name=${NEW_MAMBA_USER}" "-g ${NEW_MAMBA_USER_GID}" "${MAMBA_USER}" + # Update the expected value of MAMBA_USER for the _entrypoint.sh consistency check. + echo "${NEW_MAMBA_USER}" | sudo tee "/etc/arg_mamba_user" > /dev/null + export MAMBA_USER="${NEW_MAMBA_USER}" +fi + +# Set up the Docker if mounted. +if [[ -S /var/run/docker.sock ]] ; then + # Get the GID of the "docker" group. + docker_gid=`stat --format=%g /var/run/docker.sock` + if [ -z "$docker_gid" ] ; then + echo "No mounted Docker socket found." + else + # Create docker group if it doesn't already exist. + [ $(getent group docker) ] || sudo groupadd docker + + # Try to create a new group with this GID. This will fail if the + # GID is already assigned. + sudo groupmod -g $docker_gid docker + + # Add user to the "docker" group. + sudo usermod -a -G docker "${MAMBA_USER}" + fi +fi + +# Customize git. +if [[ ! -z "${GIT_NAME}" ]]; then + sudo --user="${MAMBA_USER}" -- git config --global user.name "${GIT_NAME}" +else + echo 'GIT_NAME is undefined.' +fi +if [[ ! -z "${GIT_EMAIL}" ]]; then + sudo --user="${MAMBA_USER}" -- git config --global user.email "${GIT_EMAIL}" +else + echo 'GIT_EMAIL is undefined.' +fi +if [[ -x "${MAMBA_ROOT_PREFIX}"/bin/pre-commit ]]; then + # pre-commit is installed, so configure it to always run. + # + + # Get home of user from username + mamba_home="$(getent passwd "$MAMBA_USER" | cut -d: -f6)" + + template_dir="${mamba_home}/.git-template" + sudo --user="${MAMBA_USER}" -- git config --global \ + init.templateDir "${template_dir}" + sudo --user="${MAMBA_USER}" -- "${MAMBA_ROOT_PREFIX}"/bin/pre-commit \ + init-templatedir "${template_dir}" +else + echo "pre-commit not found, skipping pre-commit init" +fi + +# Restore the original stdout and stderr. +exec 2>&${stderr_alias} 1>&${stdout_alias} + +# Pass execution to the CMD. +if [ ${#@} -gt 0 ]; then + exec "${@}" +else + # The arguments list is empty, so default to Bash. + exec "/bin/bash" +fi