aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorEgor Tensin <Egor.Tensin@gmail.com>2020-05-06 15:44:59 +0300
committerEgor Tensin <Egor.Tensin@gmail.com>2020-05-06 16:21:53 +0300
commitb05b07345084c415e6da431a5e247ac9afa09065 (patch)
tree8e1b98cef39de780e07fd0108e8f3aebdfd39d04
parentset up GitHub Actions (diff)
downloadjekyll-docker-b05b07345084c415e6da431a5e247ac9afa09065.tar.gz
jekyll-docker-b05b07345084c415e6da431a5e247ac9afa09065.zip
fix GitHub action tests
That's a messy commit, but it required a lot of changes to get everything right. * Docker: create user jekyll with UID/GID that match the user that built the image (for seamless writes to /project). * Docker: run the container by the current user for the same purpose. * Docker: add an ENTRYPOINT to drop root privileges & check if the running user is the same as the one who built the image. * Jekyll: use --drafts. * Makefile: add docker/logs. As a side note, Docker + non-root users + bind mounts are a pain, I even wrote a blog post to make sense of it all: https://egor-tensin.github.io/blog/2020/05/06/docker-bind-mounts.html
-rw-r--r--.dockerignore1
-rw-r--r--.gitattributes2
-rw-r--r--Dockerfile.base68
-rw-r--r--Dockerfile.project13
-rw-r--r--Makefile24
-rw-r--r--docker-compose.yml7
-rwxr-xr-xdocker-entrypoint.sh32
7 files changed, 112 insertions, 35 deletions
diff --git a/.dockerignore b/.dockerignore
index 5aded0c..6d35443 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1,3 +1,4 @@
*
!/Makefile
+!/docker-entrypoint.sh
diff --git a/.gitattributes b/.gitattributes
index 176a458..d76765e 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1 +1,3 @@
* text=auto
+
+*.sh text eol=lf
diff --git a/Dockerfile.base b/Dockerfile.base
index fbb7e5c..f56a9b3 100644
--- a/Dockerfile.base
+++ b/Dockerfile.base
@@ -7,32 +7,58 @@ ENV DEBIAN_FRONTEND=noninteractive
RUN apt update -yq && \
apt install -yq --no-install-recommends \
build-essential \
- ca-certificates wget \
+ ca-certificates gnupg wget \
sudo \
nano vim
-# Creating regular user 'developer':
-ARG JEKYLL_USER=developer
-ENV JEKYLL_USER="$JEKYLL_USER"
-RUN addgroup "$JEKYLL_USER" && \
- adduser --disabled-password --gecos "" --ingroup "$JEKYLL_USER" --home "/home/$JEKYLL_USER" "$JEKYLL_USER" && \
- addgroup "$JEKYLL_USER" sudo && \
+# Install gosu (better sudo, basically).
+ENV GOSU_VERSION 1.12
+RUN DPKG_ARCH="$( dpkg --print-architecture | awk -F- '{ print $NF }' )" && \
+ wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$DPKG_ARCH" && \
+ wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$DPKG_ARCH.asc" && \
+ export GNUPGHOME="$( mktemp -d )" && \
+ gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4 && \
+ gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu && \
+ gpgconf --kill all && \
+ rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc && \
+ chmod +x /usr/local/bin/gosu && \
+ gosu nobody true
+
+ENV DEFAULT_UID=999
+ENV DEFAULT_GID="$DEFAULT_UID"
+
+ARG JEKYLL_UID
+ARG JEKYLL_GID
+RUN test -n "$JEKYLL_UID" && test -n "$JEKYLL_GID"
+ENV JEKYLL_UID="$JEKYLL_UID"
+ENV JEKYLL_GID="$JEKYLL_GID"
+
+RUN if [ "$JEKYLL_UID" = 0 ]; then JEKYLL_UID="$DEFAULT_UID"; fi && \
+ if [ "$JEKYLL_GID" = 0 ]; then JEKYLL_GID="$DEFAULT_GID"; fi && \
+ addgroup --gid "${JEKYLL_GID:-$DEFAULT_GID}" jekyll && \
+ adduser \
+ --disabled-password \
+ --gecos '' \
+ --home /home/jekyll \
+ --ingroup jekyll \
+ --uid "${JEKYLL_UID:-$DEFAULT_UID}" \
+ jekyll && \
+ addgroup jekyll sudo && \
echo -e '%sudo ALL=(ALL) NOPASSWD:ALL\nDefaults env_keep += "HOME"' >> /etc/sudoers
-USER "$JEKYLL_USER"
-ENV PATH="/home/$JEKYLL_USER/.local/bin:$PATH"
+RUN mkdir /utils && chown jekyll /utils
+WORKDIR /utils
+COPY --chown=jekyll:jekyll ["Makefile", "./"]
-ENV MAKEFILE_DIR="/utils"
-RUN sudo mkdir -p -- "$MAKEFILE_DIR" && \
- sudo chown -- "$JEKYLL_USER:$JEKYLL_USER" "$MAKEFILE_DIR"
-WORKDIR "$MAKEFILE_DIR"
+ENV PATH="/home/jekyll/.local/bin:$PATH"
-COPY --chown="$JEKYLL_USER:$JEKYLL_USER" ["Makefile", "./"]
+RUN gosu jekyll make ruby-install && \
+ gosu jekyll make ruby-install/clean && \
+ gosu jekyll make ruby && \
+ gosu jekyll make chruby && \
+ gosu jekyll make chruby/.bashrc && \
+ gosu jekyll make chruby/clean && \
+ gosu jekyll make bundler
-RUN make ruby-install && \
- make ruby-install/clean && \
- make ruby && \
- make chruby && \
- sudo make chruby/profile.d && \
- make chruby/clean && \
- make bundler
+COPY ["docker-entrypoint.sh", "/"]
+ENTRYPOINT ["/docker-entrypoint.sh"]
diff --git a/Dockerfile.project b/Dockerfile.project
index bae05e6..c9e0b50 100644
--- a/Dockerfile.project
+++ b/Dockerfile.project
@@ -1,10 +1,11 @@
FROM jekyll_base
-ENV PROJECT_DIR="/project"
-RUN sudo mkdir -p -- "$PROJECT_DIR" && \
- sudo chown -- "$JEKYLL_USER:$JEKYLL_USER" "$PROJECT_DIR"
+ENV PROJECT_DIR=/project
+RUN mkdir /project && chown jekyll /project
+VOLUME /project
+
+COPY --chown=jekyll:jekyll ["Gemfile", "Gemfile.lock", "/project/"]
+RUN gosu jekyll make dependencies
+COPY --chown=jekyll:jekyll [".", "/project/"]
-COPY --chown="$JEKYLL_USER:$JEKYLL_USER" ["Gemfile", "Gemfile.lock", "$PROJECT_DIR/"]
-RUN make dependencies
-COPY --chown="$JEKYLL_USER:$JEKYLL_USER" [".", "$PROJECT_DIR/"]
CMD make jekyll/serve
diff --git a/Makefile b/Makefile
index df15d22..fe2a34f 100644
--- a/Makefile
+++ b/Makefile
@@ -56,17 +56,21 @@ chruby/uninstall:
chruby/clean:
rm -rf -- '$(chruby_archive)' '$(chruby_dir)'
-define chruby_profile_d
+define chruby_source
if [ -n "$$BASH_VERSION" ] || [ -n "$$ZSH_VERSION" ]; then
[ -r '$(chruby_sh)' ] && source '$(chruby_sh)'
[ -r '$(auto_sh)' ] && source '$(auto_sh)'
fi
endef
-export chruby_profile_d
+export chruby_source
+
+.PHONY: chruby/.bashrc
+chruby/.bashrc:
+ echo "$$chruby_source" >> ~/.bashrc
.PHONY: chruby/profile.d
chruby/profile.d:
- echo "$$chruby_profile_d" > /etc/profile.d/chruby.sh
+ echo "$$chruby_source" > /etc/profile.d/chruby.sh
.PHONY: chruby/profile.d/clean
chruby/profile.d/clean:
@@ -97,11 +101,15 @@ deps/update: dependencies/update
.PHONY: jekyll/build
jekyll/build:
- $(jekyll) build --config _config.yml,_config_dev.yml
+ $(jekyll) build --drafts --config _config.yml,_config_dev.yml
.PHONY: jekyll/serve
jekyll/serve:
- $(jekyll) serve --host 0.0.0.0 --config _config.yml,_config_dev.yml
+ $(jekyll) serve --drafts --config _config.yml,_config_dev.yml --host 0.0.0.0
+
+JEKYLL_UID ?= $(shell id -u)
+JEKYLL_GID ?= $(shell id -g)
+export JEKYLL_UID JEKYLL_GID
# Not an absolute path, cause you know, Windows (more specifically, Cygwin +
# native Windows docker-compose).
@@ -109,12 +117,16 @@ docker_compose := cd -- '$(makefile_dir)' && docker-compose --env-file ./.env
.PHONY: docker/build
docker/build:
- $(docker_compose) build --force-rm
+ $(docker_compose) build --force-rm --build-arg 'JEKYLL_UID=$(JEKYLL_UID)' --build-arg 'JEKYLL_GID=$(JEKYLL_GID)'
.PHONY: docker/up
docker/up:
$(docker_compose) up -d project
+.PHONY: docker/logs
+docker/logs:
+ $(docker_compose) logs
+
.PHONY: docker/down
docker/down:
$(docker_compose) down -v
diff --git a/docker-compose.yml b/docker-compose.yml
index 97aa312..5ed8a86 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -20,13 +20,15 @@ services:
context: .
dockerfile: Dockerfile.base
args:
- JEKYLL_USER: developer
+ - JEKYLL_UID
+ - JEKYLL_GID
+ user: "$JEKYLL_UID:$JEKYLL_GID"
project:
build:
context: "${PROJECT_DIR:-..}"
# Dockerfile outside of the build context.
# This is supposedly supported by Docker since 18.03, but I couldn't find
- # a less hacky way to do it.
+ # a less hacky way to do it in docker-compose.
# Source: https://github.com/docker/compose/issues/4926.
dockerfile: "$PWD/Dockerfile.project"
depends_on:
@@ -35,5 +37,6 @@ services:
- base
ports:
- 4000:4000
+ user: "$JEKYLL_UID:$JEKYLL_GID"
volumes:
- "${PROJECT_DIR:-..}:/project"
diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh
new file mode 100755
index 0000000..4b4fb4d
--- /dev/null
+++ b/docker-entrypoint.sh
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+
+# We make sure that the container is run by the same user as the one who built
+# the image (so that /project is seamlessly writable).
+# Unless, of course, the image was built by root, in which case we fall back
+# to a custom user with UID 999.
+
+set -o errexit -o nounset -o pipefail
+
+echo 'User info:'
+id
+uid="$( id -u )"
+gid="$( id -g )"
+
+if [ "$uid" = 0 ]; then
+ echo 'Going to run as jekyll instead of root, fixing /project permissions...'
+ chown -R -- jekyll:jekyll /project
+ exec gosu jekyll "$0" "$@"
+fi
+
+if [ "$uid" != "$JEKYLL_UID" ] && [ "$JEKYLL_UID" != 0 ]; then
+ echo "User jekyll was created with ID $JEKYLL_UID, are you sure you want to run the container with UID $uid?"
+ exit 1
+fi
+
+if [ "$gid" != "$JEKYLL_GID" ] && [ "$JEKYLL_GID" != 0 ]; then
+ echo "Group jekyll was created with ID $JEKYLL_GID, are you sure you want to run the container with GID $gid?"
+ exit 1
+fi
+
+echo "The container is running with UID $uid and GID $gid, just as planned..."
+exec "$@"