Skip to content

Commit c04d52c

Browse files
authored
Run shdeps self-update during update (#23)
Summary Move steady-state shdeps auto-update ownership into `shdeps update` so callers do not have to duplicate bootstrap repair logic. The automatic path is quiet, best-effort, and TTL-gated, while explicit `self-update` remains immediate and status-bearing. This keeps the current dotfiles repair bridge useful for rollout, but makes shdeps own the long-term policy once the new release has propagated across the fleet. Details - run the existing release-archive and source-checkout self-update building blocks before dependency updates when the self-update stamp is stale - preserve force/reinstall semantics so `--force`, `SHDEPS_FORCE=1`, and reinstall bypass the self-update TTL - stamp release attempts before GitHub I/O so transient GitHub failures do not cause every fleet update to retry immediately - stamp source checkout attempts only after a real pull attempt, so dirty checkouts can be cleaned and updated on the next run - pass the sourced wrapper directory as `SHDEPS_DIR` for `shdeps_update` and `shdeps_self_update`, preserving the "update what I sourced" contract - document `SHDEPS_SELF_UPDATE_TTL` and the bootstrap/update ownership boundary Testing - `cargo fmt --all --check` - `cargo clippy --locked --all-targets -- -D warnings` - `RUSTDOCFLAGS='-D missing-docs' cargo doc --locked --no-deps` - `cargo test --locked` - `cargo test --test cli` - `tests/shell/install-sh-test` - `tests/shell/installer-flow-test` - `tests/shell/lua-api-test` - `tests/shell/lua-bootstrap-test` - `tests/shell/release-scripts-test` - `SHDEPS_RUST_CLI=target/release/shdeps tests/shell/shdeps-wrapper-test` - `shellcheck install.sh shdeps.sh tests/shell/install-sh-test tests/shell/installer-flow-test tests/shell/lua-api-test tests/shell/lua-bootstrap-test tests/shell/release-scripts-test tests/shell/helpers.sh scripts/package-release.sh scripts/release.sh scripts/release-tag.sh scripts/release-version.sh scripts/smoke-install-bash32.sh scripts/smoke-release.sh examples/hooks.d/example-hook.sh demo/record.sh` - `git diff --check` Skipped locally - CI's Linux musl release package smoke setup requires `rustup`, which is not available on this host. The release/script smoke suites were run locally; CI will exercise the musl package path.
1 parent 0b2421d commit c04d52c

8 files changed

Lines changed: 701 additions & 22 deletions

File tree

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ All behavior is controlled via environment variables (no hardcoded paths):
5050
| `SHDEPS_REINSTALL` | `0` | Force reinstall all deps |
5151
| `SHDEPS_QUIET` | `0` | Suppress non-result output and interactive prompts |
5252
| `SHDEPS_REMOTE_TTL` | `3600` | Cache TTL in seconds |
53+
| `SHDEPS_SELF_UPDATE_TTL` | `86400` | Self-update attempt TTL for `shdeps update` |
5354
| `SHDEPS_GIT_DEV_DIR` | `~/git` | Dev clone directory for the `github:repo` method |
5455
| `SHDEPS_INSTALL_DIR` | `~/.local/share` | Base directory for `github:*`, `cargo`, `go`, `uv`, and `npm` installs (each dep lives in `<dir>/<name>/`) |
5556
| `SHDEPS_BIN_DIR` | `~/.local/bin` | Directory for binary symlinks |

README.md

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,13 @@ The CLI loads all `*.conf` files from `~/.config/shdeps/` (sorted alphabetically
9393
shdeps self-update
9494
```
9595

96-
Uses TTL-based caching to avoid redundant work. Release installs update from
97-
the latest matching archive, while source checkouts use `git pull --ff-only`
98-
and skip updates when the working tree has uncommitted changes (active
99-
development). Use `--force` to bypass the TTL cache.
96+
Release installs update from the latest matching archive, while source
97+
checkouts use `git pull --ff-only` and skip updates when the working tree has
98+
uncommitted changes (active development). `shdeps update` also performs a
99+
quiet, best-effort self-check for supported shdeps installs before dependency
100+
updates. That automatic check is TTL-gated by `SHDEPS_SELF_UPDATE_TTL`; use
101+
`--force` or `SHDEPS_FORCE=1` with `shdeps update` to bypass the self-update
102+
TTL.
100103

101104
### Uninstalling
102105

@@ -135,6 +138,7 @@ Use `-` for fields you want to skip. See [examples/deps.conf](examples/deps.conf
135138
| `SHDEPS_REINSTALL` | `0` | Force reinstall all deps |
136139
| `SHDEPS_QUIET` | `0` | Suppress non-result output and interactive prompts |
137140
| `SHDEPS_REMOTE_TTL` | `3600` | Cache TTL in seconds |
141+
| `SHDEPS_SELF_UPDATE_TTL` | `86400` | Self-update attempt TTL in seconds for `shdeps update` |
138142
| `SHDEPS_GIT_DEV_DIR` | `~/git` | Dev clone directory used only by `github:repo` deps (prefers `<dir>/<repo>` over a managed clone) |
139143
| `SHDEPS_INSTALL_DIR` | `~/.local/share` | Base directory for shdeps-owned install roots (`github:repo`, archive-style `github:release`, `cargo`, `go`, `uv`, `npm`). Raw release binaries install into `SHDEPS_BIN_DIR` instead. |
140144
| `SHDEPS_BIN_DIR` | `~/.local/bin` | Directory for binary symlinks and raw `github:release` binaries |
@@ -388,7 +392,7 @@ shdeps_update
388392

389393
## Bootstrapping (Client Integration)
390394

391-
For projects that embed shdeps (e.g., dotfiles managers), `install.sh --bootstrap` provides a single sourceable entry point that handles discovery, sourcing, CLI symlink, and self-update:
395+
For projects that embed shdeps (e.g., dotfiles managers), `install.sh --bootstrap` provides a single sourceable entry point that handles discovery, sourcing, and CLI symlink setup:
392396

393397
```bash
394398
# Set your project's config before bootstrapping
@@ -409,10 +413,13 @@ The `--bootstrap` flag:
409413
- **Finds shdeps.sh** via `$SHDEPS_LIB``$SHDEPS_GIT_DEV_DIR/shdeps/``$SHDEPS_DIR/` → fresh install
410414
- **Sources it** into the caller (all `shdeps_*` functions become available)
411415
- **Symlinks the CLI** into `$SHDEPS_BIN` (default `~/.local/bin/shdeps`)
412-
- **Runs `self-update`** (skips dirty working trees)
416+
- **Keeps bootstrap fast** by avoiding release freshness checks unless `SHDEPS_FORCE=1`
413417
- **Is idempotent** — safe to call multiple times
414418
- **Does not leak `set -e`** into the caller's shell
415419

420+
Call `shdeps_update` after bootstrap to install dependencies and run the
421+
TTL-gated self-check.
422+
416423
## Public API
417424

418425
The sourceable Bash API defines the public `shdeps_` functions and delegates

docs/rust-port-spec.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,15 @@ Output:
187187

188188
`shdeps self-update` updates the shdeps installation.
189189

190+
`shdeps update` also owns automatic self-update. Before dependency updates, it
191+
SHOULD perform a quiet, best-effort self-update check for every supported
192+
install target. This check MUST be TTL-gated by
193+
`SHDEPS_SELF_UPDATE_TTL` (default: 86400 seconds), MUST stamp attempts rather
194+
than only successful updates, and MUST treat failures as non-fatal so a
195+
transient GitHub outage cannot block dependency updates. `--force`,
196+
`SHDEPS_FORCE=1`, and reinstall mode MUST bypass the self-update TTL. Explicit
197+
`shdeps self-update` remains the immediate primitive and is not TTL-gated.
198+
190199
Current source-checkout behavior:
191200

192201
- If the install dir contains `.git`, it MUST skip dirty trees.
@@ -395,6 +404,7 @@ Runtime environment variables:
395404
| `SHDEPS_REINSTALL` | `0` | Force reinstall behavior |
396405
| `SHDEPS_QUIET` | `0` | Suppress non-result logs and prompts |
397406
| `SHDEPS_REMOTE_TTL` | `3600` | Remote cache TTL seconds |
407+
| `SHDEPS_SELF_UPDATE_TTL` | `86400` | Self-update attempt TTL seconds for `shdeps update` |
398408
| `SHDEPS_GIT_DEV_DIR` | `$HOME/git` | Local dev clone root |
399409
| `SHDEPS_INSTALL_DIR` | `$HOME/.local/share` | Dependency install root |
400410
| `SHDEPS_BIN_DIR` | `$HOME/.local/bin` | Public command symlink dir |
@@ -1451,7 +1461,9 @@ Sourceable bootstrap mode MUST:
14511461
install
14521462
- source `shdeps.sh` so Bash API functions become available
14531463
- set up CLI/extras links
1454-
- run method-aware `self-update` when safe
1464+
- avoid release freshness checks during normal bootstrap so callers can render
1465+
their own UI before network work begins
1466+
- allow `SHDEPS_FORCE=1` to refresh release installs during bootstrap
14551467
- return success/failure to caller instead of exiting the caller shell
14561468

14571469
Uninstall mode MUST:

man/man1/shdeps.1

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,12 @@ files within a config directory (default:
1919
.SH COMMANDS
2020
.TP
2121
.B update
22-
Install or update all configured dependencies.
22+
Install or update all configured dependencies. For supported shdeps installs,
23+
also performs a quiet, best-effort self-update check before dependency updates.
2324
.TP
2425
.B self\-update
25-
Update shdeps itself by pulling the latest from its git repository.
26-
Skips the update if the local tree has uncommitted changes.
26+
Update shdeps itself. Release installs update from the latest matching archive;
27+
source checkouts use git pull --ff-only and skip dirty trees.
2728
.TP
2829
.B list
2930
List all configured dependencies with columns for name, method, status,
@@ -401,6 +402,11 @@ to suppress non-result output and interactive prompts.
401402
Cache TTL in seconds.
402403
Default: 3600.
403404
.TP
405+
.B SHDEPS_SELF_UPDATE_TTL
406+
Self-update attempt TTL in seconds for
407+
.BR "shdeps update" .
408+
Default: 86400.
409+
.TP
404410
.B SHDEPS_GIT_DEV_DIR
405411
Dev clone directory for github:repo deps (prefers <dir>/<repo> over fresh clone).
406412
Default:

shdeps.sh

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,12 @@ _shdepsw_call() {
103103
command "$_SHDEPSW_BIN" "$@"
104104
}
105105

106+
_shdepsw_call_from_dir() {
107+
local dir
108+
dir=$(_shdepsw_dir) || return 1
109+
SHDEPS_DIR="$dir" command "$_SHDEPSW_BIN" "$@"
110+
}
111+
106112
_shdepsw_prepend_bin_dir() {
107113
local bin_dir
108114
bin_dir="${SHDEPS_BIN_DIR:-${_SHDEPSW_BIN_DIR:-$(_shdepsw_call __api bin-dir)}}"
@@ -196,8 +202,8 @@ _shdepsw_cache_env || return 1 2>/dev/null || exit 1
196202
# alone.
197203

198204
shdeps_version() { _shdepsw_call version "$@"; }
199-
shdeps_update() { _shdepsw_prepend_bin_dir && _shdepsw_call update "$@"; }
200-
shdeps_self_update() { _shdepsw_call self-update "$@"; }
205+
shdeps_update() { _shdepsw_prepend_bin_dir && _shdepsw_call_from_dir update "$@"; }
206+
shdeps_self_update() { _shdepsw_call_from_dir self-update "$@"; }
201207
shdeps_load() { _shdepsw_call __api load-count "$@"; }
202208
shdeps_prune() { _shdepsw_call prune "$@"; }
203209

0 commit comments

Comments
 (0)