generated from exteraGramPlugins/pluginBase
Initial commit
This commit is contained in:
commit
7a3d3e505d
37 files changed
+2613
No files matched your search
Executable
+31
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cd "$(dirname "${BASH_SOURCE[0]}")/.."
|
||||
source scripts/lib.sh
|
||||
|
||||
need_cmd "$ADB" "$PY"
|
||||
|
||||
DEBUG=0
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--debug) DEBUG=1 ;;
|
||||
*) die "unknown argument: $arg" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
"$ADB" start-server
|
||||
timeout 60 "$ADB" wait-for-device || die "timed out waiting for a device (ANDROID_SERIAL=${ANDROID_SERIAL:-<unset>})"
|
||||
|
||||
"$ADB" forward --remove "tcp:$DEV_PORT" >/dev/null 2>&1 || true
|
||||
"$ADB" forward "tcp:$DEV_PORT" "tcp:$DEV_PORT"
|
||||
|
||||
if [[ "$DEBUG" -eq 1 ]]; then
|
||||
"$ADB" forward --remove "tcp:$DEBUG_PORT" >/dev/null 2>&1 || true
|
||||
"$ADB" forward "tcp:$DEBUG_PORT" "tcp:$DEBUG_PORT"
|
||||
fi
|
||||
|
||||
if "$PY" tools/devclient.py ping >/dev/null 2>&1; then
|
||||
echo "connected"
|
||||
else
|
||||
die "ping failed — is the plugin engine + developer mode enabled in exteraGram Preferences > Plugins?"
|
||||
fi
|
||||
Executable
+49
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cd "$(dirname "${BASH_SOURCE[0]}")/.."
|
||||
source scripts/lib.sh
|
||||
|
||||
need_cmd "$ADB" "$PY"
|
||||
|
||||
usage() {
|
||||
cat >&2 <<'EOF'
|
||||
usage: scripts/debug.sh start|stop [--platform vscode|pycharm]
|
||||
|
||||
Requires the device-side debugger package to be present in the app's Python
|
||||
runtime: `debugpy` for vscode, `pydevd-pycharm` for pycharm. If it is not
|
||||
installed, add it to "requirements" in plugin.json so the app's pip
|
||||
controller installs it on next load.
|
||||
EOF
|
||||
}
|
||||
|
||||
[[ $# -ge 1 ]] || { usage; exit 1; }
|
||||
ACTION="$1"
|
||||
shift
|
||||
PLATFORM=vscode
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--platform)
|
||||
PLATFORM="${2:-}"
|
||||
shift 2
|
||||
;;
|
||||
--platform=*)
|
||||
PLATFORM="${1#--platform=}"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
die "unknown argument: $1"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
case "$ACTION" in
|
||||
start|stop) ;;
|
||||
*) usage; exit 1 ;;
|
||||
esac
|
||||
case "$PLATFORM" in
|
||||
vscode|pycharm) ;;
|
||||
*) die "--platform must be vscode or pycharm" ;;
|
||||
esac
|
||||
|
||||
scripts/adb-connect.sh --debug
|
||||
"$PY" tools/devclient.py debugger "$ACTION" --platform "$PLATFORM"
|
||||
Executable
+36
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cd "$(dirname "${BASH_SOURCE[0]}")/.."
|
||||
source scripts/lib.sh
|
||||
|
||||
need_cmd "$PY"
|
||||
|
||||
MODE=--dev
|
||||
ENABLE=0
|
||||
CONNECT=1
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--release) MODE=--release ;;
|
||||
--enable) ENABLE=1 ;;
|
||||
--no-connect) CONNECT=0 ;;
|
||||
*) die "unknown argument: $arg" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
"$PY" tools/bundle.py "$MODE" --out dist
|
||||
|
||||
BUNDLE="dist/${PLUGIN_ID}.plugin"
|
||||
[[ -f "$BUNDLE" ]] || die "expected bundle not found at $BUNDLE"
|
||||
echo "bundle: $BUNDLE ($(stat -c '%s' "$BUNDLE" 2>/dev/null || stat -f '%z' "$BUNDLE") bytes)"
|
||||
|
||||
if [[ "$CONNECT" -eq 1 ]]; then
|
||||
need_cmd "$ADB"
|
||||
scripts/adb-connect.sh
|
||||
fi
|
||||
|
||||
DEPLOY_ARGS=(deploy "$BUNDLE" --id "$PLUGIN_ID")
|
||||
if [[ "$ENABLE" -eq 1 ]]; then
|
||||
DEPLOY_ARGS+=(--enable)
|
||||
fi
|
||||
|
||||
"$PY" tools/devclient.py "${DEPLOY_ARGS[@]}"
|
||||
Executable
+62
@@ -0,0 +1,62 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cd "$(dirname "${BASH_SOURCE[0]}")/.."
|
||||
source scripts/lib.sh
|
||||
|
||||
need_cmd "$PY" curl unzip sha256sum
|
||||
|
||||
BETA=0
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--beta) BETA=1 ;;
|
||||
*) die "unknown argument: $arg" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
API_URL="https://api.github.com/repos/exteraSquad/plugins-pysdk-builds/releases?per_page=20"
|
||||
|
||||
RELEASE_JSON="$(mktemp)"
|
||||
trap 'rm -rf "$RELEASE_JSON" "$WORKDIR"' EXIT
|
||||
|
||||
curl -fsSL "$API_URL" -o "$RELEASE_JSON" || die "failed to query GitHub releases API"
|
||||
|
||||
read -r TAG ASSET_URL SHA_URL < <("$PY" - "$RELEASE_JSON" "$BETA" <<'PYEOF'
|
||||
import json, sys
|
||||
|
||||
path, allow_beta = sys.argv[1], sys.argv[2] == "1"
|
||||
with open(path, encoding="utf-8") as f:
|
||||
releases = json.load(f)
|
||||
|
||||
for rel in releases:
|
||||
if rel.get("prerelease") and not allow_beta:
|
||||
continue
|
||||
assets = {a["name"]: a["browser_download_url"] for a in rel.get("assets", [])}
|
||||
if "stubs.zip" in assets:
|
||||
sha_url = assets.get("stubs.zip.sha256", "")
|
||||
print(rel["tag_name"], assets["stubs.zip"], sha_url)
|
||||
break
|
||||
else:
|
||||
sys.exit("no release with a stubs.zip asset found")
|
||||
PYEOF
|
||||
) || die "no release with a stubs.zip asset found"
|
||||
|
||||
[[ -n "$TAG" ]] || die "could not determine a release to install"
|
||||
|
||||
WORKDIR="$(mktemp -d)"
|
||||
curl -fsSL "$ASSET_URL" -o "$WORKDIR/stubs.zip" || die "failed to download stubs.zip"
|
||||
|
||||
if [[ -n "$SHA_URL" ]]; then
|
||||
curl -fsSL "$SHA_URL" -o "$WORKDIR/stubs.zip.sha256" || die "failed to download stubs.zip.sha256"
|
||||
(cd "$WORKDIR" && sha256sum -c stubs.zip.sha256) || die "checksum verification failed for stubs.zip"
|
||||
else
|
||||
echo "warning: no stubs.zip.sha256 asset published for $TAG; skipping checksum verification" >&2
|
||||
fi
|
||||
|
||||
rm -rf stubs
|
||||
mkdir -p stubs
|
||||
unzip -q "$WORKDIR/stubs.zip" -d "$WORKDIR/outer"
|
||||
INNER="$WORKDIR/outer/pyi-files.zip"
|
||||
[[ -f "$INNER" ]] || die "expected pyi-files.zip inside stubs.zip, not found"
|
||||
unzip -q "$INNER" -d stubs
|
||||
|
||||
echo "installed SDK stubs from release $TAG into stubs/"
|
||||
@@ -0,0 +1,26 @@
|
||||
# Shared helpers for scripts/*.sh. Source, do not execute:
|
||||
# source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PKG=com.exteragram.messenger
|
||||
DEV_PORT=42690
|
||||
DEBUG_PORT=5678
|
||||
PY="${PYTHON:-python3}"
|
||||
ADB="${ADB:-adb}"
|
||||
|
||||
PLUGIN_ID="$("$PY" -c "import json,sys; print(json.load(open(sys.argv[1]))['id'])" "$REPO_ROOT/plugin.json")"
|
||||
|
||||
die() {
|
||||
echo "error: $*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
need_cmd() {
|
||||
for cmd in "$@"; do
|
||||
command -v "$cmd" >/dev/null 2>&1 || die "required command not found on PATH: $cmd"
|
||||
done
|
||||
}
|
||||
|
||||
app_pid() {
|
||||
"$ADB" shell pidof -s "$PKG" | tr -d '\r'
|
||||
}
|
||||
Executable
+39
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cd "$(dirname "${BASH_SOURCE[0]}")/.."
|
||||
source scripts/lib.sh
|
||||
|
||||
need_cmd "$ADB"
|
||||
|
||||
CLEAR=0
|
||||
STDIO=0
|
||||
PATTERN=""
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-c) CLEAR=1; shift ;;
|
||||
-t) STDIO=1; shift ;;
|
||||
-g) PATTERN="${2:-}"; shift 2 ;;
|
||||
*) die "unknown argument: $1" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ "$CLEAR" -eq 1 ]]; then
|
||||
"$ADB" logcat -c
|
||||
fi
|
||||
|
||||
run_logcat() {
|
||||
if [[ "$STDIO" -eq 1 ]]; then
|
||||
"$ADB" logcat -s python.stdout:'*' python.stderr:'*'
|
||||
else
|
||||
local pid
|
||||
pid="$(app_pid)"
|
||||
[[ -n "$pid" ]] || die "exteraGram is not running — open the app first"
|
||||
"$ADB" logcat --pid="$pid"
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ -n "$PATTERN" ]]; then
|
||||
run_logcat | grep --line-buffered -E "$PATTERN"
|
||||
else
|
||||
run_logcat
|
||||
fi
|
||||
Executable
+13
@@ -0,0 +1,13 @@
|
||||
#!/bin/sh
|
||||
# Companion to scripts/pre-commit. Both are enabled per clone by tools/bundle.py
|
||||
# (it sets `core.hooksPath scripts` on its first run).
|
||||
#
|
||||
# `git commit -- <pathspec>` (also how some IDEs commit a file selection) builds
|
||||
# the commit from a temporary index. The pre-commit bump therefore lands in the
|
||||
# commit, but the real index keeps the pre-bump blob and plugin.json is left
|
||||
# looking modified. The real index cannot be updated during the commit because
|
||||
# git holds index.lock, so refresh it here instead.
|
||||
#
|
||||
# Must never fail a commit that already succeeded.
|
||||
git add -- plugin.json 2>/dev/null || true
|
||||
exit 0
|
||||
Executable
+265
@@ -0,0 +1,265 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Increment the patch component of plugin.json's version as part of the commit.
|
||||
|
||||
Enabled per clone by tools/bundle.py, which sets `core.hooksPath scripts` on its
|
||||
first run (git never enables hooks from repository content by itself).
|
||||
|
||||
Runs as a pre-commit hook, which is the only commit hook whose `git add` is
|
||||
picked up by the commit currently being created:
|
||||
|
||||
* pre-commit -> staged changes land in THIS commit (correct)
|
||||
* prepare-commit-msg -> staged changes land in the NEXT commit
|
||||
* commit-msg -> staged changes are ignored entirely
|
||||
|
||||
The commit message is read from the invoking `git` process' argv (-m/-F/-C and
|
||||
long forms). `.git/COMMIT_EDITMSG` is deliberately NOT consulted: at pre-commit
|
||||
time it still holds the PREVIOUS commit's message, so a stop word there would
|
||||
silently suppress every later bump.
|
||||
|
||||
Bump is skipped when:
|
||||
* the commit message carries a stop word (see STOP_PATTERNS),
|
||||
* NO_BUMP / GIT_NO_BUMP is set in the environment,
|
||||
* plugin.json's version already differs from HEAD (manual bump),
|
||||
* a merge or rebase is in progress,
|
||||
* plugin.json is absent or its version is not x.y.z.
|
||||
|
||||
For editor-driven commits (`git commit` with no -m) the message does not exist
|
||||
yet when this hook runs, so use `NO_BUMP=1 git commit` to skip the bump there.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
PLUGIN_FILE = "plugin.json"
|
||||
|
||||
STOP_PATTERNS = (
|
||||
r"\[\s*no[-_ ]?bump\s*\]",
|
||||
r"\[\s*skip[-_ ]?bump\s*\]",
|
||||
r"\[\s*no[-_ ]?version[-_ ]?bump\s*\]",
|
||||
r"\[\s*skip[-_ ]?version\s*\]",
|
||||
)
|
||||
STOP_RE = re.compile("|".join(STOP_PATTERNS), re.IGNORECASE)
|
||||
|
||||
VERSION_RE = re.compile(r"\A(\d+)\.(\d+)\.(\d+)(.*)\Z", re.DOTALL)
|
||||
|
||||
# options whose value IS the message text
|
||||
TEXT_OPTS = ("-m", "--message")
|
||||
TEXT_OPTS_EQ = ("--message=",)
|
||||
# options whose value NAMES a file containing the message
|
||||
FILE_OPTS = ("-F", "--file")
|
||||
FILE_OPTS_EQ = ("--file=",)
|
||||
# options whose value names a commit to take the message from
|
||||
COMMIT_OPTS = ("-C", "--reuse-message", "-c", "--reedit-message")
|
||||
COMMIT_OPTS_EQ = ("--reuse-message=", "--reedit-message=")
|
||||
|
||||
|
||||
def repo_root() -> Path:
|
||||
out = subprocess.run(
|
||||
["git", "rev-parse", "--show-toplevel"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
if out.returncode != 0:
|
||||
raise RuntimeError(out.stderr.strip() or "not inside a git work tree")
|
||||
return Path(out.stdout.strip())
|
||||
|
||||
|
||||
def git_dir() -> Path:
|
||||
out = subprocess.run(
|
||||
["git", "rev-parse", "--absolute-git-dir"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
if out.returncode != 0:
|
||||
raise RuntimeError(out.stderr.strip() or "cannot resolve git dir")
|
||||
return Path(out.stdout.strip())
|
||||
|
||||
|
||||
def bump_patch(version: str) -> str:
|
||||
m = VERSION_RE.match(version.strip())
|
||||
if not m:
|
||||
raise ValueError(f"version {version!r} is not x.y.z")
|
||||
major, minor, patch, suffix = m.groups()
|
||||
return f"{major}.{minor}.{int(patch) + 1}{suffix}"
|
||||
|
||||
|
||||
def head_version(root: Path) -> str | None:
|
||||
"""Version recorded for plugin.json in HEAD, or None when unavailable."""
|
||||
if subprocess.run(
|
||||
["git", "rev-parse", "--verify", "--quiet", "HEAD"],
|
||||
cwd=root,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
).returncode != 0:
|
||||
return None
|
||||
|
||||
show = subprocess.run(
|
||||
["git", "show", f"HEAD:{PLUGIN_FILE}"],
|
||||
cwd=root,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
if show.returncode != 0:
|
||||
return None
|
||||
try:
|
||||
return str(json.loads(show.stdout)["version"])
|
||||
except (json.JSONDecodeError, KeyError, TypeError):
|
||||
return None
|
||||
|
||||
|
||||
def commit_message_text(root: Path) -> str:
|
||||
"""Commit message text of the git process that spawned this hook.
|
||||
|
||||
Covers `-m/--message` (inline), `-F/--file` (read the file) and
|
||||
`-C/-c/--reuse-message/--reedit-message` (read that commit's message).
|
||||
Returns "" for editor-driven commits, where no message exists yet.
|
||||
"""
|
||||
try:
|
||||
argv = Path(f"/proc/{os.getppid()}/cmdline").read_bytes().decode("utf-8", "replace").split("\0")
|
||||
except OSError:
|
||||
return ""
|
||||
|
||||
texts: list[str] = []
|
||||
pending: str | None = None # "text" | "file" | "commit"
|
||||
|
||||
def take(kind: str, value: str) -> None:
|
||||
if kind == "text":
|
||||
texts.append(value)
|
||||
elif kind == "file":
|
||||
try:
|
||||
texts.append(Path(value).read_text(encoding="utf-8", errors="replace"))
|
||||
except OSError:
|
||||
pass
|
||||
else:
|
||||
out = subprocess.run(
|
||||
["git", "log", "-1", "--format=%B", value],
|
||||
cwd=root,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
if out.returncode == 0:
|
||||
texts.append(out.stdout)
|
||||
|
||||
for arg in argv:
|
||||
if not arg:
|
||||
continue
|
||||
if pending:
|
||||
take(pending, arg)
|
||||
pending = None
|
||||
continue
|
||||
if arg in TEXT_OPTS:
|
||||
pending = "text"
|
||||
elif arg in FILE_OPTS:
|
||||
pending = "file"
|
||||
elif arg in COMMIT_OPTS:
|
||||
pending = "commit"
|
||||
elif arg.startswith(TEXT_OPTS_EQ):
|
||||
take("text", arg.split("=", 1)[1])
|
||||
elif arg.startswith(FILE_OPTS_EQ):
|
||||
take("file", arg.split("=", 1)[1])
|
||||
elif arg.startswith(COMMIT_OPTS_EQ):
|
||||
take("commit", arg.split("=", 1)[1])
|
||||
elif len(arg) > 1 and arg[0] == "-" and arg[1] != "-":
|
||||
# clustered short flags, e.g. -am "text" or -am"text"
|
||||
for kind, letter in (("text", "m"), ("file", "F"), ("commit", "C")):
|
||||
idx = arg.find(letter, 1)
|
||||
if idx == -1:
|
||||
continue
|
||||
if idx + 1 < len(arg):
|
||||
take(kind, arg[idx + 1:])
|
||||
else:
|
||||
pending = kind
|
||||
break
|
||||
|
||||
return "\n".join(texts)
|
||||
|
||||
|
||||
def strip_comments(text: str) -> str:
|
||||
return " ".join(
|
||||
line for line in text.splitlines() if not line.lstrip().startswith("#")
|
||||
)
|
||||
|
||||
|
||||
def stop_word_reason(root: Path) -> str | None:
|
||||
if os.environ.get("NO_BUMP") or os.environ.get("GIT_NO_BUMP"):
|
||||
return "NO_BUMP is set"
|
||||
match = STOP_RE.search(strip_comments(commit_message_text(root)))
|
||||
if match:
|
||||
return f"commit message contains {match.group(0)!r}"
|
||||
return None
|
||||
|
||||
|
||||
def merge_or_rebase_in_progress(gitdir: Path) -> bool:
|
||||
return any(
|
||||
(gitdir / name).exists()
|
||||
for name in ("MERGE_HEAD", "CHERRY_PICK_HEAD", "REVERT_HEAD", "rebase-merge", "rebase-apply")
|
||||
)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
try:
|
||||
root = repo_root()
|
||||
gitdir = git_dir()
|
||||
except RuntimeError as exc:
|
||||
print(f"pre-commit: {exc}", file=sys.stderr)
|
||||
return 0
|
||||
|
||||
if merge_or_rebase_in_progress(gitdir):
|
||||
return 0
|
||||
|
||||
reason = stop_word_reason(root)
|
||||
if reason:
|
||||
print(f"pre-commit: {reason}, keeping version unchanged")
|
||||
return 0
|
||||
|
||||
path = root / PLUGIN_FILE
|
||||
if not path.exists():
|
||||
return 0
|
||||
|
||||
raw = path.read_text(encoding="utf-8")
|
||||
try:
|
||||
data = json.loads(raw)
|
||||
except json.JSONDecodeError as exc:
|
||||
print(f"pre-commit: {PLUGIN_FILE} is not valid JSON: {exc}", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
current = str(data.get("version", ""))
|
||||
previous = head_version(root)
|
||||
if previous is not None and current != previous:
|
||||
print(
|
||||
f"pre-commit: version already changed ({previous} -> {current}), keeping it"
|
||||
)
|
||||
return 0
|
||||
|
||||
try:
|
||||
new_version = bump_patch(current)
|
||||
except ValueError as exc:
|
||||
print(f"pre-commit: {exc}, keeping version unchanged", file=sys.stderr)
|
||||
return 0
|
||||
|
||||
data["version"] = new_version
|
||||
trailing = "\n" if raw.endswith("\n") else ""
|
||||
path.write_text(json.dumps(data, indent=2) + trailing, encoding="utf-8")
|
||||
|
||||
# Stage into the index git is actually building for this commit. For
|
||||
# `git commit -- <pathspec>` that is a temporary index named by
|
||||
# GIT_INDEX_FILE, so honouring the inherited environment is what makes the
|
||||
# bump part of THIS commit.
|
||||
add = subprocess.run(["git", "add", "--", PLUGIN_FILE], cwd=root)
|
||||
if add.returncode != 0:
|
||||
print(f"pre-commit: failed to stage {PLUGIN_FILE}", file=sys.stderr)
|
||||
return add.returncode
|
||||
|
||||
# The real index may still hold the pre-bump blob after a pathspec commit;
|
||||
# scripts/post-commit refreshes it once git releases index.lock.
|
||||
|
||||
print(f"pre-commit: version {current} -> {new_version}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Executable
+39
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
cd "$(dirname "${BASH_SOURCE[0]}")/.."
|
||||
source scripts/lib.sh
|
||||
|
||||
need_cmd "$PY"
|
||||
|
||||
checksum() {
|
||||
find src plugin.json -type f -printf '%T@ %p\n' 2>/dev/null | sha256sum
|
||||
}
|
||||
|
||||
run_deploy() {
|
||||
if ! scripts/deploy.sh "$@"; then
|
||||
echo "deploy failed, still watching..." >&2
|
||||
fi
|
||||
}
|
||||
|
||||
echo "watching src/ and plugin.json for changes..."
|
||||
run_deploy "$@"
|
||||
|
||||
if command -v inotifywait >/dev/null 2>&1; then
|
||||
while true; do
|
||||
inotifywait -qq -r -e close_write,create,delete,move src plugin.json 2>/dev/null || true
|
||||
sleep 0.3
|
||||
run_deploy "$@"
|
||||
done
|
||||
else
|
||||
echo "inotifywait not found; falling back to polling" >&2
|
||||
last="$(checksum)"
|
||||
while true; do
|
||||
sleep 1
|
||||
current="$(checksum)"
|
||||
if [[ "$current" != "$last" ]]; then
|
||||
last="$current"
|
||||
sleep 0.3
|
||||
run_deploy "$@"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
Reference in new issue
Block a user