diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 000000000..eaf0f76d0 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,19 @@ +# Copilot Instructions for This Repository +# workspace + +Please follow these guidelines when using Copilot or creating code in this repository: + +- Language: English. + +- All comments, function and variable names, and code must be written in English and follow the Inkypi coding standards and templates. Use the same naming and documentation model across the repository to keep style consistent. + +- When creating a new plugin, or if you have any questions, consult the repository documentation in the `docs/` folder first. Follow the guidance there (plugin architecture, contribution guidelines, coding standards) before opening issues or pull requests. + +- When you create or modify code, always run the project's test suite and include the exact command below at the end of your chat response (so the user can run it locally): + + source ./venv/bin/activate && pytest -q + +- If you add new runnable code, run the tests locally before finishing your response and report the test outcome (pass/fail and summary) in the chat. +- Keep changes small and focused; include any required instructions to reproduce test results. + +These instructions are for developer convenience and to keep the repository stable when code is proposed. diff --git a/install/cli/inkypi-plugin b/install/cli/inkypi-plugin index b7f926dd1..fcf0e7306 100644 --- a/install/cli/inkypi-plugin +++ b/install/cli/inkypi-plugin @@ -10,6 +10,7 @@ usage() { echo "" echo "Plugin commands:" echo " inkypi plugin install " + echo " inkypi plugin update " echo " inkypi plugin remove " echo " inkypi plugin list" echo "" @@ -157,6 +158,121 @@ list_plugins() { done } | column -t -s $'\t' } +# ---------------------------- +# UPDATE +# ---------------------------- +update_plugin() { + if [[ $# -ne 1 ]]; then + echo "Usage: inkypi plugin update " + exit 1 + fi + + PLUGIN_ID="$1" + DEST_DIR="$PLUGINS_DIR/$PLUGIN_ID" + INFO_FILE="$DEST_DIR/plugin-info.json" + + require_plugins_dir + + # Check plugin is installed + if [[ ! -d "$DEST_DIR" ]]; then + echo "[ERROR] Plugin '$PLUGIN_ID' is not installed." + exit 1 + fi + + # Check plugin-info.json exists + if [[ ! -f "$INFO_FILE" ]]; then + echo "[ERROR] Plugin '$PLUGIN_ID' has no plugin-info.json." + exit 1 + fi + + # Read repository URL from plugin-info.json + REPO_URL=$(jq -r '.repository // empty' "$INFO_FILE") + + if [[ -z "$REPO_URL" ]]; then + echo "[ERROR] Plugin '$PLUGIN_ID' has no repository URL in plugin-info.json." + echo "[ERROR] Only third-party plugins with a saved repository can be updated." + exit 1 + fi + + echo "[INFO] Updating plugin '$PLUGIN_ID' from $REPO_URL" + + # Clone into a temporary directory to validate before touching DEST_DIR + TMP_DIR=$(mktemp -d) + trap 'rm -rf "$TMP_DIR"' EXIT + + if ! git clone --depth 1 --filter=blob:none --sparse "$REPO_URL" "$TMP_DIR/repo" &>/dev/null; then + echo "[ERROR] Failed to clone repository: $REPO_URL" + exit 1 + fi + + # Check if the plugin folder exists in the repo + DEFAULT_BRANCH=$(git -C "$TMP_DIR/repo" symbolic-ref --short HEAD 2>/dev/null || echo main) + if ! git -C "$TMP_DIR/repo" ls-tree --name-only "$DEFAULT_BRANCH" | grep -qx "$PLUGIN_ID"; then + echo "[ERROR] Plugin '$PLUGIN_ID' does not exist in the repository." + exit 1 + fi + + cd "$TMP_DIR/repo" + git sparse-checkout set "$PLUGIN_ID" &>/dev/null + + if [[ ! -d "$TMP_DIR/repo/$PLUGIN_ID" ]]; then + echo "[ERROR] Failed to checkout plugin files." + exit 1 + fi + + # Prepare new plugin files in staging + mkdir -p "$TMP_DIR/staging" + shopt -s dotglob + mv "$TMP_DIR/repo/$PLUGIN_ID"/* "$TMP_DIR/staging/" + + # All validated — now replace DEST_DIR + if [[ -d "$DEST_DIR" ]]; then + sudo chown -R "$(whoami)":"$(whoami)" "$DEST_DIR" 2>/dev/null || true + chmod -R u+rw "$DEST_DIR" 2>/dev/null || true + fi + + rm -rf "$DEST_DIR" + mkdir -p "$DEST_DIR" + cp -a "$TMP_DIR/staging"/. "$DEST_DIR/" + + # Ensure repository URL is preserved in plugin-info.json + if [[ -f "$DEST_DIR/plugin-info.json" ]]; then + CURRENT_REPO=$(jq -r '.repository // empty' "$DEST_DIR/plugin-info.json") + if [[ -z "$CURRENT_REPO" ]]; then + TMP_INFO=$(mktemp) + jq --arg repo "$REPO_URL" '.repository = $repo' "$DEST_DIR/plugin-info.json" > "$TMP_INFO" + mv "$TMP_INFO" "$DEST_DIR/plugin-info.json" + fi + fi + + echo "[INFO] Plugin files updated at $DEST_DIR" + + # Install requirements if any + REQ_FILE="$DEST_DIR/requirements.txt" + + if [[ -f "$REQ_FILE" ]]; then + echo "[INFO] requirements.txt found, installing dependencies..." + + if [[ ! -d "$VENV_PATH" ]]; then + echo "[ERROR] Virtual environment not found at $VENV_PATH" + exit 1 + fi + + source "$VENV_PATH/bin/activate" + pip install -r "$REQ_FILE" + deactivate + + echo "[INFO] Dependencies installed" + else + echo "[INFO] No requirements.txt found, skipping dependency install" + fi + + echo "[INFO] Restarting $APPNAME service." + sudo systemctl restart "$APPNAME.service" + + echo "[INFO] Updated plugin '$PLUGIN_ID' from $REPO_URL" +} + # ---------------------------- # COMMAND ROUTER # ---------------------------- @@ -165,6 +281,9 @@ case "$command" in install) install_plugin "$@" ;; + update) + update_plugin "$@" + ;; uninstall) uninstall_plugin "$@" ;;