Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b43d5aad2d | |||
| 081f94088d |
@@ -1,222 +0,0 @@
|
||||
# Distribution Plan for `move`
|
||||
|
||||
**Status:** Tabled 2026-06-15. Revisit after the initial Gitea push lands
|
||||
and the repo is publicly reachable at <https://gitea.cahlen.com/nokeo08/Move>.
|
||||
|
||||
This document captures the analysis and decisions for distributing the
|
||||
`move` CLI as an end-user-friendly tool, so we can resume without redoing
|
||||
the discussion.
|
||||
|
||||
---
|
||||
|
||||
## Decisions locked in
|
||||
|
||||
| Decision | Choice |
|
||||
| -------- | ------ |
|
||||
| Primary hosting | Self-hosted Gitea (`https://gitea.cahlen.com/nokeo08/Move`) |
|
||||
| Default branch | `master` |
|
||||
| Audience | Anyone who finds the repo (broader public-ish) |
|
||||
| Effort budget for now | Smallest viable curl-able `install.sh` |
|
||||
| Bun handling | Fail with a clear message when missing (no auto-install) |
|
||||
| Reinstall behavior | Idempotent via a version-marker file; `MOVE_FORCE=1` overrides |
|
||||
| Install location | `~/.local/bin` + `~/.local/share/move` (XDG, no sudo), env-overridable |
|
||||
| `move` shim | Bash wrapper script, not symlink |
|
||||
| PATH rc files | Never modified; print instructions if PATH isn't right |
|
||||
| Uninstaller | In scope; same `curl ... \| sh` pattern |
|
||||
| npm publishing | Out of scope |
|
||||
| Standalone binaries (`bun build --compile`) | Future work; depends on nut.js native-bundling testing |
|
||||
| Homebrew tap | Future work; cheap once releases exist |
|
||||
|
||||
---
|
||||
|
||||
## Distribution-option survey (from the discussion)
|
||||
|
||||
Four options were weighed:
|
||||
|
||||
1. **Compiled standalone binary** (`bun build --compile`). Best UX (single
|
||||
binary, no Bun needed) but needs per-platform builds, a release
|
||||
pipeline, code signing on macOS, and depends on nut.js's prebuilt native
|
||||
`.node` files bundling correctly under `--compile` — **unverified**.
|
||||
2. **Curl-piped installer** — **chosen**. Single command, no clone,
|
||||
source-and-Bun model. Conventions used by bun, deno, rustup, nvm.
|
||||
Slight cold-start cost vs. compiled binary; tradeoff accepted.
|
||||
3. **Package-manager distribution** (`bun add -g`, brew). `move` is taken
|
||||
on npm; brew is macOS-centric; both add publishing overhead. Out of
|
||||
scope for first cut.
|
||||
4. **Status quo** (clone + `./install.sh` + `bun link`). Developer flow
|
||||
only; will be renamed to `dev-setup.sh`.
|
||||
|
||||
---
|
||||
|
||||
## Target end-user UX
|
||||
|
||||
```sh
|
||||
curl -fsSL https://gitea.cahlen.com/nokeo08/Move/raw/branch/master/install.sh | sh
|
||||
move --help
|
||||
```
|
||||
|
||||
One command. No clone. No `bun link`. No `chmod`. After this the user has
|
||||
a global `move` on their PATH.
|
||||
|
||||
---
|
||||
|
||||
## `install.sh` design (end-user, curl-able)
|
||||
|
||||
- **Shebang:** `#!/usr/bin/env sh` (POSIX).
|
||||
- **Strict mode:** `set -eu`. Trap on errors with a clear
|
||||
"install failed at <step>" message.
|
||||
|
||||
### Top-to-bottom behavior
|
||||
|
||||
1. **Platform detection:**
|
||||
- `uname -s` → `Darwin` / `Linux` / else → "unsupported OS" + exit 1.
|
||||
- `uname -m` → `arm64` / `x86_64` (mapped to `x64`) / else →
|
||||
"unsupported arch" + exit 1.
|
||||
- nut.js supports darwin-arm64/x64, linux-x64, win32-x64. WSL users
|
||||
fall under Linux.
|
||||
2. **Bun check:**
|
||||
- `command -v bun` present → continue.
|
||||
- Absent → print
|
||||
`Error: bun is not installed. Install from https://bun.sh (e.g. 'curl -fsSL https://bun.sh/install | bash'), then re-run.`
|
||||
→ exit 1.
|
||||
3. **Resolve paths:**
|
||||
- `INSTALL_DIR=${MOVE_PREFIX:-$HOME/.local/share/move}`
|
||||
- `BIN_DIR=${MOVE_BIN_DIR:-$HOME/.local/bin}`
|
||||
- `mkdir -p` both.
|
||||
4. **Resolve version:**
|
||||
- `VERSION=${MOVE_VERSION:-master}`.
|
||||
5. **Idempotence check:**
|
||||
- If `$INSTALL_DIR/.installed-version` exists AND its content equals
|
||||
`$VERSION` AND `${MOVE_FORCE:-0}` is not `1`:
|
||||
- Print `move <version> already installed at $BIN_DIR/move. Set MOVE_FORCE=1 to reinstall, or pass a different MOVE_VERSION.`
|
||||
- Exit 0.
|
||||
6. **Clean install dir:** `rm -rf "$INSTALL_DIR"/*` (preserving the dir
|
||||
itself so user-set permissions on it are kept).
|
||||
7. **Download source:**
|
||||
- `TARBALL="https://gitea.cahlen.com/nokeo08/Move/archive/${VERSION}.tar.gz"`
|
||||
- `curl -fsSL "$TARBALL" -o "$TMP"` (mktemp) → on failure, fail
|
||||
loudly with the offending URL.
|
||||
- `tar -xzf "$TMP" -C "$INSTALL_DIR" --strip-components=1`
|
||||
- `rm -f "$TMP"`
|
||||
8. **Install deps:** `cd "$INSTALL_DIR" && bun install --production`.
|
||||
`--production` skips devDependencies (TypeScript, @types/bun); saves
|
||||
tens of MB and seconds per install.
|
||||
9. **Drop the wrapper:**
|
||||
```sh
|
||||
cat > "$BIN_DIR/move" <<EOF
|
||||
#!/usr/bin/env sh
|
||||
exec bun "$INSTALL_DIR/src/move.ts" "\$@"
|
||||
EOF
|
||||
chmod +x "$BIN_DIR/move"
|
||||
```
|
||||
Wrapper, not a symlink, so the path is absolute and stable regardless
|
||||
of how the user's shell resolves things.
|
||||
10. **Write version marker:** `printf '%s\n' "$VERSION" > "$INSTALL_DIR/.installed-version"`.
|
||||
11. **PATH sanity check:**
|
||||
- If `$BIN_DIR` is not on `$PATH`, print:
|
||||
`Warning: $BIN_DIR is not on your PATH. Add this to your shell rc: export PATH="$BIN_DIR:$PATH"`
|
||||
- Do **not** mutate the user's rc files — that breaks the "clean
|
||||
uninstall" contract.
|
||||
12. **macOS Accessibility hint** (carried over from current `install.sh`).
|
||||
13. **Final message:** `Installed move $VERSION at $BIN_DIR/move. Run 'move --help' to get started.`
|
||||
|
||||
### Env vars
|
||||
|
||||
| Var | Default | Purpose |
|
||||
| --- | ------- | ------- |
|
||||
| `MOVE_VERSION` | `master` | Branch or tag to fetch from Gitea. |
|
||||
| `MOVE_PREFIX` | `$HOME/.local/share/move` | Source install location. |
|
||||
| `MOVE_BIN_DIR` | `$HOME/.local/bin` | Wrapper install location. |
|
||||
| `MOVE_FORCE` | unset | Force reinstall even if version marker matches. |
|
||||
|
||||
---
|
||||
|
||||
## `uninstall.sh` design
|
||||
|
||||
```sh
|
||||
curl -fsSL https://gitea.cahlen.com/nokeo08/Move/raw/branch/master/uninstall.sh | sh
|
||||
```
|
||||
|
||||
**Behavior:**
|
||||
|
||||
1. `set -eu`.
|
||||
2. Respect `MOVE_PREFIX` and `MOVE_BIN_DIR` env vars (same defaults as
|
||||
install).
|
||||
3. `rm -f "$BIN_DIR/move"`.
|
||||
4. `rm -rf "$INSTALL_DIR"`.
|
||||
5. Print `Uninstalled. (Bun was not touched.)`.
|
||||
|
||||
Does **not** uninstall Bun — that's the user's runtime, not ours.
|
||||
|
||||
---
|
||||
|
||||
## `dev-setup.sh` design (renamed from current `install.sh`)
|
||||
|
||||
The current `install.sh` is appropriate for contributors but inappropriate
|
||||
as the end-user installer. Rename to `dev-setup.sh`. Content stays nearly
|
||||
identical:
|
||||
|
||||
- Verify Bun is installed (hard-error if missing).
|
||||
- `bun install`.
|
||||
- macOS Accessibility hint.
|
||||
- Trailing message updated to:
|
||||
`Done. Dev workflow: 'bun run start' to run, or 'bun link' to install the global 'move' command. End-user installer is install.sh.`
|
||||
|
||||
---
|
||||
|
||||
## README changes (when we resume)
|
||||
|
||||
- `## Install` section becomes the curl one-liner targeting `master`, plus
|
||||
a short note about env-var overrides and a pointer to uninstall.
|
||||
- `## Run` section collapses to `move` / `move --help` for the global
|
||||
install path.
|
||||
- New `## Uninstall` section with the curl-pipe uninstall command.
|
||||
- New `## For contributors` section absorbing the existing
|
||||
`bun run start` / `bun run src/move.ts` / `bun link` examples; points
|
||||
at `dev-setup.sh`.
|
||||
- `## Files` table updates: add `install.sh` (end-user, curl-able),
|
||||
`uninstall.sh` (end-user, curl-able), `dev-setup.sh` (contributor
|
||||
bootstrap).
|
||||
|
||||
---
|
||||
|
||||
## Verification plan (when we resume)
|
||||
|
||||
1. `sh -n install.sh` and `sh -n uninstall.sh` — POSIX syntax check.
|
||||
2. Local end-to-end with overridden paths:
|
||||
```sh
|
||||
MOVE_PREFIX=/tmp/movetest MOVE_BIN_DIR=/tmp/movetest/bin ./install.sh
|
||||
/tmp/movetest/bin/move --help
|
||||
./install.sh # second run: already-installed message
|
||||
MOVE_FORCE=1 ./install.sh # forced reinstall
|
||||
```
|
||||
3. Uninstall against the test prefix; confirm nothing left under it.
|
||||
4. Re-run existing acceptance tests (`tsc`, `--help`, `--version`,
|
||||
`--bogus`) — should be unaffected; no source changes in this milestone.
|
||||
5. After committing/pushing, end-to-end against the actual Gitea URL.
|
||||
|
||||
---
|
||||
|
||||
## Caveat: Gitea archive URL shape
|
||||
|
||||
The plan assumes
|
||||
`https://gitea.cahlen.com/nokeo08/Move/archive/<ref>.tar.gz` returns a
|
||||
tarball whose top-level directory contains the project files (peeled by
|
||||
`--strip-components=1`). This is the standard Gitea archive format, but
|
||||
if your Gitea version produces a different layout the installer will need
|
||||
a small tweak. The installer should error loudly with the offending URL
|
||||
if extraction fails.
|
||||
|
||||
---
|
||||
|
||||
## Open future work (out of scope for the resume session)
|
||||
|
||||
- Switch `MOVE_VERSION` default from `master` to "latest tag" via the
|
||||
Gitea API (`/api/v1/repos/nokeo08/Move/tags`) once tagged releases are
|
||||
routine.
|
||||
- Investigate `bun build --compile` + nut.js native-binary bundling for
|
||||
true zero-Bun installs. If it works, layer prebuilt binaries onto the
|
||||
curl installer so users without Bun also get one-command installs.
|
||||
- Homebrew tap for macOS users (cheap once a release pipeline exists).
|
||||
- Possible: PowerShell installer (`install.ps1`) for native Windows users
|
||||
without WSL.
|
||||
@@ -11,7 +11,7 @@ cursor leaves the position the script just commanded.
|
||||
## Requirements
|
||||
|
||||
- [Bun](https://bun.sh) >= 1.0.0
|
||||
- macOS, Linux, or Windows (relies on
|
||||
- macOS or Linux (relies on
|
||||
[`@nut-tree-fork/nut-js`](https://github.com/nut-tree-fork/nut.js) for
|
||||
cross-platform mouse + screen control)
|
||||
- On macOS: Accessibility permission for the terminal running Bun
|
||||
@@ -20,38 +20,53 @@ cursor leaves the position the script just commanded.
|
||||
## Install
|
||||
|
||||
```sh
|
||||
./install.sh
|
||||
curl -fsSL https://gitea.cahlen.com/nokeo08/Move/raw/branch/master/install.sh | sh
|
||||
```
|
||||
|
||||
The installer verifies `bun` is on `PATH` and runs `bun install`. It does
|
||||
not auto-install Bun; if Bun is missing it prints a hard error pointing at
|
||||
<https://bun.sh>.
|
||||
This fetches the latest `master` from Gitea, runs `bun install --production`
|
||||
under the install dir, and drops a `move` wrapper on your bin dir.
|
||||
|
||||
The installer respects the XDG Base Directory Specification:
|
||||
|
||||
- Source lives at `$XDG_DATA_HOME/move` (default `~/.local/share/move`).
|
||||
- Wrapper goes to `$XDG_BIN_HOME/move` (default `~/.local/bin/move`).
|
||||
`XDG_BIN_HOME` is the widely-recognized de facto convention; XDG itself
|
||||
doesn't standardize a user bin dir.
|
||||
|
||||
Env vars (all optional):
|
||||
|
||||
| Var | Default | Purpose |
|
||||
| --- | ------- | ------- |
|
||||
| `MOVE_VERSION` | `master` | Branch or tag to install. Pin with e.g. `v1.0.0`. |
|
||||
| `MOVE_FORCE` | unset | Set to `1` to reinstall when the same version is already present. |
|
||||
| `XDG_DATA_HOME` | `$HOME/.local/share` | Where the source tree is installed (under `move/`). |
|
||||
| `XDG_BIN_HOME` | `$HOME/.local/bin` | Where the `move` wrapper is placed. |
|
||||
|
||||
Bun must already be installed; the installer fails with a clear pointer
|
||||
to <https://bun.sh> if it isn't.
|
||||
|
||||
If your bin dir isn't on `PATH`, the installer prints the line to add to
|
||||
your shell rc. It will not modify rc files for you.
|
||||
|
||||
## Run
|
||||
|
||||
```sh
|
||||
bun run start
|
||||
```
|
||||
|
||||
Or directly:
|
||||
|
||||
```sh
|
||||
bun run src/move.ts
|
||||
```
|
||||
|
||||
Or install it as a global `move` command via Bun's bin-linking:
|
||||
|
||||
```sh
|
||||
bun link
|
||||
move
|
||||
move --help
|
||||
```
|
||||
|
||||
`bun link` registers the `bin.move` entry from `package.json` (`./src/move.ts`,
|
||||
which carries `#!/usr/bin/env bun`) as an executable on your `PATH`.
|
||||
|
||||
Stop with `Ctrl+C`. If `SIGINT` arrives mid-sweep, the cursor stays at
|
||||
whichever step was last commanded — by design.
|
||||
|
||||
## Uninstall
|
||||
|
||||
```sh
|
||||
curl -fsSL https://gitea.cahlen.com/nokeo08/Move/raw/branch/master/uninstall.sh | sh
|
||||
```
|
||||
|
||||
Removes the wrapper at `$XDG_BIN_HOME/move` and the install tree at
|
||||
`$XDG_DATA_HOME/move`. Bun stays — it's your runtime, not ours.
|
||||
|
||||
## Usage
|
||||
|
||||
```text
|
||||
@@ -68,12 +83,9 @@ Options:
|
||||
(default prints only the startup banner).
|
||||
```
|
||||
|
||||
Run `bun run src/move.ts --help` for the full text.
|
||||
|
||||
All flags are wired. Numeric overrides are layered onto the defaults via
|
||||
`resolveConfig` in `src/config.ts`; time-valued inputs (`-m`, `-c`) are
|
||||
expressed in seconds at the CLI boundary and converted to milliseconds
|
||||
internally.
|
||||
Numeric overrides are layered onto the defaults via `resolveConfig` in
|
||||
`src/config.ts`; time-valued inputs (`-m`, `-c`) are expressed in seconds
|
||||
at the CLI boundary and converted to milliseconds internally.
|
||||
|
||||
Logging is **quiet by default**: only the startup banner ("Teams Status
|
||||
Keeper started…") and any error from an unhandled rejection print on a
|
||||
@@ -140,21 +152,49 @@ sweep. The script controls cadence itself via `config.stepDelay`, so the
|
||||
implicit delay is disabled at module load (a side effect of importing
|
||||
`keeper.ts`).
|
||||
|
||||
## For contributors
|
||||
|
||||
Clone the repo and bootstrap a dev environment:
|
||||
|
||||
```sh
|
||||
git clone https://gitea.cahlen.com/nokeo08/Move.git
|
||||
cd Move
|
||||
./dev-setup.sh
|
||||
```
|
||||
|
||||
`dev-setup.sh` verifies Bun is installed and runs `bun install` (with
|
||||
devDependencies, unlike the end-user `install.sh`).
|
||||
|
||||
Run from the source tree:
|
||||
|
||||
```sh
|
||||
bun run start # via the package.json script
|
||||
bun run src/move.ts # direct
|
||||
bun run src/move.ts --help
|
||||
```
|
||||
|
||||
Or install a global `move` pointed at your checkout:
|
||||
|
||||
```sh
|
||||
bun link
|
||||
move --help
|
||||
```
|
||||
|
||||
## Files
|
||||
|
||||
| File | Purpose |
|
||||
| ------------------- | ----------------------------------------------------------------------- |
|
||||
| ------------------- | ----------------------------------------------------------------------------- |
|
||||
| `install.sh` | End-user installer; curl-pipeable from Gitea. |
|
||||
| `uninstall.sh` | End-user uninstaller; curl-pipeable from Gitea. |
|
||||
| `dev-setup.sh` | Contributor bootstrap (verify Bun + `bun install`). |
|
||||
| `src/move.ts` | CLI entry point: parses args, dispatches help/version, starts the loop. |
|
||||
| `src/cli.ts` | Argument parsing, validation, and help/version output. |
|
||||
| `src/config.ts` | `Config` type, `DEFAULT_CONFIG`, and `resolveConfig` overlay. |
|
||||
| `src/keeper.ts` | Synthetic-activity sweep and idle-watch loop. |
|
||||
| `install.sh` | Bootstrap: verify Bun, `bun install`, print macOS permission hint. |
|
||||
| `package.json` | Bun project manifest. Single runtime dep: `@nut-tree-fork/nut-js`. |
|
||||
| `tsconfig.json` | Strict TypeScript config tuned for Bun (ESNext, bundler resolution). |
|
||||
| `bun.lock` | Bun's lockfile. Commit this. |
|
||||
| `LICENSE` | GPLv3 license text. |
|
||||
| `CHANGES.md` | History of the post-port review fixes. |
|
||||
| `MouseMover.java` | Original Java source, kept for reference. |
|
||||
|
||||
## License
|
||||
|
||||
|
||||
Executable
+47
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# dev-setup.sh - contributor bootstrap for the `move` repo.
|
||||
#
|
||||
# Use this when you have cloned the repo and want a working development
|
||||
# environment. End users should use `install.sh` (curl-pipeable) instead.
|
||||
#
|
||||
# Verifies Bun is installed, then runs `bun install` to fetch dependencies
|
||||
# (notably `@nut-tree-fork/nut-js`, which ships prebuilt native binaries
|
||||
# for darwin-arm64/x64, linux, and windows).
|
||||
#
|
||||
# This script is idempotent: re-running it just re-resolves the dependency
|
||||
# tree against the existing `bun.lock`.
|
||||
|
||||
# Fail fast on any error, unset variable, or failed pipe stage.
|
||||
set -euo pipefail
|
||||
|
||||
# Always operate relative to the script's own directory so the install works
|
||||
# regardless of the caller's CWD.
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
echo "==> Checking for Bun..."
|
||||
if ! command -v bun >/dev/null 2>&1; then
|
||||
# Hard error rather than auto-install: keep the script's behavior
|
||||
# predictable and let the user pick their own install method.
|
||||
echo "Error: bun is not installed." >&2
|
||||
echo " Install it from https://bun.sh (e.g. 'brew install oven-sh/bun/bun') and re-run." >&2
|
||||
exit 1
|
||||
fi
|
||||
echo " bun $(bun --version)"
|
||||
|
||||
echo "==> Installing dependencies..."
|
||||
bun install
|
||||
|
||||
echo
|
||||
echo "Done. Dev workflow:"
|
||||
echo " - 'bun run start' to run from the source tree."
|
||||
echo " - 'bun link' to install a global 'move' command pointed at this checkout."
|
||||
echo "End-user installer is install.sh (curl-pipeable from Gitea)."
|
||||
|
||||
# macOS gates synthetic mouse events behind the Accessibility permission.
|
||||
# Without this hint, the first run silently fails to move the cursor and
|
||||
# the user has no obvious next step.
|
||||
if [[ "$(uname)" == "Darwin" ]]; then
|
||||
echo "Note: macOS will prompt for Accessibility permission on first mouse move."
|
||||
echo " Grant it under System Settings > Privacy & Security > Accessibility."
|
||||
fi
|
||||
+164
-28
@@ -1,41 +1,177 @@
|
||||
#!/usr/bin/env bash
|
||||
#!/usr/bin/env sh
|
||||
#
|
||||
# install.sh - bootstrap the Teams Status Keeper project.
|
||||
# install.sh - End-user installer for the `move` CLI.
|
||||
#
|
||||
# Verifies Bun is installed, then runs `bun install` to fetch dependencies
|
||||
# (notably `@nut-tree-fork/nut-js`, which ships prebuilt native binaries
|
||||
# for darwin-arm64/x64, linux, and windows).
|
||||
# Curl-pipe ready:
|
||||
#
|
||||
# This script is idempotent: re-running it just re-resolves the dependency
|
||||
# tree against the existing `bun.lock`.
|
||||
# curl -fsSL https://gitea.cahlen.com/nokeo08/Move/raw/branch/master/install.sh | sh
|
||||
#
|
||||
# What it does:
|
||||
# 1. Detect platform; bail on anything @nut-tree-fork/nut-js doesn't ship.
|
||||
# 2. Require Bun; fail with a clear hint if missing (no auto-install).
|
||||
# 3. Resolve XDG-compliant install paths.
|
||||
# 4. Idempotence check via a version marker file.
|
||||
# 5. Download the source tarball from Gitea, extract under the install dir.
|
||||
# 6. `bun install --production` (skips devDependencies).
|
||||
# 7. Drop a small wrapper script as `move` on the user's bin dir.
|
||||
# 8. Verify PATH, surface macOS Accessibility hint, print final status.
|
||||
#
|
||||
# Env vars (all optional):
|
||||
# MOVE_VERSION Branch or tag to install. Default: master.
|
||||
# MOVE_FORCE Set to 1 to reinstall even if the version marker matches.
|
||||
# XDG_DATA_HOME Source install root (default $HOME/.local/share).
|
||||
# Final source location is $XDG_DATA_HOME/move.
|
||||
# XDG_BIN_HOME Wrapper install root (default $HOME/.local/bin).
|
||||
# Final binary location is $XDG_BIN_HOME/move.
|
||||
#
|
||||
# POSIX sh; no bashisms.
|
||||
|
||||
# Fail fast on any error, unset variable, or failed pipe stage.
|
||||
set -euo pipefail
|
||||
set -eu
|
||||
|
||||
# Always operate relative to the script's own directory so the install works
|
||||
# regardless of the caller's CWD.
|
||||
cd "$(dirname "$0")"
|
||||
REPO_OWNER="nokeo08"
|
||||
REPO_NAME="Move"
|
||||
GITEA_HOST="gitea.cahlen.com"
|
||||
|
||||
MOVE_VERSION="${MOVE_VERSION:-master}"
|
||||
MOVE_FORCE="${MOVE_FORCE:-0}"
|
||||
|
||||
INSTALL_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/move"
|
||||
BIN_DIR="${XDG_BIN_HOME:-$HOME/.local/bin}"
|
||||
|
||||
die() {
|
||||
printf 'Error: %s\n' "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Refuse to operate on a directory that points somewhere catastrophic.
|
||||
# `INSTALL_DIR` derives from XDG_DATA_HOME, a broadly-scoped env var the
|
||||
# user could conceivably set to anything; the install path always
|
||||
# culminates in `.../move`, but a malformed XDG_DATA_HOME could still
|
||||
# resolve to something like '/move' which we don't want to `rm -rf`.
|
||||
assert_safe_install_dir() {
|
||||
case "$INSTALL_DIR" in
|
||||
'' | '/' | "$HOME" | "$HOME/" | '/move')
|
||||
die "refusing to operate on INSTALL_DIR='$INSTALL_DIR' (too broad)"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# --- Prerequisite tools ------------------------------------------------------
|
||||
|
||||
for tool in curl tar mktemp; do
|
||||
if ! command -v "$tool" >/dev/null 2>&1; then
|
||||
die "$tool is required (not found in PATH)"
|
||||
fi
|
||||
done
|
||||
|
||||
# --- Platform detection ------------------------------------------------------
|
||||
|
||||
OS=$(uname -s)
|
||||
ARCH=$(uname -m)
|
||||
|
||||
case "$OS" in
|
||||
Darwin|Linux) ;;
|
||||
*) die "unsupported OS '$OS' (move supports macOS and Linux)" ;;
|
||||
esac
|
||||
|
||||
case "$ARCH" in
|
||||
arm64|aarch64|x86_64|amd64) ;;
|
||||
*) die "unsupported architecture '$ARCH'" ;;
|
||||
esac
|
||||
|
||||
# --- Bun check (hard fail; no auto-install) ----------------------------------
|
||||
|
||||
echo "==> Checking for Bun..."
|
||||
if ! command -v bun >/dev/null 2>&1; then
|
||||
# Hard error rather than auto-install: keep the script's behavior
|
||||
# predictable and let the user pick their own install method.
|
||||
echo "Error: bun is not installed." >&2
|
||||
echo " Install it from https://bun.sh (e.g. 'brew install oven-sh/bun/bun') and re-run." >&2
|
||||
cat >&2 <<EOF
|
||||
Error: bun is not installed.
|
||||
Install it from https://bun.sh
|
||||
(e.g. 'curl -fsSL https://bun.sh/install | bash')
|
||||
then re-run this installer.
|
||||
EOF
|
||||
exit 1
|
||||
fi
|
||||
echo " bun $(bun --version)"
|
||||
|
||||
echo "==> Installing dependencies..."
|
||||
bun install
|
||||
BUN_VERSION=$(bun --version)
|
||||
printf '==> Using bun %s\n' "$BUN_VERSION"
|
||||
|
||||
echo
|
||||
echo "Done. Run with: bun run start"
|
||||
# --- Idempotence check -------------------------------------------------------
|
||||
|
||||
# macOS gates synthetic mouse events behind the Accessibility permission.
|
||||
# Without this hint, the first run silently fails to move the cursor and
|
||||
# the user has no obvious next step.
|
||||
if [[ "$(uname)" == "Darwin" ]]; then
|
||||
echo "Note: macOS will prompt for Accessibility permission on first mouse move."
|
||||
echo " Grant it under System Settings > Privacy & Security > Accessibility."
|
||||
assert_safe_install_dir
|
||||
|
||||
if [ "$MOVE_FORCE" != "1" ] && [ -f "$INSTALL_DIR/.installed-version" ]; then
|
||||
CURRENT=$(cat "$INSTALL_DIR/.installed-version" 2>/dev/null || printf '')
|
||||
if [ "$CURRENT" = "$MOVE_VERSION" ]; then
|
||||
printf 'move %s is already installed at %s/move.\n' "$MOVE_VERSION" "$BIN_DIR"
|
||||
printf 'Set MOVE_FORCE=1 to reinstall, or set MOVE_VERSION to a different ref.\n'
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- Clean install dir -------------------------------------------------------
|
||||
|
||||
mkdir -p "$BIN_DIR"
|
||||
rm -rf "$INSTALL_DIR"
|
||||
mkdir -p "$INSTALL_DIR"
|
||||
|
||||
# --- Download source ---------------------------------------------------------
|
||||
|
||||
TARBALL_URL="https://$GITEA_HOST/$REPO_OWNER/$REPO_NAME/archive/$MOVE_VERSION.tar.gz"
|
||||
TARBALL_TMP=$(mktemp) || die "could not create temp file"
|
||||
|
||||
cleanup() {
|
||||
rm -f "$TARBALL_TMP"
|
||||
}
|
||||
trap cleanup EXIT INT TERM
|
||||
|
||||
printf '==> Downloading %s\n' "$TARBALL_URL"
|
||||
if ! curl -fsSL "$TARBALL_URL" -o "$TARBALL_TMP"; then
|
||||
die "could not download $TARBALL_URL (check MOVE_VERSION='$MOVE_VERSION' and network)"
|
||||
fi
|
||||
|
||||
printf '==> Extracting source to %s\n' "$INSTALL_DIR"
|
||||
if ! tar -xzf "$TARBALL_TMP" -C "$INSTALL_DIR" --strip-components=1; then
|
||||
die "could not extract tarball from $TARBALL_URL"
|
||||
fi
|
||||
|
||||
# --- Install runtime deps ----------------------------------------------------
|
||||
|
||||
printf '==> Installing runtime dependencies (bun install --production)\n'
|
||||
(cd "$INSTALL_DIR" && bun install --production)
|
||||
|
||||
# --- Drop the wrapper --------------------------------------------------------
|
||||
|
||||
WRAPPER="$BIN_DIR/move"
|
||||
printf '==> Writing wrapper to %s\n' "$WRAPPER"
|
||||
cat > "$WRAPPER" <<EOF
|
||||
#!/usr/bin/env sh
|
||||
exec bun "$INSTALL_DIR/src/move.ts" "\$@"
|
||||
EOF
|
||||
chmod +x "$WRAPPER"
|
||||
|
||||
# --- Write version marker ----------------------------------------------------
|
||||
|
||||
printf '%s\n' "$MOVE_VERSION" > "$INSTALL_DIR/.installed-version"
|
||||
|
||||
# --- PATH sanity check -------------------------------------------------------
|
||||
|
||||
case ":$PATH:" in
|
||||
*":$BIN_DIR:"*) BIN_ON_PATH=1 ;;
|
||||
*) BIN_ON_PATH=0 ;;
|
||||
esac
|
||||
|
||||
if [ "$BIN_ON_PATH" = "0" ]; then
|
||||
printf '\nNote: %s is not on your PATH. Add this to your shell rc:\n' "$BIN_DIR"
|
||||
printf ' export PATH="%s:$PATH"\n' "$BIN_DIR"
|
||||
fi
|
||||
|
||||
# --- macOS Accessibility hint ------------------------------------------------
|
||||
|
||||
if [ "$OS" = "Darwin" ]; then
|
||||
printf '\nNote: macOS will prompt for Accessibility permission on first mouse move.\n'
|
||||
printf ' Grant it under System Settings > Privacy & Security > Accessibility.\n'
|
||||
fi
|
||||
|
||||
# --- Final message -----------------------------------------------------------
|
||||
|
||||
printf '\nInstalled move %s at %s.\n' "$MOVE_VERSION" "$WRAPPER"
|
||||
printf "Run 'move --help' to get started.\n"
|
||||
|
||||
Executable
+56
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env sh
|
||||
#
|
||||
# uninstall.sh - End-user uninstaller for the `move` CLI.
|
||||
#
|
||||
# Curl-pipe ready:
|
||||
#
|
||||
# curl -fsSL https://gitea.cahlen.com/nokeo08/Move/raw/branch/master/uninstall.sh | sh
|
||||
#
|
||||
# Removes the `move` wrapper from $XDG_BIN_HOME and the install tree from
|
||||
# $XDG_DATA_HOME/move. Does NOT remove Bun — that's your runtime, not ours.
|
||||
#
|
||||
# Env vars (must match what install.sh used):
|
||||
# XDG_DATA_HOME Source install root (default $HOME/.local/share).
|
||||
# XDG_BIN_HOME Wrapper install root (default $HOME/.local/bin).
|
||||
#
|
||||
# POSIX sh; no bashisms.
|
||||
|
||||
set -eu
|
||||
|
||||
INSTALL_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/move"
|
||||
BIN_DIR="${XDG_BIN_HOME:-$HOME/.local/bin}"
|
||||
WRAPPER="$BIN_DIR/move"
|
||||
|
||||
die() {
|
||||
printf 'Error: %s\n' "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Same safety guard as install.sh: refuse to wipe a directory that points
|
||||
# somewhere catastrophic.
|
||||
case "$INSTALL_DIR" in
|
||||
'' | '/' | "$HOME" | "$HOME/" | '/move')
|
||||
die "refusing to operate on INSTALL_DIR='$INSTALL_DIR' (too broad)"
|
||||
;;
|
||||
esac
|
||||
|
||||
REMOVED_SOMETHING=0
|
||||
|
||||
if [ -e "$WRAPPER" ] || [ -L "$WRAPPER" ]; then
|
||||
rm -f "$WRAPPER"
|
||||
printf 'Removed %s\n' "$WRAPPER"
|
||||
REMOVED_SOMETHING=1
|
||||
fi
|
||||
|
||||
if [ -d "$INSTALL_DIR" ]; then
|
||||
rm -rf "$INSTALL_DIR"
|
||||
printf 'Removed %s\n' "$INSTALL_DIR"
|
||||
REMOVED_SOMETHING=1
|
||||
fi
|
||||
|
||||
if [ "$REMOVED_SOMETHING" = "0" ]; then
|
||||
printf 'Nothing to remove. Checked %s and %s.\n' "$WRAPPER" "$INSTALL_DIR"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
printf '\nUninstalled. (Bun was not touched.)\n'
|
||||
Reference in New Issue
Block a user