adds development versions
This commit is contained in:
parent
790407f552
commit
36f977d0b3
13 changed files with 223 additions and 483 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -1,3 +1,3 @@
|
||||||
.env
|
.env
|
||||||
*.sql
|
.vscode
|
||||||
docker-compose.yml.local
|
letsencrypt
|
||||||
21
Dockerfile
21
Dockerfile
|
|
@ -1,5 +1,5 @@
|
||||||
FROM php:7.2.5-fpm-alpine3.7 as build
|
FROM php:7.2.5-fpm-alpine3.7 as build
|
||||||
#FROM php:7.4.4-fpm-alpine3.11 as build
|
|
||||||
RUN apk update \
|
RUN apk update \
|
||||||
&& apk add --no-cache libpng-dev zeromq-dev git \
|
&& apk add --no-cache libpng-dev zeromq-dev git \
|
||||||
$PHPIZE_DEPS \
|
$PHPIZE_DEPS \
|
||||||
|
|
@ -9,27 +9,17 @@ RUN apk update \
|
||||||
COPY pathfinder /app
|
COPY pathfinder /app
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN composer self-update 1.6.3
|
||||||
RUN composer install
|
RUN composer install
|
||||||
|
|
||||||
#FROM trafex/alpine-nginx-php7:latest
|
|
||||||
FROM trafex/alpine-nginx-php7:ba1dd422
|
FROM trafex/alpine-nginx-php7:ba1dd422
|
||||||
#USER root
|
|
||||||
RUN apk update && apk add --no-cache busybox-suid sudo php7-redis php7-pdo php7-pdo_mysql php7-fileinfo shadow gettext bash apache2-utils
|
RUN apk update && apk add --no-cache busybox-suid sudo php7-redis php7-pdo php7-pdo_mysql php7-fileinfo shadow gettext bash apache2-utils
|
||||||
#RUN usermod -u 1000 nobody
|
|
||||||
#RUN groupmod -g 1000 nobody
|
|
||||||
|
|
||||||
COPY static/nginx/nginx.conf /etc/nginx/templateNginx.conf
|
COPY static/nginx/nginx.conf /etc/nginx/templateNginx.conf
|
||||||
COPY static/nginx/site.conf /etc/nginx/sites_enabled/templateSite.conf
|
COPY static/nginx/site.conf /etc/nginx/sites_enabled/templateSite.conf
|
||||||
|
|
||||||
# Configure PHP-FPM
|
# Configure PHP-FPM
|
||||||
COPY static/php/fpm-pool.conf /etc/php7/php-fpm.d/zzz_custom.conf
|
COPY static/php/fpm-pool.conf /etc/php7/php-fpm.d/zzz_custom.conf
|
||||||
#COPY static/php/php.ini /etc/php7/conf.d/zzz_custom.ini
|
|
||||||
|
|
||||||
# DEBUG
|
|
||||||
RUN apk add php7-xdebug --repository http://dl-3.alpinelinux.org/alpine/edge/testing/
|
|
||||||
COPY static/php/xdebug.ini /etc/php7/conf.d/xdebug.ini
|
|
||||||
COPY static/php/error_reporting.ini /etc/php7/conf.d/error_reporting.ini
|
|
||||||
RUN echo "zend_extension=/usr/lib/php7/modules/xdebug.so" >> /etc/php7/php.ini
|
|
||||||
|
|
||||||
COPY static/php/php.ini /etc/zzz_custom.ini
|
COPY static/php/php.ini /etc/zzz_custom.ini
|
||||||
# configure cron
|
# configure cron
|
||||||
|
|
@ -37,17 +27,16 @@ COPY static/crontab.txt /var/crontab.txt
|
||||||
# Configure supervisord
|
# Configure supervisord
|
||||||
COPY static/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
|
COPY static/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
|
||||||
COPY static/entrypoint.sh /
|
COPY static/entrypoint.sh /
|
||||||
#RUN apk add sudo php7-redis php7-pdo php7-pdo_mysql
|
|
||||||
WORKDIR /var/www/html
|
WORKDIR /var/www/html
|
||||||
COPY --chown=nobody --from=build /app pathfinder
|
COPY --chown=nobody --from=build /app pathfinder
|
||||||
##RUN chown -R nobody:nobody /var/www/html/pathfinder
|
|
||||||
|
|
||||||
RUN chmod 0766 pathfinder/logs pathfinder/tmp/ && rm index.php && touch /etc/nginx/.setup_pass && chmod +x /entrypoint.sh
|
RUN chmod 0766 pathfinder/logs pathfinder/tmp/ && rm index.php && touch /etc/nginx/.setup_pass && chmod +x /entrypoint.sh
|
||||||
COPY static/pathfinder/routes.ini /var/www/html/pathfinder/app/
|
COPY static/pathfinder/routes.ini /var/www/html/pathfinder/app/
|
||||||
COPY static/pathfinder/environment.ini /var/www/html/pathfinder/app/templateEnvironment.ini
|
COPY static/pathfinder/environment.ini /var/www/html/pathfinder/app/templateEnvironment.ini
|
||||||
#USER nobody
|
|
||||||
WORKDIR /var/www/html
|
WORKDIR /var/www/html
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
#ENTRYPOINT ["sh", "-c", "source /.env && /entrypoint.sh", "-s"]
|
|
||||||
ENTRYPOINT ["/entrypoint.sh"]
|
ENTRYPOINT ["/entrypoint.sh"]
|
||||||
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
|
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@ MODE_MAINTENANCE = 0
|
||||||
; that other characters from the "switch character" menu have access to
|
; that other characters from the "switch character" menu have access to
|
||||||
; Syntax 0 | 1
|
; Syntax 0 | 1
|
||||||
; Default 0
|
; Default 0
|
||||||
SESSION_SHARING = 1
|
SESSION_SHARING = 0
|
||||||
|
|
||||||
; Login restrictions (white lists)
|
; Login restrictions (white lists)
|
||||||
; Login/registration can be restricted to specific groups.
|
; Login/registration can be restricted to specific groups.
|
||||||
|
|
@ -84,7 +84,7 @@ SESSION_SHARING = 1
|
||||||
; Syntax: String (comma separated)
|
; Syntax: String (comma separated)
|
||||||
; Default:
|
; Default:
|
||||||
CHARACTER =
|
CHARACTER =
|
||||||
CORPORATION =
|
CORPORATION = 98581604
|
||||||
ALLIANCE =
|
ALLIANCE =
|
||||||
|
|
||||||
[PATHFINDER.CHARACTER]
|
[PATHFINDER.CHARACTER]
|
||||||
|
|
|
||||||
|
|
@ -4,3 +4,4 @@ MODULES_ENABLED = 1
|
||||||
[PLUGIN.MODULES]
|
[PLUGIN.MODULES]
|
||||||
DEMO = ./app/ui/module/demo
|
DEMO = ./app/ui/module/demo
|
||||||
EMPTY = ./app/ui/module/empty
|
EMPTY = ./app/ui/module/empty
|
||||||
|
ZKILLTEST = ./app/ui/module/global_killboard
|
||||||
|
|
|
||||||
9
development/.env.development
Normal file
9
development/.env.development
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
CONTAINER_NAME="pfdev"
|
||||||
|
DOMAIN="localhost"
|
||||||
|
SERVER_NAME="PFDEV"
|
||||||
|
APP_PASSWORD="DEVPASSWORD"
|
||||||
|
MYSQL_PASSWORD="PASSWORD"
|
||||||
|
CCP_SSO_CLIENT_ID=""
|
||||||
|
CCP_SSO_SECRET_KEY=""
|
||||||
|
CCP_ESI_SCOPES="esi-location.read_online.v1,esi-location.read_location.v1,esi-location.read_ship_type.v1,esi-ui.write_waypoint.v1,esi-ui.open_window.v1,esi-universe.read_structures.v1,esi-corporations.read_corporation_membership.v1,esi-clones.read_clones.v1,esi-characters.read_corporation_roles.v1"
|
||||||
|
XDEBUG_CONFIG="remote_host=host.docker.internal remote_port=9000 remote_enable=1"
|
||||||
49
development/Dockerfile.development
Normal file
49
development/Dockerfile.development
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
FROM php:7.2.5-fpm-alpine3.7 as build
|
||||||
|
|
||||||
|
RUN apk update \
|
||||||
|
&& apk add --no-cache libpng-dev zeromq-dev git \
|
||||||
|
$PHPIZE_DEPS \
|
||||||
|
&& docker-php-ext-install gd && docker-php-ext-install pdo_mysql && pecl install redis && docker-php-ext-enable redis && pecl install channel://pecl.php.net/zmq-1.1.3 && docker-php-ext-enable zmq \
|
||||||
|
&& curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
|
||||||
|
|
||||||
|
COPY pathfinder /app
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN composer self-update 1.6.3
|
||||||
|
RUN composer install
|
||||||
|
|
||||||
|
FROM trafex/alpine-nginx-php7:ba1dd422
|
||||||
|
RUN apk update && apk add --no-cache busybox-suid sudo php7-redis php7-pdo php7-pdo_mysql php7-fileinfo shadow gettext bash apache2-utils
|
||||||
|
|
||||||
|
COPY static/nginx/nginx.conf /etc/nginx/templateNginx.conf
|
||||||
|
COPY static/nginx/site.conf /etc/nginx/sites_enabled/templateSite.conf
|
||||||
|
|
||||||
|
# Configure PHP-FPM
|
||||||
|
COPY static/php/fpm-pool.conf /etc/php7/php-fpm.d/zzz_custom.conf
|
||||||
|
|
||||||
|
# DEBUG
|
||||||
|
RUN apk add php7-xdebug --repository http://dl-3.alpinelinux.org/alpine/edge/testing/
|
||||||
|
COPY static/php/xdebug.ini /etc/php7/conf.d/xdebug.ini
|
||||||
|
COPY static/php/error_reporting.ini /etc/php7/conf.d/error_reporting.ini
|
||||||
|
RUN echo "zend_extension=/usr/lib/php7/modules/xdebug.so" >> /etc/php7/php.ini
|
||||||
|
|
||||||
|
COPY static/php/php.ini /etc/zzz_custom.ini
|
||||||
|
# configure cron
|
||||||
|
COPY static/crontab.txt /var/crontab.txt
|
||||||
|
# Configure supervisord
|
||||||
|
COPY static/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
|
||||||
|
COPY static/entrypoint.sh /
|
||||||
|
|
||||||
|
WORKDIR /var/www/html
|
||||||
|
COPY --chown=nobody --from=build /app pathfinder
|
||||||
|
|
||||||
|
|
||||||
|
RUN chmod 0766 pathfinder/logs pathfinder/tmp/ && rm index.php && touch /etc/nginx/.setup_pass && chmod +x /entrypoint.sh
|
||||||
|
COPY static/pathfinder/routes.ini /var/www/html/pathfinder/app/
|
||||||
|
COPY static/pathfinder/environment.ini /var/www/html/pathfinder/app/templateEnvironment.ini
|
||||||
|
|
||||||
|
WORKDIR /var/www/html
|
||||||
|
EXPOSE 80
|
||||||
|
|
||||||
|
ENTRYPOINT ["/entrypoint.sh"]
|
||||||
|
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"]
|
||||||
9
development/development.sh
Executable file
9
development/development.sh
Executable file
|
|
@ -0,0 +1,9 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
mv ./docker-compose.yml ./docker-compose.production.yml
|
||||||
|
mv ./Dockerfile ./Dockerfile.production
|
||||||
|
cp ./deployment/docker-compose.development.yml ./docker-compose.yml
|
||||||
|
cp ./deployment/Dockerfile.development ./Dockerfile
|
||||||
|
mkdir -p .vscode && cp ./deployment/launch.json ./.vscode/launch.json
|
||||||
|
echo "path=\"$(pwd)\"" > ./.env
|
||||||
|
cat ./deployment/.env.development >> ./.env
|
||||||
101
development/docker-compose.development.yml
Normal file
101
development/docker-compose.development.yml
Normal file
|
|
@ -0,0 +1,101 @@
|
||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
pfdb:
|
||||||
|
image: bianjp/mariadb-alpine:latest
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: $MYSQL_PASSWORD
|
||||||
|
container_name: "$CONTAINER_NAME-db"
|
||||||
|
networks:
|
||||||
|
pf:
|
||||||
|
aliases:
|
||||||
|
- "${CONTAINER_NAME}db"
|
||||||
|
volumes:
|
||||||
|
- db_data:/var/lib/mysql
|
||||||
|
- $path/pathfinder/export/sql/eve_universe.sql.zip:/eve_universe.sql.zip
|
||||||
|
restart: always
|
||||||
|
pf-redis:
|
||||||
|
image: redis:6.2.5-alpine3.14
|
||||||
|
container_name: "$CONTAINER_NAME-redis"
|
||||||
|
command: ["redis-server", "--appendonly", "yes"]
|
||||||
|
hostname: redis
|
||||||
|
volumes:
|
||||||
|
- redis_data:/data
|
||||||
|
networks:
|
||||||
|
pf:
|
||||||
|
aliases:
|
||||||
|
- "$CONTAINER_NAME-redis"
|
||||||
|
logging:
|
||||||
|
driver: none
|
||||||
|
restart: always
|
||||||
|
pf-socket:
|
||||||
|
image: composer:composer@sha256:d374b2e1f715621e9d9929575d6b35b11cf4a6dc237d4a08f2e6d1611f534675
|
||||||
|
container_name: "$CONTAINER_NAME-socket"
|
||||||
|
command: ["sh","-c","composer install && php cmd.php --tcpHost 0.0.0.0"]
|
||||||
|
hostname: socket
|
||||||
|
volumes:
|
||||||
|
- ${path}/websocket:/app
|
||||||
|
networks:
|
||||||
|
pf:
|
||||||
|
aliases:
|
||||||
|
- "$CONTAINER_NAME-socket"
|
||||||
|
restart: always
|
||||||
|
pf:
|
||||||
|
container_name: ${CONTAINER_NAME}
|
||||||
|
hostname: "pathfinder"
|
||||||
|
build: '.'
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.docker.network=web"
|
||||||
|
- "traefik.http.services.pf.loadbalancer.server.port=80"
|
||||||
|
- "traefik.http.routers.pf.rule=Host(`${DOMAIN}`)"
|
||||||
|
- "traefik.http.routers.pf.entrypoints=web"
|
||||||
|
- "traefik.http.routers.pf-secure.rule=Host(`${DOMAIN}`)"
|
||||||
|
- "traefik.http.routers.pf-secure.entrypoints=websecure"
|
||||||
|
- "traefik.http.routers.pf-secure.tls=true"
|
||||||
|
- "traefik.http.middlewares.redirecthttps.redirectscheme.scheme=https"
|
||||||
|
- "traefik.http.routers.pf.middlewares=redirecthttps"
|
||||||
|
networks:
|
||||||
|
- pf
|
||||||
|
- web
|
||||||
|
healthcheck:
|
||||||
|
disable: true
|
||||||
|
volumes:
|
||||||
|
- ${path}/config/pathfinder/config.ini:/var/www/html/pathfinder/app/templateConfig.ini
|
||||||
|
- ${path}/config/pathfinder/pathfinder.ini:/var/www/html/pathfinder/app/pathfinder.ini
|
||||||
|
- ${path}/config/pathfinder/plugin.ini:/var/www/html/pathfinder/app/plugin.ini
|
||||||
|
depends_on:
|
||||||
|
- pfdb
|
||||||
|
- pf-redis
|
||||||
|
- pf-socket
|
||||||
|
restart: always
|
||||||
|
traefik:
|
||||||
|
image: "traefik:v2.3"
|
||||||
|
container_name: "traefik"
|
||||||
|
command:
|
||||||
|
- "--log.level=ERROR"
|
||||||
|
- "--api.insecure=true"
|
||||||
|
- "--providers.docker=true"
|
||||||
|
- "--providers.docker.exposedbydefault=false"
|
||||||
|
- "--entrypoints.web.address=:80"
|
||||||
|
- "--entrypoints.websecure.address=:443"
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
- "443:443"
|
||||||
|
- "8080:8080"
|
||||||
|
volumes:
|
||||||
|
- "/var/run/docker.sock:/var/run/docker.sock:ro"
|
||||||
|
networks:
|
||||||
|
- web
|
||||||
|
restart: always
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
data:
|
||||||
|
db_data:
|
||||||
|
redis_data:
|
||||||
|
networks:
|
||||||
|
pf:
|
||||||
|
web:
|
||||||
|
external: true
|
||||||
|
|
@ -15,7 +15,7 @@ services:
|
||||||
- $path/pathfinder/export/sql/eve_universe.sql.zip:/eve_universe.sql.zip
|
- $path/pathfinder/export/sql/eve_universe.sql.zip:/eve_universe.sql.zip
|
||||||
restart: always
|
restart: always
|
||||||
pf-redis:
|
pf-redis:
|
||||||
image: redis:latest
|
image: redis:6.2.5-alpine3.14
|
||||||
container_name: "$CONTAINER_NAME-redis"
|
container_name: "$CONTAINER_NAME-redis"
|
||||||
command: ["redis-server", "--appendonly", "yes"]
|
command: ["redis-server", "--appendonly", "yes"]
|
||||||
hostname: redis
|
hostname: redis
|
||||||
|
|
@ -46,29 +46,64 @@ services:
|
||||||
build: '.'
|
build: '.'
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
# labels:
|
labels:
|
||||||
# caddy: map.goryn.wtf
|
- "traefik.enable=true"
|
||||||
# caddy.reverse_proxy: "{{upstreams 80}}"
|
- "traefik.docker.network=web"
|
||||||
|
- "traefik.http.services.pf.loadbalancer.server.port=80"
|
||||||
|
- "traefik.http.routers.pf.rule=Host(`${DOMAIN}`)"
|
||||||
|
- "traefik.http.routers.pf.entrypoints=web"
|
||||||
|
- "traefik.http.routers.pf-secure.rule=Host(`${DOMAIN}`)"
|
||||||
|
- "traefik.http.routers.pf-secure.entrypoints=websecure"
|
||||||
|
- "traefik.http.routers.pf-secure.tls=true"
|
||||||
|
- "traefik.http.routers.pf-secure.tls.certresolver=letsencrypt"
|
||||||
|
- "traefik.http.middlewares.redirecthttps.redirectscheme.scheme=https"
|
||||||
|
- "traefik.http.routers.pf.middlewares=redirecthttps"
|
||||||
networks:
|
networks:
|
||||||
- pf
|
- pf
|
||||||
# - caddy
|
- web
|
||||||
ports:
|
|
||||||
- 80:80
|
|
||||||
- 8030:8030
|
|
||||||
healthcheck:
|
healthcheck:
|
||||||
disable: true
|
disable: true
|
||||||
volumes:
|
volumes:
|
||||||
- ${path}/config/pathfinder/config.ini:/var/www/html/pathfinder/app/templateConfig.ini
|
- ${path}/config/pathfinder/config.ini:/var/www/html/pathfinder/app/templateConfig.ini
|
||||||
- ${path}/config/pathfinder/pathfinder.ini:/var/www/html/pathfinder/app/pathfinder.ini
|
- ${path}/config/pathfinder/pathfinder.ini:/var/www/html/pathfinder/app/pathfinder.ini
|
||||||
- ${path}/config/pathfinder/plugins.ini:/var/www/html/pathfinder/app/plugins.ini
|
- ${path}/config/pathfinder/plugin.ini:/var/www/html/pathfinder/app/plugin.ini
|
||||||
depends_on:
|
depends_on:
|
||||||
- pfdb
|
- pfdb
|
||||||
- pf-redis
|
- pf-redis
|
||||||
- pf-socket
|
- pf-socket
|
||||||
restart: always
|
restart: always
|
||||||
|
traefik:
|
||||||
|
image: "traefik:v2.3"
|
||||||
|
container_name: "traefik"
|
||||||
|
command:
|
||||||
|
- "--log.level=ERROR"
|
||||||
|
- "--api.insecure=true"
|
||||||
|
- "--providers.docker=true"
|
||||||
|
- "--providers.docker.exposedbydefault=false"
|
||||||
|
- "--entrypoints.web.address=:80"
|
||||||
|
- "--entrypoints.websecure.address=:443"
|
||||||
|
- "--certificatesresolvers.letsencrypt.acme.httpchallenge=true"
|
||||||
|
- "--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web"
|
||||||
|
# Remove below line when ready for production
|
||||||
|
- "--certificatesresolvers.letsencrypt.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
|
||||||
|
- "--certificatesresolvers.letsencrypt.acme.email=MYEMAIL@someemailprobablygmail.com"
|
||||||
|
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
- "443:443"
|
||||||
|
- "8080:8080"
|
||||||
|
volumes:
|
||||||
|
- "/var/run/docker.sock:/var/run/docker.sock:ro"
|
||||||
|
- "./letsencrypt:/letsencrypt"
|
||||||
|
networks:
|
||||||
|
- web
|
||||||
|
restart: always
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
|
data:
|
||||||
db_data:
|
db_data:
|
||||||
redis_data:
|
redis_data:
|
||||||
networks:
|
networks:
|
||||||
pf:
|
pf:
|
||||||
|
web:
|
||||||
|
external: true
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
Subproject commit c3afe9ce73bf4bfb484ed5ae02eb29b5a8427f5b
|
|
||||||
451
static/menu.sh
451
static/menu.sh
|
|
@ -1,451 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
##
|
|
||||||
# Pure BASH interactive CLI/TUI menu (single and multi-select/checkboxes)
|
|
||||||
#
|
|
||||||
# Author: Markus Geiger <mg@evolution515.net>
|
|
||||||
# Last revised 2011-09-11
|
|
||||||
#
|
|
||||||
# ATTENTION! TO BE REFACTORED! FIRST DRAFT!
|
|
||||||
#
|
|
||||||
# Demo
|
|
||||||
#
|
|
||||||
# - ASCIINEMA
|
|
||||||
# https://asciinema.org/a/Y4hLxnN20JtAlrn3hsC6dCRn8
|
|
||||||
#
|
|
||||||
# Inspired by
|
|
||||||
#
|
|
||||||
# - https://serverfault.com/questions/144939/multi-select-menu-in-bash-script
|
|
||||||
# - Copyright (C) 2017 Ingo Hollmann - All Rights Reserved
|
|
||||||
# https://www.bughunter2k.de/blog/cursor-controlled-selectmenu-in-bash
|
|
||||||
#
|
|
||||||
# Notes
|
|
||||||
#
|
|
||||||
# - This is a hacky first implementation for my shell tools/dotfiles (ZSH)
|
|
||||||
# - Intention is to use it for CLI wizards (my aim is NOT a full blown curses TUI window interface)
|
|
||||||
# - I concerted TPUT to ANSII-sequences to spare command executions (e.g. `tput ed | xxd`)
|
|
||||||
# reference: http://linuxcommand.org/lc3_adv_tput.php
|
|
||||||
#
|
|
||||||
# Permission to copy and modify is granted under the Creative Commons Attribution 4.0 license
|
|
||||||
#
|
|
||||||
|
|
||||||
# Strict bash scripting (not yet)
|
|
||||||
# set -euo pipefail -o errtrace
|
|
||||||
|
|
||||||
|
|
||||||
# Templates for ui_widget_select
|
|
||||||
declare -xr UI_WIDGET_SELECT_TPL_SELECTED='\e[33m → %s \e[39m'
|
|
||||||
declare -xr UI_WIDGET_SELECT_TPL_DEFAULT=" \e[37m%s %s\e[39m"
|
|
||||||
declare -xr UI_WIDGET_MULTISELECT_TPL_SELECTED="\e[33m → %s %s\e[39m"
|
|
||||||
declare -xr UI_WIDGET_MULTISELECT_TPL_DEFAULT=" \e[37m%s %s\e[39m"
|
|
||||||
declare -xr UI_WIDGET_TPL_CHECKED="▣"
|
|
||||||
declare -xr UI_WIDGET_TPL_UNCHECKED="□"
|
|
||||||
|
|
||||||
# We use env variable to pass results since no interactive output from subshells and we don't wanna go hacky!
|
|
||||||
declare -xg UI_WIDGET_RC=-1
|
|
||||||
|
|
||||||
|
|
||||||
##
|
|
||||||
# Get type of a BASH variable (BASH ≥v4.0)
|
|
||||||
#
|
|
||||||
# Notes
|
|
||||||
# - if references are encountered it will automatically try
|
|
||||||
# to resolve them unless '-f' is passed!
|
|
||||||
# - resolving functions can be seen as bonus since they also
|
|
||||||
# use `declare` (but with -fF). this behavior should be removed!
|
|
||||||
# - bad indicates bad referencing which normally shouldn't occur!
|
|
||||||
# - types are shorthand and associative arrays map to "map" for convenience
|
|
||||||
#
|
|
||||||
# argument
|
|
||||||
# -f (optional) force resolvement of first hit
|
|
||||||
# <variable-name> Variable name
|
|
||||||
#
|
|
||||||
# stdout
|
|
||||||
# (nil|number|array|map|reference)
|
|
||||||
#
|
|
||||||
# stderr
|
|
||||||
# -
|
|
||||||
#
|
|
||||||
# return
|
|
||||||
# 0 - always
|
|
||||||
typeof() {
|
|
||||||
# __ref: avoid local to overwrite global var declaration and therefore emmit wrong results!
|
|
||||||
local type="" resolve_ref=true __ref="" signature=()
|
|
||||||
if [[ "$1" == "-f" ]]; then
|
|
||||||
# do not resolve reference
|
|
||||||
resolve_ref=false; shift;
|
|
||||||
fi
|
|
||||||
__ref="$1"
|
|
||||||
while [[ -z "${type}" ]] || ( ${resolve_ref} && [[ "${type}" == *n* ]] ); do
|
|
||||||
IFS=$'\x20\x0a\x3d\x22' && signature=($(declare -p "$__ref" 2>/dev/null || echo "na"))
|
|
||||||
if [[ ! "${signature}" == "na" ]]; then
|
|
||||||
type="${signature[1]}" # could be -xn!
|
|
||||||
fi
|
|
||||||
if [[ -z "${__ref}" ]] || [[ "${type}" == "na" ]] || [[ "${type}" == "" ]]; then
|
|
||||||
printf "nil"
|
|
||||||
return 0
|
|
||||||
elif [[ "${type}" == *n* ]]; then
|
|
||||||
__ref="${signature[4]}"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
case "$type" in
|
|
||||||
*i*) printf "number";;
|
|
||||||
*a*) printf "array";;
|
|
||||||
*A*) printf "map";;
|
|
||||||
*n*) printf "reference";;
|
|
||||||
*) printf "string";;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
##
|
|
||||||
# Removes a value from an array
|
|
||||||
#
|
|
||||||
# alternatives
|
|
||||||
# array=( "${array[@]/$delete}"
|
|
||||||
#
|
|
||||||
# arguments
|
|
||||||
# arg1 value
|
|
||||||
# arg* list or stdin
|
|
||||||
#
|
|
||||||
# stdout
|
|
||||||
# list with space seperator
|
|
||||||
array_without_value() {
|
|
||||||
local args=() value="${1}" s
|
|
||||||
shift
|
|
||||||
for s in "${@}"; do
|
|
||||||
if [ "${value}" != "${s}" ]; then
|
|
||||||
args+=("${s}")
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
echo "${args[@]}"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
##
|
|
||||||
# check if a value is in an array
|
|
||||||
#
|
|
||||||
# alternatives
|
|
||||||
# array=( "${array[@]/$delete}"
|
|
||||||
#
|
|
||||||
# arguments
|
|
||||||
# arg1 value
|
|
||||||
# arg* list or stdin
|
|
||||||
#
|
|
||||||
# stdout
|
|
||||||
# list with space seperator
|
|
||||||
array_contains_value() {
|
|
||||||
local e match="$1"
|
|
||||||
shift
|
|
||||||
for e; do [[ "$e" == "$match" ]] && return 0; done
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
##
|
|
||||||
# BASH only string to hex
|
|
||||||
#
|
|
||||||
# stdout
|
|
||||||
# hex squence
|
|
||||||
str2hex_echo() {
|
|
||||||
# USAGE: hex_repr=$(str2hex_echo "ABC")
|
|
||||||
# returns "0x410x420x43"
|
|
||||||
local str=${1:-$(cat -)}
|
|
||||||
local fmt=""
|
|
||||||
local chr
|
|
||||||
local -i i
|
|
||||||
printf "0x"
|
|
||||||
for i in `seq 0 $((${#str}-1))`; do
|
|
||||||
chr=${str:i:1}
|
|
||||||
printf "%x" "'${chr}"
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
##
|
|
||||||
# Read key and map to human readable output
|
|
||||||
#
|
|
||||||
# notes
|
|
||||||
# output prefix (concated by `-`)
|
|
||||||
# c ctrl key
|
|
||||||
# a alt key
|
|
||||||
# c-a ctrl+alt key
|
|
||||||
# use F if you mean shift!
|
|
||||||
# uppercase `f` for `c+a` combination is not possible!
|
|
||||||
#
|
|
||||||
# arguments
|
|
||||||
# -d for debugging keycodes (hex output via xxd)
|
|
||||||
# -l lowercase all chars
|
|
||||||
# -l <timeout> timeout
|
|
||||||
#
|
|
||||||
# stdout
|
|
||||||
# mapped key code like in notes
|
|
||||||
ui_key_input() {
|
|
||||||
local key
|
|
||||||
local ord
|
|
||||||
local debug=0
|
|
||||||
local lowercase=0
|
|
||||||
local prefix=''
|
|
||||||
local args=()
|
|
||||||
local opt
|
|
||||||
|
|
||||||
while (( "$#" )); do
|
|
||||||
opt="${1}"
|
|
||||||
shift
|
|
||||||
case "${opt}" in
|
|
||||||
"-d") debug=1;;
|
|
||||||
"-l") lowercase=1;;
|
|
||||||
"-t") args+=(-t $1); shift;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
IFS= read ${args[@]} -rsn1 key 2>/dev/null >&2
|
|
||||||
read -sN1 -t 0.0001 k1; read -sN1 -t 0.0001 k2; read -sN1 -t 0.0001 k3
|
|
||||||
key+="${k1}${k2}${k3}"
|
|
||||||
if [[ "${debug}" -eq 1 ]]; then echo -n "${key}" | str2hex_echo; echo -n " : " ;fi;
|
|
||||||
case "${key}" in
|
|
||||||
'') key=enter;;
|
|
||||||
' ') key=space;;
|
|
||||||
$'\x1b') key=esc;;
|
|
||||||
$'\x1b\x5b\x36\x7e') key=pgdown;;
|
|
||||||
$'\x1b\x5b\x33\x7e') key=erase;;
|
|
||||||
$'\x7f') key=backspace;;
|
|
||||||
$'\e[A'|$'\e0A '|$'\e[D'|$'\e0D') key=up;;
|
|
||||||
$'\e[B'|$'\e0B'|$'\e[C'|$'\e0C') key=down;;
|
|
||||||
$'\e[1~'|$'\e0H'|$'\e[H') key=home;;
|
|
||||||
$'\e[4~'|$'\e0F'|$'\e[F') key=end;;
|
|
||||||
$'\e') key=enter;;
|
|
||||||
$'\e'?) prefix="a-"; key="${key:1:1}";;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# only lowercase if we have a single letter
|
|
||||||
# ctrl key is hidden within char code (no o)
|
|
||||||
if [[ "${#key}" == 1 ]]; then
|
|
||||||
ord=$(LC_CTYPE=C printf '%d' "'${key}")
|
|
||||||
if [[ "${ord}" -lt 32 ]]; then
|
|
||||||
prefix="c-${prefix}"
|
|
||||||
# ord=$(([##16] ord + 0x60))
|
|
||||||
# let "ord = [##16] ${ord} + 0x60"
|
|
||||||
ord="$(printf "%X" $((ord + 0x60)))"
|
|
||||||
key="$(printf "\x${ord}")"
|
|
||||||
fi
|
|
||||||
if [[ "${lowercase}" -eq 1 ]]; then
|
|
||||||
key="${key,,}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "${prefix}${key}"
|
|
||||||
}
|
|
||||||
|
|
||||||
##
|
|
||||||
# UI Widget Select
|
|
||||||
#
|
|
||||||
# arguments
|
|
||||||
# -i <[menu-item(s)] …> menu items
|
|
||||||
# -m activate multi-select mode (checkboxes)
|
|
||||||
# -k <[key(s)] …> keys for menu items (if none given indexes are used)
|
|
||||||
# -s <[selected-keys(s)] …> selected keys (index or key)
|
|
||||||
# if keys are used selection needs to be keys
|
|
||||||
# -c clear complete menu on exit
|
|
||||||
# -l clear menu and leave selections
|
|
||||||
#
|
|
||||||
# env
|
|
||||||
# UI_WIDGET_RC will be selected index or -1 of nothing was selected
|
|
||||||
#
|
|
||||||
# stdout
|
|
||||||
# menu display - don't use subshell since we need interactive shell and use tput!
|
|
||||||
#
|
|
||||||
# stderr
|
|
||||||
# sometimes (trying to clean up)
|
|
||||||
#
|
|
||||||
# return
|
|
||||||
# 0 success
|
|
||||||
# -1 cancelled
|
|
||||||
ui_widget_select() {
|
|
||||||
local menu=() keys=() selection=() selection_index=()
|
|
||||||
local cur=0 oldcur=0 collect="item" select="one"
|
|
||||||
local sel="" marg="" drawn=false ref v=""
|
|
||||||
local opt_clearonexit=false opt_leaveonexit=false
|
|
||||||
export UI_WIDGET_RC=-1
|
|
||||||
while (( "$#" )); do
|
|
||||||
opt="${1}"; shift
|
|
||||||
case "${opt}" in
|
|
||||||
-k) collect="key";;
|
|
||||||
-i) collect="item";;
|
|
||||||
-s) collect="selection";;
|
|
||||||
-m) select="multi";;
|
|
||||||
-l) opt_clearonexit=true; opt_leaveonexit=true;;
|
|
||||||
-c) opt_clearonexit=true;;
|
|
||||||
*)
|
|
||||||
if [[ "${collect}" == "selection" ]]; then
|
|
||||||
selection+=("${opt}")
|
|
||||||
elif [[ "${collect}" == "key" ]]; then
|
|
||||||
keys+=("${opt}")
|
|
||||||
else
|
|
||||||
menu+=("$opt")
|
|
||||||
fi;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
|
|
||||||
# sanity check
|
|
||||||
if [[ "${#menu[@]}" -eq 0 ]]; then
|
|
||||||
>&2 echo "no menu items given"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "${#keys[@]}" -gt 0 ]]; then
|
|
||||||
# if keys are used
|
|
||||||
# sanity check
|
|
||||||
if [[ "${#keys[@]}" -gt 0 ]] && [[ "${#keys[@]}" != "${#menu[@]}" ]]; then
|
|
||||||
>&2 echo "number of keys do not match menu options!"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
# map keys to indexes
|
|
||||||
selection_index=()
|
|
||||||
for sel in "${selection[@]}"; do
|
|
||||||
for ((i=0;i<${#keys[@]};i++)); do
|
|
||||||
if [[ "${keys[i]}" == "${sel}" ]]; then
|
|
||||||
selection_index+=("$i")
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
done
|
|
||||||
else
|
|
||||||
# if no keys are used assign by indexes
|
|
||||||
selection_index=(${selection[@]})
|
|
||||||
fi
|
|
||||||
|
|
||||||
clear_menu() {
|
|
||||||
local str=""
|
|
||||||
for i in "${menu[@]}"; do str+="\e[2K\r\e[1A"; done
|
|
||||||
echo -en "${str}"
|
|
||||||
}
|
|
||||||
|
|
||||||
##
|
|
||||||
# draws menu in three different states
|
|
||||||
# - initial: draw every line as intenden
|
|
||||||
# - update: only draw updated lines and skip existing
|
|
||||||
# - exit: only draw selected lines
|
|
||||||
draw_menu() {
|
|
||||||
local mode="${initial:-$1}" check=false check_tpl="" str="" msg="" tpl_selected="" tpl_default="" marg=()
|
|
||||||
|
|
||||||
if ${drawn} && [[ "$mode" != "exit" ]]; then
|
|
||||||
# reset position
|
|
||||||
str+="\r\e[2K"
|
|
||||||
for i in "${menu[@]}"; do str+="\e[1A"; done
|
|
||||||
# str+="${TPUT_ED}"
|
|
||||||
fi
|
|
||||||
if [[ "$select" == "one" ]]; then
|
|
||||||
tpl_selected="$UI_WIDGET_SELECT_TPL_SELECTED"
|
|
||||||
tpl_default="$UI_WIDGET_SELECT_TPL_DEFAULT"
|
|
||||||
else
|
|
||||||
tpl_selected="$UI_WIDGET_MULTISELECT_TPL_SELECTED"
|
|
||||||
tpl_default="$UI_WIDGET_MULTISELECT_TPL_DEFAULT"
|
|
||||||
fi
|
|
||||||
|
|
||||||
for ((i=0;i<${#menu[@]};i++)); do
|
|
||||||
check=false
|
|
||||||
if [[ "$select" == "one" ]]; then
|
|
||||||
# single selection
|
|
||||||
marg=("${menu[${i}]}")
|
|
||||||
if [[ ${cur} == ${i} ]]; then
|
|
||||||
check=true
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
# multi-select
|
|
||||||
check_tpl="$UI_WIDGET_TPL_UNCHECKED";
|
|
||||||
if array_contains_value "$i" "${selection_index[@]}"; then
|
|
||||||
check_tpl="$UI_WIDGET_TPL_CHECKED"; check=true
|
|
||||||
fi
|
|
||||||
marg=("${check_tpl}" "${menu[${i}]}")
|
|
||||||
fi
|
|
||||||
if [[ "${mode}" != "exit" ]] && [[ ${cur} == ${i} ]]; then
|
|
||||||
str+="$(printf "\e[2K${tpl_selected}" "${marg[@]}")\n";
|
|
||||||
elif ([[ "${mode}" != "exit" ]] && ([[ "${oldcur}" == "${i}" ]] || [[ "${mode}" == "initial" ]])) || (${check} && [[ "${mode}" == "exit" ]]); then
|
|
||||||
str+="$(printf "\e[2K${tpl_default}" "${marg[@]}")\n";
|
|
||||||
elif [[ "${mode}" -eq "update" ]] && [[ "${mode}" != "exit" ]]; then
|
|
||||||
str+="\e[1B\r"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
echo -en "${str}"
|
|
||||||
export drawn=true
|
|
||||||
}
|
|
||||||
|
|
||||||
# initial draw
|
|
||||||
draw_menu initial
|
|
||||||
|
|
||||||
# action loop
|
|
||||||
while true; do
|
|
||||||
oldcur=${cur}
|
|
||||||
key=$(ui_key_input)
|
|
||||||
case "${key}" in
|
|
||||||
up|left|i|j) ((cur > 0)) && ((cur--));;
|
|
||||||
down|right|k|l) ((cur < ${#menu[@]}-1)) && ((cur++));;
|
|
||||||
home) cur=0;;
|
|
||||||
pgup) let cur-=5; if [[ "${cur}" -lt 0 ]]; then cur=0; fi;;
|
|
||||||
pgdown) let cur+=5; if [[ "${cur}" -gt $((${#menu[@]}-1)) ]]; then cur=$((${#menu[@]}-1)); fi;;
|
|
||||||
end) ((cur=${#menu[@]}-1));;
|
|
||||||
space)
|
|
||||||
if [[ "$select" == "one" ]]; then
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
if ! array_contains_value "$cur" "${selection_index[@]}"; then
|
|
||||||
selection_index+=("$cur")
|
|
||||||
else
|
|
||||||
selection_index=($(array_without_value "$cur" "${selection_index[@]}"))
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
enter)
|
|
||||||
if [[ "${select}" == "multi" ]]; then
|
|
||||||
export UI_WIDGET_RC=()
|
|
||||||
for i in ${selection_index[@]}; do
|
|
||||||
if [[ "${#keys[@]}" -gt 0 ]]; then
|
|
||||||
export UI_WIDGET_RC+=("${keys[${i}]}")
|
|
||||||
else
|
|
||||||
export UI_WIDGET_RC+=("${i}")
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
else
|
|
||||||
if [[ "${#keys[@]}" -gt 0 ]]; then
|
|
||||||
export UI_WIDGET_RC="${keys[${cur}]}";
|
|
||||||
else
|
|
||||||
export UI_WIDGET_RC=${cur};
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
if $opt_clearonexit; then clear_menu; fi
|
|
||||||
if $opt_leaveonexit; then draw_menu exit; fi
|
|
||||||
return
|
|
||||||
;;
|
|
||||||
[1-9])
|
|
||||||
let "cur = ${key}"
|
|
||||||
if [[ ${#menu[@]} -gt 9 ]]; then
|
|
||||||
echo -n "${key}"
|
|
||||||
sleep 1
|
|
||||||
key="$(ui_key_input -t 0.5 )"
|
|
||||||
if [[ "$key" =~ [0-9] ]]; then
|
|
||||||
let "cur = cur * 10 + ${key}"
|
|
||||||
elif [[ "$key" != "enter" ]]; then
|
|
||||||
echo -en "\e[2K\r$key invalid input!"
|
|
||||||
sleep 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
let "cur = cur - 1"
|
|
||||||
if [[ ${cur} -gt ${#menu[@]}-1 ]]; then
|
|
||||||
echo -en "\e[2K\rinvalid index!"
|
|
||||||
sleep 1
|
|
||||||
cur="${oldcur}"
|
|
||||||
fi
|
|
||||||
echo -en "\e[2K\r"
|
|
||||||
;;
|
|
||||||
esc|q|$'\e')
|
|
||||||
if $opt_clearonexit; then clear_menu; fi
|
|
||||||
return 1;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# Redraw menu
|
|
||||||
draw_menu update
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
Subproject commit f6b4f5ae309e21ad793197a77b5d095a33cfc9f1
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue