diff options
| author | Paul Buetow <paul@buetow.org> | 2026-06-27 10:52:37 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-06-27 10:52:37 +0300 |
| commit | 1d6a00fd71cb8ea093da2f217f1724f4d5df1c85 (patch) | |
| tree | 08afaa1a82801d46f3b510f156f2e5726015cbeb | |
| parent | bde6e6d0f5910477c2d47f1bc3a28b39b6c7f3da (diff) | |
Fix src/shuriken.sh lib source list missing 5 modules
Running `bash src/shuriken.sh --generate ...` directly from a source
checkout printed "command not found" for camera_label_from_make_model,
photo_exif_values_to and cached_photo_identify_output for every photo,
silently emptying EXIF tooltips/details and, with STATS_PAGE=yes,
omitting the whole stats/ tree. The hand-maintained source list inside
the SHURIKEN_LIB_SOURCES_BEGIN/END marker block had drifted from the
authoritative Justfile LIB_SOURCES, missing metadata-label,
metadata-cache, stats-aggregate, stats-render and stats-filter-album.
`just build` replaces the marker block with LIB_SOURCES when generating
bin/shuriken, so the installed binary and the bin-based test suite never
noticed; only direct src execution was affected.
- Add the 5 missing `source` lines to the marker block in the same order
as Justfile LIB_SOURCES, so the two lists now match exactly.
- Add tests/cli.sh case test_lib_sources_match_justfile_lib_sources that
extracts the marker-block module names and asserts they equal the
Justfile LIB_SOURCES (same set and order) to prevent future drift.
- shellcheck --check-sourced now follows the 5 newly-sourced libs;
suppress the cross-module nameref false positives (SC2178/SC2128/
SC2154) with explained directives and genuinely fix SC2004
(counts_ref[$key] -> [key]) and quote the TITLE default (${TITLE:-}).
bin/shuriken changes only by these propagated lib edits; the marker-block
source list it generates is unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
| -rwxr-xr-x | bin/shuriken | 32 | ||||
| -rw-r--r-- | src/lib/album-metadata.source.sh | 9 | ||||
| -rw-r--r-- | src/lib/metadata-cache.source.sh | 5 | ||||
| -rw-r--r-- | src/lib/stats-filter-album.source.sh | 5 | ||||
| -rw-r--r-- | src/lib/stats-render.source.sh | 7 | ||||
| -rw-r--r-- | src/lib/template.source.sh | 6 | ||||
| -rwxr-xr-x | src/shuriken.sh | 10 | ||||
| -rwxr-xr-x | tests/cli.sh | 56 |
8 files changed, 122 insertions, 8 deletions
diff --git a/bin/shuriken b/bin/shuriken index b5d8839..cc8cac2 100755 --- a/bin/shuriken +++ b/bin/shuriken @@ -1290,7 +1290,11 @@ prepare_template_render_var__config_html() { context_value="${THUMBHEIGHT:-}" ;; TITLE) - context_value="$TITLE" + # Always defaulted by apply_config_defaults; degrade to empty if + # somehow unset, matching the other config cases above. The :- + # default also silences SC2153 (a lowercase "title" local in another + # --check-sourced module makes shellcheck suspect a misspelling). + context_value="${TITLE:-}" ;; *) config_error "unknown template render config $source_name" @@ -2190,7 +2194,10 @@ photo_exif_values_to() { # shellcheck disable=SC2034 output_ref["${BASH_REMATCH[1]}"]="${BASH_REMATCH[2]}" elif [[ "$line" =~ ^[[:space:]]*Geometry:[[:space:]]*(.*)$ ]]; then - # shellcheck disable=SC2034 + # __geometry is a literal array key, not a variable; --check-sourced + # nameref aliasing across modules misreads it (SC2154). SC2034 covers + # the caller-owned array write. + # shellcheck disable=SC2034,SC2154 output_ref[__geometry]="${BASH_REMATCH[1]}" fi done @@ -2336,15 +2343,22 @@ photo_exif_details_html() { printf '</table>\n' } +# output_ref is a string nameref here. Other modules (e.g. +# metadata-cache.source.sh) reuse the name "output_ref" as an associative array, +# so --check-sourced cross-file nameref aliasing misreports each string write as +# an array assignment (SC2178). Both writes below are correct string nameref +# assignments, so SC2178 is suppressed at each one. _first_exif_value_to() { + # shellcheck disable=SC2178 local -n output_ref="$1"; shift local -n exif_ref="$1"; shift local key + # shellcheck disable=SC2178 output_ref='' for key in "$@"; do if [ -n "${exif_ref[$key]:-}" ]; then - # shellcheck disable=SC2034 + # shellcheck disable=SC2034,SC2178 output_ref="${exif_ref[$key]}" return fi @@ -4725,7 +4739,7 @@ _stats_max_count() { local -i max=0 for key in "${!counts_ref[@]}"; do - if (( counts_ref[$key] > max )); then + if (( counts_ref[key] > max )); then max=${counts_ref[$key]} fi done @@ -5034,10 +5048,15 @@ _stats_random_background() { # namespace is unchanged so selection is identical to the former inline code. _stats_pick_background() { local -r context="$1"; shift + # photos is a newline-joined string of photo paths, not an array. Other + # modules reuse the name "photos" as an array, so --check-sourced nameref + # aliasing misreports SC2178/SC2128; both are false positives here. + # shellcheck disable=SC2178 local -r photos="$1"; shift local -a list=() local photo + # shellcheck disable=SC2128 while IFS= read -r photo; do [ -n "$photo" ] && list+=("$photo") done <<< "$photos" @@ -5126,11 +5145,16 @@ declare -gr STATS_FILTER_BACKHREF='../..' # <div class="thumbs-grid"> container the main pages use. _stats_build_filter_thumbs() { local -r backhref_html="$1"; shift + # photos is a newline-joined string of photo paths, not an array. Other + # modules reuse the name "photos" as an array, so --check-sourced nameref + # aliasing misreports SC2178/SC2128; both are false positives here. + # shellcheck disable=SC2178 local -r photos="$1"; shift local -a photo_list=() local photo local thumbs='' + # shellcheck disable=SC2128 while IFS= read -r photo; do [ -n "$photo" ] && photo_list+=("$photo") done <<< "$photos" diff --git a/src/lib/album-metadata.source.sh b/src/lib/album-metadata.source.sh index b1bed7a..cb3f2ee 100644 --- a/src/lib/album-metadata.source.sh +++ b/src/lib/album-metadata.source.sh @@ -60,15 +60,22 @@ photo_exif_details_html() { printf '</table>\n' } +# output_ref is a string nameref here. Other modules (e.g. +# metadata-cache.source.sh) reuse the name "output_ref" as an associative array, +# so --check-sourced cross-file nameref aliasing misreports each string write as +# an array assignment (SC2178). Both writes below are correct string nameref +# assignments, so SC2178 is suppressed at each one. _first_exif_value_to() { + # shellcheck disable=SC2178 local -n output_ref="$1"; shift local -n exif_ref="$1"; shift local key + # shellcheck disable=SC2178 output_ref='' for key in "$@"; do if [ -n "${exif_ref[$key]:-}" ]; then - # shellcheck disable=SC2034 + # shellcheck disable=SC2034,SC2178 output_ref="${exif_ref[$key]}" return fi diff --git a/src/lib/metadata-cache.source.sh b/src/lib/metadata-cache.source.sh index 7c6d2e3..aa580b6 100644 --- a/src/lib/metadata-cache.source.sh +++ b/src/lib/metadata-cache.source.sh @@ -135,7 +135,10 @@ photo_exif_values_to() { # shellcheck disable=SC2034 output_ref["${BASH_REMATCH[1]}"]="${BASH_REMATCH[2]}" elif [[ "$line" =~ ^[[:space:]]*Geometry:[[:space:]]*(.*)$ ]]; then - # shellcheck disable=SC2034 + # __geometry is a literal array key, not a variable; --check-sourced + # nameref aliasing across modules misreads it (SC2154). SC2034 covers + # the caller-owned array write. + # shellcheck disable=SC2034,SC2154 output_ref[__geometry]="${BASH_REMATCH[1]}" fi done diff --git a/src/lib/stats-filter-album.source.sh b/src/lib/stats-filter-album.source.sh index cc4cf2e..562a72e 100644 --- a/src/lib/stats-filter-album.source.sh +++ b/src/lib/stats-filter-album.source.sh @@ -52,11 +52,16 @@ declare -gr STATS_FILTER_BACKHREF='../..' # <div class="thumbs-grid"> container the main pages use. _stats_build_filter_thumbs() { local -r backhref_html="$1"; shift + # photos is a newline-joined string of photo paths, not an array. Other + # modules reuse the name "photos" as an array, so --check-sourced nameref + # aliasing misreports SC2178/SC2128; both are false positives here. + # shellcheck disable=SC2178 local -r photos="$1"; shift local -a photo_list=() local photo local thumbs='' + # shellcheck disable=SC2128 while IFS= read -r photo; do [ -n "$photo" ] && photo_list+=("$photo") done <<< "$photos" diff --git a/src/lib/stats-render.source.sh b/src/lib/stats-render.source.sh index b9bc067..1050ef2 100644 --- a/src/lib/stats-render.source.sh +++ b/src/lib/stats-render.source.sh @@ -27,7 +27,7 @@ _stats_max_count() { local -i max=0 for key in "${!counts_ref[@]}"; do - if (( counts_ref[$key] > max )); then + if (( counts_ref[key] > max )); then max=${counts_ref[$key]} fi done @@ -336,10 +336,15 @@ _stats_random_background() { # namespace is unchanged so selection is identical to the former inline code. _stats_pick_background() { local -r context="$1"; shift + # photos is a newline-joined string of photo paths, not an array. Other + # modules reuse the name "photos" as an array, so --check-sourced nameref + # aliasing misreports SC2178/SC2128; both are false positives here. + # shellcheck disable=SC2178 local -r photos="$1"; shift local -a list=() local photo + # shellcheck disable=SC2128 while IFS= read -r photo; do [ -n "$photo" ] && list+=("$photo") done <<< "$photos" diff --git a/src/lib/template.source.sh b/src/lib/template.source.sh index 6201647..cd38b3d 100644 --- a/src/lib/template.source.sh +++ b/src/lib/template.source.sh @@ -688,7 +688,11 @@ prepare_template_render_var__config_html() { context_value="${THUMBHEIGHT:-}" ;; TITLE) - context_value="$TITLE" + # Always defaulted by apply_config_defaults; degrade to empty if + # somehow unset, matching the other config cases above. The :- + # default also silences SC2153 (a lowercase "title" local in another + # --check-sourced module makes shellcheck suspect a misspelling). + context_value="${TITLE:-}" ;; *) config_error "unknown template render config $source_name" diff --git a/src/shuriken.sh b/src/shuriken.sh index d9f7d1b..2b8cf9b 100755 --- a/src/shuriken.sh +++ b/src/shuriken.sh @@ -115,6 +115,10 @@ source "$SHURIKEN_SOURCE_DIR/lib/image.source.sh" source "$SHURIKEN_SOURCE_DIR/lib/random.source.sh" # shellcheck source=src/lib/photo-list.source.sh source "$SHURIKEN_SOURCE_DIR/lib/photo-list.source.sh" +# shellcheck source=src/lib/metadata-label.source.sh +source "$SHURIKEN_SOURCE_DIR/lib/metadata-label.source.sh" +# shellcheck source=src/lib/metadata-cache.source.sh +source "$SHURIKEN_SOURCE_DIR/lib/metadata-cache.source.sh" # shellcheck source=src/lib/image-pipeline.source.sh source "$SHURIKEN_SOURCE_DIR/lib/image-pipeline.source.sh" # shellcheck source=src/lib/album-metadata.source.sh @@ -133,6 +137,12 @@ source "$SHURIKEN_SOURCE_DIR/lib/album-photo-select.source.sh" source "$SHURIKEN_SOURCE_DIR/lib/album-render.source.sh" # shellcheck source=src/lib/album.source.sh source "$SHURIKEN_SOURCE_DIR/lib/album.source.sh" +# shellcheck source=src/lib/stats-aggregate.source.sh +source "$SHURIKEN_SOURCE_DIR/lib/stats-aggregate.source.sh" +# shellcheck source=src/lib/stats-render.source.sh +source "$SHURIKEN_SOURCE_DIR/lib/stats-render.source.sh" +# shellcheck source=src/lib/stats-filter-album.source.sh +source "$SHURIKEN_SOURCE_DIR/lib/stats-filter-album.source.sh" # shellcheck source=src/lib/config.source.sh source "$SHURIKEN_SOURCE_DIR/lib/config.source.sh" # shellcheck source=src/lib/config.print.source.sh diff --git a/tests/cli.sh b/tests/cli.sh index 3ce8939..28aacbb 100755 --- a/tests/cli.sh +++ b/tests/cli.sh @@ -6701,6 +6701,59 @@ test_camera_label_from_make_model() { _assert_camera_label 'canon Canon EOS 5D' 'canon' 'Canon EOS 5D' } +# Guards against drift between the hand-maintained 'source ...' lines inside the +# SHURIKEN_LIB_SOURCES_BEGIN/END marker block of src/shuriken.sh and the +# authoritative Justfile LIB_SOURCES list. 'just build' regenerates bin/shuriken +# by replacing that marker block with LIB_SOURCES, so the installed bin/ never +# reveals divergence; only direct execution of src/shuriken.sh sources the +# hand-maintained list. This test asserts both lists carry the same module names +# in the same order, so a missing or misordered source line fails CI. +test_lib_sources_match_justfile_lib_sources() { + local src_modules justfile_modules + + # Module basenames from the marker block in src/shuriken.sh, in file order. + src_modules=$( + awk ' + /# SHURIKEN_LIB_SOURCES_BEGIN/ { inside = 1; next } + /# SHURIKEN_LIB_SOURCES_END/ { inside = 0 } + inside && /^source / { + line = $0 + sub(/.*\/lib\//, "", line) + sub(/".*/, "", line) + print line + } + ' "$TEST_REPO_ROOT/src/shuriken.sh" + ) + + # Module basenames from the Justfile LIB_SOURCES assignment, in list order. + justfile_modules=$( + awk ' + /^LIB_SOURCES :=/ { + n = split($0, parts, /"/) + list = parts[2] + m = split(list, words, /[ \t]+/) + for (i = 1; i <= m; i++) { + word = words[i] + if (word == "") continue + sub(/.*\/lib\//, "", word) + print word + } + exit + } + ' "$TEST_REPO_ROOT/Justfile" + ) + + if [ "$src_modules" != "$justfile_modules" ]; then + echo 'FAIL: src/shuriken.sh lib source list diverged from Justfile LIB_SOURCES' >&2 + echo '--- src/shuriken.sh marker block ---' >&2 + echo "$src_modules" >&2 + echo '--- Justfile LIB_SOURCES ---' >&2 + echo "$justfile_modules" >&2 + diff <(echo "$src_modules") <(echo "$justfile_modules") >&2 || true + exit 1 + fi +} + main() { trap test::teardown EXIT @@ -7119,6 +7172,9 @@ main() { test::run_case \ 'GNU-tool guard rejects non-GNU stat' \ test_gnu_tool_guard_rejects_non_gnu_stat + test::run_case \ + 'src/shuriken.sh lib source list matches Justfile LIB_SOURCES' \ + test_lib_sources_match_justfile_lib_sources } main "$@" |
