Frappe Helm Building Custom App & Image

Good Day, I would like to ask how to create a custom image for a custom app that I will then use to install frappe erpnext using helm. So I already created a Dockerfile, but it seems that you can only use the get-app when your custom app has a git repository. Is it possible to install the custom app just by copying it into the apps folder? So in short, I want to install the app without cloning the custom app from a cloud repo.

ARG ERPNEXT_VERSION=v14.17.1
FROM frappe/erpnext:${ERPNEXT_VERSION}

COPY --chown=frappe:frappe repos apps

RUN bench get-app custom_app

RUN bench build --production --verbose --hard-link

I’m not sure if I’m doing it the wrong way or if I’m missing something. Thanks in advance.

read this Frequently Asked Questions · frappe/frappe_docker Wiki · GitHub

if you have already cloned repositories, then copy them to apps and then run bench setup requirements

if you need to do get-app, instead use apps.json it is doing the same

Thanks for the reply. When I tried bench setup requirements i got this error:

#6 [3/4] RUN bench setup requirements
#6 sha256:ca3364a54a4e99abcd8ef94b40f503977bd608850058f46969f2e1d70f1de33e
#6 0.690 $ /home/frappe/frappe-bench/env/bin/python -m pip install --quiet --upgrade pip
#6 2.687 Traceback (most recent call last):
#6 2.687 Installing 3 applications...
#6 2.687 ERROR: /home/frappe/frappe-bench/apps/frappe
#6 2.688   File "/usr/local/bin/bench", line 8, in <module>
#6 2.688     sys.exit(cli())
#6 2.688   File "/usr/local/lib/python3.10/site-packages/bench/cli.py", line 127, in cli
#6 2.689     bench_command()
#6 2.689   File "/usr/local/lib/python3.10/site-packages/click/core.py", line 1130, in __call__
#6 2.691     return self.main(*args, **kwargs)
#6 2.691   File "/usr/local/lib/python3.10/site-packages/click/core.py", line 1055, in main
#6 2.691     rv = self.invoke(ctx)
#6 2.691   File "/usr/local/lib/python3.10/site-packages/click/core.py", line 1657, in invoke
#6 2.692     return _process_result(sub_ctx.command.invoke(sub_ctx))
#6 2.692   File "/usr/local/lib/python3.10/site-packages/click/core.py", line 1657, in invoke
#6 2.692     return _process_result(sub_ctx.command.invoke(sub_ctx))
#6 2.692   File "/usr/local/lib/python3.10/site-packages/click/core.py", line 1404, in invoke
#6 2.692     return ctx.invoke(self.callback, **ctx.params)
#6 2.692   File "/usr/local/lib/python3.10/site-packages/click/core.py", line 760, in invoke
#6 2.692     return __callback(*args, **kwargs)
#6 2.692   File "/usr/local/lib/python3.10/site-packages/bench/commands/setup.py", line 228, in setup_requirements
#6 2.693     bench.setup.requirements(apps=apps)
#6 2.693   File "/usr/local/lib/python3.10/site-packages/bench/utils/render.py", line 105, in wrapper_fn
#6 2.694     return fn(*args, **kwargs)
#6 2.694   File "/usr/local/lib/python3.10/site-packages/bench/bench.py", line 463, in requirements
#6 2.694     app = App(path_to_app, bench=self.bench, to_clone=False).install(
#6 2.694   File "/usr/local/lib/python3.10/site-packages/bench/app.py", line 176, in __init__
#6 2.695     super().__init__(name, branch, *args, **kwargs)
#6 2.695   File "/usr/local/lib/python3.10/site-packages/bench/app.py", line 77, in __init__
#6 2.695     self.setup_details()
#6 2.695   File "/usr/local/lib/python3.10/site-packages/bench/app.py", line 93, in setup_details
#6 2.695     self._setup_details_from_mounted_disk()
#6 2.695   File "/usr/local/lib/python3.10/site-packages/bench/app.py", line 111, in _setup_details_from_mounted_disk
#6 2.695     self.git_repo = git.Repo(self.mount_path)
#6 2.695   File "/usr/local/lib/python3.10/site-packages/git/repo/base.py", line 265, in __init__
#6 2.705     raise InvalidGitRepositoryError(epath)
#6 2.705 git.exc.InvalidGitRepositoryError: /home/frappe/frappe-bench/apps/frappe
#6 ERROR: executor failed running [/bin/sh -c bench setup requirements]: exit code: 1

if you need to do get-app, instead use apps.json it is doing the same

The one in the custom app docs? So get-app and that guide only work when you uploaded your custom app in the cloud repo? Just confirming.

by get-app I thought, remote is present.

if you don’t have remote repository and doing development then I’m not familiar with such practices.

You will need to build your images somehow from the vm where you’ve the source changes. I can’t help you in such custom workflow.

if you’ve 1 or not frappe apps, on private git repos use the apps.json image building process

I see. Anyway, the get-app works with no problem. I’ll try the apps.json too. Thank you so much for the help.

if you have repos directory where you are cloning multiple frappe apps.

e.g repos/payments, repos/erpnext, repos/hrm, repos/custom_app

It may be the case if you are using a monorepo or something else where you need to prepare apps directory instead of cloning apps. Prepare a repos directory on your host/vm/runner, you can gitignore it. Copy it during build and then run bench setup requirements.

Example file using repos directory, I’ll not recommend it as you need to keep the file updated. With apps.json you’ll always get the updated file from frappe_docker

ARG PYTHON_VERSION=3.10.5
FROM python:${PYTHON_VERSION}-slim-bullseye AS base

ARG ERPNEXT_VERSION=v14.17.1
COPY --from=frappe/erpnext:${ERPNEXT_VERSION} /templates/nginx/frappe.conf.template /templates/nginx/frappe.conf.template
COPY --from=frappe/erpnext:${ERPNEXT_VERSION} /usr/local/bin/nginx-entrypoint.sh /usr/local/bin/nginx-entrypoint.sh

ARG WKHTMLTOPDF_VERSION=0.12.6-1
ARG NODE_VERSION=16.18.0
ENV NVM_DIR=/home/frappe/.nvm
ENV PATH ${NVM_DIR}/versions/node/v${NODE_VERSION}/bin/:${PATH}

RUN useradd -ms /bin/bash frappe \
    && apt-get update \
    && apt-get install --no-install-recommends -y \
    curl \
    git \
    vim \
    nginx \
    gettext-base \
    # weasyprint dependencies
    libpango-1.0-0 \
    libharfbuzz0b \
    libpangoft2-1.0-0 \
    libpangocairo-1.0-0 \
    # For backups
    restic \
    # MariaDB
    mariadb-client \
    # Postgres
    libpq-dev \
    postgresql-client \
    # For healthcheck
    wait-for-it \
    jq \
    # NodeJS
    && mkdir -p ${NVM_DIR} \
    && curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.2/install.sh | bash \
    && . ${NVM_DIR}/nvm.sh \
    && nvm install ${NODE_VERSION} \
    && nvm use v${NODE_VERSION} \
    && npm install -g yarn \
    && nvm alias default v${NODE_VERSION} \
    && rm -rf ${NVM_DIR}/.cache \
    && echo 'export NVM_DIR="/home/frappe/.nvm"' >>/home/frappe/.bashrc \
    && echo '[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm' >>/home/frappe/.bashrc \
    && echo '[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion' >>/home/frappe/.bashrc \
    # Install wkhtmltopdf with patched qt
    && if [ "$(uname -m)" = "aarch64" ]; then export ARCH=arm64; fi \
    && if [ "$(uname -m)" = "x86_64" ]; then export ARCH=amd64; fi \
    && downloaded_file=wkhtmltox_$WKHTMLTOPDF_VERSION.buster_${ARCH}.deb \
    && curl -sLO https://github.com/wkhtmltopdf/packaging/releases/download/$WKHTMLTOPDF_VERSION/$downloaded_file \
    && apt-get install -y ./$downloaded_file \
    && rm $downloaded_file \
    # Clean up
    && rm -rf /var/lib/apt/lists/* \
    && rm -fr /etc/nginx/sites-enabled/default \
    && pip3 install frappe-bench \
    # Fixes for non-root nginx and logs to stdout
    && sed -i '/user www-data/d' /etc/nginx/nginx.conf \
    && ln -sf /dev/stdout /var/log/nginx/access.log && ln -sf /dev/stderr /var/log/nginx/error.log \
    && touch /run/nginx.pid \
    && chown -R frappe:frappe /etc/nginx/conf.d \
    && chown -R frappe:frappe /etc/nginx/nginx.conf \
    && chown -R frappe:frappe /var/log/nginx \
    && chown -R frappe:frappe /var/lib/nginx \
    && chown -R frappe:frappe /run/nginx.pid \
    && chmod 755 /usr/local/bin/nginx-entrypoint.sh \
    && chmod 644 /templates/nginx/frappe.conf.template

FROM base AS builder

RUN apt-get update \
    && DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \
    # For frappe framework
    wget \
    # For psycopg2
    libpq-dev \
    # Other
    libffi-dev \
    liblcms2-dev \
    libldap2-dev \
    libmariadb-dev \
    libsasl2-dev \
    libtiff5-dev \
    libwebp-dev \
    redis-tools \
    rlwrap \
    tk8.6-dev \
    cron \
    # For pandas
    gcc \
    build-essential \
    libbz2-dev \
    && rm -rf /var/lib/apt/lists/*

# apps.json includes
ARG APPS_JSON_BASE64
RUN if [ -n "${APPS_JSON_BASE64}" ]; then \
    mkdir /opt/frappe && echo "${APPS_JSON_BASE64}" | base64 -d > /opt/frappe/apps.json; \
  fi

USER frappe

ARG FRAPPE_BRANCH=version-14
ARG FRAPPE_PATH=https://github.com/frappe/frappe
RUN export APP_INSTALL_ARGS="" && \
  if [ -n "${APPS_JSON_BASE64}" ]; then \
    export APP_INSTALL_ARGS="--apps_path=/opt/frappe/apps.json"; \
  fi && \
  bench init ${APP_INSTALL_ARGS}\
    --frappe-branch=${FRAPPE_BRANCH} \
    --frappe-path=${FRAPPE_PATH} \
    --no-procfile \
    --no-backups \
    --skip-redis-config-generation \
    --verbose \
    /home/frappe/frappe-bench && \
  cd /home/frappe/frappe-bench && \
  echo "$(jq 'del(.db_host, .redis_cache, .redis_queue, .redis_socketio)' sites/common_site_config.json)" \
    > sites/common_site_config.json && \
  find apps -mindepth 1 -path "*/.git" | xargs rm -fr

COPY --chown=frappe:frappe repos /home/frappe/frappe-bench/apps

RUN cd /home/frappe/frappe-bench && \
  bench setup requirements && bench build --production

WORKDIR /home/frappe/frappe-bench

FROM base as backend

USER frappe

COPY --from=builder --chown=frappe:frappe /home/frappe/frappe-bench /home/frappe/frappe-bench

WORKDIR /home/frappe/frappe-bench

VOLUME [ \
  "/home/frappe/frappe-bench/sites", \
  "/home/frappe/frappe-bench/sites/assets", \
  "/home/frappe/frappe-bench/logs" \
]

CMD [ \
  "/home/frappe/frappe-bench/env/bin/gunicorn", \
  "--chdir=/home/frappe/frappe-bench/sites", \
  "--bind=0.0.0.0:8000", \
  "--threads=4", \
  "--workers=2", \
  "--worker-class=gthread", \
  "--worker-tmp-dir=/dev/shm", \
  "--timeout=120", \
  "--preload", \
  "frappe.app:application" \
]

Alright, I’ll try this out. I’ll reply to this thread if ever it works out. Thanks again.