diff options
| author | Danilo M. <danix@danix.xyz> | 2026-05-18 10:49:06 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-05-18 10:49:06 +0200 |
| commit | 9f1ad23a187a493ffa812064e6641061553e6386 (patch) | |
| tree | a164278161830d2f168f830f81fbafbb07670f9e | |
| parent | e647c20e9f387b250469cda2b515cf767d2955a8 (diff) | |
| download | mkhintfile-master.tar.gz mkhintfile-master.zip | |
- Add parse_multiline_var, prompt_continuation_urls, build_multiline_value,
_process_download_var; refactor update_checksums to handle multi-URL vars
- First URL always re-downloaded; continuation URLs prompt user, skip
re-download if unchanged
- Add tests/mkhint_test.sh: 44 cases covering all commands and edge cases
- Update README and CLAUDE.md: correct -N semantics, --new behavior,
multiline DOWNLOAD flow, test suite docs
| -rw-r--r-- | CLAUDE.md | 39 | ||||
| -rw-r--r-- | README.md | 88 | ||||
| -rwxr-xr-x | mkhint | 148 | ||||
| -rwxr-xr-x | tests/mkhint_test.sh | 347 |
4 files changed, 571 insertions, 51 deletions
@@ -26,23 +26,52 @@ bash mkhint --help bash mkhint --list bash mkhint --version 2.0.1 --hintfile mypackage bash mkhint --version 1.2.3 --new mypackage -bash mkhint --new mypackage # empty hint, no version -bash mkhint --version 2.0.1 --hintfile mypackage --no-dl # skip downloads, add NODOWNLOAD=yes +bash mkhint --new mypackage # keep VERSION from .info, no downloads +bash mkhint --version 2.0.1 --hintfile mypackage --no-dl # downloads + md5 + NODOWNLOAD=yes bash mkhint --new mypackage --no-dl # create hint with NODOWNLOAD=yes bash mkhint --delete mypackage bash mkhint --clean ``` -No test suite exists. Test manually against real `REPO_DIR`/`HINT_DIR` or with mock directories. +### Automated test suite + +Uses mock `REPO_DIR`/`HINT_DIR` and a fake `wget` — no real downloads, no real directories needed. Requires `/var/lib/sbopkg/SBo-git/` for `.info` file fixtures (read-only). + +```bash +bash tests/mkhint_test.sh +``` + +Test coverage: + +| ID | Scenario | +|-----|----------| +| T1 | `--new` from `.info`, no version — template copied, VERSION kept from .info | +| T2 | `--new -v` — version updated, md5 recalculated | +| T3 | `--new -N` no version — NODOWNLOAD=yes added, no downloads | +| T4 | `--new -v -N` — version + md5 + NODOWNLOAD=yes | +| T5 | `--new` when hint exists — backup + empty skeleton | +| T6 | `--hintfile -v` single URL — version + md5 updated, backup created | +| T7 | `--hintfile -v -N` — version + md5 + NODOWNLOAD=yes | +| T8 | `DOWNLOAD=UNSUPPORTED`, `DOWNLOAD_x86_64` has URL — 32-bit skipped, x86_64 md5 recalculated | +| T9 | `-N` alone — exits 1 | +| T10 | `--hintfile` on nonexistent file — exits 2 | +| T11 | `--delete` — removes hint and .bak | +| T12 | `--delete` nonexistent — exits 2 | +| T13 | `--new` multiline `.info`, no version — template copied, original md5s kept | +| T14 | `--new` multiline `.info` with `-v` — first URL+md5 updated, continuation md5 kept | +| T15 | `--clean` — removes all .bak files | + +When adding new features, add a corresponding test case to `tests/mkhint_test.sh`. ## Key Behaviors - `--hintfile` update: backs up to `.bak`, replaces old version string globally via `sed`, re-downloads both URLs to recalculate MD5 checksums. Skips download if value is `UNSUPPORTED` or `UNTESTED`. -- `--new` with existing `.info`: copies `.info` as template, strips `PRGNAM`, `HOMEPAGE`, `MAINTAINER`, `EMAIL`, comments out `REQUIRES`, sets `ARCH="x86_64"`. +- `--new` with existing `.info`: copies `.info` as template, strips `PRGNAM`, `HOMEPAGE`, `MAINTAINER`, `EMAIL`, comments out `REQUIRES`, sets `ARCH="x86_64"`. Keeps `VERSION` from `.info`. If `-v` given, updates version string and recalculates checksums. - `--new` when hint already exists: backs up old, creates empty skeleton. -- `--no-dl` / `-N`: skips all downloads; inserts `NODOWNLOAD=yes` after `MD5SUM_x86_64=` line. Works with `--hintfile` or `--new`. Error if used alone. +- `--no-dl` / `-N`: downloads and recalculates checksums as normal, then appends `NODOWNLOAD=yes` after `MD5SUM_x86_64=`. Works with `--hintfile` or `--new`. Error if used alone. - `--delete` / `-d`: removes hint file and `.bak` if present. Accepts multiple package names. Exits 2 on first missing file. - Downloads go to `/tmp/mkhint/download` (single shared temp file, deleted after md5 calculation). +- Multiline `DOWNLOAD`/`DOWNLOAD_x86_64`: parsed via `parse_multiline_var` (awk). First URL always re-downloaded. Continuation URLs (2+) prompt user interactively — changed URLs re-downloaded, unchanged URLs keep existing md5. Written back via `perl -i` with `\` continuation format preserved. Shared logic in `update_checksums` → `_process_download_var`. ## Exit Codes @@ -25,6 +25,8 @@ REPO_DIR="/var/lib/sbopkg/SBo-danix/" # Repository containing .info files HINT_DIR="/etc/slackrepo/SBo-danix/hintfiles/" # Directory where .hint files are stored ``` +Keep the same paths in sync in `mkhint.bash-completion` (lines 8–9). + ## Usage ### Update an existing hint file @@ -32,47 +34,60 @@ HINT_DIR="/etc/slackrepo/SBo-danix/hintfiles/" # Directory where .hint files ar Updates the package version, downloads the archive, and recalculates the MD5 checksums: ```bash -mkhint --version 2.0.1 --hintfile mypackage +mkhint --hintfile mypackage --version 2.0.1 +mkhint -f mypackage -v 2.0.1 ``` -Replaces the old version with 2.0.1 in mypackage.hint and re-downloads the URLs from DOWNLOAD and DOWNLOAD_x86_64 to update MD5SUM and MD5SUM_x86_64. +Replaces the old version string with 2.0.1 everywhere in mypackage.hint, re-downloads the URLs from DOWNLOAD and DOWNLOAD_x86_64, and updates MD5SUM and MD5SUM_x86_64. Backs up the old file to mypackage.hint.bak first. + +URLs set to `UNSUPPORTED` or `UNTESTED` are skipped. -### Update without downloading +### Update and add NODOWNLOAD -Updates the version string and adds `NODOWNLOAD=yes` to skip checksum verification in slackrepo: +Downloads and recalculates checksums, then appends `NODOWNLOAD=yes` to tell slackrepo to skip checksum verification at build time: ```bash -mkhint --version 2.0.1 --hintfile mypackage --no-dl +mkhint -f mypackage -v 2.0.1 -N +mkhint --hintfile mypackage --version 2.0.1 --no-dl ``` ### Create a new hint file from .info -Generates a new hint file from the corresponding repository .info file: +Copies the corresponding .info file as a template, removes PRGNAM, HOMEPAGE, MAINTAINER, EMAIL, comments out REQUIRES, and sets ARCH to x86_64. If `-v` is given, the version string is updated and checksums are recalculated: ```bash -mkhint --version 1.2.3 --new mypackage +mkhint --new mypackage # copy .info as-is, keep VERSION from .info +mkhint -n mypackage -v 1.2.3 # copy .info, update version + recalculate md5 +mkhint -n mypackage -v 1.2.3 -N # same, also add NODOWNLOAD=yes +mkhint -n mypackage -N # copy .info, add NODOWNLOAD=yes, no downloads ``` -The .info file is copied as a template, unwanted variables (PRGNAM, HOMEPAGE, MAINTAINER, EMAIL) are removed, and ARCH is set to x86_64. - -### Create a new hint file with NODOWNLOAD +If no .info file exists in REPO_DIR, a skeleton hint with empty variables is created instead. -```bash -mkhint --new mypackage --no-dl -``` +If the hint file already exists it is backed up and a fresh empty skeleton is written. -### Create an empty hint file +### Multiline DOWNLOAD -Creates a fresh hint file with empty variables: +Some packages have multiple download URLs on continuation lines: -```bash -mkhint --new mypackage ``` +DOWNLOAD="https://example.com/foo-1.0.tar.gz \ + https://example.com/extra-data.tar.gz" +MD5SUM="aabbcc... \ + ddeeff..." +``` + +When updating a hint with multiline DOWNLOAD, mkhint: + +- Always re-downloads the first URL (version changed → new content) +- Prompts interactively for each continuation URL — enter a new URL or leave blank to keep the current one +- Only re-downloads continuation URLs that were changed; unchanged URLs keep their existing md5 ### List hint files ```bash mkhint --list +mkhint -l ``` ### Delete a hint file @@ -81,7 +96,7 @@ Removes the hint file and its .bak backup if present: ```bash mkhint --delete mypackage -mkhint --delete pkg1 pkg2 pkg3 +mkhint -d pkg1 pkg2 pkg3 ``` ### Clean backup files @@ -90,34 +105,41 @@ Removes all .bak files from HINT_DIR: ```bash mkhint --clean +mkhint -c ``` ### Help ```bash mkhint --help +mkhint -h ``` ## Exit Codes -- 0 - Success -- 1 - Invalid arguments -- 2 - File not found -- 3 - File already exists -- 4 - wget not available +| Code | Meaning | +|------|---------| +| 0 | Success | +| 1 | Invalid arguments | +| 2 | File not found | +| 3 | File already exists (unused — backup logic replaces this) | +| 4 | wget not available | ## Hint File Variables -- VERSION - Package version -- ARCH - Architecture -- DOWNLOAD - Download URL (generic/32-bit) -- MD5SUM - MD5 checksum of the generic archive -- DOWNLOAD_x86_64 - Download URL (x86_64 specific) -- MD5SUM_x86_64 - MD5 checksum of the x86_64 archive -- NODOWNLOAD - Set to `yes` to skip download/checksum verification in slackrepo +| Variable | Description | +|----------|-------------| +| VERSION | Package version | +| ARCH | Architecture (x86_64) | +| DOWNLOAD | Download URL for generic/32-bit build | +| MD5SUM | MD5 checksum of the generic archive | +| DOWNLOAD_x86_64 | Download URL for x86_64-specific build | +| MD5SUM_x86_64 | MD5 checksum of the x86_64 archive | +| NODOWNLOAD | Set to `yes` to skip download/checksum verification in slackrepo | ## Notes -- Old versions of hint files are backed up with a .bak extension before being modified. -- If a download URL is set to UNSUPPORTED or UNTESTED, the link is skipped during update. -- Bash completion for -f/--hintfile, -n/--new, and -d/--delete autocompletes package names from their respective directories. +- Hint files are backed up to `.bak` before any modification. +- If DOWNLOAD or DOWNLOAD_x86_64 is `UNSUPPORTED` or `UNTESTED`, that URL is skipped and its MD5SUM is left unchanged. +- `--no-dl` / `-N` does **not** skip downloads — it downloads and recalculates checksums as normal, then appends `NODOWNLOAD=yes` to the hint file. +- Bash completion for `-f`/`--hintfile`, `-n`/`--new`, and `-d`/`--delete` autocompletes package names from their respective directories. Short flags (`-v`, `-f`, `-n`, `-l`, `-c`, `-d`, `-N`, `-h`) are also completed. @@ -218,25 +218,147 @@ add_nodownload() { fi } +# Parse multiline variable value (handles \ continuation lines) +# Prints each whitespace-separated token on its own line +parse_multiline_var() { + local varname="$1" + local file="$2" + # Join continuation lines, strip variable name and quotes, print one token per line + awk -v var="${varname}" ' + BEGIN { found=0; buf="" } + !found && $0 ~ "^"var"=\"" { + found=1 + buf=$0 + sub("^"var"=\"", "", buf) + if (buf !~ /\\[[:space:]]*$/) { + gsub(/"[[:space:]]*$/, "", buf) + n=split(buf, arr, /[[:space:]]+/) + for (k=1;k<=n;k++) if(arr[k]!="") print arr[k] + found=0; buf="" + } else { + gsub(/\\[[:space:]]*$/, "", buf) + } + next + } + found { + if ($0 ~ /\\[[:space:]]*$/) { + line=$0; gsub(/\\[[:space:]]*$/, "", line) + buf=buf" "line + } else { + line=$0; gsub(/"[[:space:]]*$/, "", line) + buf=buf" "line + n=split(buf, arr, /[[:space:]]+/) + for (k=1;k<=n;k++) if(arr[k]!="") print arr[k] + found=0; buf="" + } + } + ' "$file" +} + +# Prompt user for updated continuation URLs; returns updated URLs via nameref array +# First URL is always kept as-is (already updated by version sed before this call) +prompt_continuation_urls() { + local -n _urls="$1" # nameref: array of current URLs + local varname="$2" + + local i + for (( i=1; i<${#_urls[@]}; i++ )); do + local current="${_urls[$i]}" + echo "" + echo " ${varname} line $((i+1)) (current): $current" + read -r -p " New URL (leave blank to keep): " new_url + if [[ -n "$new_url" ]]; then + _urls[$i]="$new_url" + fi + done +} + +# Build multiline variable string for writing back to file +# Usage: build_multiline_value urls_array -> prints quoted multiline value +build_multiline_value() { + local -n _arr="$1" + local count=${#_arr[@]} + local i + for (( i=0; i<count; i++ )); do + if (( i == 0 )); then + printf '"%s' "${_arr[$i]}" + else + printf ' \\\n %s' "${_arr[$i]}" + fi + done + printf '"\n' +} + # Download files and update MD5SUM/MD5SUM_x86_64 in hint file update_checksums() { local file="$1" - local download - download=$(grep '^DOWNLOAD=' "$file" | sed 's/DOWNLOAD="//;s/"$//') - if [[ -n "$download" && $download != "UNSUPPORTED" && $download != "UNTESTED" ]]; then - local sum - sum=$(download_file "$download") - sed -i "/^MD5SUM=/s/MD5SUM=\"[^\"]*\"/MD5SUM=\"$sum\"/" "$file" - fi + _process_download_var "DOWNLOAD" "MD5SUM" "$file" + _process_download_var "DOWNLOAD_x86_64" "MD5SUM_x86_64" "$file" +} - local download_x64 - download_x64=$(grep '^DOWNLOAD_x86_64=' "$file" | sed 's/DOWNLOAD_x86_64="//;s/"$//') - if [[ -n "$download_x64" && $download_x64 != "UNSUPPORTED" && $download_x64 != "UNTESTED" ]]; then - local sum64 - sum64=$(download_file "$download_x64") - sed -i "/^MD5SUM_x86_64=/s/MD5SUM_x86_64=\"[^\"]*\"/MD5SUM_x86_64=\"$sum64\"/" "$file" +# Process one DOWNLOAD/MD5SUM variable pair in a hint file +_process_download_var() { + local dl_var="$1" + local md5_var="$2" + local file="$3" + + # Read current URLs into array + mapfile -t urls < <(parse_multiline_var "$dl_var" "$file") + + [[ ${#urls[@]} -eq 0 ]] && return + [[ "${urls[0]}" == "UNSUPPORTED" || "${urls[0]}" == "UNTESTED" ]] && return + + # Read current md5sums into array (parallel to urls) + mapfile -t md5s < <(parse_multiline_var "$md5_var" "$file") + + # Save original URLs for change detection after prompt + local orig_urls=("${urls[@]}") + + # Prompt user to update continuation URLs if present + if (( ${#urls[@]} > 1 )); then + echo "" + echo "Multiline ${dl_var} detected in $(basename "$file")." + prompt_continuation_urls urls "$dl_var" fi + + # Download and calculate md5 for each URL + local new_md5s=() + local i + for (( i=0; i<${#urls[@]}; i++ )); do + local url="${urls[$i]}" + if (( i == 0 )); then + # Always re-download first URL + echo "Downloading: $url" + new_md5s+=( "$(download_file "$url")" ) + else + # Only re-download if URL changed from original + if [[ "$url" != "${orig_urls[$i]}" ]]; then + echo "Downloading (updated): $url" + new_md5s+=( "$(download_file "$url")" ) + else + echo "Keeping existing md5 for: $url" + new_md5s+=( "${md5s[$i]}" ) + fi + fi + done + + # Rebuild and write back DOWNLOAD variable (may have updated continuation URLs) + local new_dl_value + new_dl_value=$(build_multiline_value urls) + # Strip surrounding quotes — perl wraps them in the substitution + new_dl_value="${new_dl_value#\"}" + new_dl_value="${new_dl_value%\"}" + perl -i -0pe 'BEGIN{$v=shift} s|^'"${dl_var}"'="[^"]*(?:\\\n[^"]*)*"|'"${dl_var}"'="$v"|m' \ + "$new_dl_value" "$file" + + # Rebuild and write back MD5SUM variable + local new_md5_value + new_md5_value=$(build_multiline_value new_md5s) + new_md5_value="${new_md5_value#\"}" + new_md5_value="${new_md5_value%\"}" + perl -i -0pe 'BEGIN{$v=shift} s|^'"${md5_var}"'="[^"]*(?:\\\n[^"]*)*"|'"${md5_var}"'="$v"|m' \ + "$new_md5_value" "$file" } # Update existing hint file diff --git a/tests/mkhint_test.sh b/tests/mkhint_test.sh new file mode 100755 index 0000000..dbcc86a --- /dev/null +++ b/tests/mkhint_test.sh @@ -0,0 +1,347 @@ +#!/bin/bash +# mkhint test suite - uses mock dirs, no real downloads + +SCRIPT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/mkhint" +MOCK_BASE="/tmp/mkhint_test_$$" +MOCK_REPO="$MOCK_BASE/repo" +MOCK_HINT="$MOCK_BASE/hints" +MOCK_TMP="$MOCK_BASE/tmp" + +PASS=0 +FAIL=0 +ERRORS=() + +setup() { + mkdir -p "$MOCK_REPO/network/curl" \ + "$MOCK_REPO/development/protoc-gen-go-grpc" \ + "$MOCK_REPO/development/clion" \ + "$MOCK_HINT" \ + "$MOCK_TMP" + + # Standard single-URL .info + cat > "$MOCK_REPO/network/curl/curl.info" << 'EOF' +PRGNAM="curl" +VERSION="8.5.0" +HOMEPAGE="https://curl.se/" +DOWNLOAD="https://curl.se/download/curl-8.5.0.tar.gz" +MD5SUM="abc123def456abc123def456abc123de" +DOWNLOAD_x86_64="" +MD5SUM_x86_64="" +REQUIRES="" +MAINTAINER="Test" +EMAIL="test@test.com" +EOF + + # Multiline DOWNLOAD .info + cat > "$MOCK_REPO/development/protoc-gen-go-grpc/protoc-gen-go-grpc.info" << 'EOF' +PRGNAM="protoc-gen-go-grpc" +VERSION="1.3.0" +HOMEPAGE="https://github.com/grpc/grpc-go" +DOWNLOAD="https://github.com/grpc/grpc-go/archive/refs/tags/cmd/protoc-gen-go-grpc/v1.3.0/grpc-go-cmd-protoc-gen-go-grpc-v1.3.0.tar.gz \ + https://github.com/protocolbuffers/protobuf-go/archive/v1.28.1/protobuf-go-1.28.1.tar.gz" +MD5SUM="9d3abc100f411a59907528e55e772a10 \ + e11cccd452bbf4296f72bf323d7b8690" +DOWNLOAD_x86_64="" +MD5SUM_x86_64="" +REQUIRES="protoc-gen-go" +MAINTAINER="Test" +EMAIL="test@test.com" +EOF + + # DOWNLOAD=UNSUPPORTED, DOWNLOAD_x86_64 has URL + cat > "$MOCK_REPO/development/clion/clion.info" << 'EOF' +PRGNAM="clion" +VERSION="2025.3" +HOMEPAGE="https://www.jetbrains.com/clion/" +DOWNLOAD="UNSUPPORTED" +MD5SUM="" +DOWNLOAD_x86_64="https://download.jetbrains.com/cpp/CLion-2025.3.tar.gz" +MD5SUM_x86_64="dff91fe793b8d3ee2446dd340288eef5" +REQUIRES="" +MAINTAINER="Test" +EMAIL="test@test.com" +EOF +} + +teardown() { + rm -rf "$MOCK_BASE" +} + +run_mkhint() { + local tmp_script + tmp_script=$(mktemp /tmp/mkhint_patched_XXXXXX) + sed \ + -e "s|REPO_DIR=\".*\"|REPO_DIR=\"$MOCK_REPO\"|" \ + -e "s|HINT_DIR=\".*\"|HINT_DIR=\"$MOCK_HINT\"|" \ + -e "s|TMP_DIR=\".*\"|TMP_DIR=\"$MOCK_TMP\"|" \ + "$SCRIPT" > "$tmp_script" + bash "$tmp_script" "$@" + local rc=$? + rm -f "$tmp_script" + return $rc +} + +# Mock wget — writes fake content, md5 will be deterministic +mock_wget() { + # Replace wget in PATH with a fake that writes URL as content + mkdir -p "$MOCK_BASE/bin" + cat > "$MOCK_BASE/bin/wget" << 'EOF' +#!/bin/bash +# fake wget: write URL to -O target +url="" +out="" +while [[ $# -gt 0 ]]; do + case "$1" in + -O) out="$2"; shift 2 ;; + *) url="$1"; shift ;; + esac +done +echo "FAKE_CONTENT_FOR_${url}" > "$out" +exit 0 +EOF + chmod +x "$MOCK_BASE/bin/wget" + export PATH="$MOCK_BASE/bin:$PATH" +} + +assert_contains() { + local desc="$1" file="$2" pattern="$3" + if grep -q "$pattern" "$file" 2>/dev/null; then + echo " PASS: $desc" + (( PASS++ )) + else + echo " FAIL: $desc" + echo " expected pattern: $pattern" + echo " file contents:" + cat "$file" 2>/dev/null | sed 's/^/ /' + (( FAIL++ )) + ERRORS+=("$desc") + fi +} + +assert_not_contains() { + local desc="$1" file="$2" pattern="$3" + if ! grep -q "$pattern" "$file" 2>/dev/null; then + echo " PASS: $desc" + (( PASS++ )) + else + echo " FAIL: $desc" + echo " unexpected pattern found: $pattern" + (( FAIL++ )) + ERRORS+=("$desc") + fi +} + +assert_file_exists() { + local desc="$1" file="$2" + if [[ -f "$file" ]]; then + echo " PASS: $desc" + (( PASS++ )) + else + echo " FAIL: $desc (file not found: $file)" + (( FAIL++ )) + ERRORS+=("$desc") + fi +} + +assert_file_not_exists() { + local desc="$1" file="$2" + if [[ ! -f "$file" ]]; then + echo " PASS: $desc" + (( PASS++ )) + else + echo " FAIL: $desc (file should not exist: $file)" + (( FAIL++ )) + ERRORS+=("$desc") + fi +} + +assert_exit_code() { + local desc="$1" expected="$2" actual="$3" + if [[ "$actual" -eq "$expected" ]]; then + echo " PASS: $desc (exit $actual)" + (( PASS++ )) + else + echo " FAIL: $desc (expected exit $expected, got $actual)" + (( FAIL++ )) + ERRORS+=("$desc") + fi +} + +# ─── TESTS ──────────────────────────────────────────────────────────────────── + +echo "========================================" +echo " mkhint test suite" +echo "========================================" + +setup +mock_wget + +# ── T1: --new from .info, no version ────────────────────────────────────────── +echo "" +echo "T1: --new from .info template, no version" +run_mkhint -n curl +assert_file_exists "hint file created" "$MOCK_HINT/curl.hint" +assert_contains "VERSION from .info" "$MOCK_HINT/curl.hint" 'VERSION="8.5.0"' +assert_not_contains "no PRGNAM" "$MOCK_HINT/curl.hint" '^PRGNAM=' +assert_not_contains "no HOMEPAGE" "$MOCK_HINT/curl.hint" '^HOMEPAGE=' +assert_not_contains "no MAINTAINER" "$MOCK_HINT/curl.hint" '^MAINTAINER=' +assert_contains "ARCH set x86_64" "$MOCK_HINT/curl.hint" 'ARCH="x86_64"' +assert_contains "REQUIRES commented out" "$MOCK_HINT/curl.hint" '#REQUIRES=' +assert_not_contains "no NODOWNLOAD" "$MOCK_HINT/curl.hint" 'NODOWNLOAD' + +# ── T2: --new from .info with version → updates version + md5 ───────────────── +echo "" +echo "T2: --new from .info with -v → version set, md5 recalculated" +rm "$MOCK_HINT/curl.hint" +run_mkhint -n curl -v 8.6.0 +assert_contains "VERSION updated" "$MOCK_HINT/curl.hint" 'VERSION="8.6.0"' +assert_contains "URL has new version" "$MOCK_HINT/curl.hint" 'curl-8.6.0' +# md5 should not be the original (was recalculated via mock wget) +assert_not_contains "MD5SUM not original" "$MOCK_HINT/curl.hint" 'MD5SUM="abc123def456' + +# ── T3: --new with -N, no version → NODOWNLOAD added, no downloads ──────────── +echo "" +echo "T3: --new -N no version → NODOWNLOAD=yes, no download" +rm "$MOCK_HINT/curl.hint" +run_mkhint -n curl -N +assert_contains "NODOWNLOAD present" "$MOCK_HINT/curl.hint" 'NODOWNLOAD=yes' + +# ── T4: --new with -v -N → version set, downloads run, NODOWNLOAD added ─────── +echo "" +echo "T4: --new -v -N → version + md5 updated + NODOWNLOAD=yes" +rm "$MOCK_HINT/curl.hint" +run_mkhint -n curl -v 8.7.0 -N +assert_contains "VERSION updated" "$MOCK_HINT/curl.hint" 'VERSION="8.7.0"' +assert_not_contains "MD5SUM not original" "$MOCK_HINT/curl.hint" 'MD5SUM="abc123def456' +assert_contains "NODOWNLOAD present" "$MOCK_HINT/curl.hint" 'NODOWNLOAD=yes' + +# ── T5: --new when hint already exists → backup + empty skeleton ─────────────── +echo "" +echo "T5: --new when hint exists → backup + empty skeleton" +run_mkhint -n curl # creates again (hint already gone from T4 rm... wait, we didn't rm) +assert_file_exists "backup created" "$MOCK_HINT/curl.hint.bak" +assert_contains "skeleton VERSION" "$MOCK_HINT/curl.hint" 'VERSION=' +assert_contains "skeleton DOWNLOAD" "$MOCK_HINT/curl.hint" 'DOWNLOAD=""' + +# ── T6: --hintfile update single URL ────────────────────────────────────────── +echo "" +echo "T6: --hintfile -v update single URL → version + md5 updated" +# Prepare a hint to update +cat > "$MOCK_HINT/curl.hint" << 'EOF' +VERSION="8.5.0" +ARCH="x86_64" +DOWNLOAD="https://curl.se/download/curl-8.5.0.tar.gz" +MD5SUM="abc123def456abc123def456abc123de" +DOWNLOAD_x86_64="" +MD5SUM_x86_64="" +EOF +run_mkhint -f curl -v 8.9.0 +assert_contains "VERSION updated" "$MOCK_HINT/curl.hint" 'VERSION="8.9.0"' +assert_contains "URL updated" "$MOCK_HINT/curl.hint" 'curl-8.9.0' +assert_not_contains "MD5SUM updated" "$MOCK_HINT/curl.hint" 'abc123def456' +assert_file_exists "backup created" "$MOCK_HINT/curl.hint.bak" + +# ── T7: --hintfile -v -N → version + md5 updated + NODOWNLOAD ───────────────── +echo "" +echo "T7: --hintfile -v -N → version + md5 + NODOWNLOAD=yes" +cat > "$MOCK_HINT/curl.hint" << 'EOF' +VERSION="8.5.0" +ARCH="x86_64" +DOWNLOAD="https://curl.se/download/curl-8.5.0.tar.gz" +MD5SUM="abc123def456abc123def456abc123de" +DOWNLOAD_x86_64="" +MD5SUM_x86_64="" +EOF +run_mkhint -f curl -v 9.0.0 -N +assert_contains "VERSION updated" "$MOCK_HINT/curl.hint" 'VERSION="9.0.0"' +assert_not_contains "MD5SUM recalculated" "$MOCK_HINT/curl.hint" 'abc123def456' +assert_contains "NODOWNLOAD present" "$MOCK_HINT/curl.hint" 'NODOWNLOAD=yes' + +# ── T8: DOWNLOAD=UNSUPPORTED, DOWNLOAD_x86_64 has URL ───────────────────────── +echo "" +echo "T8: DOWNLOAD=UNSUPPORTED → skip 32bit, recalc x86_64 md5" +run_mkhint -n clion +assert_contains "DOWNLOAD UNSUPPORTED kept" "$MOCK_HINT/clion.hint" 'DOWNLOAD="UNSUPPORTED"' + +# Update it +run_mkhint -f clion -v 2025.4 +assert_contains "VERSION updated" "$MOCK_HINT/clion.hint" 'VERSION="2025.4"' +assert_contains "DOWNLOAD still UNSUPPORTED" "$MOCK_HINT/clion.hint" 'DOWNLOAD="UNSUPPORTED"' +assert_not_contains "x86_64 md5 updated" "$MOCK_HINT/clion.hint" 'dff91fe793b8d3ee2446dd340288eef5' + +# ── T9: --no-dl alone → error exit 1 ────────────────────────────────────────── +echo "" +echo "T9: --no-dl alone → exit 1" +set +e +run_mkhint -N 2>/dev/null +code=$? +set -e +assert_exit_code "-N alone exits 1" 1 "$code" + +# ── T10: --hintfile missing file → exit 2 ───────────────────────────────────── +echo "" +echo "T10: --hintfile on nonexistent file → exit 2" +set +e +run_mkhint -f nonexistent -v 1.0 2>/dev/null +code=$? +set -e +assert_exit_code "missing hintfile exits 2" 2 "$code" + +# ── T11: --delete existing hint ─────────────────────────────────────────────── +echo "" +echo "T11: --delete removes hint and .bak" +touch "$MOCK_HINT/curl.hint" "$MOCK_HINT/curl.hint.bak" +run_mkhint -d curl +assert_file_not_exists "hint deleted" "$MOCK_HINT/curl.hint" +assert_file_not_exists "bak deleted" "$MOCK_HINT/curl.hint.bak" + +# ── T12: --delete nonexistent → exit 2 ──────────────────────────────────────── +echo "" +echo "T12: --delete nonexistent → exit 2" +set +e +run_mkhint -d ghost_package 2>/dev/null +code=$? +set -e +assert_exit_code "delete missing exits 2" 2 "$code" + +# ── T13: --new multiline hint, no version ───────────────────────────────────── +echo "" +echo "T13: --new multiline .info, no version → template copied, no md5 update" +run_mkhint -n protoc-gen-go-grpc +assert_file_exists "hint created" "$MOCK_HINT/protoc-gen-go-grpc.hint" +assert_contains "first URL present" "$MOCK_HINT/protoc-gen-go-grpc.hint" 'grpc-go-cmd' +assert_contains "second URL present" "$MOCK_HINT/protoc-gen-go-grpc.hint" 'protobuf-go' +# md5s should be original (no version → no download) +assert_contains "original md5 kept" "$MOCK_HINT/protoc-gen-go-grpc.hint" '9d3abc100f411a59907528e55e772a10' + +# ── T14: --new multiline .info, with version → first md5 recalculated ───────── +echo "" +echo "T14: --new multiline .info -v → first URL+md5 updated, second md5 kept (no prompt in test)" +rm "$MOCK_HINT/protoc-gen-go-grpc.hint" +# Pipe empty input so read -r gets blank (keep continuation URL) +echo "" | run_mkhint -n protoc-gen-go-grpc -v 1.4.0 +assert_contains "VERSION updated" "$MOCK_HINT/protoc-gen-go-grpc.hint" 'VERSION="1.4.0"' +assert_contains "first URL has new ver" "$MOCK_HINT/protoc-gen-go-grpc.hint" 'v1.4.0' +assert_not_contains "first md5 recalculated" "$MOCK_HINT/protoc-gen-go-grpc.hint" '9d3abc100f411a59907528e55e772a10' +assert_contains "second md5 unchanged" "$MOCK_HINT/protoc-gen-go-grpc.hint" 'e11cccd452bbf4296f72bf323d7b8690' + +# ── T15: --clean removes .bak files ─────────────────────────────────────────── +echo "" +echo "T15: --clean removes all .bak files" +touch "$MOCK_HINT/a.hint.bak" "$MOCK_HINT/b.hint.bak" +run_mkhint -c +assert_file_not_exists "a.bak removed" "$MOCK_HINT/a.hint.bak" +assert_file_not_exists "b.bak removed" "$MOCK_HINT/b.hint.bak" + +# ─── SUMMARY ────────────────────────────────────────────────────────────────── +teardown + +echo "" +echo "========================================" +echo " Results: $PASS passed, $FAIL failed" +if [[ ${#ERRORS[@]} -gt 0 ]]; then + echo " Failed tests:" + for e in "${ERRORS[@]}"; do echo " - $e"; done +fi +echo "========================================" +[[ $FAIL -eq 0 ]] |
