Compare commits

..
Author SHA1 Message Date
github-actions[bot] 85f5936dfa Update CHANGELOG.md 2026-11-13 14:33:44 +00:00
community-scripts-pr-app[bot]andgithub-actions[bot] 075828b1e9 Update CHANGELOG.md (#18705)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-13 14:26:28 +00:00
community-scripts-pr-app[bot]andgithub-actions[bot] df3ebb4dd1 Update CHANGELOG.md (#18704)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-13 13:48:25 +00:00
push-app-to-main[bot] 2c2265e44b Add marimo (ct) (#18672)
Co-authored-by: push-app-to-main[bot] <203845782+push-app-to-main[bot]@users.noreply.github.com>
2026-11-13 14:47:44 +01:00
Rögl-Brunner Michel 38cccdd417 pr-script-test: run external installers, and catch failed installs on Incus
CloudPanel (DevScripts PR 2614) installs through upstream's install.sh,
which core runs only after asking; CI cannot answer, so set
var_external_installer=yes for the build and the update.

A failed install on Incus also showed as passed: with dev_mode=keep the
engine still prints "Completed Successfully", and the line that gives the
failure away names the container ci-9000 there, not 9000, so the check
for it never matched.
2026-11-13 14:45:05 +01:00
community-scripts-pr-app[bot]andgithub-actions[bot] 09a1164445 Update CHANGELOG.md (#18703)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-13 13:26:56 +00:00
Rögl-Brunner Michel 32bd1440d2 pr-script-test: remove the temporary token probes
They found the cause: marimo-team has an IP allow list, so GitHub
refuses the token from the lab with a 403 that core read as a rate
limit. Fixed in core. The quota line stays.
2026-11-13 13:32:06 +01:00
Rögl-Brunner Michel f9693f2fd3 pr-script-test: probe what GitHub answers the token in the guest (temporary) 2026-11-13 13:25:23 +01:00
Rögl-Brunner Michel c8799b0bcb pr-script-test: log the token length each shell of the update sees (temporary) 2026-11-13 13:20:01 +01:00
Rögl-Brunner Michel 0033568ca5 pr-script-test: log which GitHub calls in the guest carry the token (temporary) 2026-11-13 13:14:04 +01:00
Rögl-Brunner Michel d887fb8e95 pr-script-test: probe whether the guest profile clears the token 2026-11-13 13:08:55 +01:00
Rögl-Brunner Michel fe4e449b2f pr-script-test: probe the token inside the guest, not on the host
On the host the job token reads another repo's release fine (200), yet
installs and updates in the guest hit the anonymous limit. Probe there,
just before the update. Also drops the /tmp file named after $$.
2026-11-13 13:02:11 +01:00
Rögl-Brunner Michel cd4b7aa63d pr-script-test: probe whether the job token can read another repo's release 2026-11-13 12:56:10 +01:00
Rögl-Brunner Michel b80d038846 pr-script-test: log the GitHub API quota, with and without the token
marimo (ProxmoxVE PR 18672) hit the rate limit in its update although the
job token is passed as GITHUB_TOKEN there. Log what both the token and the
lab address have left at the start and when an update hits the limit.
2026-11-13 12:49:35 +01:00
Rögl-Brunner Michel 9368f43aa9 pr-script-test: the label builds only for write access or a bot
Triage can set labels too; /test needs write, so the label does as well.
The merge workflow sets "Ready For Testing" through an app, which passes.
2026-11-13 12:44:20 +01:00
community-scripts-pr-app[bot]andgithub-actions[bot] d03f124259 Update CHANGELOG.md (#18701)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-13 11:41:57 +00:00
community-scripts-pr-app[bot]andgithub-actions[bot] cd685377dc Update CHANGELOG.md (#18700)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-13 11:41:28 +00:00
push-app-to-main[bot] 58310699e7 Add goatcounter (ct) (#18662)
Co-authored-by: push-app-to-main[bot] <203845782+push-app-to-main[bot]@users.noreply.github.com>
2026-11-13 12:41:15 +01:00
push-app-to-main[bot] f71c4c2ee1 Add unsloth (ct) (#18697)
Co-authored-by: push-app-to-main[bot] <203845782+push-app-to-main[bot]@users.noreply.github.com>
2026-11-13 12:40:55 +01:00
Rögl-Brunner Michel c8661ac328 pr-script-test: build the app when an issue gets "Ready For Testing"
The label on a script issue now starts the same build as `/test <app>`
in that issue: the app from the default branch. The app is the issue
form's "Script path", else "Name of the Script" as a slug (the rule of
pr-merged-ready-for-testing.yml). tools/ issues are skipped. Any other
label gets a group of its own, like a comment that is not a /test.
2026-11-13 12:40:49 +01:00
community-scripts-pr-app[bot]andgithub-actions[bot] 373b564c29 Update CHANGELOG.md (#18698)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-11-13 11:30:41 +00:00
10 changed files with 432 additions and 64 deletions

No files matched your search

+40 -4
View File
@@ -34,6 +34,10 @@ name: PR script test
on:
issue_comment:
types: [created]
# The "Ready For Testing" label on a script issue builds that app from the
# default branch, as `/test <app>` in the issue would.
issues:
types: [labeled]
# Every PR, see above. pull_request_target runs this file from main, never
# the PR's copy of it, and does not wait for "approve and run" on forks.
pull_request_target:
@@ -62,11 +66,13 @@ permissions: {}
# One queue per PR. A newer push replaces a run that is still waiting (its
# gate would skip anyway: the head moved), but never kills one that is
# building: a cancelled job leaves its guest behind until the sweeper. Any
# comment starts this workflow; one that is not a /test gets a group of its
# own, so it cannot push a waiting test out of the queue.
# comment and any label starts this workflow; one that is not a /test (or
# not the "Ready For Testing" label) gets a group of its own, so it cannot
# push a waiting test out of the queue.
concurrency:
group: >-
pr-script-test-${{ github.event_name == 'issue_comment' && !startsWith(github.event.comment.body, '/test')
pr-script-test-${{ ((github.event_name == 'issue_comment' && !startsWith(github.event.comment.body, '/test'))
|| (github.event_name == 'issues' && github.event.label.name != 'Ready For Testing'))
&& format('noop-{0}', github.run_id)
|| github.event.pull_request.number || github.event.issue.number || inputs.pr }}
cancel-in-progress: false
@@ -76,6 +82,7 @@ jobs:
if: >-
github.event_name == 'workflow_dispatch' ||
(github.event_name == 'issue_comment' && startsWith(github.event.comment.body, '/test')) ||
(github.event_name == 'issues' && github.event.label.name == 'Ready For Testing' && !github.event.issue.pull_request) ||
(github.event_name == 'pull_request_target' && !github.event.pull_request.draft)
runs-on: ubuntu-latest
permissions:
@@ -120,6 +127,35 @@ jobs:
} else if (context.eventName === 'workflow_dispatch') {
prNumber = Number(context.payload.inputs.pr);
requested = `${context.payload.inputs.sha || ''} ${context.payload.inputs.scripts || ''}`;
} else if (context.eventName === 'issues') {
// "Ready For Testing" on a script issue: the app the issue form
// names, as `/test <app>` would. Only people who can triage set a
// label, and the default branch is what gets built.
const issue = context.payload.issue;
prNumber = issue.number;
// Triage can label too; building needs write, as for /test. The
// merge workflow labels through an app, i.e. a bot.
const sender = context.payload.sender;
if (sender.type !== 'Bot') {
const { data: perm } = await github.rest.repos.getCollaboratorPermissionLevel({ owner, repo, username: sender.login });
if (!['admin', 'maintain', 'write'].includes(perm.permission)) {
core.notice(`${sender.login} has ${perm.permission} access; a test needs write`);
return;
}
}
const body = (issue.body || '').replace(/\r/g, '');
const field = name => {
const m = body.match(new RegExp(`^###[ \t]*${name}[ \t]*\n+([^\n#][^\n]*)`, 'm'));
return m && m[1].trim() !== '_No response_' ? m[1].trim() : '';
};
const type = field('Script Type');
if (type && !/^(CT|VM)\b/.test(type)) { core.notice(`${type}: only ct/ and vm/ scripts are built`); return; }
const path = field('Script path').replace(/^\/+/, '');
// Same slug rule as pr-merged-ready-for-testing.yml: the "Name of
// the Script" field, else the title, lowercased, spaces -> dashes.
const slug = (field('Name of the Script') || issue.title).trim().toLowerCase().replace(/\s+/g, '-');
requested = /^(ct|vm)\/[A-Za-z0-9._-]+\.sh$/.test(path) ? path : slug;
core.info(`Ready For Testing: building ${requested}`);
} else {
prNumber = context.payload.issue.number;
const body = context.payload.comment.body.trim();
@@ -140,7 +176,7 @@ jobs:
// `/test <app>` in an issue (not a PR) builds that app from the
// default branch as it is right now, e.g. to see whether a reported
// error reproduces. No PR, so no check run, no review, no approval.
const isIssue = context.eventName === 'issue_comment' && !context.payload.issue.pull_request;
const isIssue = ['issue_comment', 'issues'].includes(context.eventName) && !context.payload.issue.pull_request;
let pr = null, head, headSha;
if (isIssue) {
const { data: info } = await github.rest.repos.get({ owner, repo });
+15 -2
View File
@@ -468,8 +468,19 @@ fi
touch "$RAW_LOG.start"
[[ -n "$OS" ]] && build_env+=(var_os="$OS")
[[ -n "$VAR_ARM64" ]] && build_env+=(var_arm64=yes)
# A script that hands over to upstream's own installer (run_external_installer)
# asks first; nobody can answer in CI, and testing the script means running it.
build_env+=(var_external_installer=yes)
# Both engines turn var_github_token into GITHUB_TOKEN inside the container.
[[ -n "${GH_API_TOKEN:-}" ]] && build_env+=(var_github_token="$GH_API_TOKEN")
# How much API quota this build starts with, with the token and without (the
# lab's one address). /rate_limit itself costs nothing. A "rate limit" in the
# build is then easy to place.
gh_quota() {
curl -s --max-time 10 ${1:+-H "Authorization: Bearer $1"} https://api.github.com/rate_limit 2>/dev/null |
jq -r '.rate | "\(.remaining) of \(.limit) left, resets in \(((.reset - now) / 60) | floor) min"' 2>/dev/null || echo "unknown"
}
echo "GitHub API quota: token $(gh_quota "${GH_API_TOKEN:-}") · no token $(gh_quota "")"
# --- Answers for the script's questions ------------------------------------------
# Nobody types into CI, so anything the install asks has to be answered up front.
@@ -588,7 +599,8 @@ if ((exit_code != 0)); then
fi
# With dev_mode=keep (needed to read the install log) the engine carries on
# after a failed install and still prints "Completed Successfully", exit 0.
install_exit=$(strip_ansi <"$RAW_LOG" | grep -oE 'Installation failed in container [0-9]+ \(exit code: [0-9]+\)' |
# The container is a number on Proxmox (9000) and a name on Incus (ci-9000).
install_exit=$(strip_ansi <"$RAW_LOG" | grep -oE 'Installation failed in container [A-Za-z0-9._-]+ \(exit code: [0-9]+\)' |
grep -oE '[0-9]+\)$' | tr -d ')' | tail -n 1)
if [[ -n "$install_exit" ]]; then
last=$(failure_line)
@@ -735,7 +747,7 @@ run_update() {
# timeout runs a program, not a shell function, so spell out the exec here.
if [[ "$PLATFORM" == "incus" ]]; then guest_cmd=(incus exec "$ctid" --); else guest_cmd=(pct exec "$ctid" --); fi
timeout --kill-after=1m "$UPDATE_TIMEOUT" "${guest_cmd[@]}" env PHS_SILENT=1 mode=default MODE=default \
TERM=dumb DIAGNOSTICS=no var_ignore_breaking_changes=yes ${GH_API_TOKEN:+GITHUB_TOKEN="$GH_API_TOKEN"} update </dev/null \
TERM=dumb DIAGNOSTICS=no var_ignore_breaking_changes=yes var_external_installer=yes ${GH_API_TOKEN:+GITHUB_TOKEN="$GH_API_TOKEN"} update </dev/null \
> >(tee "$raw" | live_filter) 2>&1 &
update_pid=$!
wait "$update_pid"
@@ -761,6 +773,7 @@ fi
update_error=$(strip_ansi <"$UPDATE_RAW" | grep -E '✖|\[ERROR\]' | tail -n 1 | sed -E 's/^\s*(✖️?|\s)*//' | cut -c1-180)
if [[ -n "$update_error" ]]; then
if [[ "${update_error,,}" == *"rate limit"* ]]; then
echo "GitHub API quota now: token $(gh_quota "${GH_API_TOKEN:-}") · no token $(gh_quota "")"
update_status="error"
update_step="could not be tested: ${update_error}"
status="pass"
+10 -2
View File
@@ -585,7 +585,11 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
### 🆕 New Scripts
- SimpleLogin ([#18680](https://github.com/community-scripts/ProxmoxVE/pull/18680))
- marimo ([#18672](https://github.com/community-scripts/ProxmoxVE/pull/18672))
- GoatCounter ([#18662](https://github.com/community-scripts/ProxmoxVE/pull/18662))
- Unsloth ([#18697](https://github.com/community-scripts/ProxmoxVE/pull/18697))
- colanode ([#18685](https://github.com/community-scripts/ProxmoxVE/pull/18685))
- SimpleLogin ([#18680](https://github.com/community-scripts/ProxmoxVE/pull/18680))
- Nightingale ([#18674](https://github.com/community-scripts/ProxmoxVE/pull/18674))
- Temporal ([#18670](https://github.com/community-scripts/ProxmoxVE/pull/18670))
- TrailBase ([#18669](https://github.com/community-scripts/ProxmoxVE/pull/18669))
@@ -608,7 +612,11 @@ Exercise vigilance regarding copycat or coat-tailing sites that seek to exploit
### 💾 Core
- Shield pct start and the template check from a closed console [@MickLesk](https://github.com/MickLesk) ([core#239](https://github.com/community-scripts/core/pull/239))
- fix: run Meilisearch --upgrade-db on patch updates too [@CrazyWolf13](https://github.com/CrazyWolf13) ([core#259](https://github.com/community-scripts/core/pull/259))
- db: only chmod a database credentials file that is a regular file [@MickLesk](https://github.com/MickLesk) ([core#253](https://github.com/community-scripts/core/pull/253))
- fix: take a Debian cloud image from the newest build that has it [@MickLesk](https://github.com/MickLesk) ([core#261](https://github.com/community-scripts/core/pull/261))
- telemetry: always on for DevScripts, with a one-time notice [@michelroegl-brunner](https://github.com/michelroegl-brunner) ([core#245](https://github.com/community-scripts/core/pull/245))
- Shield pct start and the template check from a closed console [@MickLesk](https://github.com/MickLesk) ([core#239](https://github.com/community-scripts/core/pull/239))
- core: say why a container stopped by itself during its setup [@MickLesk](https://github.com/MickLesk) ([core#234](https://github.com/community-scripts/core/pull/234))
- Download a corrupt local template again instead of keeping it [@MickLesk](https://github.com/MickLesk) ([core#235](https://github.com/community-scripts/core/pull/235))
- fetch the install script before the container is created [@MickLesk](https://github.com/MickLesk) ([core#236](https://github.com/community-scripts/core/pull/236))
+63
View File
@@ -0,0 +1,63 @@
#!/usr/bin/env bash
_cs_boot="${COMMUNITY_SCRIPTS_CORE_DIR:-$(dirname "${BASH_SOURCE[0]}")/../../core}/core/build.func"
source "$_cs_boot" 2>/dev/null || source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/core/build.func")
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/arp242/goatcounter
APP="GoatCounter"
var_tags="${var_tags:-analytics;privacy}"
var_cpu="${var_cpu:-1}"
var_ram="${var_ram:-512}"
var_disk="${var_disk:-2}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
var_arm64="${var_arm64:-yes}"
var_unprivileged="${var_unprivileged:-1}"
export var_admin_email="${var_admin_email:-}"
export var_admin_pass="${var_admin_pass:-}"
export var_goatcounter_domain="${var_goatcounter_domain:-}"
header_info "$APP"
variables
color
catch_errors
function update_script() {
header_info
check_container_storage
check_container_resources
if [[ ! -f /opt/goatcounter/goatcounter ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
if check_for_gh_release "goatcounter" "arp242/goatcounter"; then
msg_info "Stopping GoatCounter"
systemctl stop goatcounter
msg_ok "Stopped GoatCounter"
fetch_and_deploy_gh_release "goatcounter" "arp242/goatcounter" "singlefile" "latest" "/opt/goatcounter" "goatcounter-v*-linux-$(arch_resolve).gz"
mv /opt/goatcounter/goatcounter /opt/goatcounter/goatcounter.gz
gunzip -f /opt/goatcounter/goatcounter.gz
msg_info "Starting GoatCounter"
systemctl start goatcounter
msg_ok "Started GoatCounter"
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 "${GATEWAY}${BGN}http://${IP}:8080${CL}"
echo -e "${INFO}${YW}Admin login: /opt/goatcounter_data/.env (inside the container)${CL}"
+23 -18
View File
@@ -2,12 +2,12 @@
_cs_boot="${COMMUNITY_SCRIPTS_CORE_DIR:-$(dirname "${BASH_SOURCE[0]}")/../../core}/core/build.func"
source "$_cs_boot" 2>/dev/null || source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/core/build.func")
# Copyright (c) 2021-2026 community-scripts ORG
# Author: Yusuf Gundogdu (yusuf-gundogdu)
# Author: MickLesk (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://libredb.org
# Source: https://github.com/marimo-team/marimo
APP="LibreDB Studio"
var_tags="${var_tags:-database;sql}"
APP="marimo"
var_tags="${var_tags:-notebook;python;data}"
var_cpu="${var_cpu:-2}"
var_ram="${var_ram:-2048}"
var_disk="${var_disk:-6}"
@@ -16,6 +16,8 @@ var_version="${var_version:-13}"
var_arm64="${var_arm64:-yes}"
var_unprivileged="${var_unprivileged:-1}"
export var_token_password="${var_token_password:-}"
header_info "$APP"
variables
color
@@ -26,25 +28,28 @@ function update_script() {
check_container_storage
check_container_resources
if [[ ! -f /usr/bin/libredb-studio ]]; then
if [[ ! -d /opt/marimo ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
if check_for_gh_release "libredb-studio" "libredb/libredb-studio"; then
msg_info "Stopping Service"
systemctl stop libredb-studio
msg_ok "Stopped Service"
if check_for_gh_release "marimo" "marimo-team/marimo"; then
msg_info "Stopping marimo"
systemctl stop marimo
msg_ok "Stopped marimo"
create_backup /etc/libredb-studio/env
PYTHON_VERSION="3.13" setup_uv
DPKG_FORCE_CONFOLD=1 fetch_and_deploy_gh_release "libredb-studio" "libredb/libredb-studio" "binary" "latest" "" "libredb-studio_*_$(arch_resolve).deb"
msg_info "Updating marimo"
$STD uv pip install --upgrade --python /opt/marimo/.venv/bin/python "marimo[recommended]==${CHECK_UPDATE_RELEASE}" pandas
cat <<EOF >~/.marimo
${CHECK_UPDATE_RELEASE}
EOF
msg_ok "Updated marimo"
restore_backup
msg_info "Starting Service"
systemctl start libredb-studio
msg_ok "Started Service"
msg_info "Starting marimo"
systemctl start marimo
msg_ok "Started marimo"
msg_ok "Updated successfully!"
fi
exit
@@ -57,5 +62,5 @@ 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 "${GATEWAY}${BGN}http://${IP}:3000${CL}"
echo -e "${INFO}${YW}Admin credentials: /etc/libredb-studio/env inside the container${CL}"
echo -e "${GATEWAY}${BGN}http://${IP}:2718${CL}"
echo -e "${INFO}${YW}Password: MARIMO_TOKEN_PASSWORD in /opt/marimo_data/.env (inside the container)${CL}"
+68
View File
@@ -0,0 +1,68 @@
#!/usr/bin/env bash
_cs_boot="${COMMUNITY_SCRIPTS_CORE_DIR:-$(dirname "${BASH_SOURCE[0]}")/../../core}/core/build.func"
source "$_cs_boot" 2>/dev/null || source <(curl -fsSL "${COMMUNITY_SCRIPTS_CORE_URL:-https://raw.githubusercontent.com/community-scripts/core/main}/core/build.func")
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/unslothai/unsloth
APP="Unsloth"
var_tags="${var_tags:-ai;llm}"
var_cpu="${var_cpu:-4}"
var_ram="${var_ram:-8192}"
var_disk="${var_disk:-40}"
var_os="${var_os:-debian}"
var_version="${var_version:-13}"
var_gpu="${var_gpu:-yes}"
var_arm64="${var_arm64:-no}"
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/unsloth ]]; then
msg_error "No ${APP} Installation Found!"
exit
fi
msg_info "Stopping Service"
systemctl stop unsloth
msg_ok "Stopped Service"
if [[ -d /dev/dri || -e /dev/nvidia0 || -e /dev/kfd ]]; then
ENABLE_GPU="yes" setup_hwaccel
fi
msg_info "Updating ${APP} (Patience)"
curl -fsSL https://unsloth.ai/install.sh -o /tmp/unsloth-install.sh
export UNSLOTH_STUDIO_HOME=/opt/unsloth
export UNSLOTH_SKIP_AUTOSTART=1
export UNSLOTH_PYTHON=3.12
$STD sh /tmp/unsloth-install.sh
unset UNSLOTH_SKIP_AUTOSTART UNSLOTH_PYTHON
rm -f /tmp/unsloth-install.sh
msg_ok "Updated ${APP}"
msg_info "Starting Service"
systemctl start unsloth
msg_ok "Started Service"
msg_ok "Updated successfully!"
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 "${GATEWAY}${BGN}http://${IP}:8888${CL}"
echo -e "${INFO}${YW}Login credentials: ~/unsloth.creds (inside the container)${CL}"
+61
View File
@@ -0,0 +1,61 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/arp242/goatcounter
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
color
verb_ip6
catch_errors
setting_up_container
network_check
update_os
fetch_and_deploy_gh_release "goatcounter" "arp242/goatcounter" "singlefile" "latest" "/opt/goatcounter" "goatcounter-v*-linux-$(arch_resolve).gz"
mv /opt/goatcounter/goatcounter /opt/goatcounter/goatcounter.gz
gunzip -f /opt/goatcounter/goatcounter.gz
msg_info "Configuring GoatCounter"
ADMIN_EMAIL="${var_admin_email:-admin@goatcounter.local}"
ADMIN_PASS="${var_admin_pass:-$(random_password)}"
mkdir -p /opt/goatcounter_data
cat <<EOF >/opt/goatcounter_data/.env
GOATCOUNTER_DB=sqlite+/opt/goatcounter_data/db.sqlite3
GOATCOUNTER_LISTEN=:8080
GOATCOUNTER_TLS=http
ADMIN_EMAIL=${ADMIN_EMAIL}
ADMIN_PASSWORD=${ADMIN_PASS}
EOF
chmod 600 /opt/goatcounter_data/.env
$STD /opt/goatcounter/goatcounter db create site -createdb \
-db sqlite+/opt/goatcounter_data/db.sqlite3 \
-vhost "${var_goatcounter_domain:-$LOCAL_IP}" \
-user.email "$ADMIN_EMAIL" \
-user.password "$ADMIN_PASS"
msg_ok "Configured GoatCounter"
msg_info "Creating Service"
cat <<EOF >/etc/systemd/system/goatcounter.service
[Unit]
Description=GoatCounter
After=network.target
[Service]
Type=simple
WorkingDirectory=/opt/goatcounter_data
EnvironmentFile=/opt/goatcounter_data/.env
ExecStart=/opt/goatcounter/goatcounter serve -automigrate
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
systemctl enable -q --now goatcounter
msg_ok "Created Service"
motd_ssh
customize
cleanup_lxc
-38
View File
@@ -1,38 +0,0 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: Yusuf Gundogdu (yusuf-gundogdu)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://libredb.org
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
color
verb_ip6
catch_errors
setting_up_container
network_check
update_os
fetch_and_deploy_gh_release "libredb-studio" "libredb/libredb-studio" "binary" "latest" "" "libredb-studio_*_$(arch_resolve).deb"
msg_info "Configuring LibreDB Studio"
JWT_SECRET="$(openssl rand -hex 32)"
ADMIN_PASSWORD="$(openssl rand -base64 18 | tr -d '/+=' | cut -c1-16)"
mkdir -p /etc/libredb-studio
cat <<EOF >/etc/libredb-studio/env
HOSTNAME=0.0.0.0
PORT=3000
# Cookie is marked Secure on non-loopback hosts; plain-HTTP LXC login loops without this.
AUTH_COOKIE_SECURE=false
AUTH_BOOTSTRAP=off
JWT_SECRET=${JWT_SECRET}
ADMIN_EMAIL=admin@libredb.org
ADMIN_PASSWORD=${ADMIN_PASSWORD}
EOF
chmod 600 /etc/libredb-studio/env
systemctl enable -q --now libredb-studio
msg_ok "Configured LibreDB Studio"
motd_ssh
customize
cleanup_lxc
+62
View File
@@ -0,0 +1,62 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/marimo-team/marimo
source /dev/stdin <<<"$FUNCTIONS_FILE_PATH"
color
verb_ip6
catch_errors
setting_up_container
network_check
update_os
PYTHON_VERSION="3.13" setup_uv
MARIMO_VERSION="${var_appversion:-$(get_latest_github_release "marimo-team/marimo")}"
msg_info "Installing marimo ${MARIMO_VERSION}"
$STD uv venv --python 3.13 /opt/marimo/.venv
$STD uv pip install --python /opt/marimo/.venv/bin/python "marimo[recommended]==${MARIMO_VERSION}" pandas
cat <<EOF >~/.marimo
${MARIMO_VERSION}
EOF
msg_ok "Installed marimo ${MARIMO_VERSION}"
msg_info "Configuring marimo"
mkdir -p /opt/marimo_data/notebooks /root/.config/marimo
cat <<EOF >/opt/marimo_data/.env
MARIMO_TOKEN_PASSWORD=${var_token_password:-$(random_password)}
EOF
chmod 600 /opt/marimo_data/.env
cat <<EOF >/root/.config/marimo/marimo.toml
[package_management]
manager = "uv"
EOF
msg_ok "Configured marimo"
msg_info "Creating Service"
cat <<EOF >/etc/systemd/system/marimo.service
[Unit]
Description=marimo
After=network.target
[Service]
Type=simple
WorkingDirectory=/opt/marimo_data/notebooks
EnvironmentFile=/opt/marimo_data/.env
Environment=MARIMO_SKIP_UPDATE_CHECK=1
ExecStart=/opt/marimo/.venv/bin/marimo -y edit --headless --host 0.0.0.0 --port 2718 --token-password \${MARIMO_TOKEN_PASSWORD} /opt/marimo_data/notebooks
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
systemctl enable -q --now marimo
msg_ok "Created Service"
motd_ssh
customize
cleanup_lxc
+90
View File
@@ -0,0 +1,90 @@
#!/usr/bin/env bash
# Copyright (c) 2021-2026 community-scripts ORG
# Author: MickLesk (CanbiZ)
# License: MIT | https://github.com/community-scripts/ProxmoxVE/raw/main/LICENSE
# Source: https://github.com/unslothai/unsloth
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 \
build-essential \
libgomp1 \
git
msg_ok "Installed Dependencies"
UV_PYTHON="3.12" setup_uv
NODE_VERSION="22" setup_nodejs
setup_hwaccel
msg_info "Setting up Unsloth Studio (Patience)"
mkdir -p /opt/unsloth_data
curl -fsSL https://unsloth.ai/install.sh -o /tmp/unsloth-install.sh
export UNSLOTH_STUDIO_HOME=/opt/unsloth
export UNSLOTH_SKIP_AUTOSTART=1
export UNSLOTH_PYTHON=3.12
$STD sh /tmp/unsloth-install.sh
unset UNSLOTH_SKIP_AUTOSTART UNSLOTH_PYTHON
rm -f /tmp/unsloth-install.sh
[[ -x /opt/unsloth/unsloth_studio/bin/unsloth ]] || {
msg_error "Unsloth Studio was not installed to /opt/unsloth/unsloth_studio"
exit 1
}
msg_ok "Set up Unsloth Studio"
msg_info "Configuring Unsloth Studio"
cat <<EOF >/opt/unsloth.env
UNSLOTH_STUDIO_HOME=/opt/unsloth
HF_HOME=/opt/unsloth_data/huggingface
EOF
chmod 600 /opt/unsloth.env
UNSLOTH_PASSWORD=$(UNSLOTH_STUDIO_HOME=/opt/unsloth \
/opt/unsloth/unsloth_studio/bin/unsloth studio reset-password |
sed -n "s/^New password for 'unsloth': //p")
[[ -n "$UNSLOTH_PASSWORD" ]] || {
msg_error "Could not set the initial Unsloth admin password"
exit 1
}
cat <<EOF >~/unsloth.creds
Unsloth Studio
Username: unsloth
Password: ${UNSLOTH_PASSWORD}
EOF
chmod 600 ~/unsloth.creds
msg_ok "Configured Unsloth Studio"
msg_info "Creating Service"
cat <<EOF >/etc/systemd/system/unsloth.service
[Unit]
Description=Unsloth Studio
Wants=network-online.target
After=network-online.target
[Service]
Type=simple
User=root
WorkingDirectory=/opt/unsloth_data
EnvironmentFile=/opt/unsloth.env
ExecStart=/opt/unsloth/unsloth_studio/bin/unsloth studio -H 0.0.0.0 -p 8888
Restart=on-failure
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
systemctl enable -q --now unsloth
msg_ok "Created Service"
motd_ssh
customize
cleanup_lxc