diff --git a/Dockerfile b/Dockerfile index c072f48..1cae772 100644 --- a/Dockerfile +++ b/Dockerfile @@ -207,7 +207,7 @@ ARG LICENSE="WTFPL" ARG ENV_PORTS="${EXPOSE_PORTS}" USER ${USER} -WORKDIR /root +WORKDIR /app LABEL maintainer="CasjaysDev " LABEL org.opencontainers.image.vendor="CasjaysDev" @@ -245,10 +245,13 @@ ENV NODE_MANAGER="${NODE_MANAGER}" ENV PHP_VERSION="${PHP_VERSION}" ENV DISTRO_VERSION="${IMAGE_VERSION}" ENV WWW_ROOT_DIR="${WWW_ROOT_DIR}" +ENV RUSTUP_HOME="/usr/local/share/rustup" +ENV CARGO_HOME="/usr/local/share/cargo" +ENV RUSTUP_TOOLCHAIN="stable" COPY --from=build /. / -VOLUME [ "/config","/data" ] +VOLUME [ "/config","/data","/usr/local/share/cargo","/usr/local/share/rustup" ] EXPOSE ${SERVICE_PORT} ${ENV_PORTS} diff --git a/rootfs/root/docker/setup/05-custom.sh b/rootfs/root/docker/setup/05-custom.sh index 042a104..df8ba7e 100755 --- a/rootfs/root/docker/setup/05-custom.sh +++ b/rootfs/root/docker/setup/05-custom.sh @@ -8,9 +8,8 @@ # @@Copyright : Copyright 2026 CasjaysDev # @@Created : Sun May 31 11:04:50 AM EDT 2026 # @@File : 05-custom.sh -# @@Description : script to run custom +# @@Description : Install latest stable Rust toolchain with static-build tooling # @@Changelog : newScript -# @@TODO : Refactor code # @@Other : N/A # @@Resource : N/A # @@Terminal App : yes @@ -32,6 +31,245 @@ exitCode=0 # - - - - - - - - - - - - - - - - - - - - - - - - - # Main script +# Install C/C++ toolchain and static-build dependencies needed by Rust -sys crates +pkmgr install build-base musl-dev clang lld cmake make perl openssl-dev pkgconf + +# Install cross-compile toolchains — failures are non-fatal on minimal mirrors +pkmgr install mingw-w64-gcc || true + +# Install zig for cargo-zigbuild (C-dep cross-compilation without a sysroot) +pkmgr install zig || true + +# Install binaryen (wasm-opt) for WASM size optimisation tooling +pkmgr install binaryen || true + +# - - - - - - - - - - - - - - - - - - - - - - - - - +# rustup paths — match the official rust:alpine convention so existing +# workflows work unchanged; volumes are declared at these paths in the Dockerfile +export RUSTUP_HOME="/usr/local/share/rustup" +export CARGO_HOME="/usr/local/share/cargo" +export PATH="$CARGO_HOME/bin:$PATH" + +mkdir -p "$RUSTUP_HOME" "$CARGO_HOME/bin" + +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Download the architecture-appropriate rustup-init and verify its SHA256 +# before executing — this is the only thing that ever touches rust-lang.org +# directly; all subsequent installs go through rustup or cargo-binstall +RUSTUP_ARCH="$(uname -m)-unknown-linux-musl" +RUSTUP_URL="https://static.rust-lang.org/rustup/dist/${RUSTUP_ARCH}/rustup-init" + +curl -sSfL "$RUSTUP_URL" -o /tmp/rustup-init +curl -sSfL "${RUSTUP_URL}.sha256" -o /tmp/rustup-init.sha256 + +EXPECTED_SHA="$(awk '{print $1}' /tmp/rustup-init.sha256)" +ACTUAL_SHA="$(sha256sum /tmp/rustup-init | awk '{print $1}')" +[ "$EXPECTED_SHA" = "$ACTUAL_SHA" ] || { + echo "rustup-init SHA256 mismatch: expected $EXPECTED_SHA got $ACTUAL_SHA" >&2 + exit 1 +} +chmod +x /tmp/rustup-init + +# Install the latest stable toolchain; --profile default includes rustfmt and clippy +/tmp/rustup-init -y --no-modify-path \ + --default-toolchain stable \ + --profile default + +rm -f /tmp/rustup-init /tmp/rustup-init.sha256 + +# Add components not included in the default profile +rustup component add rust-src rust-analyzer llvm-tools-preview + +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Cross-compile targets — Linux musl (fully static, no libc dependency) +rustup target add \ + x86_64-unknown-linux-musl \ + aarch64-unknown-linux-musl \ + i686-unknown-linux-musl \ + armv7-unknown-linux-musleabihf \ + riscv64gc-unknown-linux-musl + +# Cross-compile targets — Linux glibc +rustup target add \ + x86_64-unknown-linux-gnu \ + aarch64-unknown-linux-gnu \ + i686-unknown-linux-gnu \ + armv7-unknown-linux-gnueabihf \ + arm-unknown-linux-gnueabi \ + riscv64gc-unknown-linux-gnu \ + powerpc64le-unknown-linux-gnu \ + s390x-unknown-linux-gnu + +# Cross-compile targets — Windows GNU ABI (MSVC ABI is not supported) +rustup target add \ + x86_64-pc-windows-gnu \ + i686-pc-windows-gnu \ + aarch64-pc-windows-gnullvm + +# Cross-compile targets — macOS (pure-Rust only; Apple SDK not bundled) +rustup target add \ + x86_64-apple-darwin \ + aarch64-apple-darwin + +# Cross-compile targets — BSD +rustup target add \ + x86_64-unknown-freebsd + +# Cross-compile targets — WebAssembly +rustup target add \ + wasm32-unknown-unknown \ + wasm32-wasip1 \ + wasm32-wasip2 \ + wasm32-unknown-emscripten + +# Cross-compile targets — Embedded ARM (require no_std source) +rustup target add \ + thumbv6m-none-eabi \ + thumbv7em-none-eabihf \ + thumbv8m.main-none-eabi + +# Cross-compile targets — Embedded RISC-V (require no_std source) +rustup target add \ + riscv32imc-unknown-none-elf \ + riscv32imac-unknown-none-elf + +# Cross-compile targets — Android +rustup target add \ + aarch64-linux-android + +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Bootstrap cargo-binstall — downloads prebuilt binaries instead of +# compiling every tool from source, cutting install time dramatically +BINSTALL_ARCH="$(uname -m)" +BINSTALL_URL="https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-${BINSTALL_ARCH}-unknown-linux-musl.tgz" +curl -sSfL "$BINSTALL_URL" -o /tmp/cargo-binstall.tgz +tar xzf /tmp/cargo-binstall.tgz -C /tmp cargo-binstall +install -m 755 /tmp/cargo-binstall "$CARGO_HOME/bin/cargo-binstall" +rm -f /tmp/cargo-binstall.tgz /tmp/cargo-binstall + +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Workflow and development tools — all have musl prebuilt binaries +cargo binstall -y \ + cargo-edit \ + cargo-watch \ + cargo-update \ + cargo-outdated \ + cargo-expand \ + cargo-info \ + bacon \ + cargo-nextest \ + cargo-llvm-cov \ + cargo-tarpaulin \ + cargo-audit \ + cargo-deny \ + cargo-machete \ + cargo-semver-checks \ + cargo-make \ + cargo-deb \ + cargo-generate \ + cargo-release \ + cargo-chef \ + cargo-zigbuild \ + just \ + tokei \ + hyperfine \ + wasm-pack \ + wasm-tools \ + wasm-bindgen-cli \ + cbindgen \ + cargo-binutils \ + cargo-bloat \ + cargo-asm \ + mdbook \ + mdbook-toc + +# Tools that occasionally lack musl prebuilts — fall back to source compilation +cargo binstall -y cargo-dist 2>/dev/null || cargo install cargo-dist +cargo binstall -y cargo-msrv 2>/dev/null || cargo install cargo-msrv +cargo binstall -y cargo-mutants 2>/dev/null || cargo install cargo-mutants +cargo binstall -y flip-link 2>/dev/null || cargo install flip-link +cargo binstall -y cargo-ndk 2>/dev/null || cargo install cargo-ndk +cargo binstall -y trunk 2>/dev/null || cargo install trunk 2>/dev/null || true + +# cross (the cross-rs cross-compilation runner) +cargo binstall -y cross 2>/dev/null || cargo install cross 2>/dev/null || true + +# probe-rs requires the cli feature flag and is best built from source +cargo install probe-rs --features cli 2>/dev/null || true + +# samply and cargo-flamegraph require a system perf or dtrace — best-effort +cargo binstall -y samply 2>/dev/null || true +cargo binstall -y cargo-flamegraph 2>/dev/null || true + +# sqlx-cli and sea-orm-cli need project-specific feature flags at runtime; +# install a broadly compatible build here as a convenience +cargo install sqlx-cli --no-default-features --features native-tls,postgres,mysql,sqlite 2>/dev/null || true +cargo install sea-orm-cli 2>/dev/null || true + +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Linker configuration for cross-compilation targets. +# rust-lld handles ARM/RISC-V/embedded; mingw-w64 gcc handles Windows GNU. +mkdir -p "$CARGO_HOME" +cat > "$CARGO_HOME/config.toml" << 'CARGOCONFIG' +[target.aarch64-unknown-linux-musl] +linker = "rust-lld" + +[target.armv7-unknown-linux-musleabihf] +linker = "rust-lld" + +[target.armv7-unknown-linux-gnueabihf] +linker = "rust-lld" + +[target.arm-unknown-linux-gnueabi] +linker = "rust-lld" + +[target.thumbv6m-none-eabi] +linker = "rust-lld" + +[target.thumbv7em-none-eabihf] +linker = "rust-lld" + +[target.thumbv8m.main-none-eabi] +linker = "rust-lld" + +[target.riscv32imc-unknown-none-elf] +linker = "rust-lld" + +[target.riscv32imac-unknown-none-elf] +linker = "rust-lld" + +[target.x86_64-pc-windows-gnu] +linker = "x86_64-w64-mingw32-gcc" +ar = "x86_64-w64-mingw32-ar" + +[target.i686-pc-windows-gnu] +linker = "i686-w64-mingw32-gcc" +ar = "i686-w64-mingw32-ar" +CARGOCONFIG + +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Symlink every cargo/bin tool into /usr/local/bin so they are reachable +# without a login shell — the Dockerfile's PATH includes /usr/local/bin +for bin_file in "$CARGO_HOME/bin"/*; do + [ -x "$bin_file" ] || continue + ln -sf "$bin_file" "/usr/local/bin/${bin_file##*/}" +done + +# Home-relative symlinks expected by rustup, cargo, and most documentation +ln -sf "$CARGO_HOME" "/root/.cargo" +ln -sf "$RUSTUP_HOME" "/root/.rustup" + +# Profile.d entry so login shells get the full environment +cat > /etc/profile.d/rust.sh << PROFILE +export RUSTUP_HOME="${RUSTUP_HOME}" +export CARGO_HOME="${CARGO_HOME}" +export RUSTUP_TOOLCHAIN="stable" +export PATH="${CARGO_HOME}/bin:\${PATH}" +PROFILE + +# Work directories mounted or referenced in the README +mkdir -p /app /work /root/app /root/project /data/build + # - - - - - - - - - - - - - - - - - - - - - - - - - # Set the exit code exitCode=$? @@ -40,4 +278,3 @@ exit $exitCode # - - - - - - - - - - - - - - - - - - - - - - - - - # ex: ts=2 sw=2 et filetype=sh # - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/rootfs/usr/local/bin/copy b/rootfs/usr/local/bin/copy new file mode 100755 index 0000000..e9878d8 --- /dev/null +++ b/rootfs/usr/local/bin/copy @@ -0,0 +1,78 @@ +#!/usr/bin/env sh +# shellcheck shell=sh +# - - - - - - - - - - - - - - - - - - - - - - - - - +##@Version : 202605051306-git +# @@Author : Jason Hempstead +# @@Contact : jason@casjaysdev.pro +# @@License : WTFPL +# @@ReadME : copy --help +# @@Copyright : Copyright: (c) 2026 Jason Hempstead, Casjays Developments +# @@Created : Tuesday, May 05, 2026 13:06 EDT +# @@File : copy +# @@Description : copies a file and shows progress +# @@Changelog : Refactored for self-contained operation +# @@TODO : Better documentation +# @@Other : +# @@Resource : +# @@Terminal App : no +# @@sudo/root : no +# @@Template : shell/sh +# - - - - - - - - - - - - - - - - - - - - - - - - - +# shellcheck disable=SC1001,SC1003,SC2001,SC2003,SC2016,SC2031,SC2090,SC2115,SC2120,SC2155,SC2199,SC2229,SC2317,SC2329 +# - - - - - - - - - - - - - - - - - - - - - - - - - +APPNAME="$(basename -- "$0" 2>/dev/null)" +# - - - - - - - - - - - - - - - - - - - - - - - - - +# colorization +if [ -n "$NO_COLOR" ]; then + __printf_color() { printf '%b' "$1\n" | tr -d '\t' | sed '/^%b$/d;s,\x1B\[ 0-9;]*[a-zA-Z],,g'; } +else + __printf_color() { { [ -z "$2" ] || DEFAULT_COLOR=$2; } && printf "%b" "$(tput setaf "$DEFAULT_COLOR" 2>/dev/null)" "$1\n" "$(tput sgr0 2>/dev/null)"; } +fi +# - - - - - - - - - - - - - - - - - - - - - - - - - +__unlink() { [ -L "$1" ] && rm -f -- "$1" >/dev/null; } +# - - - - - - - - - - - - - - - - - - - - - - - - - +# custom functions +__copy() { + exitCode=0 + if [ -d "$1" ]; then + __printf_color "Copying $1/* to $2/" + __unlink "$2" + mkdir -p "$2" + for f in "$1"/* "$1"/.[!.]* "$1"/..?*; do + [ -e "$f" ] || [ -L "$f" ] || continue + base=$(basename -- "$f") + __copy "$f" "$2/$base" || exitCode=$? + done + elif [ -f "$1" ] || [ -L "$1" ]; then + __printf_color "Copying $1 to $2" + __unlink "$2" + cp -Rf "$1" "$2" + exitCode=$? + fi + return $exitCode +} +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Define variables +DEFAULT_COLOR="254" +COPY_EXIT_STATUS=0 +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Main application +if [ $# -ne 2 ]; then + __printf_color "USAGE: $APPNAME to from" "1" >&2 + COPY_EXIT_STATUS=1 +elif [ ! -e "$1" ]; then + __printf_color "$1 does not exist" >&2 + COPY_EXIT_STATUS=2 +else + __printf_color "Copying $1 to $2" "4" + __copy "$1" "$2" >/dev/null + COPY_EXIT_STATUS=$? +fi +# - - - - - - - - - - - - - - - - - - - - - - - - - +# End application +# - - - - - - - - - - - - - - - - - - - - - - - - - +# lets exit with code +# - - - - - - - - - - - - - - - - - - - - - - - - - +exit $COPY_EXIT_STATUS +# - - - - - - - - - - - - - - - - - - - - - - - - - +# ex: ts=2 sw=2 et filetype=sh diff --git a/rootfs/usr/local/bin/entrypoint.sh b/rootfs/usr/local/bin/entrypoint.sh index 55da5fb..94867c4 100755 --- a/rootfs/usr/local/bin/entrypoint.sh +++ b/rootfs/usr/local/bin/entrypoint.sh @@ -506,9 +506,11 @@ if [ "$START_SERVICES" = "yes" ] || [ -z "$1" ]; then echo "$$" >"$ENTRYPOINT_PID_FILE" __start_init_scripts "/usr/local/etc/docker/init.d" CONTAINER_INIT="${CONTAINER_INIT:-no}" - # Services started successfully - enter monitoring mode - __no_exit - exit $? + # Only block in monitoring mode when no user command was given + if [ $# -eq 0 ]; then + __no_exit + exit $? + fi fi START_SERVICES="no" fi @@ -674,11 +676,8 @@ start) # Execute primary command *) if [ $# -eq 0 ]; then - if [ ! -f "$ENTRYPOINT_PID_FILE" ]; then - echo "$$" >"$ENTRYPOINT_PID_FILE" - [ "$START_SERVICES" = "no" ] && [ "$CONTAINER_INIT" = "yes" ] || __start_init_scripts "/usr/local/etc/docker/init.d" - fi - __no_exit + # No args: run the default Rust workflow (fmt → clippy → test → build) + __exec_command rust-workflow else __exec_command "$@" fi diff --git a/rootfs/usr/local/bin/healthcheck b/rootfs/usr/local/bin/healthcheck new file mode 100755 index 0000000..8dab380 --- /dev/null +++ b/rootfs/usr/local/bin/healthcheck @@ -0,0 +1,249 @@ +#!/usr/bin/env sh +# shellcheck shell=sh +# - - - - - - - - - - - - - - - - - - - - - - - - - +##@Version : 202605051654-git +# @@Author : Jason Hempstead +# @@Contact : jason@casjaysdev.pro +# @@License : WTFPL +# @@ReadME : healthcheck --help +# @@Copyright : Copyright: (c) 2026 Jason Hempstead, Casjays Developments +# @@Created : Tuesday, May 05, 2026 16:54 EDT +# @@File : healthcheck +# @@Description : Docker container healthcheck — HTTP/TCP/process/file checks +# @@Changelog : Rewrote as a real Docker HEALTHCHECK probe +# @@TODO : Better documentation +# @@Other : +# @@Resource : +# @@Terminal App : no +# @@sudo/root : no +# @@Template : shell/sh +# - - - - - - - - - - - - - - - - - - - - - - - - - +# shellcheck disable=SC1001,SC1003,SC2001,SC2003,SC2016,SC2031,SC2090,SC2115,SC2120,SC2155,SC2199,SC2229,SC2317,SC2329 +# - - - - - - - - - - - - - - - - - - - - - - - - - +APPNAME="$(basename -- "$0" 2>/dev/null)" +VERSION="202605051654-git" +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Defaults (env vars override built-ins, CLI flags override env vars) +HEALTHCHECK_URL="${HEALTHCHECK_URL:-}" +HEALTHCHECK_HTTP_STATUS="${HEALTHCHECK_HTTP_STATUS:-2,3}" +HEALTHCHECK_HOST="${HEALTHCHECK_HOST:-127.0.0.1}" +HEALTHCHECK_PORT="${HEALTHCHECK_PORT:-}" +HEALTHCHECK_PROCESS="${HEALTHCHECK_PROCESS:-}" +HEALTHCHECK_FILE="${HEALTHCHECK_FILE:-}" +HEALTHCHECK_FILE_MAX_AGE="${HEALTHCHECK_FILE_MAX_AGE:-}" +HEALTHCHECK_TIMEOUT="${HEALTHCHECK_TIMEOUT:-5}" +HEALTHCHECK_VERBOSE="${HEALTHCHECK_VERBOSE:-}" +# - - - - - - - - - - - - - - - - - - - - - - - - - +__cmd_exists() { command -v "$1" >/dev/null 2>&1; } +__log() { [ -n "$HEALTHCHECK_VERBOSE" ] && printf '%s\n' "$*" >&2; return 0; } +__fail() { printf 'UNHEALTHY: %s\n' "$*" >&2; exit 1; } +# - - - - - - - - - - - - - - - - - - - - - - - - - +__usage() { + cat <&2; __usage >&2; exit 1 ;; + *) printf 'Unexpected argument: %s\n' "$1" >&2; exit 1 ;; + esac +done +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Individual checks — each prints why it failed and exits 1 on failure +__trim() { printf '%s' "$1" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//'; } + +__check_one_http() { + url="$1"; accepted="$2"; timeout="$3" + if __cmd_exists curl; then + code="$(curl -ksSL -o /dev/null -w '%{http_code}' --max-time "$timeout" "$url" 2>/dev/null)" \ + || __fail "HTTP request to $url failed (curl error)" + elif __cmd_exists wget; then + code="$(wget -q -S --spider --timeout="$timeout" --tries=1 "$url" 2>&1 \ + | awk '/^ HTTP\// {c=$2} END {print c+0}')" + [ "$code" -gt 0 ] 2>/dev/null || __fail "HTTP request to $url failed (wget error)" + else + __fail "HTTP check requires curl or wget" + fi + IFS=',' + for prefix in $accepted; do + case "$code" in + "$prefix"*) unset IFS; __log "HTTP ok: $url -> $code"; return 0 ;; + esac + done + unset IFS + __fail "HTTP $url returned $code (expected prefix in: $accepted)" +} + +__check_http() { + urls="$1"; accepted="$2"; timeout="$3" + __log "HTTP: urls=$urls (timeout=${timeout}s, accept=${accepted})" + IFS=',' + for u in $urls; do + unset IFS + u="$(__trim "$u")" + [ -n "$u" ] || { IFS=','; continue; } + __check_one_http "$u" "$accepted" "$timeout" + IFS=',' + done + unset IFS + return 0 +} + +__check_one_tcp() { + host="$1"; port="$2"; timeout="$3" + if __cmd_exists nc; then + nc -z -w "$timeout" "$host" "$port" >/dev/null 2>&1 && { __log "TCP ok: $host:$port"; return 0; } + fi + if __cmd_exists ncat; then + ncat -z -w "${timeout}s" "$host" "$port" >/dev/null 2>&1 && { __log "TCP ok (ncat): $host:$port"; return 0; } + fi + # Last resort: bash /dev/tcp (only if bash is available; sh-only systems skip) + if __cmd_exists bash; then + bash -c "exec 3<>/dev/tcp/$host/$port" >/dev/null 2>&1 && { __log "TCP ok (bash): $host:$port"; return 0; } + fi + return 1 +} + +__check_tcp() { + host="$1"; ports="$2"; timeout="$3" + __log "TCP: host=$host ports=$ports (timeout=${timeout}s)" + IFS=',' + for p in $ports; do + unset IFS + p="$(__trim "$p")" + [ -n "$p" ] || { IFS=','; continue; } + __check_one_tcp "$host" "$p" "$timeout" || __fail "TCP $host:$p not reachable" + IFS=',' + done + unset IFS + return 0 +} + +__check_one_process() { + pattern="$1" + if __cmd_exists pgrep; then + # Match against process name (not full cmdline) so our own argv doesn't self-match + pgrep -- "$pattern" >/dev/null 2>&1 && return 0 + else + # Portable fallback: ps -o comm= prints just the command name + ps -e -o comm= 2>/dev/null | grep -v -e "^grep$" -e "^$APPNAME$" | grep -q -- "$pattern" && return 0 + fi + return 1 +} + +__check_process() { + patterns="$1" + __log "Process: patterns=$patterns" + IFS=',' + for p in $patterns; do + unset IFS + p="$(__trim "$p")" + [ -n "$p" ] || { IFS=','; continue; } + __check_one_process "$p" || __fail "Process not running: $p" + __log "Process ok: $p" + IFS=',' + done + unset IFS + return 0 +} + +__check_one_file() { + path="$1"; max_age="$2" + [ -e "$path" ] || __fail "File not found: $path" + if [ -n "$max_age" ]; then + now="$(date +%s)" + mtime="$(stat -c %Y "$path" 2>/dev/null || stat -f %m "$path" 2>/dev/null \ + || perl -e 'print((stat(shift))[9])' "$path" 2>/dev/null)" + [ -n "$mtime" ] || __fail "Cannot determine mtime of $path" + age=$(( now - mtime )) + [ "$age" -le "$max_age" ] || __fail "File $path is stale (age=${age}s, max=${max_age}s)" + fi + __log "File ok: $path" + return 0 +} + +__check_file() { + paths="$1"; max_age="$2" + __log "File: paths=$paths max_age=${max_age:-none}" + IFS=',' + for f in $paths; do + unset IFS + f="$(__trim "$f")" + [ -n "$f" ] || { IFS=','; continue; } + __check_one_file "$f" "$max_age" + IFS=',' + done + unset IFS + return 0 +} +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Run checks +ran_any=0 +[ -n "$HEALTHCHECK_URL" ] && { __check_http "$HEALTHCHECK_URL" "$HEALTHCHECK_HTTP_STATUS" "$HEALTHCHECK_TIMEOUT"; ran_any=1; } +[ -n "$HEALTHCHECK_PORT" ] && { __check_tcp "$HEALTHCHECK_HOST" "$HEALTHCHECK_PORT" "$HEALTHCHECK_TIMEOUT"; ran_any=1; } +[ -n "$HEALTHCHECK_PROCESS" ] && { __check_process "$HEALTHCHECK_PROCESS"; ran_any=1; } +[ -n "$HEALTHCHECK_FILE" ] && { __check_file "$HEALTHCHECK_FILE" "$HEALTHCHECK_FILE_MAX_AGE"; ran_any=1; } + +[ "$ran_any" -eq 1 ] || __fail "no checks configured (set HEALTHCHECK_URL/PORT/PROCESS/FILE or pass --url/--port/--process/--file)" + +__log "All checks passed" +exit 0 +# - - - - - - - - - - - - - - - - - - - - - - - - - +# ex: ts=2 sw=2 et filetype=sh diff --git a/rootfs/usr/local/bin/rust-workflow b/rootfs/usr/local/bin/rust-workflow new file mode 100755 index 0000000..090d791 --- /dev/null +++ b/rootfs/usr/local/bin/rust-workflow @@ -0,0 +1,88 @@ +#!/usr/bin/env bash +# shellcheck shell=bash +# - - - - - - - - - - - - - - - - - - - - - - - - - +##@Version : 202605311104-git +# @@Author : Jason Hempstead +# @@Contact : jason@casjaysdev.pro +# @@License : WTFPL +# @@ReadME : rust-workflow --help +# @@Copyright : Copyright: (c) 2026 Jason Hempstead, Casjays Developments +# @@Created : Sunday, May 31, 2026 11:04 EDT +# @@File : rust-workflow +# @@Description : Default Rust workflow: fmt → clippy → test → build +# @@Changelog : New script +# @@Other : +# @@Resource : +# @@Terminal App : no +# @@sudo/root : no +# @@Template : other/docker-workflow +# - - - - - - - - - - - - - - - - - - - - - - - - - +# shellcheck disable=SC1001,SC1003,SC2001,SC2003,SC2016,SC2031,SC2090,SC2115,SC2120,SC2155,SC2199,SC2229,SC2317,SC2329 +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Runs automatically when `docker run casjaysdev/rust` is called with no args. +# Working directory must be a Cargo workspace or crate root (Cargo.toml must exist). +set -euo pipefail + +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Resolve the working directory — prefer /app if mounted, then cwd +WORK_DIR="${CARGO_WORKDIR:-${PWD}}" +cd "$WORK_DIR" + +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Require a Cargo.toml so we fail fast with a clear message instead of +# cryptic cargo errors about missing manifests +if [ ! -f "Cargo.toml" ]; then + echo "Error: no Cargo.toml found in ${WORK_DIR}" >&2 + echo "Mount your project with: docker run --rm -v \"\$(pwd)\":/app casjaysdev/rust" >&2 + exit 1 +fi + +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Resolve the crate or workspace name from the manifest +CRATE="$(awk -F'"' '/^\[package\]/{p=1} p && /^name[[:space:]]*=/{print $2; exit}' Cargo.toml)" +[ -z "$CRATE" ] && CRATE="$(basename "$WORK_DIR")" + +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Optional target override — set CARGO_BUILD_TARGET for cross-compilation or +# static musl builds (e.g. x86_64-unknown-linux-musl) +BUILD_TARGET="${CARGO_BUILD_TARGET:-}" + +# Collect extra flags for steps that accept a target +build_flags=(--release) +test_flags=() +if [ -n "$BUILD_TARGET" ]; then + build_flags+=(--target "$BUILD_TARGET") + test_flags+=(--target "$BUILD_TARGET") +fi + +# - - - - - - - - - - - - - - - - - - - - - - - - - +echo "" +echo "▶ Rust workflow: ${CRATE}" +echo " Working dir: ${WORK_DIR}" +echo " Rust: $(rustc --version)" +echo " Cargo: $(cargo --version)" +[ -n "$BUILD_TARGET" ] && echo " Target: ${BUILD_TARGET}" +echo "" + +# - - - - - - - - - - - - - - - - - - - - - - - - - +run_step() { + local label="$1"; shift + echo "── ${label}" + "$@" + echo "" +} + +# - - - - - - - - - - - - - - - - - - - - - - - - - +# 1. Check formatting — report violations but do not auto-modify source +run_step "cargo fmt --check" cargo fmt --all -- --check + +# 2. Lint with clippy across all targets and features; treat warnings as errors +run_step "cargo clippy" cargo clippy --all-targets --all-features -- -D warnings + +# 3. Run the full test suite before spending time on a release build +run_step "cargo test --all" cargo test --all "${test_flags[@]}" + +# 4. Build the release binary (static if CARGO_BUILD_TARGET is set) +run_step "cargo build --release" cargo build --all "${build_flags[@]}" + +echo "✅ Done." diff --git a/rootfs/usr/local/bin/symlink b/rootfs/usr/local/bin/symlink new file mode 100755 index 0000000..ac2db15 --- /dev/null +++ b/rootfs/usr/local/bin/symlink @@ -0,0 +1,77 @@ +#!/usr/bin/env sh +# shellcheck shell=sh +# - - - - - - - - - - - - - - - - - - - - - - - - - +##@Version : 202605051306-git +# @@Author : Jason Hempstead +# @@Contact : jason@casjaysdev.pro +# @@License : WTFPL +# @@ReadME : symlink --help +# @@Copyright : Copyright: (c) 2026 Jason Hempstead, Casjays Developments +# @@Created : Tuesday, May 05, 2026 13:06 EDT +# @@File : symlink +# @@Description : +# @@Changelog : New script +# @@TODO : Better documentation +# @@Other : +# @@Resource : +# @@Terminal App : no +# @@sudo/root : no +# @@Template : shell/sh +# - - - - - - - - - - - - - - - - - - - - - - - - - +# shellcheck disable=SC1001,SC1003,SC2001,SC2003,SC2016,SC2031,SC2090,SC2115,SC2120,SC2155,SC2199,SC2229,SC2317,SC2329 +# - - - - - - - - - - - - - - - - - - - - - - - - - +APPNAME="$(basename -- "$0" 2>/dev/null)" +# - - - - - - - - - - - - - - - - - - - - - - - - - +# colorization +if [ -n "$NO_COLOR" ]; then + __printf_color() { printf '%b' "$1\n" | tr -d '\t' | sed '/^%b$/d;s,\x1B\[ 0-9;]*[a-zA-Z],,g'; } +else + __printf_color() { { [ -z "$2" ] || DEFAULT_COLOR=$2; } && printf "%b" "$(tput setaf "$DEFAULT_COLOR" 2>/dev/null)" "$1\n" "$(tput sgr0 2>/dev/null)"; } +fi +# - - - - - - - - - - - - - - - - - - - - - - - - - +__unlink() { [ -L "$1" ] && rm -f -- "$1" >/dev/null; } +# - - - - - - - - - - - - - - - - - - - - - - - - - +# custom functions +__ln_sf() { + exitCode=0 + if [ -d "$1" ] && [ ! -L "$1" ]; then + __printf_color "symlinking contents of $1 into $2/" "4" + __unlink "$2" + mkdir -p "$2" + for f in "$1"/* "$1"/.[!.]* "$1"/..?*; do + [ -e "$f" ] || [ -L "$f" ] || continue + base=$(basename -- "$f") + __ln_sf "$f" "$2/$base" || exitCode=$? + done + else + __printf_color "symlinking $2 to $1" "4" + __unlink "$2" + ln -sf "$1" "$2" + exitCode=$? + fi + return $exitCode +} +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Define variables +DEFAULT_COLOR="254" +SYMLINK_EXIT_STATUS=0 +# - - - - - - - - - - - - - - - - - - - - - - - - - +# Main application +if [ $# -ne 2 ]; then + __printf_color "USAGE: $APPNAME from to" "2" >&2 + SYMLINK_EXIT_STATUS=1 +elif [ ! -e "$1" ]; then + __printf_color "$1 does not exist" >&2 + SYMLINK_EXIT_STATUS=2 +else + __ln_sf "$1" "$2" >/dev/null + SYMLINK_EXIT_STATUS=$? +fi +# - - - - - - - - - - - - - - - - - - - - - - - - - +# End application +# - - - - - - - - - - - - - - - - - - - - - - - - - +# lets exit with code +# - - - - - - - - - - - - - - - - - - - - - - - - - +exit $SYMLINK_EXIT_STATUS +# - - - - - - - - - - - - - - - - - - - - - - - - - +# ex: ts=2 sw=2 et filetype=sh