modifying things for portainer

This commit is contained in:
2025-11-26 20:07:00 -08:00
parent a2a5f34c51
commit 52f1705324
18 changed files with 74 additions and 433 deletions
+41 -26
View File
@@ -59,7 +59,6 @@ def find_logo_url(dir_path: Path, nologo: bool = False) -> str:
return f"{base_url}default.png" return f"{base_url}default.png"
# --------------------------------------------------------- # ---------------------------------------------------------
# Parse image from docker-compose.yml using sample.env defaults # Parse image from docker-compose.yml using sample.env defaults
# --------------------------------------------------------- # ---------------------------------------------------------
@@ -80,9 +79,7 @@ def parse_image(compose_path: Path, env_vars: list) -> str:
bash_default = match.group(2) bash_default = match.group(2)
return env_defaults.get(var_name, bash_default if bash_default else "") return env_defaults.get(var_name, bash_default if bash_default else "")
# Regex matches ${VAR} or ${VAR:-default}
image = re.sub(r"\$\{([^:}]+)(?:[:-]([^}]+))?\}", replace_var, image) image = re.sub(r"\$\{([^:}]+)(?:[:-]([^}]+))?\}", replace_var, image)
return image return image
return "" return ""
@@ -107,37 +104,47 @@ def parse_ports(compose_path: Path):
if in_ports: if in_ports:
if not stripped or not stripped.startswith("-"): if not stripped or not stripped.startswith("-"):
break break
# Remove "- " prefix, quotes, and strip comments
port_str = stripped[1:].strip().split("#", 1)[0].strip().strip('"').strip("'") port_str = stripped[1:].strip().split("#", 1)[0].strip().strip('"').strip("'")
if not port_str: if not port_str:
continue continue
if ":" in port_str:
host_port, container_port = port_str.split(":", 1) protocol = "tcp"
if "/" in port_str:
port_part, protocol_part = port_str.split("/", 1)
protocol = protocol_part.strip()
else: else:
container_port = port_str port_part = port_str
if ":" in port_part:
host_port, container_port = port_part.split(":", 1)
else:
container_port = port_part
host_port = container_port host_port = container_port
try: try:
container_port = int(container_port.split("/")[0].strip()) container_port = int(container_port.strip())
host_port = int(host_port.split("/")[0].strip()) host_port = int(host_port.strip())
except ValueError: except ValueError:
continue continue
ports_list.append({ ports_list.append({
"container": container_port, "container": container_port,
"published": host_port, "published": host_port,
"protocol": "tcp" "protocol": protocol
}) })
return ports_list return ports_list
# ---------------------------------------------------------
# Parse volumes from docker-compose.yml
# Returns a list of dicts: {"name": "volume_name", "container": "/container/path"}
# ---------------------------------------------------------
# --------------------------------------------------------- # ---------------------------------------------------------
# Parse volumes from docker-compose.yml # Parse volumes from docker-compose.yml
# Returns a list of dicts: {"name": "unique-name", "container": "/container/path"} # Returns a list of dicts: {"name": "unique-name", "container": "/container/path"}
# --------------------------------------------------------- # ---------------------------------------------------------
def parse_volumes(compose_path: Path, stack_name: str): def parse_volumes(compose_path: Path, stack_name: str):
"""
Parse volumes from docker-compose.yml and generate Portainer-ready entries.
Host paths and VOL_PATH references are ignored; only container paths are used.
"""
if not compose_path.exists(): if not compose_path.exists():
return [] return []
@@ -153,15 +160,27 @@ def parse_volumes(compose_path: Path, stack_name: str):
if in_volumes: if in_volumes:
if not stripped or not stripped.startswith("-"): if not stripped or not stripped.startswith("-"):
break break
# Strip "- ", quotes, inline comments
volume_str = stripped[1:].strip().split("#", 1)[0].strip().strip('"').strip("'") volume_str = stripped[1:].strip().split("#", 1)[0].strip().strip('"').strip("'")
if not volume_str: if not volume_str:
continue continue
parts = volume_str.rsplit(":", 1)
container_path = parts[-1].strip() # Split host:container[:ro|rw]
parts = volume_str.split(":")
if len(parts) == 1:
container_path = parts[0].strip()
else:
container_path = parts[-1].strip() # always take the last part as container path
# Remove :ro or :rw suffix if present
container_path = re.sub(r":(ro|rw)$", "", container_path) container_path = re.sub(r":(ro|rw)$", "", container_path)
# Generate unique name for Portainer from stack + container path
name = f"{stack_name}-{container_path.strip('/').replace('/', '-')}" name = f"{stack_name}-{container_path.strip('/').replace('/', '-')}"
if not name: if not name:
name = f"{stack_name}-volume" name = f"{stack_name}-volume"
volumes.append({"name": name, "container": container_path}) volumes.append({"name": name, "container": container_path})
return volumes return volumes
@@ -188,7 +207,6 @@ def parse_env_vars(dir_path: Path):
if "=" in line: if "=" in line:
key, value = line.split("=", 1) key, value = line.split("=", 1)
key = key.strip() key = key.strip()
# Remove inline comment after #, then strip quotes and whitespace
value = value.split("#", 1)[0].strip().strip('"').strip("'") value = value.split("#", 1)[0].strip().strip('"').strip("'")
if key: if key:
env_list.append({"name": key, "default": value}) env_list.append({"name": key, "default": value})
@@ -198,25 +216,23 @@ def parse_env_vars(dir_path: Path):
# --------------------------------------------------------- # ---------------------------------------------------------
# Build template object for a stack directory # Build template object for a stack directory
# --------------------------------------------------------- # ---------------------------------------------------------
def generate_template_object(dir_path: Path): def generate_template_object(dir_path: Path, nologo: bool = False):
name = dir_path.name name = dir_path.name
compose_file = dir_path / "docker-compose.yml" compose_file = dir_path / "docker-compose.yml"
readme_file = dir_path / "README.md" readme_file = dir_path / "README.md"
# Extract info
description = extract_overview(readme_file, name) description = extract_overview(readme_file, name)
logo = find_logo_url(dir_path, nologo=False) # for dev/test logo = find_logo_url(dir_path, nologo=False)
env_vars = parse_env_vars(dir_path) env_vars = parse_env_vars(dir_path)
image = parse_image(compose_file, env_vars) image = parse_image(compose_file, env_vars)
ports = parse_ports(compose_file) ports = parse_ports(compose_file)
volumes = parse_volumes(compose_file, name) # pass stack name for unique volume naming volumes = parse_volumes(compose_file, name)
# Environment variables entries for Portainer
env_entries = [ env_entries = [
{"name": v["name"], "label": v["name"], "default": v["default"]} for v in env_vars {"name": v["name"], "label": v["name"], "default": v["default"]}
for v in env_vars if v["default"] is not None
] ]
# Build template object
return { return {
"type": 1, "type": 1,
"title": name, "title": name,
@@ -252,9 +268,8 @@ def main():
if not (item / "docker-compose.yml").exists(): if not (item / "docker-compose.yml").exists():
continue continue
templates.append(generate_template_object(item)) templates.append(generate_template_object(item, nologo=True))
# Wrap in Portainer top-level object
output_data = { output_data = {
"version": "3", "version": "3",
"templates": templates "templates": templates
+5 -2
View File
@@ -3,9 +3,12 @@ services:
image: ${ARCHIVEBOX_IMAGE:-archivebox/archivebox:stable} image: ${ARCHIVEBOX_IMAGE:-archivebox/archivebox:stable}
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- ${VOL_PATH:-./data}/archive-data:/data - "archivebox-data:/data"
environment: environment:
- ARCHIVEBOX_UID=1000 - ARCHIVEBOX_UID=1000
- ARCHIVEBOX_GID=1000 - ARCHIVEBOX_GID=1000
- PYTHONUNBUFFERED=1 # Ensures logs are output in real-time - PYTHONUNBUFFERED=1
command: server --quick-init 0.0.0.0:8000 command: server --quick-init 0.0.0.0:8000
volumes:
archivebox-data:
-3
View File
@@ -1,5 +1,2 @@
COOKBOOK=/git/docker-compose-cookbooks #HELP: cookbooks
VOL_PATH=./data
# ARCHIVEBOX # ARCHIVEBOX
ARCHIVEBOX_IMAGE=archivebox/archivebox:sha-1d49bee ARCHIVEBOX_IMAGE=archivebox/archivebox:sha-1d49bee
+13 -7
View File
@@ -1,13 +1,19 @@
services: services:
mariadb: mariadb:
image: ${MARIADB_IMAGE:-mariadb:latest} image: ${MARIADB_IMAGE:-mariadb:latest}
restart: unless-stopped restart: unless-stopped
command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW --innodb-file-per-table=1 --skip-innodb-read-only-compressed command: >
--transaction-isolation=READ-COMMITTED
--binlog-format=ROW
--innodb-file-per-table=1
--skip-innodb-read-only-compressed
environment: environment:
- MARIADB_DATABASE=${MARIADB_MARIADB_DATABASE:-mydb} - MARIADB_DATABASE=${MARIADB_DATABASE:-mydb}
- MARIADB_ROOT_PASSWORD=${MARIADB_MARIADB_ROOT_PASSWORD:-ChangeThisPassword} - MARIADB_ROOT_PASSWORD=${MARIADB_ROOT_PASSWORD:-ChangeThisPassword}
- MARIADB_USER=${MARIADB_MARIADB_USER:-dbuser} - MARIADB_USER=${MARIADB_USER:-dbuser}
- MARIADB_PASSWORD=${MARIADB_MARIADB_PASSWORD:-AlsoChangeThisPassword} - MARIADB_PASSWORD=${MARIADB_PASSWORD:-AlsoChangeThisPassword}
volumes: volumes:
- ${VOL_PATH:-./data/}/mariadb:/var/lib/mysql - mariadb-data:/var/lib/mysql
volumes:
mariadb-data:
-6
View File
@@ -1,9 +1,3 @@
# Global
COMPOSE_PROJECT_NAME=project
TZ=America/Vancouver
VOL_CONFIG_PATH=/data/projectName/config
VOL_PATH=/data/projectName/data
# MariaDB # MariaDB
MARIADB_IMAGE=mariadb:latest MARIADB_IMAGE=mariadb:latest
MARIADB_MARIADB_DATABASE=mydb MARIADB_MARIADB_DATABASE=mydb
-8
View File
@@ -1,8 +0,0 @@
services:
nc:
image: ${NOCODB_IMAGE:-nocodb/nocodb:latest}
restart: unless-stopped
# environment:
# - NC_DB=${NCDB_NC_DB:-mysql2://root_db:3306?u=noco&p=password&d=root_db}
volumes:
- "${VOL_PATH:-./data}/nc_data:/usr/app/data"
@@ -11,3 +11,8 @@ Proxy Port: 8080
## Description ## Description
This project is setup to use sql lite. Although it can use mysql with some env vars. This project is setup to use sql lite. Although it can use mysql with some env vars.
## Notes
# environment:
# - NC_DB=${NCDB_NC_DB:-mysql2://root_db:3306?u=noco&p=password&d=root_db}
+9
View File
@@ -0,0 +1,9 @@
services:
nc:
image: ${NOCODB_IMAGE:-nocodb/nocodb:latest}
restart: unless-stopped
volumes:
- "nocodb-data:/usr/app/data"
volumes:
nocodb-data:
+1 -377
View File
@@ -1,66 +1,6 @@
{ {
"version": "3", "version": "3",
"templates": [ "templates": [
{
"type": 1,
"title": "phpcontainer",
"description": "phpcontainer description to come",
"note": "Auto-generated template from repository",
"categories": [
"Auto"
],
"platform": "linux",
"logo": "https://i.4lt.ca/cookbooks/default.png",
"image": "4lights/phpcontainer:latest",
"repository": {
"url": "https://github.com/nickyeoman/docker-compose-cookbooks",
"stackfile": "phpcontainer/docker-compose.yml",
"stackfile_plain": false
},
"env": [
{
"name": "COOKBOOK",
"label": "COOKBOOK",
"default": "/docker-compose-cookbooks"
},
{
"name": "COMPOSE_PROJECT_NAME",
"label": "COMPOSE_PROJECT_NAME",
"default": "project"
},
{
"name": "TZ",
"label": "TZ",
"default": "America/Vancouver"
},
{
"name": "VOL_CONFIG_PATH",
"label": "VOL_CONFIG_PATH",
"default": "/data/projectName/config"
},
{
"name": "VOL_PATH",
"label": "VOL_PATH",
"default": "/data/projectName/data"
},
{
"name": "PHPCONTAINER_IMAGE",
"label": "PHPCONTAINER_IMAGE",
"default": "4lights/phpcontainer:latest"
}
],
"ports": [],
"volumes": [
{
"name": "phpcontainer-data",
"container": "/data"
},
{
"name": "phpcontainer-usr-local-etc-php-php.ini",
"container": "/usr/local/etc/php/php.ini"
}
]
},
{ {
"type": 1, "type": 1,
"title": "wallabag", "title": "wallabag",
@@ -235,26 +175,6 @@
"stackfile_plain": false "stackfile_plain": false
}, },
"env": [ "env": [
{
"name": "COMPOSE_PROJECT_NAME",
"label": "COMPOSE_PROJECT_NAME",
"default": "project"
},
{
"name": "TZ",
"label": "TZ",
"default": "America/Vancouver"
},
{
"name": "VOL_CONFIG_PATH",
"label": "VOL_CONFIG_PATH",
"default": "/data/projectName/config"
},
{
"name": "VOL_PATH",
"label": "VOL_PATH",
"default": "/data/projectName/data"
},
{ {
"name": "MARIADB_IMAGE", "name": "MARIADB_IMAGE",
"label": "MARIADB_IMAGE", "label": "MARIADB_IMAGE",
@@ -595,7 +515,7 @@
{ {
"container": 53, "container": 53,
"published": 53, "published": 53,
"protocol": "tcp" "protocol": "udp"
}, },
{ {
"container": 80, "container": 80,
@@ -751,117 +671,6 @@
} }
] ]
}, },
{
"type": 1,
"title": "nocodb",
"description": "Docker Hub: https://hub.docker.com/r/nocodb/nocodb Project: https://nocodb.com/ Docker Compose Example: https://github.com/nocodb/nocodb/blob/master/docker-compose/mysql/docker-compose.yml Proxy Port: 8080",
"note": "Auto-generated template from repository",
"categories": [
"Auto"
],
"platform": "linux",
"logo": "https://i.4lt.ca/cookbooks/default.png",
"image": "nocodb/nocodb:latest",
"repository": {
"url": "https://github.com/nickyeoman/docker-compose-cookbooks",
"stackfile": "nocodb/docker-compose.yml",
"stackfile_plain": false
},
"env": [
{
"name": "COOKBOOK",
"label": "COOKBOOK",
"default": "/git/docker-compose-cookbooks"
},
{
"name": "NOCODB_IMAGE",
"label": "NOCODB_IMAGE",
"default": "nocodb/nocodb:latest"
}
],
"ports": [],
"volumes": [
{
"name": "nocodb-usr-app-data",
"container": "/usr/app/data"
}
]
},
{
"type": 1,
"title": "directus",
"description": "directus description to come",
"note": "Auto-generated template from repository",
"categories": [
"Auto"
],
"platform": "linux",
"logo": "https://i.4lt.ca/cookbooks/default.png",
"image": "directus/directus:latest",
"repository": {
"url": "https://github.com/nickyeoman/docker-compose-cookbooks",
"stackfile": "directus/docker-compose.yml",
"stackfile_plain": false
},
"env": [
{
"name": "COMPOSE_PROJECT_NAME",
"label": "COMPOSE_PROJECT_NAME",
"default": "directus"
},
{
"name": "TZ",
"label": "TZ",
"default": "America/Vancouver"
},
{
"name": "VOL_CONFIG_PATH",
"label": "VOL_CONFIG_PATH",
"default": "/docker/directus/config"
},
{
"name": "VOL_PATH",
"label": "VOL_PATH",
"default": "/docker/directus/data"
},
{
"name": "DIRECTUS_SECRET",
"label": "DIRECTUS_SECRET",
"default": "01234567890123456789012345678912"
},
{
"name": "DIRECTUS_ADMIN_EMAIL",
"label": "DIRECTUS_ADMIN_EMAIL",
"default": "noreply@example.com"
},
{
"name": "DIRECTUS_ADMIN_PASSWORD",
"label": "DIRECTUS_ADMIN_PASSWORD",
"default": "enter_secure_password_here"
}
],
"ports": [
{
"container": 8055,
"published": 8000,
"protocol": "tcp"
}
],
"volumes": [
{
"name": "directus-directus-uploads",
"container": "/directus/uploads"
},
{
"name": "directus-directus-extensions",
"container": "/directus/extensions"
},
{
"name": "directus-directus-templates",
"container": "/directus/templates"
}
]
},
{ {
"type": 1, "type": 1,
"title": "matomo", "title": "matomo",
@@ -1882,90 +1691,6 @@
} }
] ]
}, },
{
"type": 1,
"title": "semaphore",
"description": "* [Docker Hub](https://hub.docker.com/r/semaphoreui/semaphore) * [Official Website](https://semaphoreui.com/) * [Docker Compose Example](https://docs.semaphoreui.com/administration-guide/installation/#docker)",
"note": "Auto-generated template from repository",
"categories": [
"Auto"
],
"platform": "linux",
"logo": "https://i.4lt.ca/cookbooks/default.png",
"image": "semaphoreui/semaphore:latest",
"repository": {
"url": "https://github.com/nickyeoman/docker-compose-cookbooks",
"stackfile": "semaphore/docker-compose.yml",
"stackfile_plain": false
},
"env": [
{
"name": "COOKBOOK",
"label": "COOKBOOK",
"default": "/git/docker-compose-cookbooks"
},
{
"name": "TZ",
"label": "TZ",
"default": "America/Vancouver"
},
{
"name": "VOL_CONFIG_PATH",
"label": "VOL_CONFIG_PATH",
"default": "./config"
},
{
"name": "VOL_PATH",
"label": "VOL_PATH",
"default": "./data"
},
{
"name": "SEMAPHORE_IMAGE",
"label": "SEMAPHORE_IMAGE",
"default": "semaphoreui/semaphore:latest"
},
{
"name": "SEMAPHORE_ACCESS_KEY_ENCRYPTION",
"label": "SEMAPHORE_ACCESS_KEY_ENCRYPTION",
"default": "1234567890123456789012345678901234567890"
},
{
"name": "SEMAPHORE_ADMIN_EMAIL",
"label": "SEMAPHORE_ADMIN_EMAIL",
"default": "example@example.com"
},
{
"name": "SEMAPHORE_ADMIN_NAME",
"label": "SEMAPHORE_ADMIN_NAME",
"default": "admin"
},
{
"name": "SEMAPHORE_ADMIN_PASSWORD",
"label": "SEMAPHORE_ADMIN_PASSWORD",
"default": "password"
},
{
"name": "SEMAPHORE_PLAYBOOK_PATH",
"label": "SEMAPHORE_PLAYBOOK_PATH",
"default": "/tmp/semaphore/"
}
],
"ports": [],
"volumes": [
{
"name": "semaphore---tmp-semaphore-}",
"container": "-/tmp/semaphore/}"
},
{
"name": "semaphore-home-semaphore-.ssh",
"container": "/home/semaphore/.ssh/"
},
{
"name": "semaphore-etc-hosts",
"container": "/etc/hosts"
}
]
},
{ {
"type": 1, "type": 1,
"title": "uptimekuma", "title": "uptimekuma",
@@ -2019,21 +1744,6 @@
"stackfile_plain": false "stackfile_plain": false
}, },
"env": [ "env": [
{
"name": "VOL_PATH",
"label": "VOL_PATH",
"default": "./data"
},
{
"name": "TZ",
"label": "TZ",
"default": "America/Vancouver"
},
{
"name": "COMPOSE_PROJECT_NAME",
"label": "COMPOSE_PROJECT_NAME",
"default": "zammad"
},
{ {
"name": "ZAMMAD_RAILS_IMAGE", "name": "ZAMMAD_RAILS_IMAGE",
"label": "ZAMMAD_RAILS_IMAGE", "label": "ZAMMAD_RAILS_IMAGE",
@@ -3497,82 +3207,6 @@
} }
] ]
}, },
{
"type": 1,
"title": "stashapp",
"description": "This Docker Compose configuration sets up the Stash application using Docker containers. - Stash App: [Stash on Docker Hub](https://hub.docker.com/r/stashapp/stash) - Compose Example: [Stash GitHub Repository](https://github.com/stashapp/stash/blob/develop/docker/production/docker-compose.yml) - ProxyPort: 9999",
"note": "Auto-generated template from repository",
"categories": [
"Auto"
],
"platform": "linux",
"logo": "https://i.4lt.ca/cookbooks/default.png",
"image": "stashapp/stash:latest",
"repository": {
"url": "https://github.com/nickyeoman/docker-compose-cookbooks",
"stackfile": "stashapp/docker-compose.yml",
"stackfile_plain": false
},
"env": [
{
"name": "STASHAPP_IMAGE",
"label": "STASHAPP_IMAGE",
"default": "stashapp/stash:latest"
},
{
"name": "STASHAPP_STASH_CACHE",
"label": "STASHAPP_STASH_CACHE",
"default": "/cache/"
},
{
"name": "STASHAPP_STASH_DOMAIN_NAME",
"label": "STASHAPP_STASH_DOMAIN_NAME",
"default": "stash.yourdomain.ca"
},
{
"name": "STASHAPP_STASH_GENERATED",
"label": "STASHAPP_STASH_GENERATED",
"default": "/generated/"
},
{
"name": "STASHAPP_STASH_METADATA",
"label": "STASHAPP_STASH_METADATA",
"default": "/metadata/"
},
{
"name": "STASHAPP_STASH_STASH",
"label": "STASHAPP_STASH_STASH",
"default": "/data/"
}
],
"ports": [],
"volumes": [
{
"name": "stashapp-cache",
"container": "/cache"
},
{
"name": "stashapp-generated",
"container": "/generated"
},
{
"name": "stashapp-metadata",
"container": "/metadata"
},
{
"name": "stashapp-root",
"container": "/root"
},
{
"name": "stashapp-data",
"container": "/data"
},
{
"name": "stashapp-ro",
"container": "ro"
}
]
},
{ {
"type": 1, "type": 1,
"title": "archivebox", "title": "archivebox",
@@ -3590,16 +3224,6 @@
"stackfile_plain": false "stackfile_plain": false
}, },
"env": [ "env": [
{
"name": "COOKBOOK",
"label": "COOKBOOK",
"default": "/git/docker-compose-cookbooks"
},
{
"name": "VOL_PATH",
"label": "VOL_PATH",
"default": "./data"
},
{ {
"name": "ARCHIVEBOX_IMAGE", "name": "ARCHIVEBOX_IMAGE",
"label": "ARCHIVEBOX_IMAGE", "label": "ARCHIVEBOX_IMAGE",
-4
View File
@@ -3,10 +3,6 @@
# https://docs.zammad.org/en/latest/install/docker-compose/environment.html # https://docs.zammad.org/en/latest/install/docker-compose/environment.html
############################################# #############################################
VOL_PATH=./data
TZ=America/Vancouver
COMPOSE_PROJECT_NAME=zammad
ZAMMAD_RAILS_IMAGE=ghcr.io/zammad/zammad:latest ZAMMAD_RAILS_IMAGE=ghcr.io/zammad/zammad:latest
ZAMMAD_RESTART=always ZAMMAD_RESTART=always
scheme=http scheme=http