From 270104320c19d32c9269afccf97b5254cbb4e6ba Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 20 Feb 2026 19:58:26 +0000 Subject: [PATCH 1/5] Validate user input: DNS server, VNC port, fix regex patterns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Validate DNS server as IPv4 format, fallback to 1.0.0.1 if invalid - Validate VNC port as numeric in range 1-65535 - Escape dots in GREP_PATTERN regex: . → \. (6 occurrences) https://claude.ai/code/session_01GUjwhhcF9R4o72g3wx98ey --- ProxRescue.sh | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/ProxRescue.sh b/ProxRescue.sh index a7f1e76..a07f668 100644 --- a/ProxRescue.sh +++ b/ProxRescue.sh @@ -79,7 +79,12 @@ while [[ $# -gt 0 ]]; do shift ;; -vport) - NOVNC_PORT="$2" + if [[ "$2" =~ ^[0-9]+$ ]] && [ "$2" -ge 1 ] && [ "$2" -le 65535 ]; then + NOVNC_PORT="$2" + else + echo "Error: Invalid port number: $2" + exit 1 + fi shift shift ;; @@ -96,7 +101,12 @@ while [[ $# -gt 0 ]]; do shift ;; -dns) - NAME_SERVER="$2" + if [[ "$2" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then + NAME_SERVER="$2" + else + echo "Warning: Invalid DNS server IP address: $2. Using fallback 1.0.0.1" + NAME_SERVER="1.0.0.1" + fi shift shift ;; @@ -402,15 +412,15 @@ select_proxmox_product_and_version() { case "$PRODUCT_CHOICE" in "Proxmox Virtual Environment") - GREP_PATTERN='proxmox-ve_([0-9]+.[0-9]+-[0-9]+).iso' + GREP_PATTERN='proxmox-ve_([0-9]+\.[0-9]+-[0-9]+)\.iso' PRODUCT_NAME="Proxmox Virtual Environment" ;; "Proxmox Backup Server") - GREP_PATTERN='proxmox-backup-server_([0-9]+.[0-9]+-[0-9]+).iso' + GREP_PATTERN='proxmox-backup-server_([0-9]+\.[0-9]+-[0-9]+)\.iso' PRODUCT_NAME="Proxmox Backup Server" ;; "Proxmox Mail Gateway") - GREP_PATTERN='proxmox-mail-gateway_([0-9]+.[0-9]+-[0-9]+).iso' + GREP_PATTERN='proxmox-mail-gateway_([0-9]+\.[0-9]+-[0-9]+)\.iso' PRODUCT_NAME="Proxmox Mail Gateway" ;; esac @@ -426,9 +436,9 @@ select_proxmox_product_and_version() { read -r -p "Enter number (1-4): " product_choice case "$product_choice" in - 1) GREP_PATTERN='proxmox-ve_([0-9]+.[0-9]+-[0-9]+).iso'; PRODUCT_NAME="Proxmox Virtual Environment"; valid_choice=1 ;; - 2) GREP_PATTERN='proxmox-backup-server_([0-9]+.[0-9]+-[0-9]+).iso'; PRODUCT_NAME="Proxmox Backup Server"; valid_choice=1 ;; - 3) GREP_PATTERN='proxmox-mail-gateway_([0-9]+.[0-9]+-[0-9]+).iso'; PRODUCT_NAME="Proxmox Mail Gateway"; valid_choice=1 ;; + 1) GREP_PATTERN='proxmox-ve_([0-9]+\.[0-9]+-[0-9]+)\.iso'; PRODUCT_NAME="Proxmox Virtual Environment"; valid_choice=1 ;; + 2) GREP_PATTERN='proxmox-backup-server_([0-9]+\.[0-9]+-[0-9]+)\.iso'; PRODUCT_NAME="Proxmox Backup Server"; valid_choice=1 ;; + 3) GREP_PATTERN='proxmox-mail-gateway_([0-9]+\.[0-9]+-[0-9]+)\.iso'; PRODUCT_NAME="Proxmox Mail Gateway"; valid_choice=1 ;; 4) echo "Returning to main menu..."; return ;; *) echo "Invalid selection. Please, try again."; ;; esac From 189187b0aad58f5c99b98b8566b163823dfca80d Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 20 Feb 2026 19:59:24 +0000 Subject: [PATCH 2/5] Add dependency and environment checks before QEMU launch - Check qemu-system-x86_64 is installed before run_qemu - Verify OVMF firmware exists when UEFI mode is requested - Error out when no suitable disks are found - Validate IP/CIDR format in get_network_info https://claude.ai/code/session_01GUjwhhcF9R4o72g3wx98ey --- ProxRescue.sh | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/ProxRescue.sh b/ProxRescue.sh index a07f668..d75c320 100644 --- a/ProxRescue.sh +++ b/ProxRescue.sh @@ -161,6 +161,10 @@ get_network_info() { fi IP_CIDR=$(ip addr show "$INTERFACE_NAME" | grep "inet\b" | head -n 1 | awk '{print $2}' || true) + if [ -z "$IP_CIDR" ] || [[ ! "$IP_CIDR" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/[0-9]+$ ]]; then + echo "Error: No valid IP configuration found for interface $INTERFACE_NAME" + exit 1 + fi GATEWAY=$(ip route | grep default | awk '{print $3}' || true) IP_ADDRESS=$(echo "$IP_CIDR" | cut -d'/' -f1) CIDR=$(echo "$IP_CIDR" | cut -d'/' -f2) @@ -289,6 +293,10 @@ select_disks() { } run_qemu() { + if ! command -v qemu-system-x86_64 &>/dev/null; then + echo "Error: qemu-system-x86_64 not found. Install: apt install qemu-system-x86" + return 1 + fi get_network_info local task=$1 if [ ${#QEMU_DISK_ARGS[@]} -eq 0 ]; then @@ -299,11 +307,19 @@ run_qemu() { QEMU_DISK_ARGS+=(-drive "file=/dev/${disk},format=raw,if=virtio,index=${disk_index},media=disk") disk_index=$((disk_index + 1)) done + if [ ${#QEMU_DISK_ARGS[@]} -eq 0 ]; then + echo "Error: No suitable disks found on the system." + return 1 + fi fi local QEMU_COMMON_ARGS=(-daemonize -enable-kvm -m "$QEMU_MEMORY" -vnc ":0,password=on" -monitor "telnet:127.0.0.1:4444,server,nowait") if [ "$USE_UEFI" = "true" ]; then + if [ ! -f "/usr/share/ovmf/OVMF.fd" ]; then + echo "Error: OVMF firmware not found. Install: apt install ovmf" + return 1 + fi QEMU_COMMON_ARGS=(-bios /usr/share/ovmf/OVMF.fd "${QEMU_COMMON_ARGS[@]}") fi if [ "$task" = "install" ]; then From 8b2d80b5835b71bb9e773f1a29ac6172aeb28fe0 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 20 Feb 2026 20:00:42 +0000 Subject: [PATCH 3/5] Improve robustness: apt error handling, git clone, ISO temp file - Check apt install exit code, abort on failure - Use return 1 instead of exit 1 in git clone failures - Download ISO to temp file, rename only after checksum passes - Accept iso_path parameter in verify_iso_checksum https://claude.ai/code/session_01GUjwhhcF9R4o72g3wx98ey --- ProxRescue.sh | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/ProxRescue.sh b/ProxRescue.sh index d75c320..691aad1 100644 --- a/ProxRescue.sh +++ b/ProxRescue.sh @@ -186,8 +186,11 @@ check_and_install_packages() { echo "Installing required packages..." apt update -qq || { echo "Error: apt update failed."; exit 1; } for package in "${missing_packages[@]}"; do - echo "Install package: $package" - apt install -y "$package" -qq + echo "Installing package: $package" + if ! apt install -y "$package" -qq; then + echo "Error: Failed to install $package" + exit 1 + fi done clear echo "$logo" @@ -201,12 +204,12 @@ install_novnc() { echo "noVNC not found. Cloning noVNC from GitHub..." if ! git clone https://github.com/novnc/noVNC.git; then echo "Error: Failed to clone noVNC repository." - exit 1 + return 1 fi echo "Cloning websockify for noVNC..." if ! git clone https://github.com/novnc/websockify noVNC/utils/websockify; then echo "Error: Failed to clone websockify repository." - exit 1 + return 1 fi echo "Renaming vnc.html to index.html..." cp noVNC/vnc.html noVNC/index.html @@ -396,6 +399,7 @@ run_qemu() { verify_iso_checksum() { local iso_name="$1" + local iso_path="${2:-/tmp/proxmox.iso}" echo "Downloading SHA256SUMS for verification..." if ! curl -sf "https://download.proxmox.com/iso/SHA256SUMS" -o /tmp/proxmox_sha256sums; then echo "Warning: Could not download SHA256SUMS file. Skipping verification." @@ -410,7 +414,7 @@ verify_iso_checksum() { fi echo "Verifying SHA256 checksum..." local actual_hash - actual_hash=$(sha256sum /tmp/proxmox.iso | awk '{print $1}') + actual_hash=$(sha256sum "$iso_path" | awk '{print $1}') rm -f /tmp/proxmox_sha256sums if [ "$expected_hash" = "$actual_hash" ]; then echo "SHA256 checksum verified successfully." @@ -507,17 +511,20 @@ select_proxmox_product_and_version() { fi ISO_URL="https://download.proxmox.com/iso/$selected_iso" + local tmp_iso="/tmp/proxmox_download_$$.iso" echo "Downloading $ISO_URL..." - if ! curl -f "$ISO_URL" -o /tmp/proxmox.iso --progress-bar; then + if ! curl -f "$ISO_URL" -o "$tmp_iso" --progress-bar; then echo "Error: Failed to download ISO from $ISO_URL" + rm -f "$tmp_iso" return fi - if ! verify_iso_checksum "$selected_iso"; then + if ! verify_iso_checksum "$selected_iso" "$tmp_iso"; then echo "SHA256 checksum verification FAILED. The downloaded ISO may be corrupted or tampered with." echo "Please try downloading again or verify manually." - rm -f /tmp/proxmox.iso + rm -f "$tmp_iso" return fi + mv "$tmp_iso" /tmp/proxmox.iso print_logo run_qemu "install" } From b0a7723e0f660bd4077504f6a7d59ffb17d14303 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 20 Feb 2026 21:02:59 +0000 Subject: [PATCH 4/5] Add shfmt and actionlint CI jobs, apply shfmt formatting - Add shfmt job: checks shell formatting with 4-space indent - Add actionlint job: lints GitHub Actions workflow files - Apply shfmt -i 4 -ci formatting to ProxRescue.sh (trailing whitespace, case pattern spacing, comment alignment) https://claude.ai/code/session_01GUjwhhcF9R4o72g3wx98ey --- .github/workflows/lint.yml | 21 ++++++ ProxRescue.sh | 133 ++++++++++++++++++++++--------------- 2 files changed, 101 insertions(+), 53 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index a33c950..e533584 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -47,3 +47,24 @@ jobs: with: check_filenames: true skip: .git,noVNC + + shfmt: + name: Shell Formatting + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install shfmt + run: | + SHFMT_VERSION="v3.10.0" + curl -Lo shfmt "https://github.com/mvdan/sh/releases/download/${SHFMT_VERSION}/shfmt_${SHFMT_VERSION}_linux_amd64" + chmod +x shfmt + sudo mv shfmt /usr/local/bin/ + - name: Check formatting + run: shfmt -d -i 4 -ci . + + actionlint: + name: Lint GitHub Actions + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: raven-actions/actionlint@v2 diff --git a/ProxRescue.sh b/ProxRescue.sh index 691aad1..163ce1b 100644 --- a/ProxRescue.sh +++ b/ProxRescue.sh @@ -2,21 +2,21 @@ set -euo pipefail # ============================================================================================ -# ██████ ██████ ██████ ██ ██ ███ ███ ██████ ██ ██ ██ ███ ██ ███████ ██████ -# ██ ██ ██ ██ ██ ██ ██ ██ ████ ████ ██ ██ ██ ██ ██ ████ ██ ██ ██ ██ -# ██████ ██████ ██ ██ ███ ██ ████ ██ ██ ██ ███ ██ ██ ██ ██ █████ ██ ██ -# ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ -# ██ ██ ██ ██████ ██ ██ ██ ██ ██████ ██ ██ ██ ██ ██ ████ ██ ██████ -# +# ██████ ██████ ██████ ██ ██ ███ ███ ██████ ██ ██ ██ ███ ██ ███████ ██████ +# ██ ██ ██ ██ ██ ██ ██ ██ ████ ████ ██ ██ ██ ██ ██ ████ ██ ██ ██ ██ +# ██████ ██████ ██ ██ ███ ██ ████ ██ ██ ██ ███ ██ ██ ██ ██ █████ ██ ██ +# ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ +# ██ ██ ██ ██████ ██ ██ ██ ██ ██████ ██ ██ ██ ██ ██ ████ ██ ██████ +# # Proxmox Products Installer in Rescue Mode for Hetzner -# +# # © 2024 Proxmox UA www.proxmox.info. Все права защищены. -# +# # Сообщества и поддержка: # - Telegram:https://t.me/Proxmox_UA # - GitHub: https://github.com/Proxmoxinfo/ProxMoxRescueHelper # - Website: https://proxmox.info -# +# # Этот скрипт предназначен для установки продуктов Proxmox в режиме восстановления на серверах Hetzner. # ============================================================================================ @@ -34,13 +34,13 @@ logo=' ' VNC_PASSWORD="" -VNC_PASSWORD_LENGTH=10 # min 8, max 20 +VNC_PASSWORD_LENGTH=10 # min 8, max 20 PRODUCT_CHOICE="" NOVNC_PORT="" USE_UEFI="" NAME_SERVER="1.1.1.1" -QEMU_MEMORY="3000" # in megabytes +QEMU_MEMORY="3000" # in megabytes QEMU_DISK_ARGS=() if [ -z "$VNC_PASSWORD" ]; then @@ -50,8 +50,8 @@ if [ -z "$VNC_PASSWORD" ]; then fi VNC_PASSWORD=$(head -c 256 /dev/urandom | tr -dc A-Za-z0-9 | head -c "$VNC_PASSWORD_LENGTH") fi -if [ -z "$NOVNC_PORT" ]; then - NOVNC_PORT=8080 +if [ -z "$NOVNC_PORT" ]; then + NOVNC_PORT=8080 fi show_help() { @@ -73,10 +73,10 @@ show_help() { while [[ $# -gt 0 ]]; do case $1 in - -p|--password) + -p | --password) VNC_PASSWORD="$2" - shift - shift + shift + shift ;; -vport) if [[ "$2" =~ ^[0-9]+$ ]] && [ "$2" -ge 1 ] && [ "$2" -le 65535 ]; then @@ -87,18 +87,18 @@ while [[ $# -gt 0 ]]; do fi shift shift - ;; + ;; -ve) PRODUCT_CHOICE="Proxmox Virtual Environment" - shift + shift ;; -bs) PRODUCT_CHOICE="Proxmox Backup Server" - shift + shift ;; -mg) PRODUCT_CHOICE="Proxmox Mail Gateway" - shift + shift ;; -dns) if [[ "$2" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then @@ -114,10 +114,10 @@ while [[ $# -gt 0 ]]; do USE_UEFI="true" shift ;; - -h|--help) + -h | --help) show_help exit 0 - ;; + ;; *) echo "Warning: Unknown option: $1" shift @@ -136,7 +136,7 @@ clear_list() { print_logo() { clear - echo "$logo" + echo "$logo" } get_network_info() { @@ -170,7 +170,6 @@ get_network_info() { CIDR=$(echo "$IP_CIDR" | cut -d'/' -f2) } - check_and_install_packages() { local required_packages=(curl sshpass dialog) local missing_packages=() @@ -184,7 +183,10 @@ check_and_install_packages() { echo "$logo" else echo "Installing required packages..." - apt update -qq || { echo "Error: apt update failed."; exit 1; } + apt update -qq || { + echo "Error: apt update failed." + exit 1 + } for package in "${missing_packages[@]}"; do echo "Installing package: $package" if ! apt install -y "$package" -qq; then @@ -197,7 +199,6 @@ check_and_install_packages() { fi } - install_novnc() { echo "Checking for noVNC installation..." if [ ! -d "noVNC" ]; then @@ -212,9 +213,9 @@ install_novnc() { return 1 fi echo "Renaming vnc.html to index.html..." - cp noVNC/vnc.html noVNC/index.html + cp noVNC/vnc.html noVNC/index.html else - echo "noVNC is already installed." + echo "noVNC is already installed." if [ ! -f "noVNC/index.html" ]; then echo "Renaming vnc.html to index.html..." cp noVNC/vnc.html noVNC/index.html @@ -229,7 +230,7 @@ configure_network() { local tmp_netcfg tmp_netcfg=$(mktemp /tmp/proxmox_network_config.XXXXXX) trap 'rm -f "$tmp_netcfg"' RETURN - cat > "$tmp_netcfg" <"$tmp_netcfg" < /dev/null 2>&1 & + ./noVNC/utils/novnc_proxy --vnc 127.0.0.1:5900 --listen "$IP_ADDRESS:$NOVNC_PORT" >/dev/null 2>&1 & NOVNC_PID=$! while true; do # pgrep is used here because qemu runs with -daemonize (no direct PID) - if ! pgrep -f "qemu-system-x86_64" > /dev/null; then + if ! pgrep -f "qemu-system-x86_64" >/dev/null; then echo "QEMU process has stopped unexpectedly." kill "$NOVNC_PID" 2>/dev/null || true echo "noVNC stopped." @@ -373,7 +374,7 @@ run_qemu() { echo -e "Ip for vnc connect: $IP_ADDRESS\n" echo "For use NoVNC open in browser http://$IP_ADDRESS:$NOVNC_PORT" echo -e "\nYour password for connect: \033[1m$VNC_PASSWORD\033[0m\n" - ./noVNC/utils/novnc_proxy --vnc 127.0.0.1:5900 --listen "$IP_ADDRESS:$NOVNC_PORT" > /dev/null 2>&1 & + ./noVNC/utils/novnc_proxy --vnc 127.0.0.1:5900 --listen "$IP_ADDRESS:$NOVNC_PORT" >/dev/null 2>&1 & NOVNC_PID=$! while true; do if ! kill -0 "$QEMU_PID" 2>/dev/null; then @@ -394,7 +395,7 @@ run_qemu() { break fi done - fi + fi } verify_iso_checksum() { @@ -456,11 +457,26 @@ select_proxmox_product_and_version() { read -r -p "Enter number (1-4): " product_choice case "$product_choice" in - 1) GREP_PATTERN='proxmox-ve_([0-9]+\.[0-9]+-[0-9]+)\.iso'; PRODUCT_NAME="Proxmox Virtual Environment"; valid_choice=1 ;; - 2) GREP_PATTERN='proxmox-backup-server_([0-9]+\.[0-9]+-[0-9]+)\.iso'; PRODUCT_NAME="Proxmox Backup Server"; valid_choice=1 ;; - 3) GREP_PATTERN='proxmox-mail-gateway_([0-9]+\.[0-9]+-[0-9]+)\.iso'; PRODUCT_NAME="Proxmox Mail Gateway"; valid_choice=1 ;; - 4) echo "Returning to main menu..."; return ;; - *) echo "Invalid selection. Please, try again."; ;; + 1) + GREP_PATTERN='proxmox-ve_([0-9]+\.[0-9]+-[0-9]+)\.iso' + PRODUCT_NAME="Proxmox Virtual Environment" + valid_choice=1 + ;; + 2) + GREP_PATTERN='proxmox-backup-server_([0-9]+\.[0-9]+-[0-9]+)\.iso' + PRODUCT_NAME="Proxmox Backup Server" + valid_choice=1 + ;; + 3) + GREP_PATTERN='proxmox-mail-gateway_([0-9]+\.[0-9]+-[0-9]+)\.iso' + PRODUCT_NAME="Proxmox Mail Gateway" + valid_choice=1 + ;; + 4) + echo "Returning to main menu..." + return + ;; + *) echo "Invalid selection. Please, try again." ;; esac done fi @@ -482,13 +498,13 @@ select_proxmox_product_and_version() { echo "Error: No ISO versions found for $PRODUCT_NAME." return fi - IFS=$'\n' read -r -d '' -a iso_array <<< "$AVAILABLE_ISOS" || true + IFS=$'\n' read -r -d '' -a iso_array <<<"$AVAILABLE_ISOS" || true echo "Please select the version to install (default is the latest version):" for i in "${!iso_array[@]}"; do - echo "$((i+1))) ${iso_array[i]}" + echo "$((i + 1))) ${iso_array[i]}" done - echo "$(( ${#iso_array[@]} + 1 )) Return to product selection" - echo "$(( ${#iso_array[@]} + 2 )) Return to main menu" + echo "$((${#iso_array[@]} + 1)) Return to product selection" + echo "$((${#iso_array[@]} + 2)) Return to main menu" read -r -t 30 -p "Enter number (1-$((${#iso_array[@]} + 2))) or wait for auto-selection: " version_choice || true if [ -z "${version_choice:-}" ]; then @@ -496,15 +512,15 @@ select_proxmox_product_and_version() { echo "Auto-selected the latest version due to timeout." fi - if [ "$version_choice" -eq "$(( ${#iso_array[@]} + 1 ))" ]; then + if [ "$version_choice" -eq "$((${#iso_array[@]} + 1))" ]; then echo "Returning to product selection..." select_proxmox_product_and_version return - elif [ "$version_choice" -eq "$(( ${#iso_array[@]} + 2 ))" ]; then + elif [ "$version_choice" -eq "$((${#iso_array[@]} + 2))" ]; then echo "Returning to main menu..." return elif [[ "$version_choice" =~ ^[0-9]+$ ]] && [ "$version_choice" -ge 1 ] && [ "$version_choice" -le "${#iso_array[@]}" ]; then - selected_iso="${iso_array[$((version_choice-1))]}" + selected_iso="${iso_array[$((version_choice - 1))]}" else echo "Invalid selection, using the latest version." selected_iso="${iso_array[0]}" @@ -529,16 +545,15 @@ select_proxmox_product_and_version() { run_qemu "install" } - reboot_server() { echo "Are you sure you want to reboot the server? (Y/n)" read -r answer - if [[ $answer =~ ^[Yy]?$ ]]; then + if [[ $answer =~ ^[Yy]?$ ]]; then echo "Rebooting..." shutdown -r now else echo "Reboot canceled. Returning to main menu..." - return + return fi } @@ -578,14 +593,26 @@ show_menu() { read -r -p "Enter choice: " choice case "$choice" in 1) select_proxmox_product_and_version ;; - 2) USE_UEFI=true; select_proxmox_product_and_version ;; + 2) + USE_UEFI=true + select_proxmox_product_and_version + ;; 3) runInstalledSystem ;; - 4) USE_UEFI=true; runInstalledSystem ;; + 4) + USE_UEFI=true + runInstalledSystem + ;; 5) changeVncPassword ;; - 6) reboot_server; return ;; + 6) + reboot_server + return + ;; 7) exitScript ;; 8) select_disks ;; - *) echo "Invalid selection. Please, try again."; continue ;; + *) + echo "Invalid selection. Please, try again." + continue + ;; esac show_menu break From ba9702d949481d00feaf4df4c935eca4b102023c Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 20 Feb 2026 21:20:05 +0000 Subject: [PATCH 5/5] Add markdownlint CI job for Markdown files Add markdownlint-cli2-action to lint workflow to enforce consistent Markdown formatting in README.md and other .md files. Includes config to disable line-length rule (MD013). https://claude.ai/code/session_01GUjwhhcF9R4o72g3wx98ey --- .github/workflows/lint.yml | 9 +++++++++ .markdownlint-cli2.yaml | 2 ++ 2 files changed, 11 insertions(+) create mode 100644 .markdownlint-cli2.yaml diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index e533584..1077661 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -68,3 +68,12 @@ jobs: steps: - uses: actions/checkout@v4 - uses: raven-actions/actionlint@v2 + + markdownlint: + name: Markdown Lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: DavidAnson/markdownlint-cli2-action@v19 + with: + globs: "**/*.md" diff --git a/.markdownlint-cli2.yaml b/.markdownlint-cli2.yaml new file mode 100644 index 0000000..9358a7f --- /dev/null +++ b/.markdownlint-cli2.yaml @@ -0,0 +1,2 @@ +config: + MD013: false