summaryrefslogtreecommitdiff
path: root/src/lib/action.source.sh
AgeCommit message (Collapse)Author
2026-07-18Add CHRONOLOGICAL_ORDER config option to order albums by EXIF date takenPaul Buetow
Adds a new yes/no config setting (default no, preserving current behavior) that orders the main album's photos by EXIF date taken (ascending) instead of the default filename/shuffle order. Reuses the existing EXIF cache and tag fallback chain (DateTimeOriginal -> DateTimeDigitized -> DateTime) already used for tooltips/details/stats, so ordering never disagrees with what those features show. Photos with no usable EXIF date fall back to their source file's mtime, staying fully deterministic and crash-free. CHRONOLOGICAL_ORDER takes precedence over SHUFFLE when both are enabled, documented in album-photo-select.source.sh and docs/configuration.md. Wired through the config registry (CONFIG_SPECS), validation, CLI flags (--chronological/--no-chronological), --print-config, --dry-run, --verbose logging, and shuriken.json generation metadata. Adds unit and end-to-end tests covering default-off behavior, EXIF-date ordering with shuffle precedence, and mtime fallback for EXIF-less photos. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-18Add DETAILS_PAGE setting to make per-photo details pages optionalPaul Buetow
Album owners can now set DETAILS_PAGE=no (or pass --no-details) to skip generating each photo's *-details.html EXIF summary page and its "Details" navigation redirects, without touching the normal thumbnail overview, per-photo view pages, EXIF tooltips, or STATS_PAGE, which all stay independently controlled. Every "Details" link (on view pages and stats filter mini-album view pages) and every "-details" redirect stub is gated on the setting so no generated page ever links to a file that was not rendered. Wired the new field through CONFIG_SPECS (registry-driven defaults/validation/print-config/CLI override), the --details/--no-details CLI flags, usage() help, the verbose effective-config log, shuriken.json generation metadata, and the dry-run plan. DETAILS_PAGE=yes (the default) keeps prior output byte-for-byte identical. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-06-28mr0: finish config consolidation; document bespoke log/dry-run consumersPaul Buetow
Consumers 5 (log_configured_action) and 6 (the --dry-run plan) are consciously left bespoke: both are human-facing prose that interleaves a curated subset of config fields (each with its own label and per-field decoration) with non-config values (resolved rc_file path, SHURIKEN_FORCE_GENERATE, computed image/page/redirect counts, planned tarball name, and whole non-config 'Planned directories/files' sections). Driving them from CONFIG_SPECS would require per-line label+format+marker facets that contort the schema for no DRY benefit, since each string appears exactly once. They already read the canonical registry-driven globals, so CONFIG_SPECS remains the single source of truth for the config schema; only the presentation stays hand-written. Added comments to both explaining the decision. No behavior change -- output stays byte-identical (asserted by the effective-config log and dry-run plan tests). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28Centralize DIST_DIR-derived paths via working_dir/exif_cache_dir helpersPaul Buetow
The EXIF cache dir ($(dirname "$DIST_DIR")/cache/exif) was recomputed inline, byte for byte, in both cached_photo_identify_output (read/write) and clear_exif_cache (--force/--clean removal). The plain parent of DIST_DIR (dirname "$DIST_DIR") was likewise recomputed in metadata-cache and in action.source.sh's staging-artifact cleanup. Extract two helpers computed once from DIST_DIR: - working_dir() in config.source.sh (next to existing_parent_dir, the other DIST_DIR-parent resolver): plain `dirname "$DIST_DIR"`. - exif_cache_dir() in metadata-cache.source.sh (owns the EXIF cache): `working_dir()/cache/exif`. Route cached_photo_identify_output, clear_exif_cache, and clean_generation_staging_artifacts through them so the cache reader and the cleaner can never drift to different directories. Paths are byte-identical to the prior inline code (dirname semantics, cache/exif suffix, trailing-slash and relative/absolute handling all preserved). Stale "recompute the cache dir" comments removed; helpers document the path once. Add a unit test asserting the helpers agree and resolve beside dist for several DIST_DIR shapes. Broader DIST_DIR parameterization of leaf pipeline helpers was intentionally left out of scope (only the duplicated path computation is centralized here). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27ur0: remove dead status-capture/return path in main() and run_action()Paul Buetow
Under "set -euo pipefail" the pattern cmd status=$? if (( status != 0 )); then return "$status"; fi is redundant for these positions: errexit already aborts on cmd's failure with cmd's exact exit code before the status check could run. Replace it with bare calls, dropping the now-pointless "local -i status=0" and the status-capture boilerplate (and the stale comments describing it). Deliberately NOT using "cmd || return $?" here: main -> run_action -> run_configured_action -> generate_staged relies on errexit staying ACTIVE so generate_staged's internal "set -e" parallel-job failure detection fires. Putting the call in a "||" list suppresses inner errexit (the gotcha documented in album.source.sh's splash-render note) and lets a failing job sail past. Bare calls preserve the exact exit codes and step ordering. bin/shuriken regenerated from src/ via "just build". Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27qr0: timeout + per-destination isolation for rsync sync_distPaul Buetow
Wrap each rsync in sync_dist in run_with_timeout (new SYNC_TIMEOUT config, default 300s) so a hung or unreachable mirror cannot block the whole sync, matching every other external call. Make destinations isolated: under set -euo pipefail a single failing destination used to abort the loop and silently skip the rest. Now each destination runs under a localized set +e (the project's refresh_splash idiom), results are collected per destination, a clear pass/fail summary is logged, and sync returns non-zero if any destination failed while still attempting all of them. SYNC_TIMEOUT is plumbed like TAR_TIMEOUT: shuriken.default.conf, apply_config_defaults, print_config, verbose config log, and positive-integer validation in both validate_config (generate path) and validate_sync_config (sync path). No CLI flag, matching TAR_TIMEOUT. Tests: a sync where one destination fails still attempts the others and exits non-zero with the summary; SYNC_TIMEOUT=0 is rejected as a positive integer. Adds install_rsync_spy_failing_one helper and updates the print_config expected blocks. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17Add configurable SOURCE_URL footer link; release 0.9.00.9.0Paul Buetow
The footer "Site generated ... with <link>" was hardcoded to the shuriken.sh repository. Make it configurable via the SOURCE_URL config variable and the --source-url CLI flag (defaulting to the shuriken.sh repo, so existing sites are unchanged). The footer derives the displayed text from the URL by stripping its scheme. Plumbed through apply_config_defaults, CLI override targets/spec, --print-config, the verbose effective-config log, and the header template's new render_source_url_html (config_html) render var. Documented in shuriken.default.conf and README; added a generation test asserting a custom SOURCE_URL replaces the default footer link, and updated the print-config and header render-var-subset expectations. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17gn0: dispatch CLI actions via registration tablePaul Buetow
Adding a CLI action previously required editing two case statements (run_action and run_configured_action). Introduce ACTION_SPECS, a single '|'-delimited registry (flag|handler|requires_config|validation_fn| validation_arg) matching the CLI_OPTION_SPEC / STATS_CATEGORIES encoding, and replace both dispatchers with table lookups via action_spec_field. - run_action: looks up requires_config; routes non-config actions (--version/--init) through run_unconfigured_action (shared config/override/force precheck) and the rest through run_configured_action. - run_configured_action: keeps the force-generate guard and config load/log, then runs the entry's validation_fn (with optional arg, used by --dry-run) and handler via run_configured_action_body. - Extracted the --clean inline rm body into a clean_dist handler and added an action_print_version handler so every action is just a registry entry plus named functions. - Unknown/empty actions have no entry, so dispatch falls through to the same usage + exit 1 behavior as the old case "*)" arm. Adding an action is now one ACTION_SPECS entry plus its handler/ validation functions; neither dispatcher changes (Open/Closed). Added test_action_dispatch_is_registry_driven proving every parser action flag has a registry entry, all handlers/validators resolve, and unknown actions are rejected. All existing action/dispatcher tests pass unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17ln0 make --clean remove leftover staging directoriesPaul Buetow
The --clean action deleted DIST_DIR but left behind the staging/backup directories the generation pipeline creates as siblings of DIST_DIR (.shuriken.<basename>.staging.* / .backup.*). Users expect --clean to remove all generation output, so extend it to also delete those. Cleanup runs after the validate_clean_dist_dir safety guard (8n0), so a dangerous DIST_DIR still aborts before any deletion. It only matches shuriken's own basename-specific staging/backup prefixes (the exact mktemp templates from config.staging.source.sh), uses nullglob so a missing match never expands to a literal pattern, and only removes directories. Unrelated dotfiles in the parent are never touched. Update test_clean to assert the staging/backup dirs are removed while unrelated entries survive, and document the behavior in the CLI usage text and README. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17on0 remove run_action_body serialization runnerPaul Buetow
Shuriken is a single-process CLI, yet the action layer could serialize 30+ globals plus every function definition (declare -p / declare -f) and pipe them into a fresh "bash -euo pipefail" process to run an action. Production already forced the in-process run_action_body_direct via SHURIKEN_ACTION_BODY_RUNNER, so the serialized-subprocess path was dead in production and only added complexity (a hand-maintained variable list to keep in sync). Per KISS, drop it. - Remove run_action_body_context and the run_action_body dispatcher. - Collapse run_configured_action_body to call the action in-process directly and remove the SHURIKEN_ACTION_BODY_RUNNER indirection in main(). - Move the only genuinely needed isolation into a test-only shim (test::run_action_isolated in tests/helpers.sh) for the generate real-failure test, which must capture a failure status without the in-process errexit abort ending the caller (correct in production, where main runs under errexit). - Update the errexit/status-propagation tests to exercise the direct path. Template-engine serialization is unrelated and left untouched. just test, just shellcheck, just check-generated and git diff --check all pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-168n0 validate DIST_DIR before --clean rm -rfPaul Buetow
The --clean action ran `rm -rf "$DIST_DIR"` after only an `[ -d ]` check, so a misconfigured DIST_DIR (empty, /, $HOME, system dirs, etc.) could recursively delete the wrong tree. Add validate_clean_dist_dir (and resolve_dist_dir_path) in config.validate.source.sh and call it in the --clean case before any deletion. The guard canonicalizes DIST_DIR with `pwd -P` (handling ./ trailing slashes, symlinks and relative paths; for a not-yet-existing dir it resolves the existing parent and re-attaches the basename) and refuses to clean when the resolved path is empty, the filesystem root, a well-known system directory, the resolved $HOME, or the current working directory. Rejection uses config_error with a clear message and a non-zero exit, so nothing is deleted. Normal DIST_DIRs still clean. Tests (tests/cli.sh, registered in main): a HOME-as-DIST_DIR case (uses a fake HOME under TEST_TMPDIR with a sentinel file, so a regression can only touch the throwaway temp dir) and an empty-DIST_DIR case both assert rejection and that nothing is removed. Note: leftover staging artifacts on --clean are out of scope (task ln0). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15Make the favicon configurable via FAVICON config / --favicon flagPaul Buetow
The generated pages link a favicon.ico that was always the bundled shuriken favicon (copy_site_favicon hard-copied share/shuriken/assets/favicon.ico). Add a FAVICON config variable and a --favicon PATH CLI flag: when set, that file is published as favicon.ico instead of the bundled default; when empty, the bundled favicon is used as before. Plumbed through apply_config_defaults, CLI_OPTION_SPEC + override allowlist, usage, print_config, the action config list and effective-setting log, and validated (a non-empty FAVICON must be a readable file) before generation. shuriken.default.conf and the README document it; a test covers a custom favicon, its appearance in --print-config, and rejection of a missing file. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14rm0 wire stats feature into generate with STATS_PAGE togglePaul Buetow
Final integration of the stats-site feature: - Add STATS_PAGE config (default yes) mirroring SPLASH_PAGE through every layer: shuriken.default.conf, apply_config_defaults, CLI_OPTION_SPEC (--stats/--no-stats), the CLI_CONFIG_OVERRIDE_TARGETS allowlist (without which the flag was silently ignored), usage text, yes/no validation, print_config, the action config list + effective-setting log, the generation metadata, and the dry-run plan. - generate() now calls a gated generate_stats_pages helper after the album pages (and before archiving, so the stats/camera pages are tarballed): collect_photo_exif_stats + render_stats_page . . + render_camera_pages . . - Emit "stats_page" in shuriken.json next to "splash_page". - Gate the header Stats nav link behind STATS_PAGE via a new render_stats_page_html config_html field, so it is hidden (no 404) when stats are disabled. - Document STATS_PAGE and --stats/--no-stats in the README. - Tests: full --generate produces stats.html + camera-<slug>.html + the nav link and stats_page=true; --no-stats suppresses all three and sets stats_page=false; print-config and metadata expectations updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13jm0: centralize runtime config defaultsPaul Buetow
2026-06-12Make run_action status-safe for bm0Paul Buetow
2026-06-12Harden status-tested validation paths for bm0Paul Buetow
2026-06-12Fix remaining errexit masking for bm0Paul Buetow
2026-06-12Capture action and template setup failures for bm0Paul Buetow
2026-06-12Propagate dispatcher validation failures for bm0Paul Buetow
2026-06-11Make main CLI state explicit for task 8m0Paul Buetow
2026-06-11Rename photoalbum to shurikenPaul Buetow
2026-06-11Split photoalbum source into lib modulesPaul Buetow