From 88c52db53c2b010cdc1fc8933d0ca8f54a758080 Mon Sep 17 00:00:00 2001
From: Yassine Doghri <yassine@doghri.fr>
Date: Mon, 24 May 2021 10:08:15 +0000
Subject: [PATCH] build(docker): optimize Dockerfile and setup vscode
 devcontainer with all required dependencies

- fix: set mediaBaseURL as baseURL if not defined
- update dev documentation seting up a dev environment
---
 .devcontainer/devcontainer.json       |  7 ++-
 .devcontainer/docker-compose.yml      | 10 ++++
 Dockerfile                            | 84 +++++++++++++--------------
 app/Controllers/InstallController.php |  2 +-
 app/Helpers/media_helper.php          |  8 ++-
 docker-compose.yml                    | 11 ++--
 docs/setup-development.md             | 65 +++++++++++++++++----
 7 files changed, 123 insertions(+), 64 deletions(-)
 create mode 100644 .devcontainer/docker-compose.yml

diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 621ed45dbc..84e45dd03e 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -2,10 +2,13 @@
 // https://github.com/microsoft/vscode-dev-containers/tree/v0.117.1/containers/docker-existing-dockerfile
 {
   "name": "Castopod Host dev",
-  "dockerComposeFile": ["../docker-compose.yml"],
+  "dockerComposeFile": ["../docker-compose.yml", "./docker-compose.yml"],
   "service": "app",
   "workspaceFolder": "/castopod-host",
-  "postCreateCommand": "cron && php spark serve --host 0.0.0.0",
+  "postCreateCommand": "composer install && npm install && npm run build:dev",
+  "postStartCommand": "crontab ./crontab && cron && php spark serve --host 0.0.0.0",
+  "postAttachCommand": "crontab ./crontab && service cron reload",
+  "shutdownAction": "stopCompose",
   "settings": {
     "terminal.integrated.defaultProfile.linux": "/bin/bash",
     "editor.formatOnSave": true,
diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml
new file mode 100644
index 0000000000..c7323a5b52
--- /dev/null
+++ b/.devcontainer/docker-compose.yml
@@ -0,0 +1,10 @@
+version: "3"
+services:
+  app:
+    volumes:
+      # Mounts the project folder to '/workspace'. While this file is in .devcontainer,
+      # mounts are relative to the first file in the list, which is a level up.
+      - .:/castopod-host:cached
+
+    # Overrides default command so things don't shut down after the process ends.
+    command: /bin/sh -c "while sleep 1000; do :; done"
diff --git a/Dockerfile b/Dockerfile
index 9c58eae0f9..9eb3536d74 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,10 +1,9 @@
 ####################################################
 # Castopod Host development Docker file
 ####################################################
-# NOT optimized for production
+# ⚠️ NOT optimized for production
 # should be used only for development purposes
-####################################################
-
+#---------------------------------------------------
 FROM php:8.0-fpm
 
 LABEL maintainer="Yassine Doghri <yassine@doghri.fr>"
@@ -13,50 +12,51 @@ COPY . /castopod-host
 WORKDIR /castopod-host
 
 # Install composer
-COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
-
-# Install latest npm
-RUN apt-get update && \
-    apt-get install -y --no-install-recommends gnupg && \
-    curl -fsSL https://deb.nodesource.com/setup_lts.x | bash - && \
-    apt-get update && \
-    apt-get install -y --no-install-recommends nodejs && \
-    npm install --global npm
-
-# Install git + vim
-RUN apt-get update && \
-    apt-get upgrade -y && \
-    apt-get install -y git vim
-
-### Install CodeIgniter's server requirements
-#-- https://github.com/codeigniter4/appstarter#server-requirements
-
-# Install intl extension using https://github.com/mlocati/docker-php-extension-installer
-RUN apt-get update && apt-get install -y \
+COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
+
+# Install server requirements
+RUN apt-get update \
+    # gnupg to sign commits with gpg
+    && apt-get install --yes --no-install-recommends gnupg \
+    # npm through the nodejs package
+    && curl -fsSL https://deb.nodesource.com/setup_14.x | bash - \
+    && apt-get update \
+    && apt-get install --yes --no-install-recommends nodejs \
+    # update npm
+    && npm install --global npm@7 \
+    && apt-get update \
+    && apt-get install --yes --no-install-recommends \
+    git \
+    openssh-client \
+    vim \
+    # cron for scheduled tasks
+    cron \
+    # unzip used by composer
+    unzip \
+    # required libraries to install php extensions using
+    # https://github.com/mlocati/docker-php-extension-installer (included in php's docker image)
     libicu-dev \
     libpng-dev \
     libjpeg-dev \
     zlib1g-dev \
-    && docker-php-ext-install intl
-
-RUN docker-php-ext-configure gd --with-jpeg \
-    && docker-php-ext-install gd
-
-RUN pecl install -o -f redis \
-    &&  rm -rf /tmp/pear \
-    &&  docker-php-ext-enable redis
-
-RUN docker-php-ext-install mysqli && docker-php-ext-enable mysqli
-
-RUN echo "file_uploads = On\n" \
+    libzip-dev \
+    # intl for Internationalization
+    && docker-php-ext-install intl  \
+    && docker-php-ext-install zip \
+    # gd for image processing
+    && docker-php-ext-configure gd --with-jpeg \
+    && docker-php-ext-install gd \
+    # redis extension for cache
+    && pecl install -o -f redis \
+    && rm -rf /tmp/pear \
+    && docker-php-ext-enable redis \
+    # mysqli for database access
+    && docker-php-ext-install mysqli \
+    && docker-php-ext-enable mysqli \
+    # configure php
+    && echo "file_uploads = On\n" \
          "memory_limit = 512M\n" \
          "upload_max_filesize = 500M\n" \
          "post_max_size = 512M\n" \
          "max_execution_time = 300\n" \
-         > /usr/local/etc/php/conf.d/uploads.ini
-
-# install cron
-RUN apt-get update && \
-    apt-get install -y cron
-
-RUN crontab /castopod-host/crontab
+         > /usr/local/etc/php/conf.d/uploads.ini \
diff --git a/app/Controllers/InstallController.php b/app/Controllers/InstallController.php
index 9b6f92082c..8e80b35dfc 100644
--- a/app/Controllers/InstallController.php
+++ b/app/Controllers/InstallController.php
@@ -163,7 +163,7 @@ class InstallController extends Controller
         self::writeEnv([
             'app.baseURL' => $baseUrl,
             'app.mediaBaseURL' =>
-                $mediaBaseUrl === null ? $baseUrl : $mediaBaseUrl,
+                $mediaBaseUrl === '' ? $baseUrl : $mediaBaseUrl,
             'app.adminGateway' => $this->request->getPost('admin_gateway'),
             'app.authGateway' => $this->request->getPost('auth_gateway'),
         ]);
diff --git a/app/Helpers/media_helper.php b/app/Helpers/media_helper.php
index 18b03f2e89..17df25714a 100644
--- a/app/Helpers/media_helper.php
+++ b/app/Helpers/media_helper.php
@@ -117,10 +117,12 @@ if (! function_exists('media_base_url')) {
         }
         $uri = trim($uri, '/');
 
-        return rtrim(config('App')->mediaBaseURL, '/') .
+        $appConfig = config('App');
+        $mediaBaseUrl = $appConfig->mediaBaseURL === '' ? $appConfig->baseURL : $appConfig->mediaBaseURL;
+
+        return rtrim($mediaBaseUrl, '/') .
             '/' .
-            config('App')
-                ->mediaRoot .
+            $appConfig->mediaRoot .
             '/' .
             $uri;
     }
diff --git a/docker-compose.yml b/docker-compose.yml
index 7b138da1b8..4142285e49 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -8,9 +8,8 @@ services:
     build:
       context: .
       dockerfile: Dockerfile
-    container_name: castopod_host
-    command: >
-      sh -c "cron && php spark serve --host 0.0.0.0"
+    container_name: castopod-host
+    command: /bin/sh -c "crontab ./crontab && cron && service cron reload && php spark serve --host 0.0.0.0"
     ports:
       - 8080:8080
     volumes:
@@ -23,7 +22,7 @@ services:
 
   redis:
     image: redis:alpine
-    container_name: castopod_host_redis
+    container_name: castopod-host_redis
     ports:
       - 6379:6379
     volumes:
@@ -33,7 +32,7 @@ services:
 
   mariadb:
     image: mariadb:latest
-    container_name: castopod_host_mariadb
+    container_name: castopod-host_mariadb
     ports:
       - 3306:3306
     volumes:
@@ -48,7 +47,7 @@ services:
 
   phpmyadmin:
     image: phpmyadmin/phpmyadmin:latest
-    container_name: castopod_host_phpmyadmin
+    container_name: castopod-host_phpmyadmin
     environment:
       PMA_HOST: mariadb
       PMA_PORT: 3306
diff --git a/docs/setup-development.md b/docs/setup-development.md
index a65c178f4a..838b9e7506 100644
--- a/docs/setup-development.md
+++ b/docs/setup-development.md
@@ -12,6 +12,8 @@
 - [Going Further](#going-further)
   - [Useful docker / docker-compose commands](#useful-docker--docker-compose-commands)
 - [Known issues](#known-issues)
+  - [Allocation failed - JavaScript heap out of memory](#allocation-failed---javascript-heap-out-of-memory)
+  - [Files created inside container are attributed to root locally (Linux)](#files-created-inside-container-are-attributed-to-root-locally-linux)
 
 ## Introduction
 
@@ -74,7 +76,7 @@ to help you kickstart your contribution.
 3. (for docker desktop) Add the repository you've cloned to docker desktop's
    `Settings` > `Resources` > `File Sharing`
 
-### (recommended) Develop inside the app Container with VSCode
+## (recommended) Develop inside the app Container with VSCode
 
 If you're working in VSCode, you can take advantage of the `.devcontainer/`
 folder. It defines a development environment (dev container) with preinstalled
@@ -110,7 +112,7 @@ required services will be loaded automagically!
 For more info, see
 [VSCode Remote Containers](https://code.visualstudio.com/docs/remote/containers)
 
-### (not-recommended) Develop outside the app container
+## (not-recommended) Develop outside the app container
 
 You do not wish to use the VSCode devcontainer? No problem!
 
@@ -134,13 +136,13 @@ You do not wish to use the VSCode devcontainer? No problem!
    > The `docker-compose up -d` command will boot 4 containers in the
    > background:
    >
-   > - `castopod_host_app`: a php based container with CodeIgniter4 requirements
+   > - `castopod-host_app`: a php based container with CodeIgniter4 requirements
    >   installed
-   > - `castopod_host_redis`: a [redis](https://redis.io/) database to handle
+   > - `castopod-host_redis`: a [redis](https://redis.io/) database to handle
    >   queries and pages caching
-   > - `castopod_host_mariadb`: a [mariadb](https://mariadb.org/) server for
+   > - `castopod-host_mariadb`: a [mariadb](https://mariadb.org/) server for
    >   persistent data
-   > - `castopod_host_phpmyadmin`: a phpmyadmin server to visualize the mariadb
+   > - `castopod-host_phpmyadmin`: a phpmyadmin server to visualize the mariadb
    >   database.
 
 2. Run any command by prefixing them with `docker-compose run --rm app`:
@@ -292,7 +294,7 @@ To see your changes, go to:
 docker-compose logs --tail 50 --follow --timestamps app
 
 # interact with redis server using included redis-cli command
-docker exec -it castopod_host_redis redis-cli
+docker exec -it castopod-host_redis redis-cli
 
 # monitor the redis container
 docker-compose logs --tail 50 --follow --timestamps redis
@@ -319,7 +321,50 @@ more insights.
 
 ## Known issues
 
-- `Allocation failed - JavaScript heap out of memory` when running `npm install`
+### Allocation failed - JavaScript heap out of memory
 
-  👉 By default, docker might not have access to enough RAM. Allocate more
-  memory and run `npm install` again.
+This happens when running `npm install`.
+
+👉 By default, docker might not have access to enough RAM. Allocate more memory
+and run `npm install` again.
+
+### Files created inside container are attributed to root locally (Linux)
+
+You may use Linux user namespaces to fix this:
+
+> **Note:**
+>
+> Replace "username" with your local username
+
+1. Go to `/etc/docker/daemon.json` and add:
+
+   ```json
+   {
+     "userns-remap": "username"
+   }
+   ```
+
+2. Configure the subordinate uid/guid:
+
+   ```bash
+   username:1000:1
+   username:100000:65536
+   ```
+
+   ```bash
+   username:1000:1
+   username:100000:65536
+   ```
+
+3. Restart docker:
+
+   ```bash
+   sudo systemctl restart docker
+   ```
+
+4. That's it! Now, the root user in the container will be mapped to the user on
+   your local machine, no more permission problems! 🎉
+
+You can check
+[this great article](https://www.jujens.eu/posts/en/2017/Jul/02/docker-userns-remap/)
+to know more about how it works.
-- 
GitLab