Re: [yocto-patches] [layerindex-web][PATCH v3 1/4] layerindex: Add app dir support for configurable container

Tim Orling <[email protected]>
Newsgroups org.yoctoproject.lists.yocto-patches
Message-ID <CAOyo-84JJuvhVMc0L02vCFkHUq0E+C=-B28vi_3SBGKCAv8ydw@mail.gmail.com>
merged.

On Tue, Jun 9, 2026 at 9:40 AM Sandeep Gundlupet Raju <
[email protected]> wrote:

> Add a new --app-dir command-line argument (default: /opt) to allow the
> base directory used inside the containers to be configurable instead of
> being hardcoded to /opt.
>
> Changes:
> - dockersetup.py: add --app-dir argument; write APP_DIR to .env file via
>   new write_env_file() so docker-compose passes it as a build arg; update
>   edit_dockercompose() to expand build: block and inject APP_DIR build arg
>   reference, and rewrite volume mounts and celery --workdir accordingly;
>   propagate app_dir through setup_https(), check_connectivity(), and all
>   direct docker-compose run call sites
> - Dockerfile: add ARG APP_DIR=/opt + ENV APP_DIR=${APP_DIR}; replace all
>   hardcoded /opt paths with ${APP_DIR} in COPY, RUN and CMD instructions
> - docker/migrate.sh: replace /opt with ${APP_DIR:-/opt}
> - docker/refreshlayers.sh: replace /opt with ${APP_DIR:-/opt}
> - docker/updatelayers.sh: replace /opt with ${APP_DIR:-/opt}
> - Pass app_dir to edit_settings_py() and replace the hardcoded
>   LAYER_FETCH_DIR = "/opt/workdir" with app_dir + "/workdir", so
>   the fetch directory respects the --app-dir option.
>
> AI-Generated: GitHub Copilot (Claude Sonnet 4.6)
>
> Signed-off-by: Sandeep Gundlupet Raju <[email protected]>
> ---
>  Dockerfile              | 24 ++++++------
>  docker/migrate.sh       |  2 +-
>  docker/refreshlayers.sh |  2 +-
>  docker/updatelayers.sh  |  2 +-
>  dockersetup.py          | 86 +++++++++++++++++++++++++++++++++--------
>  5 files changed, 85 insertions(+), 31 deletions(-)
>
> diff --git a/Dockerfile b/Dockerfile
> index 72f57d2..8e6e508 100644
> --- a/Dockerfile
> +++ b/Dockerfile
> @@ -3,6 +3,8 @@
>  FROM ubuntu:jammy
>  LABEL maintainer="Michael Halstead <[email protected]>"
>
> +ARG APP_DIR=/opt
> +ENV APP_DIR=${APP_DIR}
>  ENV PYTHONUNBUFFERED=1 \
>      LANGUAGE=en_US \
>      LANG=en_US.UTF-8 \
> @@ -55,22 +57,22 @@ RUN DEBIAN_FRONTEND=noninteractive apt-get update \
>         && rm -rf /var/lib/apt/lists/* \
>         && apt-get clean
>
> -COPY . /opt/layerindex
> -RUN rm -rf /opt/layerindex/docker
> -COPY docker/settings.py /opt/layerindex/settings.py
> -COPY docker/refreshlayers.sh /opt/refreshlayers.sh
> -COPY docker/updatelayers.sh /opt/updatelayers.sh
> -COPY docker/migrate.sh /opt/migrate.sh
> -COPY docker/connectivity_check.sh /opt/connectivity_check.sh
> +COPY . ${APP_DIR}/layerindex
> +RUN rm -rf ${APP_DIR}/layerindex/docker
> +COPY docker/settings.py ${APP_DIR}/layerindex/settings.py
> +COPY docker/refreshlayers.sh ${APP_DIR}/refreshlayers.sh
> +COPY docker/updatelayers.sh ${APP_DIR}/updatelayers.sh
> +COPY docker/migrate.sh ${APP_DIR}/migrate.sh
> +COPY docker/connectivity_check.sh ${APP_DIR}/connectivity_check.sh
>
> -RUN mkdir /opt/workdir \
> +RUN mkdir ${APP_DIR}/workdir \
>         && adduser --system --uid=500 layers \
> -       && chown -R layers /opt/workdir
> +       && chown -R layers ${APP_DIR}/workdir
>  USER layers
>
>  # Always copy in .gitconfig and proxy helper script (they need editing to
> be active)
>  COPY docker/.gitconfig /home/layers/.gitconfig
> -COPY docker/git-proxy /opt/bin/git-proxy
> +COPY docker/git-proxy ${APP_DIR}/bin/git-proxy
>
>  # Start Gunicorn
> -CMD ["/usr/local/bin/gunicorn", "wsgi:application", "--workers=4",
> "--bind=:5000", "--timeout=60", "--log-level=debug",
> "--chdir=/opt/layerindex"]
> +CMD ["/bin/sh", "-c", "/usr/local/bin/gunicorn wsgi:application
> --workers=4 --bind=:5000 --timeout=60 --log-level=debug
> --chdir=${APP_DIR}/layerindex"]
> diff --git a/docker/migrate.sh b/docker/migrate.sh
> index ca15368..bb9f646 100755
> --- a/docker/migrate.sh
> +++ b/docker/migrate.sh
> @@ -1,2 +1,2 @@
>  #!/bin/bash
> -python3 /opt/layerindex/manage.py migrate "$@"
> +python3 ${APP_DIR:-/opt}/layerindex/manage.py migrate "$@"
> diff --git a/docker/refreshlayers.sh b/docker/refreshlayers.sh
> index 0c550bd..5adaf86 100755
> --- a/docker/refreshlayers.sh
> +++ b/docker/refreshlayers.sh
> @@ -1,3 +1,3 @@
>  #!/bin/bash
> -update=/opt/layerindex/layerindex/update.py
> +update=${APP_DIR:-/opt}/layerindex/layerindex/update.py
>  $update -q -r
> diff --git a/docker/updatelayers.sh b/docker/updatelayers.sh
> index 21640ba..6f0e8f0 100755
> --- a/docker/updatelayers.sh
> +++ b/docker/updatelayers.sh
> @@ -1,3 +1,3 @@
>  #!/bin/bash
> -update=/opt/layerindex/layerindex/update.py
> +update=${APP_DIR:-/opt}/layerindex/layerindex/update.py
>  $update -q
> diff --git a/dockersetup.py b/dockersetup.py
> index bc7478c..3a74eb3 100755
> --- a/dockersetup.py
> +++ b/dockersetup.py
> @@ -70,6 +70,7 @@ def get_args():
>      parser.add_argument('--no-migrate', action="store_true",
> default=False, help='Skip running database migrations')
>      parser.add_argument('--no-admin-user', action="store_true",
> default=False, help='Skip adding admin user')
>      parser.add_argument('--no-connectivity', action="store_true",
> default=False, help='Skip checking external network connectivity')
> +    parser.add_argument('--app-dir', type=str, help='Base directory
> inside the container for application files. Default is %(default)s',
> required=False, default='/opt')
>
>      args = parser.parse_args()
>
> @@ -253,14 +254,16 @@ def yaml_comment(line):
>
>
>  # Add hostname, secret key, db info, and email host in docker-compose.yml
> -def edit_dockercompose(hostname, dbpassword, dbapassword, secretkey,
> rmqpassword, portmapping, letsencrypt, email_host, email_port, email_user,
> email_password, email_ssl, email_tls):
> +def edit_dockercompose(hostname, dbpassword, dbapassword, secretkey,
> rmqpassword, portmapping, letsencrypt, email_host, email_port, email_user,
> email_password, email_ssl, email_tls, app_dir='/opt'):
> +
> +    in_layersapp_build = False
>
>      def adjust_cert_mount_line(ln):
>          linesplit = ln.split(':')
>          if letsencrypt:
>              linesplit[1] = '/etc/letsencrypt'
>          else:
> -            linesplit[1] = '/opt/cert'
> +            linesplit[1] = app_dir + '/cert'
>          # This allows us to handle if there is a ":ro" or similar on the
> end
>          return ':'.join(linesplit)
>
> @@ -305,6 +308,33 @@ def edit_dockercompose(hostname, dbpassword,
> dbapassword, secretkey, rmqpassword
>                  newlines.append(ucline + '\n')
>              else:
>                  newlines.append(yaml_comment(line) + '\n')
> +        elif re.match(r'^\s+build:\s+\.$', line):
> +            # Convert inline 'build: .' to block form so we can pass
> APP_DIR as a build arg
> +            indent = line[:len(line) - len(line.lstrip())]
> +            newlines.append(indent + 'build:\n')
> +            newlines.append(indent + '  context: .\n')
> +            newlines.append(indent + '  args:\n')
> +            newlines.append(indent + '    - APP_DIR=${APP_DIR}\n')
> +            in_layersapp_build = True
> +            continue
> +        elif '- APP_DIR=' in line:
> +            # Keep APP_DIR referencing the .env file (do not embed value
> here)
> +            format = line[:line.find('- APP_DIR=')].replace('#', '')
> +            newlines.append(format + '- APP_DIR=${APP_DIR}\n')
> +            continue
> +        elif re.match(r'^\s+context:\s+\.$', line) and in_layersapp_build:
> +            in_layersapp_build = False
> +            newlines.append(line + '\n')
> +            continue
> +        elif ':/opt/workdir' in line:
> +            newlines.append(line.replace(':/opt/workdir', ':' + app_dir +
> '/workdir') + '\n')
> +            continue
> +        elif ':/opt/layerindex-task-logs' in line:
> +            newlines.append(line.replace(':/opt/layerindex-task-logs',
> ':' + app_dir + '/layerindex-task-logs') + '\n')
> +            continue
> +        elif '--workdir=/opt/layerindex' in line:
> +            newlines.append(line.replace('--workdir=/opt/layerindex',
> '--workdir=' + app_dir + '/layerindex') + '\n')
> +            continue
>          elif "layersweb:" in line:
>              in_layersweb = True
>              newlines.append(line + "\n")
> @@ -426,7 +456,7 @@ def edit_nginx_ssl_conf(hostname, https_port, certdir,
> certfile, keyfile):
>      writefile("docker/nginx-ssl-edited.conf", ''.join(newlines))
>
>
> -def edit_settings_py(emailaddr):
> +def edit_settings_py(emailaddr, app_dir='/opt'):
>      filedata = readfile('docker/settings.py')
>      newlines = []
>      lines = filedata.splitlines()
> @@ -444,6 +474,9 @@ def edit_settings_py(emailaddr):
>                  newlines.append("  ('Admin', '%s'),\n" % emailaddr)
>              newlines.append(")\n")
>              continue
> +        elif line.lstrip().startswith('LAYER_FETCH_DIR'):
> +            newlines.append("LAYER_FETCH_DIR = \"%s/workdir\"\n" %
> app_dir)
> +            continue
>          newlines.append(line + "\n")
>      writefile("docker/settings.py", ''.join(newlines))
>
> @@ -474,9 +507,9 @@ def edit_dockerfile_web(hostname, no_https):
>      writefile("Dockerfile.web", ''.join(newlines))
>
>
> -def setup_https(hostname, http_port, https_port, letsencrypt,
> letsencrypt_production, cert, cert_key, emailaddr):
> +def setup_https(hostname, http_port, https_port, letsencrypt,
> letsencrypt_production, cert, cert_key, emailaddr, app_dir='/opt'):
>      local_cert_dir = os.path.abspath('docker/certs')
> -    container_cert_dir = '/opt/cert'
> +    container_cert_dir = app_dir + '/cert'
>      if letsencrypt:
>          # Create dummy cert
>          container_cert_dir = '/etc/letsencrypt'
> @@ -578,8 +611,26 @@ def edit_options_file(project_name):
>          f.write('project_name=%s\n' % project_name)
>
>
> -def check_connectivity():
> -    return_code = subprocess.call(['docker-compose', 'run', '--rm',
> 'layersapp', '/opt/connectivity_check.sh'], shell=False)
> +def write_env_file(app_dir):
> +    """Write APP_DIR to .env so docker-compose passes it as a build
> arg."""
> +    env_vars = {}
> +    try:
> +        with open('.env', 'r') as f:
> +            for line in f:
> +                line = line.strip()
> +                if '=' in line and not line.startswith('#'):
> +                    k, v = line.split('=', 1)
> +                    env_vars[k.strip()] = v.strip()
> +    except FileNotFoundError:
> +        pass
> +    env_vars['APP_DIR'] = app_dir
> +    with open('.env', 'w') as f:
> +        for k, v in env_vars.items():
> +            f.write('%s=%s\n' % (k, v))
> +
> +
> +def check_connectivity(app_dir='/opt'):
> +    return_code = subprocess.call(['docker-compose', 'run', '--rm',
> 'layersapp', app_dir + '/connectivity_check.sh'], shell=False)
>      if return_code != 0:
>          print("Connectivity check failed - if you are behind a proxy,
> please check that you have correctly specified the proxy settings on the
> command line (see --help for details)")
>          sys.exit(1)
> @@ -741,24 +792,25 @@ if args.uninstall:
>  if args.update:
>      args.no_https = read_dockerfile_web()
>      if not args.no_https:
> -        container_cert_dir = '/opt/cert'
> +        container_cert_dir = args.app_dir + '/cert'
>          args.hostname, https_port, certdir, certfile, keyfile =
> read_nginx_ssl_conf(container_cert_dir)
>          edit_nginx_ssl_conf(args.hostname, https_port, certdir, certfile,
> keyfile)
>  else:
>      # Always edit these in case we switch from proxy to no proxy
>      edit_gitproxy(socks_proxy_host, socks_proxy_port, args.no_proxy)
>      edit_dockerfile(args.http_proxy, args.https_proxy, args.no_proxy)
> +    write_env_file(args.app_dir)
>
> -    edit_dockercompose(args.hostname, dbpassword, dbapassword, secretkey,
> rmqpassword, args.portmapping, args.letsencrypt, email_host, email_port,
> args.email_user, args.email_password, args.email_ssl, args.email_tls)
> +    edit_dockercompose(args.hostname, dbpassword, dbapassword, secretkey,
> rmqpassword, args.portmapping, args.letsencrypt, email_host, email_port,
> args.email_user, args.email_password, args.email_ssl, args.email_tls,
> args.app_dir)
>
>      edit_dockerfile_web(args.hostname, args.no_https)
>
> -    edit_settings_py(emailaddr)
> +    edit_settings_py(emailaddr, args.app_dir)
>
>      edit_options_file(args.project_name)
>
>      if not args.no_https:
> -        setup_https(args.hostname, http_port, https_port,
> args.letsencrypt, args.letsencrypt_production, args.cert, args.cert_key,
> emailaddr)
> +        setup_https(args.hostname, http_port, https_port,
> args.letsencrypt, args.letsencrypt_production, args.cert, args.cert_key,
> emailaddr, args.app_dir)
>
>  ## Start up containers
>  return_code = subprocess.call(['docker-compose', 'up', '-d', '--build'],
> shell=False)
> @@ -768,7 +820,7 @@ if return_code != 0:
>
>  if not (args.update or args.no_connectivity):
>      ## Run connectivity check
> -    check_connectivity()
> +    check_connectivity(args.app_dir)
>
>  # Get real project name (if only there were a reasonable way to do
> this... ugh)
>  real_project_name = ''
> @@ -830,7 +882,7 @@ if not args.no_migrate:
>      env = os.environ.copy()
>      env['DATABASE_USER'] = 'root'
>      env['DATABASE_PASSWORD'] = dbapassword
> -    return_code = subprocess.call(['docker-compose', 'run', '--rm', '-e',
> 'DATABASE_USER', '-e', 'DATABASE_PASSWORD', 'layersapp',
> '/opt/migrate.sh'], shell=False, env=env)
> +    return_code = subprocess.call(['docker-compose', 'run', '--rm', '-e',
> 'DATABASE_USER', '-e', 'DATABASE_PASSWORD', 'layersapp', args.app_dir +
> '/migrate.sh'], shell=False, env=env)
>      if return_code != 0:
>          print("Applying migrations failed")
>          sys.exit(1)
> @@ -864,13 +916,13 @@ if not args.update:
>                  break
>      for volume in volumes:
>          volname = '%s_%s' % (real_project_name, volume)
> -        return_code = subprocess.call(['docker', 'run', '--rm', '-v',
> '%s:/opt/mount' % volname, 'debian:stretch', 'chown', '500', '/opt/mount'],
> shell=False)
> +        return_code = subprocess.call(['docker', 'run', '--rm', '-v',
> '%s:%s/mount' % (volname, args.app_dir), 'debian:stretch', 'chown', '500',
> args.app_dir + '/mount'], shell=False)
>          if return_code != 0:
>              print("Setting volume permissions for volume %s failed" %
> volume)
>              sys.exit(1)
>
>  ## Generate static assets. Run this command again to regenerate at any
> time (when static assets in the code are updated)
> -return_code = subprocess.call("docker-compose run --rm -e
> STATIC_ROOT=/usr/share/nginx/html -v %s_layersstatic:/usr/share/nginx/html
> layersapp /opt/layerindex/manage.py collectstatic --noinput" %
> quote(real_project_name), shell = True)
> +return_code = subprocess.call("docker-compose run --rm -e
> STATIC_ROOT=/usr/share/nginx/html -v %s_layersstatic:/usr/share/nginx/html
> layersapp %s/layerindex/manage.py collectstatic --noinput" %
> (quote(real_project_name), args.app_dir), shell = True)
>  if return_code != 0:
>      print("Collecting static files failed")
>      sys.exit(1)
> @@ -891,12 +943,12 @@ else:
>  if not args.update:
>      if not args.databasefile:
>          ## Set site name
> -        return_code = subprocess.call(['docker-compose', 'run', '--rm',
> 'layersapp', '/opt/layerindex/layerindex/tools/site_name.py', host,
> 'OpenEmbedded Layer Index'], shell=False)
> +        return_code = subprocess.call(['docker-compose', 'run', '--rm',
> 'layersapp', args.app_dir + '/layerindex/layerindex/tools/site_name.py',
> host, 'OpenEmbedded Layer Index'], shell=False)
>
>      if not args.no_admin_user:
>          ## For a fresh database, create an admin account
>          print("Creating database superuser. Input user name and password
> when prompted.")
> -        return_code = subprocess.call(['docker-compose', 'run', '--rm',
> 'layersapp', '/opt/layerindex/manage.py', 'createsuperuser', '--email',
> emailaddr], shell=False)
> +        return_code = subprocess.call(['docker-compose', 'run', '--rm',
> 'layersapp', args.app_dir + '/layerindex/manage.py', 'createsuperuser',
> '--email', emailaddr], shell=False)
>          if return_code != 0:
>              print("Creating superuser failed")
>              sys.exit(1)
> --
> 2.43.0
>
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.