Overhaul the telemetry system to enforce a single-reporter model where only the HOST sends terminal statuses (success/failed/aborted) to the API. Containers now write local artifact files (.failed flag + .errinfo) that the host picks up after lxc-attach returns, preventing the race condition where a metadata-less container payload would win over the full host payload.
Key changes:
- Add TELEMETRY_CONTEXT=container export in install.func/alpine-install.func to mark container context before error handling starts
- Introduce _is_container_context() heuristic and _container_write_failure() artifact writer in error_handler.func
- Refactor api.func: unified _tm_payload() builder (full metadata on every send), _tm_send() curl wrapper, _tm_enabled() gate, telemetry_collect_sysinfo() cached collector
- Add structured .errinfo capture in silent() (core.func) using byte-offset to extract exactly the failing command's output
- Pull .errinfo from container in build.func after lxc-attach for precise error traces
- Add categorize_error(), telemetry_new_attempt(), detect_arm(), REPO_SLUG tracking
- Signal exits (129/130/143) now report as 'aborted' instead of 'failed'
- Remove post_update_to_api_extended() (superseded by unified _tm_payload)
- Export REPO_SOURCE, REPO_SLUG, TELEMETRY_PLATFORM into container environment
CLEAN_INSTALL now removes dotfiles (e.g. .env) along with other files, using `find -mindepth 1 -delete` instead of `rm -rf *`. Update scripts for affine, nametag, and postiz are updated to explicitly back up and restore .env (via create_backup/restore_backup or cp) BEFORE the build step that sources it, rather than relying on the wipe preserving dotfiles.
* tools.func: replace rm -rf with find for safer directory cleanup
* tools.func: replace rm -rf with find for safer directory cleanup
* refactor(tools.func): centralize deploy tail + trap-based tmpdir cleanup
Extract the duplicated post-download logic of the fetch_and_deploy_*
helpers into two shared functions and switch per-branch cleanup to a
single RETURN trap.
- _deploy_source_tarball: shared source-tarball tail (6 call sites)
- _deploy_unpacked_archive: shared prebuild-archive tail (3 call sites)
- RETURN trap per function guarantees tmpdir/unpack_tmp cleanup on every
return path, replacing ~40 manual `rm -rf "$tmpdir"` lines
- CLEAN_INSTALL now lives in the helpers instead of 12 copies
Behavior-preserving except: codeberg prebuild gains .txz support and
uses helper return codes; from_url resets shopt on error paths.
* Add _download_source_tarball with retry/validation
Introduce `_download_source_tarball` helper that validates gzip integrity after download and retries up to 3 times. This guards against truncated-but-valid-HTTP responses from GitHub/GitLab/Codeberg on-the-fly archive generation. Replace ad-hoc curl/curl_download calls in fetch_and_deploy_* functions with the new helper. Also remove redundant tmpdir cleanup before early returns (tmpdir is cleaned up at function exit).
* Fix Manyfold Ruby setup home initialization
Create the `manyfold` service user before calling `setup_ruby` so `/home/manyfold` exists when rbenv profile snippets are written. Also harden `setup_ruby` by creating `$HOME` if missing, preventing profile-write failures for installers that pass a home directory before creating the user.
* Update install/manyfold-install.sh
---------
Co-authored-by: Sam Heinz <sam@samheinz.com>
Introduce host CA certificate propagation in the shared LXC build flow so containers can trust enterprise/private PKI roots during early package bootstrap. Add an advanced-install toggle with default auto behavior so unattended installs remain seamless while interactive users can explicitly opt out.
Co-authored-by: Michel Roegl-Brunner <michel.roegl-brunner@example.com>
Query PocketBase pinned_version/pin_reason during LXC updates and show an informational message when the installed version matches the pin, so users know not to open issues for missing newer updates.
Add optional tag prefix filtering to GitHub/GitLab release helpers and use web-v2 for Storyteller install/update so latest non-web tags no longer break deployment.
* Update tools.func
- Add _TOOLS_FUNC_LOADED guard to prevent double-sourcing
- Remove duplicate is_alpine() (core.func version is more robust)
- Fix end_timer: now actually outputs duration (was silent)
- Fix SQL injection in setup_mariadb_db: escape single quotes in identifiers
- Fix SQL injection in setup_postgresql_db: escape single quotes in identifiers
- Fix sed injection in edit_yaml_config: escape | and & in value
- Fix command injection in curl_with_retry: use array instead of string eval
- Fix command injection in curl_api_with_retry: use array instead of string eval
* Update misc/tools.func
Co-authored-by: Sam Heinz <sam@samheinz.com>
---------
Co-authored-by: Sam Heinz <sam@samheinz.com>
Replace the destructive multi-select container update block with a
proper per-container Y/N workflow:
- Unattended / non-interactive (DOCKER_NONINTERACTIVE=1 or no tty):
skip silently -- no docker pulls, no prompts.
- Interactive: stop_spinner() before any prompt to keep the terminal
clean, then for each container with a newer image:
Compose-managed → prompt Y/N (auto-no after 60 s)
→ on Y: docker compose pull <service> &&
docker compose up -d <service>
Standalone run → prompt Y/N (auto-no after 60 s)
→ on Y: docker pull only; no stop/rm;
user is told to recreate manually
- portainer / portainer_agent are excluded (handled earlier in
setup_docker)
The old code always stopped and removed the container without
recreating it, leaving users with a destroyed service. Standalone
containers without a Compose project can never be auto-recreated
safely, so only the image is pulled.
Fixes#15601
The 'Interactive Container Update Check' block scanned ALL running
Docker containers, pulled their images, then stopped and removed them
with only a message asking the user to manually recreate them. This
is destructive and outside the scope of the Docker LXC update script,
which is responsible for updating the Docker engine itself and the
Portainer / Portainer-Agent containers that this script originally
installed.
Removes the entire block. Updates now cover:
- OS packages (apt)
- Docker engine (via setup_docker / repo)
- Portainer CE (if installed by this script)
- Portainer Agent (if installed by this script)
Self-hosted / user-managed containers are intentionally left alone.
Fixes#15601
Ensure Node module setup runs non-interactively by exporting COREPACK_ENABLE_DOWNLOAD_PROMPT=0, then handle corepack first when requested (including versioned specs). pnpm/yarn are now provisioned through corepack when enabled to avoid shim EEXIST collisions, with corepack modules skipped in the generic npm loop and conflicting shims cleaned only for npm-global installs. Also replace the global pnpm dangerouslyAllowAllBuilds setting with strictDepBuilds=false to avoid project-level config conflicts while keeping installs usable.
Add a fallback retry using `npm install -g --force` when the initial install fails. This handles EEXIST errors caused by corepack-provided shims for pnpm/yarn that ship with recent Node.js versions and block installation to /usr/bin/<tool>.
Allow users to set var_ignore_disable=true to continue past the 'script disabled' guard in runtime_script_status_guard(). A warning is still shown, but execution continues instead of aborting. Deleted scripts remain a hard stop.
During the container update check, a failing 'docker pull' (local-only images, registry or permission errors) aborted the whole script under errexit. Ignore pull failures and skip containers whose digest could not be resolved.