Compare commits

..

11 Commits

Author SHA1 Message Date
CanbiZ (MickLesk)
a6be48f22b Remove Memos script - project no longer provides release binaries (#11464) 2026-02-02 13:13:59 +01:00
community-scripts-pr-app[bot]
8226360700 Update .app files (#11456)
Co-authored-by: GitHub Actions <github-actions[bot]@users.noreply.github.com>
2026-02-02 09:04:01 +01:00
community-scripts-pr-app[bot]
b706775a4b Update CHANGELOG.md (#11455)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-02 08:02:34 +00:00
push-app-to-main[bot]
2f907cc4e0 KitchenOwl (#11453)
* Add kitchenowl (ct)

* Remove commented line and update wsgi.py config

* Remove existing web directory before deployment

Remove the web directory before deploying the kitchenowl-web release.

* Update kitchenowl.sh

* Update kitchenowl.json

* Update kitchenowl.sh

---------

Co-authored-by: push-app-to-main[bot] <203845782+push-app-to-main[bot]@users.noreply.github.com>
Co-authored-by: CanbiZ (MickLesk) <47820557+MickLesk@users.noreply.github.com>
Co-authored-by: Tobias <96661824+CrazyWolf13@users.noreply.github.com>
2026-02-02 09:02:11 +01:00
community-scripts-pr-app[bot]
b1927d2678 chore: update github-versions.json (#11452)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-02 06:25:21 +00:00
community-scripts-pr-app[bot]
a35b0fbd79 Update CHANGELOG.md (#11451)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-02 00:21:49 +00:00
community-scripts-pr-app[bot]
4418ed4615 chore: update github-versions.json (#11450)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-02 00:21:27 +00:00
community-scripts-pr-app[bot]
19d8592104 Update CHANGELOG.md (#11447)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-01 21:27:52 +00:00
CanbiZ (MickLesk)
627587c54b feat(autocaliweb): migrate from GitHub to Codeberg (#11440)
- Add Codeberg API functions: codeberg_api_call, get_latest_codeberg_release, check_for_codeberg_release
- Add fetch_and_deploy_codeberg_release function for Codeberg releases
- Update autocaliweb install and update scripts to use Codeberg
- Update autocaliweb.json documentation and website URLs
2026-02-01 22:27:31 +01:00
community-scripts-pr-app[bot]
b36609dfd5 Update CHANGELOG.md (#11446)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-01 21:26:40 +00:00
CanbiZ (MickLesk)
98dc00a1a0 fix(2fauth): export PHP_VERSION for nginx config (#11441)
The PHP_VERSION variable was only available within the setup_php
function call scope. By setting it separately before the function
call, it remains available for the nginx configuration heredoc.

Fixes #11439
2026-02-01 22:26:18 +01:00
17 changed files with 990 additions and 225 deletions

View File

@@ -772,6 +772,12 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
</details>
## 2026-02-02
### 🆕 New Scripts
- KitchenOwl ([#11453](https://github.com/community-scripts/ProxmoxVE/pull/11453))
## 2026-02-01
### 🚀 Updated Scripts
@@ -780,6 +786,7 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
- #### 🐞 Bug Fixes
- 2fauth: export PHP_VERSION for nginx config [@MickLesk](https://github.com/MickLesk) ([#11441](https://github.com/community-scripts/ProxmoxVE/pull/11441))
- Prometheus Paperless NGX Exporter: Set correct binary path in systemd unit file [@andygrunwald](https://github.com/andygrunwald) ([#11438](https://github.com/community-scripts/ProxmoxVE/pull/11438))
- tracearr: install/update new prestart script from upstream [@durzo](https://github.com/durzo) ([#11433](https://github.com/community-scripts/ProxmoxVE/pull/11433))
- n8n: Fix dependencies [@tremor021](https://github.com/tremor021) ([#11429](https://github.com/community-scripts/ProxmoxVE/pull/11429))
@@ -788,6 +795,7 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
- #### ✨ New Features
- tools.func: add codeberg functions & autocaliweb: migrate from GitHub to Codeberg [@MickLesk](https://github.com/MickLesk) ([#11440](https://github.com/community-scripts/ProxmoxVE/pull/11440))
- Immich Refactor #2 [@vhsdream](https://github.com/vhsdream) ([#11375](https://github.com/community-scripts/ProxmoxVE/pull/11375))
- #### 🔧 Refactor

View File

@@ -3,7 +3,7 @@ source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxV
# Copyright (c) 2021-2026 community-scripts ORG
# Author: vhsdream
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/gelbphoenix/autocaliweb
# Source: https://codeberg.org/gelbphoenix/autocaliweb
APP="Autocaliweb"
var_tags="${var_tags:-ebooks}"
@@ -30,8 +30,8 @@ function update_script() {
setup_uv
RELEASE=$(get_latest_github_release "gelbphoenix/autocaliweb")
if check_for_gh_release "autocaliweb" "gelbphoenix/autocaliweb"; then
RELEASE=$(get_latest_codeberg_release "gelbphoenix/autocaliweb")
if check_for_codeberg_release "autocaliweb" "gelbphoenix/autocaliweb"; then
msg_info "Stopping Services"
systemctl stop autocaliweb metadata-change-detector acw-ingest-service acw-auto-zipper
msg_ok "Stopped Services"
@@ -39,7 +39,7 @@ function update_script() {
INSTALL_DIR="/opt/autocaliweb"
export VIRTUAL_ENV="${INSTALL_DIR}/venv"
$STD tar -cf ~/autocaliweb_bkp.tar "$INSTALL_DIR"/{metadata_change_logs,dirs.json,.env,scripts/ingest_watcher.sh,scripts/auto_zipper_wrapper.sh,scripts/metadata_change_detector_wrapper.sh}
fetch_and_deploy_gh_release "autocaliweb" "gelbphoenix/autocaliweb" "tarball" "latest" "/opt/autocaliweb"
fetch_and_deploy_codeberg_release "autocaliweb" "gelbphoenix/autocaliweb" "tarball" "latest" "/opt/autocaliweb"
msg_info "Updating Autocaliweb"
cd "$INSTALL_DIR"

6
ct/headers/kitchenowl Normal file
View File

@@ -0,0 +1,6 @@
__ __ _ __ __ ____ __
/ //_/(_) /______/ /_ ___ ____ / __ \_ __/ /
/ ,< / / __/ ___/ __ \/ _ \/ __ \/ / / / | /| / / /
/ /| |/ / /_/ /__/ / / / __/ / / / /_/ /| |/ |/ / /
/_/ |_/_/\__/\___/_/ /_/\___/_/ /_/\____/ |__/|__/_/

View File

@@ -1,6 +0,0 @@
__ ___
/ |/ /__ ____ ___ ____ _____
/ /|_/ / _ \/ __ `__ \/ __ \/ ___/
/ / / / __/ / / / / / /_/ (__ )
/_/ /_/\___/_/ /_/ /_/\____/____/

79
ct/kitchenowl.sh Normal file
View File

@@ -0,0 +1,79 @@
#!/usr/bin/env bash
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
# Copyright (c) 2021-2026 community-scripts ORG
# Author: snazzybean
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/TomBursch/kitchenowl
APP="KitchenOwl"
var_tags="${var_tags:-food;recipes}"
var_cpu="${var_cpu:-1}"
var_ram="${var_ram:-2048}"
var_disk="${var_disk:-6}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
var_unprivileged="${var_unprivileged:-1}"
header_info "$APP"
variables
color
catch_errors
function update_script() {
header_info
check_container_storage
check_container_resources
if [[ ! -d /opt/kitchenowl ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
if check_for_gh_release "kitchenowl" "TomBursch/kitchenowl"; then
msg_info "Stopping Service"
systemctl stop kitchenowl
msg_ok "Stopped Service"
msg_info "Creating Backup"
mkdir -p /opt/kitchenowl_backup
cp -r /opt/kitchenowl/data /opt/kitchenowl_backup/
cp -f /opt/kitchenowl/kitchenowl.env /opt/kitchenowl_backup/
msg_ok "Created Backup"
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "kitchenowl" "TomBursch/kitchenowl" "tarball" "latest" "/opt/kitchenowl"
rm -rf /opt/kitchenowl/web
CLEAN_INSTALL=1 fetch_and_deploy_gh_release "kitchenowl-web" "TomBursch/kitchenowl" "prebuild" "latest" "/opt/kitchenowl/web" "kitchenowl_Web.tar.gz"
msg_info "Restoring data"
sed -i 's/default=True/default=False/' /opt/kitchenowl/backend/wsgi.py
cp -r /opt/kitchenowl_backup/data /opt/kitchenowl/
cp -f /opt/kitchenowl_backup/kitchenowl.env /opt/kitchenowl/
rm -rf /opt/kitchenowl_backup
msg_ok "Restored data"
msg_info "Updating KitchenOwl"
cd /opt/kitchenowl/backend
$STD uv sync --frozen
cd /opt/kitchenowl/backend
set -a
source /opt/kitchenowl/kitchenowl.env
set +a
$STD uv run flask db upgrade
msg_ok "Updated KitchenOwl"
msg_info "Starting Service"
systemctl start kitchenowl
msg_ok "Started Service"
msg_ok "Updated successfully!"
fi
exit
}
start
build_container
description
msg_ok "Completed successfully!\n"
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:80${CL}"

View File

@@ -1,52 +0,0 @@
#!/usr/bin/env bash
source <(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/misc/build.func)
# Copyright (c) 2021-2026 tteck
# Author: MickLesk (Canbiz)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://www.usememos.com/
APP="Memos"
var_tags="${var_tags:-notes}"
var_cpu="${var_cpu:-1}"
var_ram="${var_ram:-1024}"
var_disk="${var_disk:-3}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
var_unprivileged="${var_unprivileged:-1}"
header_info "$APP"
variables
color
catch_errors
function update_script() {
header_info
check_container_storage
check_container_resources
if [[ ! -d /opt/memos ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
if check_for_gh_release "memos" "usememos/memos"; then
msg_info "Stopping service"
systemctl stop memos
msg_ok "Service stopped"
fetch_and_deploy_gh_release "memos" "usememos/memos" "prebuild" "latest" "/opt/memos" "memos*linux_amd64.tar.gz"
msg_info "Starting service"
systemctl start memos
msg_ok "Service started"
msg_ok "Updated successfully!"
fi
exit
}
start
build_container
description
msg_ok "Completed successfully!\n"
echo -e "${CREATING}${GN}${APP} setup has been successfully initialized!${CL}"
echo -e "${INFO}${YW} Access it using the following URL:${CL}"
echo -e "${TAB}${GATEWAY}${BGN}http://${IP}:9030${CL}"

View File

@@ -28,8 +28,12 @@ function update_script() {
exit
fi
VAULT=$(get_latest_github_release "dani-garcia/vaultwarden")
WVRELEASE=$(get_latest_github_release "dani-garcia/bw_web_builds")
VAULT=$(curl -fsSL https://api.github.com/repos/dani-garcia/vaultwarden/releases/latest |
grep "tag_name" |
awk '{print substr($2, 2, length($2)-3) }')
WVRELEASE=$(curl -fsSL https://api.github.com/repos/dani-garcia/bw_web_builds/releases/latest |
grep "tag_name" |
awk '{print substr($2, 2, length($2)-3) }')
UPD=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "SUPPORT" --radiolist --cancel-button Exit-Script "Spacebar = Select" 11 58 3 \
"1" "VaultWarden $VAULT" ON \
@@ -38,68 +42,57 @@ function update_script() {
3>&1 1>&2 2>&3)
if [ "$UPD" == "1" ]; then
if check_for_gh_release "vaultwarden" "dani-garcia/vaultwarden"; then
msg_info "Stopping Service"
systemctl stop vaultwarden
msg_ok "Stopped Service"
msg_info "Stopping Service"
systemctl stop vaultwarden
msg_ok "Stopped Service"
fetch_and_deploy_gh_release "vaultwarden" "dani-garcia/vaultwarden" "tarball" "latest" "/tmp/vaultwarden-src"
msg_info "Updating VaultWarden to $VAULT (Patience)"
cd /tmp/vaultwarden-src
$STD cargo build --features "sqlite,mysql,postgresql" --release
if [[ -f /usr/bin/vaultwarden ]]; then
cp target/release/vaultwarden /usr/bin/
else
cp target/release/vaultwarden /opt/vaultwarden/bin/
fi
cd ~ && rm -rf /tmp/vaultwarden-src
msg_ok "Updated VaultWarden to ${VAULT}"
msg_info "Starting Service"
systemctl start vaultwarden
msg_ok "Started Service"
msg_ok "Updated successfully!"
msg_info "Updating VaultWarden to $VAULT (Patience)"
cd ~ && rm -rf vaultwarden
$STD git clone https://github.com/dani-garcia/vaultwarden
cd vaultwarden
$STD cargo build --features "sqlite,mysql,postgresql" --release
DIR=/usr/bin/vaultwarden
if [ -d "$DIR" ]; then
cp target/release/vaultwarden /usr/bin/
else
msg_ok "VaultWarden is already up-to-date"
cp target/release/vaultwarden /opt/vaultwarden/bin/
fi
cd ~ && rm -rf vaultwarden
msg_ok "Updated VaultWarden"
msg_info "Starting Service"
systemctl start vaultwarden
msg_ok "Started Service"
msg_ok "Updated successfully!"
exit
fi
if [ "$UPD" == "2" ]; then
if check_for_gh_release "vaultwarden_webvault" "dani-garcia/bw_web_builds"; then
msg_info "Stopping Service"
systemctl stop vaultwarden
msg_ok "Stopped Service"
msg_info "Stopping Service"
systemctl stop vaultwarden
msg_ok "Stopped Service"
fetch_and_deploy_gh_release "vaultwarden_webvault" "dani-garcia/bw_web_builds" "prebuild" "latest" "/opt/vaultwarden" "bw_web_*.tar.gz"
msg_info "Updating Web-Vault to $WVRELEASE"
$STD curl -fsSLO https://github.com/dani-garcia/bw_web_builds/releases/download/"$WVRELEASE"/bw_web_"$WVRELEASE".tar.gz
$STD tar -zxf bw_web_"$WVRELEASE".tar.gz -C /opt/vaultwarden/
rm bw_web_"$WVRELEASE".tar.gz
msg_ok "Updated Web-Vault"
msg_info "Updating Web-Vault to $WVRELEASE"
rm -rf /opt/vaultwarden/web-vault
chown -R root:root /opt/vaultwarden/web-vault/
msg_ok "Updated Web-Vault to ${WVRELEASE}"
msg_info "Starting Service"
systemctl start vaultwarden
msg_ok "Started Service"
msg_ok "Updated successfully!"
else
msg_ok "Web-Vault is already up-to-date"
fi
msg_info "Starting Service"
systemctl start vaultwarden
msg_ok "Started Service"
msg_ok "Updated successfully!"
exit
fi
if [ "$UPD" == "3" ]; then
if NEWTOKEN=$(whiptail --backtitle "Proxmox VE Helper Scripts" --passwordbox "Set the ADMIN_TOKEN" 10 58 3>&1 1>&2 2>&3); then
if [[ -z "$NEWTOKEN" ]]; then exit; fi
ensure_dependencies argon2
if ! command -v argon2 >/dev/null 2>&1; then $STD apt-get install -y argon2; fi
TOKEN=$(echo -n "${NEWTOKEN}" | argon2 "$(openssl rand -base64 32)" -t 2 -m 16 -p 4 -l 64 -e)
sed -i "s|ADMIN_TOKEN=.*|ADMIN_TOKEN='${TOKEN}'|" /opt/vaultwarden/.env
if [[ -f /opt/vaultwarden/data/config.json ]]; then
sed -i "s|\"admin_token\":.*|\"admin_token\": \"${TOKEN}\"|" /opt/vaultwarden/data/config.json
fi
systemctl restart vaultwarden
msg_ok "Admin token updated"
fi
exit
fi

View File

@@ -9,9 +9,9 @@
"updateable": true,
"privileged": false,
"interface_port": 8083,
"documentation": "https://github.com/gelbphoenix/autocaliweb/wiki",
"documentation": "https://codeberg.org/gelbphoenix/autocaliweb/wiki",
"config_path": "/etc/autocaliweb",
"website": "https://github.com/gelbphoenix/autocaliweb",
"website": "https://codeberg.org/gelbphoenix/autocaliweb",
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/autocaliweb.webp",
"description": "A modern web management system for eBooks, eComics and PDFs",
"install_methods": [

View File

@@ -1,5 +1,5 @@
{
"generated": "2026-02-01T18:07:47Z",
"generated": "2026-02-02T06:25:10Z",
"versions": [
{
"slug": "2fauth",
@@ -116,9 +116,9 @@
{
"slug": "beszel",
"repo": "henrygd/beszel",
"version": "v0.18.2",
"version": "v0.18.3",
"pinned": false,
"date": "2026-01-12T23:58:00Z"
"date": "2026-02-01T19:02:42Z"
},
{
"slug": "bitmagnet",
@@ -242,9 +242,9 @@
{
"slug": "discopanel",
"repo": "nickheyer/discopanel",
"version": "v1.0.32",
"version": "v1.0.35",
"pinned": false,
"date": "2026-01-31T22:24:53Z"
"date": "2026-02-02T05:20:12Z"
},
{
"slug": "dispatcharr",
@@ -487,9 +487,9 @@
{
"slug": "homebox",
"repo": "sysadminsmedia/homebox",
"version": "v0.23.0",
"version": "v0.23.1",
"pinned": false,
"date": "2026-01-30T17:41:01Z"
"date": "2026-02-01T22:53:32Z"
},
{
"slug": "homepage",
@@ -515,9 +515,9 @@
{
"slug": "huntarr",
"repo": "plexguide/Huntarr.io",
"version": "9.1.5",
"version": "9.1.8",
"pinned": false,
"date": "2026-01-31T22:55:29Z"
"date": "2026-02-02T01:29:45Z"
},
{
"slug": "inspircd",
@@ -543,9 +543,9 @@
{
"slug": "jackett",
"repo": "Jackett/Jackett",
"version": "v0.24.1003",
"version": "v0.24.1008",
"pinned": false,
"date": "2026-02-01T05:55:30Z"
"date": "2026-02-02T05:55:21Z"
},
{
"slug": "joplin-server",
@@ -1558,9 +1558,9 @@
{
"slug": "zigbee2mqtt",
"repo": "Koenkk/zigbee2mqtt",
"version": "2.7.2",
"version": "2.8.0",
"pinned": false,
"date": "2026-01-01T13:43:47Z"
"date": "2026-02-01T19:27:25Z"
},
{
"slug": "zipline",

View File

@@ -0,0 +1,35 @@
{
"name": "KitchenOwl",
"slug": "kitchenowl",
"categories": [
13
],
"date_created": "2026-02-02",
"type": "ct",
"updateable": true,
"privileged": false,
"interface_port": 80,
"documentation": "https://docs.kitchenowl.org/",
"website": "https://kitchenowl.org/",
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/kitchenowl.webp",
"config_path": "/opt/kitchenowl/kitchenowl.env",
"description": "KitchenOwl is a smart self-hosted grocery list and recipe manager with real-time synchronization, recipe management, meal planning, and expense tracking.",
"install_methods": [
{
"type": "default",
"script": "ct/kitchenowl.sh",
"resources": {
"cpu": 1,
"ram": 2048,
"hdd": 6,
"os": "Debian",
"version": "13"
}
}
],
"default_credentials": {
"username": null,
"password": null
},
"notes": []
}

View File

@@ -1,35 +0,0 @@
{
"name": "Memos",
"slug": "memos",
"categories": [
12
],
"date_created": "2024-10-31",
"type": "ct",
"updateable": true,
"privileged": false,
"interface_port": 9030,
"documentation": "https://www.usememos.com/docs",
"website": "https://www.usememos.com/",
"logo": "https://cdn.jsdelivr.net/gh/selfhst/icons@main/webp/memos.webp",
"config_path": "",
"description": "Memos is an open-source, self-hosted platform designed for fast, privacy-focused note-taking. Users can create, organize, and format notes with Markdown, which are securely stored in a local database. It\u2019s lightweight and customizable, built for quick access and adaptability to individual or team needs.",
"install_methods": [
{
"type": "default",
"script": "ct/memos.sh",
"resources": {
"cpu": 1,
"ram": 1024,
"hdd": 3,
"os": "debian",
"version": "13"
}
}
],
"default_credentials": {
"username": null,
"password": null
},
"notes": []
}

View File

@@ -17,7 +17,8 @@ msg_info "Installing Dependencies"
$STD apt install -y nginx
msg_ok "Installed Dependencies"
PHP_VERSION="8.4" PHP_FPM="YES" setup_php
export PHP_VERSION="8.4"
PHP_FPM="YES" setup_php
setup_composer
setup_mariadb
MARIADB_DB_NAME="2fauth_db" MARIADB_DB_USER="2fauth" setup_mariadb_db

View File

@@ -3,7 +3,7 @@
# Copyright (c) 2021-2026 community-scripts ORG
# Author: vhsdream
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/gelbphoenix/autocaliweb
# Source: https://codeberg.org/gelbphoenix/autocaliweb
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
color
@@ -56,7 +56,7 @@ msg_ok "Installed Calibre"
setup_uv
fetch_and_deploy_gh_release "autocaliweb" "gelbphoenix/autocaliweb" "tarball" "latest" "/opt/autocaliweb"
fetch_and_deploy_codeberg_release "autocaliweb" "gelbphoenix/autocaliweb" "tarball" "latest" "/opt/autocaliweb"
msg_info "Configuring Autocaliweb"
INSTALL_DIR="/opt/autocaliweb"
@@ -111,8 +111,8 @@ msg_info "Initializing databases"
KEPUBIFY_PATH=$(command -v kepubify 2>/dev/null || echo "/usr/bin/kepubify")
EBOOK_CONVERT_PATH=$(command -v ebook-convert 2>/dev/null || echo "/usr/bin/ebook-convert")
CALIBRE_BIN_DIR=$(dirname "$EBOOK_CONVERT_PATH")
curl -fsSL https://github.com/gelbphoenix/autocaliweb/raw/refs/heads/main/library/metadata.db -o "$CALIBRE_LIB_DIR"/metadata.db
curl -fsSL https://github.com/gelbphoenix/autocaliweb/raw/refs/heads/main/library/app.db -o "$CONFIG_DIR"/app.db
curl -fsSL https://codeberg.org/gelbphoenix/autocaliweb/raw/branch/main/library/metadata.db -o "$CALIBRE_LIB_DIR"/metadata.db
curl -fsSL https://codeberg.org/gelbphoenix/autocaliweb/raw/branch/main/library/app.db -o "$CONFIG_DIR"/app.db
sqlite3 "$CONFIG_DIR/app.db" <<EOS
UPDATE settings SET
config_kepubifypath='$KEPUBIFY_PATH',

View File

@@ -0,0 +1,145 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2025 community-scripts ORG
# Author: snazzybean
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/TomBursch/kitchenowl
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
color
verb_ip6
catch_errors
setting_up_container
network_check
update_os
msg_info "Installing Dependencies"
$STD apt install -y \
nginx \
build-essential \
gfortran \
pkg-config \
ninja-build \
autoconf \
automake \
libpq-dev \
libffi-dev \
libssl-dev \
libpcre2-dev \
libre2-dev \
libxml2-dev \
libxslt-dev \
libopenblas-dev \
liblapack-dev \
zlib1g-dev \
libjpeg62-turbo-dev \
libsqlite3-dev \
libexpat1-dev \
libicu-dev
msg_ok "Installed Dependencies"
PYTHON_VERSION="3.14" setup_uv
fetch_and_deploy_gh_release "kitchenowl" "TomBursch/kitchenowl" "tarball" "latest" "/opt/kitchenowl"
rm -rf /opt/kitchenowl/web
fetch_and_deploy_gh_release "kitchenowl-web" "TomBursch/kitchenowl" "prebuild" "latest" "/opt/kitchenowl/web" "kitchenowl_Web.tar.gz"
msg_info "Setting up KitchenOwl"
cd /opt/kitchenowl/backend
$STD uv sync --no-dev
sed -i 's/default=True/default=False/' /opt/kitchenowl/backend/wsgi.py
mkdir -p /nltk_data
$STD uv run python -m nltk.downloader -d /nltk_data averaged_perceptron_tagger_eng
JWT_SECRET=$(openssl rand -hex 32)
mkdir -p /opt/kitchenowl/data
cat <<EOF >/opt/kitchenowl/kitchenowl.env
STORAGE_PATH=/opt/kitchenowl/data
JWT_SECRET_KEY=${JWT_SECRET}
NLTK_DATA=/nltk_data
FRONT_URL=http://${LOCAL_IP}
FLASK_APP=wsgi.py
FLASK_ENV=production
EOF
set -a
source /opt/kitchenowl/kitchenowl.env
set +a
$STD uv run flask db upgrade
msg_ok "Set up KitchenOwl"
msg_info "Creating Systemd Service"
cat <<EOF >/etc/systemd/system/kitchenowl.service
[Unit]
Description=KitchenOwl Backend
After=network.target
[Service]
Type=simple
User=root
WorkingDirectory=/opt/kitchenowl/backend
EnvironmentFile=/opt/kitchenowl/kitchenowl.env
ExecStart=/usr/local/bin/uv run wsgi.py
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
systemctl enable -q --now kitchenowl
msg_ok "Created and Started Service"
msg_info "Configuring Nginx"
rm -f /etc/nginx/sites-enabled/default
cat <<'EOF' >/etc/nginx/sites-available/kitchenowl.conf
server {
listen 80;
server_name _;
root /opt/kitchenowl/web;
index index.html;
client_max_body_size 100M;
# Security Headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
location / {
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://127.0.0.1:5000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
location /socket.io {
proxy_pass http://127.0.0.1:5000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# WebSocket Timeouts - allow long-lived connections
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
}
}
EOF
ln -sf /etc/nginx/sites-available/kitchenowl.conf /etc/nginx/sites-enabled/
rm -f /etc/nginx/sites-enabled/default
$STD systemctl reload nginx
msg_ok "Configured Nginx"
motd_ssh
customize
cleanup_lxc

View File

@@ -1,42 +0,0 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 tteck
# Author: tteck
# Co-Author: MickLesk (Canbiz)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/usememos/memos
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
color
verb_ip6
catch_errors
setting_up_container
network_check
update_os
fetch_and_deploy_gh_release "memos" "usememos/memos" "prebuild" "latest" "/opt/memos" "memos*linux_amd64.tar.gz"
mkdir -p /opt/memos_data
msg_info "Creating Service"
cat <<EOF >/etc/systemd/system/memos.service
[Unit]
Description=Memos Server
After=network.target
[Service]
ExecStart=/opt/memos/memos
Environment="MEMOS_MODE=prod"
Environment="MEMOS_PORT=9030"
Environment="MEMOS_DATA=/opt/memos_data"
WorkingDirectory=/opt/memos
Restart=always
[Install]
WantedBy=multi-user.target
EOF
systemctl enable -q --now memos
msg_ok "Created Service"
motd_ssh
customize
cleanup_lxc

View File

@@ -14,7 +14,7 @@ network_check
update_os
msg_info "Installing Dependencies"
$STD apt install -y \
$STD apt install -y git \
build-essential \
pkgconf \
libssl-dev \
@@ -24,25 +24,34 @@ $STD apt install -y \
ssl-cert
msg_ok "Installed Dependencies"
setup_rust
fetch_and_deploy_gh_release "vaultwarden" "dani-garcia/vaultwarden" "tarball" "latest" "/tmp/vaultwarden-src"
WEBVAULT=$(curl -fsSL https://api.github.com/repos/dani-garcia/bw_web_builds/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }')
VAULT=$(curl -fsSL https://api.github.com/repos/dani-garcia/vaultwarden/releases/latest | grep "tag_name" | awk '{print substr($2, 2, length($2)-3) }')
msg_info "Building Vaultwarden (Patience)"
cd /tmp/vaultwarden-src
msg_info "Installing Rust"
curl -fsSL https://sh.rustup.rs -o rustup-init.sh
$STD bash rustup-init.sh -y --profile minimal
echo 'export PATH="$HOME/.cargo/bin:$PATH"' >>~/.bashrc
export PATH="$HOME/.cargo/bin:$PATH"
rm rustup-init.sh
msg_ok "Installed Rust"
msg_info "Building Vaultwarden ${VAULT} (Patience)"
$STD git clone https://github.com/dani-garcia/vaultwarden
cd vaultwarden
$STD cargo build --features "sqlite,mysql,postgresql" --release
msg_ok "Built Vaultwarden"
msg_ok "Built Vaultwarden ${VAULT}"
msg_info "Setting up Vaultwarden"
$STD addgroup --system vaultwarden
$STD adduser --system --home /opt/vaultwarden --shell /usr/sbin/nologin --no-create-home --gecos 'vaultwarden' --ingroup vaultwarden --disabled-login --disabled-password vaultwarden
mkdir -p /opt/vaultwarden/{bin,data,web-vault}
mkdir -p /opt/vaultwarden/bin
mkdir -p /opt/vaultwarden/data
cp target/release/vaultwarden /opt/vaultwarden/bin/
cd ~ && rm -rf /tmp/vaultwarden-src
msg_ok "Set up Vaultwarden"
fetch_and_deploy_gh_release "vaultwarden_webvault" "dani-garcia/bw_web_builds" "prebuild" "latest" "/opt/vaultwarden/web-vault" "bw_web_*.tar.gz"
msg_info "Downloading Web-Vault ${WEBVAULT}"
$STD curl -fsSLO https://github.com/dani-garcia/bw_web_builds/releases/download/"$WEBVAULT"/bw_web_"$WEBVAULT".tar.gz
$STD tar -xzf bw_web_"$WEBVAULT".tar.gz -C /opt/vaultwarden/
msg_ok "Downloaded Web-Vault ${WEBVAULT}"
msg_info "Configuring Vaultwarden"
cat <<EOF >/opt/vaultwarden/.env
ADMIN_TOKEN=''
ROCKET_ADDRESS=0.0.0.0
@@ -52,23 +61,22 @@ DATABASE_MAX_CONNS=10
WEB_VAULT_FOLDER=/opt/vaultwarden/web-vault
WEB_VAULT_ENABLED=true
EOF
mv /etc/ssl/certs/ssl-cert-snakeoil.pem /opt/vaultwarden/
mv /etc/ssl/private/ssl-cert-snakeoil.key /opt/vaultwarden/
msg_info "Creating Service"
chown -R vaultwarden:vaultwarden /opt/vaultwarden/
chown root:root /opt/vaultwarden/bin/vaultwarden
chmod +x /opt/vaultwarden/bin/vaultwarden
chown -R root:root /opt/vaultwarden/web-vault/
chmod +r /opt/vaultwarden/.env
msg_ok "Configured Vaultwarden"
msg_info "Creating Service"
cat <<EOF >/etc/systemd/system/vaultwarden.service
[Unit]
service_path="/etc/systemd/system/vaultwarden.service"
echo "[Unit]
Description=Bitwarden Server (Powered by Vaultwarden)
Documentation=https://github.com/dani-garcia/vaultwarden
After=network.target
[Service]
User=vaultwarden
Group=vaultwarden
@@ -91,11 +99,10 @@ LockPersonality=yes
WorkingDirectory=/opt/vaultwarden
ReadWriteDirectories=/opt/vaultwarden/data
AmbientCapabilities=CAP_NET_BIND_SERVICE
[Install]
WantedBy=multi-user.target
EOF
systemctl enable -q --now vaultwarden
WantedBy=multi-user.target" >$service_path
systemctl daemon-reload
$STD systemctl enable --now vaultwarden
msg_ok "Created Service"
motd_ssh

View File

@@ -821,6 +821,54 @@ github_api_call() {
return 1
}
# ------------------------------------------------------------------------------
# Codeberg API call with retry logic
# ------------------------------------------------------------------------------
codeberg_api_call() {
local url="$1"
local output_file="${2:-/dev/stdout}"
local max_retries=3
local retry_delay=2
for attempt in $(seq 1 $max_retries); do
local http_code
http_code=$(curl -fsSL -w "%{http_code}" -o "$output_file" \
-H "Accept: application/json" \
"$url" 2>/dev/null || echo "000")
case "$http_code" in
200)
return 0
;;
403)
# Rate limit - retry
if [[ $attempt -lt $max_retries ]]; then
msg_warn "Codeberg API rate limit, waiting ${retry_delay}s... (attempt $attempt/$max_retries)"
sleep "$retry_delay"
retry_delay=$((retry_delay * 2))
continue
fi
msg_error "Codeberg API rate limit exceeded."
return 1
;;
404)
msg_error "Codeberg API endpoint not found: $url"
return 1
;;
*)
if [[ $attempt -lt $max_retries ]]; then
sleep "$retry_delay"
continue
fi
msg_error "Codeberg API call failed with HTTP $http_code"
return 1
;;
esac
done
return 1
}
should_upgrade() {
local current="$1"
local target="$2"
@@ -1385,6 +1433,37 @@ get_latest_github_release() {
echo "$version"
}
# ------------------------------------------------------------------------------
# Get latest Codeberg release version
# ------------------------------------------------------------------------------
get_latest_codeberg_release() {
local repo="$1"
local strip_v="${2:-true}"
local temp_file=$(mktemp)
# Codeberg API: get all releases and pick the first non-draft/non-prerelease
if ! codeberg_api_call "https://codeberg.org/api/v1/repos/${repo}/releases" "$temp_file"; then
rm -f "$temp_file"
return 1
fi
local version
# Codeberg uses same JSON structure but releases endpoint returns array
version=$(jq -r '[.[] | select(.draft==false and .prerelease==false)][0].tag_name // empty' "$temp_file")
if [[ "$strip_v" == "true" ]]; then
version="${version#v}"
fi
rm -f "$temp_file"
if [[ -z "$version" ]]; then
return 1
fi
echo "$version"
}
# ------------------------------------------------------------------------------
# Debug logging (only if DEBUG=1)
# ------------------------------------------------------------------------------
@@ -1559,6 +1638,119 @@ check_for_gh_release() {
return 1
}
# ------------------------------------------------------------------------------
# Checks for new Codeberg release (latest tag).
#
# Description:
# - Queries the Codeberg API for the latest release tag
# - Compares it to a local cached version (~/.<app>)
# - If newer, sets global CHECK_UPDATE_RELEASE and returns 0
#
# Usage:
# if check_for_codeberg_release "autocaliweb" "gelbphoenix/autocaliweb" [optional] "v0.11.3"; then
# # trigger update...
# fi
# exit 0
# } (end of update_script not from the function)
#
# Notes:
# - Requires `jq` (auto-installed if missing)
# - Does not modify anything, only checks version state
# - Does not support pre-releases
# ------------------------------------------------------------------------------
check_for_codeberg_release() {
local app="$1"
local source="$2"
local pinned_version_in="${3:-}" # optional
local app_lc="${app,,}"
local current_file="$HOME/.${app_lc}"
msg_info "Checking for update: ${app}"
# DNS check
if ! getent hosts codeberg.org >/dev/null 2>&1; then
msg_error "Network error: cannot resolve codeberg.org"
return 1
fi
ensure_dependencies jq
# Fetch releases from Codeberg API
local releases_json=""
releases_json=$(curl -fsSL --max-time 20 \
-H 'Accept: application/json' \
"https://codeberg.org/api/v1/repos/${source}/releases" 2>/dev/null) || {
msg_error "Unable to fetch releases for ${app}"
return 1
}
mapfile -t raw_tags < <(jq -r '.[] | select(.draft==false and .prerelease==false) | .tag_name' <<<"$releases_json")
if ((${#raw_tags[@]} == 0)); then
msg_error "No stable releases found for ${app}"
return 1
fi
local clean_tags=()
for t in "${raw_tags[@]}"; do
clean_tags+=("${t#v}")
done
local latest_raw="${raw_tags[0]}"
local latest_clean="${clean_tags[0]}"
# current installed (stored without v)
local current=""
if [[ -f "$current_file" ]]; then
current="$(<"$current_file")"
else
# Migration: search for any /opt/*_version.txt
local legacy_files
mapfile -t legacy_files < <(find /opt -maxdepth 1 -type f -name "*_version.txt" 2>/dev/null)
if ((${#legacy_files[@]} == 1)); then
current="$(<"${legacy_files[0]}")"
echo "${current#v}" >"$current_file"
rm -f "${legacy_files[0]}"
fi
fi
current="${current#v}"
# Pinned version handling
if [[ -n "$pinned_version_in" ]]; then
local pin_clean="${pinned_version_in#v}"
local match_raw=""
for i in "${!clean_tags[@]}"; do
if [[ "${clean_tags[$i]}" == "$pin_clean" ]]; then
match_raw="${raw_tags[$i]}"
break
fi
done
if [[ -z "$match_raw" ]]; then
msg_error "Pinned version ${pinned_version_in} not found upstream"
return 1
fi
if [[ "$current" != "$pin_clean" ]]; then
CHECK_UPDATE_RELEASE="$match_raw"
msg_ok "Update available: ${app} ${current:-not installed}${pin_clean}"
return 0
fi
msg_ok "No update available: ${app} is already on pinned version (${current})"
return 1
fi
# No pinning → use latest
if [[ -z "$current" || "$current" != "$latest_clean" ]]; then
CHECK_UPDATE_RELEASE="$latest_raw"
msg_ok "Update available: ${app} ${current:-not installed}${latest_clean}"
return 0
fi
msg_ok "No update available: ${app} (${latest_clean})"
return 1
}
# ------------------------------------------------------------------------------
# Creates and installs self-signed certificates.
#
@@ -1648,6 +1840,440 @@ function ensure_usr_local_bin_persist() {
fi
}
# ------------------------------------------------------------------------------
# Downloads and deploys latest Codeberg release (source, binary, tarball, asset).
#
# Description:
# - Fetches latest release metadata from Codeberg API
# - Supports the following modes:
# - tarball: Source code tarball (default if omitted)
# - source: Alias for tarball (same behavior)
# - binary: .deb package install (arch-dependent)
# - prebuild: Prebuilt .tar.gz archive (e.g. Go binaries)
# - singlefile: Standalone binary (no archive, direct chmod +x install)
# - tag: Direct tag download (bypasses Release API)
# - Handles download, extraction/installation and version tracking in ~/.<app>
#
# Parameters:
# $1 APP - Application name (used for install path and version file)
# $2 REPO - Codeberg repository in form user/repo
# $3 MODE - Release type:
# tarball → source tarball (.tar.gz)
# binary → .deb file (auto-arch matched)
# prebuild → prebuilt archive (e.g. tar.gz)
# singlefile→ standalone binary (chmod +x)
# tag → direct tag (bypasses Release API)
# $4 VERSION - Optional release tag (default: latest)
# $5 TARGET_DIR - Optional install path (default: /opt/<app>)
# $6 ASSET_FILENAME - Required for:
# - prebuild → archive filename or pattern
# - singlefile→ binary filename or pattern
#
# Examples:
# # 1. Minimal: Fetch and deploy source tarball
# fetch_and_deploy_codeberg_release "autocaliweb" "gelbphoenix/autocaliweb"
#
# # 2. Binary install via .deb asset (architecture auto-detected)
# fetch_and_deploy_codeberg_release "myapp" "myuser/myapp" "binary"
#
# # 3. Prebuilt archive (.tar.gz) with asset filename match
# fetch_and_deploy_codeberg_release "myapp" "myuser/myapp" "prebuild" "latest" "/opt/myapp" "myapp_Linux_x86_64.tar.gz"
#
# # 4. Single binary (chmod +x)
# fetch_and_deploy_codeberg_release "myapp" "myuser/myapp" "singlefile" "v1.0.0" "/opt/myapp" "myapp-linux-amd64"
#
# # 5. Explicit tag version
# fetch_and_deploy_codeberg_release "autocaliweb" "gelbphoenix/autocaliweb" "tag" "v0.11.3" "/opt/autocaliweb"
# ------------------------------------------------------------------------------
function fetch_and_deploy_codeberg_release() {
local app="$1"
local repo="$2"
local mode="${3:-tarball}" # tarball | binary | prebuild | singlefile | tag
local version="${4:-latest}"
local target="${5:-/opt/$app}"
local asset_pattern="${6:-}"
local app_lc=$(echo "${app,,}" | tr -d ' ')
local version_file="$HOME/.${app_lc}"
local api_timeout="--connect-timeout 10 --max-time 60"
local download_timeout="--connect-timeout 15 --max-time 900"
local current_version=""
[[ -f "$version_file" ]] && current_version=$(<"$version_file")
ensure_dependencies jq
### Tag Mode (bypass Release API) ###
if [[ "$mode" == "tag" ]]; then
if [[ "$version" == "latest" ]]; then
msg_error "Mode 'tag' requires explicit version (not 'latest')"
return 1
fi
local tag_name="$version"
[[ "$tag_name" =~ ^v ]] && version="${tag_name:1}" || version="$tag_name"
if [[ "$current_version" == "$version" ]]; then
$STD msg_ok "$app is already up-to-date (v$version)"
return 0
fi
# DNS check
if ! getent hosts "codeberg.org" &>/dev/null; then
msg_error "DNS resolution failed for codeberg.org check /etc/resolv.conf or networking"
return 1
fi
local tmpdir
tmpdir=$(mktemp -d) || return 1
msg_info "Fetching Codeberg tag: $app ($tag_name)"
local safe_version="${version//@/_}"
safe_version="${safe_version//\//_}"
local filename="${app_lc}-${safe_version}.tar.gz"
local download_success=false
# Codeberg archive URL format: https://codeberg.org/{owner}/{repo}/archive/{tag}.tar.gz
local archive_url="https://codeberg.org/$repo/archive/${tag_name}.tar.gz"
if curl $download_timeout -fsSL -o "$tmpdir/$filename" "$archive_url"; then
download_success=true
fi
if [[ "$download_success" != "true" ]]; then
msg_error "Download failed for $app ($tag_name)"
rm -rf "$tmpdir"
return 1
fi
mkdir -p "$target"
if [[ "${CLEAN_INSTALL:-0}" == "1" ]]; then
rm -rf "${target:?}/"*
fi
tar --no-same-owner -xzf "$tmpdir/$filename" -C "$tmpdir" || {
msg_error "Failed to extract tarball"
rm -rf "$tmpdir"
return 1
}
local unpack_dir
unpack_dir=$(find "$tmpdir" -mindepth 1 -maxdepth 1 -type d | head -n1)
shopt -s dotglob nullglob
cp -r "$unpack_dir"/* "$target/"
shopt -u dotglob nullglob
echo "$version" >"$version_file"
msg_ok "Deployed: $app ($version)"
rm -rf "$tmpdir"
return 0
fi
# Codeberg API: https://codeberg.org/api/v1/repos/{owner}/{repo}/releases
local api_url="https://codeberg.org/api/v1/repos/$repo/releases"
if [[ "$version" != "latest" ]]; then
# Get release by tag: /repos/{owner}/{repo}/releases/tags/{tag}
api_url="https://codeberg.org/api/v1/repos/$repo/releases/tags/$version"
fi
# dns pre check
if ! getent hosts "codeberg.org" &>/dev/null; then
msg_error "DNS resolution failed for codeberg.org check /etc/resolv.conf or networking"
return 1
fi
local max_retries=3 retry_delay=2 attempt=1 success=false resp http_code
while ((attempt <= max_retries)); do
resp=$(curl $api_timeout -fsSL -w "%{http_code}" -o /tmp/codeberg_rel.json "$api_url") && success=true && break
sleep "$retry_delay"
((attempt++))
done
if ! $success; then
msg_error "Failed to fetch release metadata from $api_url after $max_retries attempts"
return 1
fi
http_code="${resp:(-3)}"
[[ "$http_code" != "200" ]] && {
msg_error "Codeberg API returned HTTP $http_code"
return 1
}
local json tag_name
json=$(</tmp/codeberg_rel.json)
# For "latest", the API returns an array - take the first (most recent) release
if [[ "$version" == "latest" ]]; then
json=$(echo "$json" | jq '.[0]')
fi
tag_name=$(echo "$json" | jq -r '.tag_name // .name // empty')
[[ "$tag_name" =~ ^v ]] && version="${tag_name:1}" || version="$tag_name"
if [[ "$current_version" == "$version" ]]; then
$STD msg_ok "$app is already up-to-date (v$version)"
return 0
fi
local tmpdir
tmpdir=$(mktemp -d) || return 1
local filename="" url=""
msg_info "Fetching Codeberg release: $app ($version)"
### Tarball Mode ###
if [[ "$mode" == "tarball" || "$mode" == "source" ]]; then
local safe_version="${version//@/_}"
safe_version="${safe_version//\//_}"
filename="${app_lc}-${safe_version}.tar.gz"
local download_success=false
# Codeberg archive URL format
local archive_url="https://codeberg.org/$repo/archive/${tag_name}.tar.gz"
if curl $download_timeout -fsSL -o "$tmpdir/$filename" "$archive_url"; then
download_success=true
fi
if [[ "$download_success" != "true" ]]; then
msg_error "Download failed for $app ($tag_name)"
rm -rf "$tmpdir"
return 1
fi
mkdir -p "$target"
if [[ "${CLEAN_INSTALL:-0}" == "1" ]]; then
rm -rf "${target:?}/"*
fi
tar --no-same-owner -xzf "$tmpdir/$filename" -C "$tmpdir" || {
msg_error "Failed to extract tarball"
rm -rf "$tmpdir"
return 1
}
local unpack_dir
unpack_dir=$(find "$tmpdir" -mindepth 1 -maxdepth 1 -type d | head -n1)
shopt -s dotglob nullglob
cp -r "$unpack_dir"/* "$target/"
shopt -u dotglob nullglob
### Binary Mode ###
elif [[ "$mode" == "binary" ]]; then
local arch
arch=$(dpkg --print-architecture 2>/dev/null || uname -m)
[[ "$arch" == "x86_64" ]] && arch="amd64"
[[ "$arch" == "aarch64" ]] && arch="arm64"
local assets url_match=""
# Codeberg assets are in .assets[].browser_download_url
assets=$(echo "$json" | jq -r '.assets[].browser_download_url')
# If explicit filename pattern is provided, match that first
if [[ -n "$asset_pattern" ]]; then
for u in $assets; do
case "${u##*/}" in
$asset_pattern)
url_match="$u"
break
;;
esac
done
fi
# Fall back to architecture heuristic
if [[ -z "$url_match" ]]; then
for u in $assets; do
if [[ "$u" =~ ($arch|amd64|x86_64|aarch64|arm64).*\.deb$ ]]; then
url_match="$u"
break
fi
done
fi
# Fallback: any .deb file
if [[ -z "$url_match" ]]; then
for u in $assets; do
[[ "$u" =~ \.deb$ ]] && url_match="$u" && break
done
fi
if [[ -z "$url_match" ]]; then
msg_error "No suitable .deb asset found for $app"
rm -rf "$tmpdir"
return 1
fi
filename="${url_match##*/}"
curl $download_timeout -fsSL -o "$tmpdir/$filename" "$url_match" || {
msg_error "Download failed: $url_match"
rm -rf "$tmpdir"
return 1
}
chmod 644 "$tmpdir/$filename"
$STD apt install -y "$tmpdir/$filename" || {
$STD dpkg -i "$tmpdir/$filename" || {
msg_error "Both apt and dpkg installation failed"
rm -rf "$tmpdir"
return 1
}
}
### Prebuild Mode ###
elif [[ "$mode" == "prebuild" ]]; then
local pattern="${6%\"}"
pattern="${pattern#\"}"
[[ -z "$pattern" ]] && {
msg_error "Mode 'prebuild' requires 6th parameter (asset filename pattern)"
rm -rf "$tmpdir"
return 1
}
local asset_url=""
for u in $(echo "$json" | jq -r '.assets[].browser_download_url'); do
filename_candidate="${u##*/}"
case "$filename_candidate" in
$pattern)
asset_url="$u"
break
;;
esac
done
[[ -z "$asset_url" ]] && {
msg_error "No asset matching '$pattern' found"
rm -rf "$tmpdir"
return 1
}
filename="${asset_url##*/}"
curl $download_timeout -fsSL -o "$tmpdir/$filename" "$asset_url" || {
msg_error "Download failed: $asset_url"
rm -rf "$tmpdir"
return 1
}
local unpack_tmp
unpack_tmp=$(mktemp -d)
mkdir -p "$target"
if [[ "${CLEAN_INSTALL:-0}" == "1" ]]; then
rm -rf "${target:?}/"*
fi
if [[ "$filename" == *.zip ]]; then
ensure_dependencies unzip
unzip -q "$tmpdir/$filename" -d "$unpack_tmp" || {
msg_error "Failed to extract ZIP archive"
rm -rf "$tmpdir" "$unpack_tmp"
return 1
}
elif [[ "$filename" == *.tar.* || "$filename" == *.tgz ]]; then
tar --no-same-owner -xf "$tmpdir/$filename" -C "$unpack_tmp" || {
msg_error "Failed to extract TAR archive"
rm -rf "$tmpdir" "$unpack_tmp"
return 1
}
else
msg_error "Unsupported archive format: $filename"
rm -rf "$tmpdir" "$unpack_tmp"
return 1
fi
local top_dirs
top_dirs=$(find "$unpack_tmp" -mindepth 1 -maxdepth 1 -type d | wc -l)
local top_entries inner_dir
top_entries=$(find "$unpack_tmp" -mindepth 1 -maxdepth 1)
if [[ "$(echo "$top_entries" | wc -l)" -eq 1 && -d "$top_entries" ]]; then
inner_dir="$top_entries"
shopt -s dotglob nullglob
if compgen -G "$inner_dir/*" >/dev/null; then
cp -r "$inner_dir"/* "$target/" || {
msg_error "Failed to copy contents from $inner_dir to $target"
rm -rf "$tmpdir" "$unpack_tmp"
return 1
}
else
msg_error "Inner directory is empty: $inner_dir"
rm -rf "$tmpdir" "$unpack_tmp"
return 1
fi
shopt -u dotglob nullglob
else
shopt -s dotglob nullglob
if compgen -G "$unpack_tmp/*" >/dev/null; then
cp -r "$unpack_tmp"/* "$target/" || {
msg_error "Failed to copy contents to $target"
rm -rf "$tmpdir" "$unpack_tmp"
return 1
}
else
msg_error "Unpacked archive is empty"
rm -rf "$tmpdir" "$unpack_tmp"
return 1
fi
shopt -u dotglob nullglob
fi
### Singlefile Mode ###
elif [[ "$mode" == "singlefile" ]]; then
local pattern="${6%\"}"
pattern="${pattern#\"}"
[[ -z "$pattern" ]] && {
msg_error "Mode 'singlefile' requires 6th parameter (asset filename pattern)"
rm -rf "$tmpdir"
return 1
}
local asset_url=""
for u in $(echo "$json" | jq -r '.assets[].browser_download_url'); do
filename_candidate="${u##*/}"
case "$filename_candidate" in
$pattern)
asset_url="$u"
break
;;
esac
done
[[ -z "$asset_url" ]] && {
msg_error "No asset matching '$pattern' found"
rm -rf "$tmpdir"
return 1
}
filename="${asset_url##*/}"
mkdir -p "$target"
local use_filename="${USE_ORIGINAL_FILENAME:-false}"
local target_file="$app"
[[ "$use_filename" == "true" ]] && target_file="$filename"
curl $download_timeout -fsSL -o "$target/$target_file" "$asset_url" || {
msg_error "Download failed: $asset_url"
rm -rf "$tmpdir"
return 1
}
if [[ "$target_file" != *.jar && -f "$target/$target_file" ]]; then
chmod +x "$target/$target_file"
fi
else
msg_error "Unknown mode: $mode"
rm -rf "$tmpdir"
return 1
fi
echo "$version" >"$version_file"
msg_ok "Deployed: $app ($version)"
rm -rf "$tmpdir"
}
# ------------------------------------------------------------------------------
# Downloads and deploys latest GitHub release (source, binary, tarball, asset).
#