From 0cd7a24adb2501169397198329267702c9db9627 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Wed, 5 Aug 2026 20:06:41 +0200 Subject: [PATCH] feat: add install scripts, packaging and an example plugin install.sh places the package in a version independent directory under XDG_DATA_HOME instead of site-packages, which would be orphaned the day the distribution moves to a new Python. It verifies the package imports before reporting success, and warns when the pre-split extension is still installed alongside it. pyproject.toml makes pip install --user an equally valid route; the bootstrap in each plugin finds the package either way. examples/nemoRepoLastCommit.py is a complete working plugin in sixty lines, which is the point of the split. --- examples/nemoRepoLastCommit.py | 75 ++++++++++++++++++++++++++++++++++ install.sh | 74 +++++++++++++++++++++++++++++++++ pyproject.toml | 27 ++++++++++++ uninstall.sh | 25 ++++++++++++ 4 files changed, 201 insertions(+) create mode 100644 examples/nemoRepoLastCommit.py create mode 100755 install.sh create mode 100644 pyproject.toml create mode 100755 uninstall.sh diff --git a/examples/nemoRepoLastCommit.py b/examples/nemoRepoLastCommit.py new file mode 100644 index 0000000..634481b --- /dev/null +++ b/examples/nemoRepoLastCommit.py @@ -0,0 +1,75 @@ +#!/usr/bin/python3 +"""Example plugin: a "Last Commit" column for Nemo's list view. + +Copy this file to ~/.local/share/nemo-python/extensions/ and restart Nemo +with `nemo -q` to try it. It is about sixty lines of real work because +nemo-repo-core already owns the config file, the git call and the cache. + +The block above the imports is the core bootstrap. Copy it verbatim into +every plugin: it makes the plugin work whether the core was installed with +its install.sh or with pip, and it is the only piece that knows where the +core lives. +""" + +import importlib.util +import os +import sys + + +def _ensureCoreOnPath(): + if importlib.util.find_spec("nemoRepoCore") is not None: + return + dataHome = os.environ.get("XDG_DATA_HOME") or os.path.expanduser("~/.local/share") + dataDirs = (os.environ.get("XDG_DATA_DIRS") or "/usr/local/share:/usr/share").split(":") + for base in [dataHome] + dataDirs: + candidate = os.path.join(base, "nemo-repo-core") + if os.path.isdir(os.path.join(candidate, "nemoRepoCore")): + sys.path.insert(0, candidate) + return + + +_ensureCoreOnPath() + +import gi + +gi.require_version("Nemo", "3.0") +from gi.repository import GObject, Nemo + +from nemoRepoCore import (findRepoRoot, localDirectoryPath, repoCache, + requireCore) + +requireCore("1.0.0") + + +class LastCommitColumnProvider(GObject.GObject, Nemo.ColumnProvider, + Nemo.InfoProvider, Nemo.NameAndDescProvider): + + def get_columns(self): + return [ + Nemo.Column(name="NemoRepoExample::last_commit", + attribute="repo_last_commit", + label="Last Commit", + description="Subject and age of the most recent commit"), + ] + + def update_file_info(self, file): + path = localDirectoryPath(file) + if path is None: + return + root = findRepoRoot(path) + if root is None: + file.add_string_attribute("repo_last_commit", "") + return + + repoCache.registerFileObject(root, file) + info = repoCache.get(root) + if info.error or not info.lastCommit: + file.add_string_attribute("repo_last_commit", "") + return + + commit = info.lastCommit + file.add_string_attribute("repo_last_commit", + "%s (%s)" % (commit.subject, commit.relativeDate)) + + def get_name_and_desc(self): + return ["nemo-repo-example:::Example column showing the last commit"] diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..8df7aab --- /dev/null +++ b/install.sh @@ -0,0 +1,74 @@ +#!/usr/bin/env bash +# Installs the nemoRepoCore library for the current user. No root needed. +# +# ./install.sh link the package (edits in this checkout go live) +# ./install.sh --copy copy the package instead of linking it +# +# The package is placed in a version independent directory under +# XDG_DATA_HOME, so a Python upgrade cannot orphan it. Plugins locate it from +# there; see "Writing a plugin" in README.md. +set -euo pipefail + +sourceDir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +dataHome="${XDG_DATA_HOME:-$HOME/.local/share}" +installDir="$dataHome/nemo-repo-core" +configDir="${XDG_CONFIG_HOME:-$HOME/.config}" +extensionsDir="$dataHome/nemo-python/extensions" + +mode="link" +if [ "${1:-}" = "--copy" ]; then + mode="copy" +elif [ -n "${1:-}" ]; then + echo "usage: $0 [--copy]" >&2 + exit 2 +fi + +if ! command -v python3 >/dev/null 2>&1; then + echo "python3 is required but was not found in PATH" >&2 + exit 1 +fi + +mkdir -p "$installDir" +rm -rf "$installDir/nemoRepoCore" + +if [ "$mode" = "copy" ]; then + cp -r "$sourceDir/nemoRepoCore" "$installDir/nemoRepoCore" + echo "Copied nemoRepoCore -> $installDir/nemoRepoCore" +else + ln -s "$sourceDir/nemoRepoCore" "$installDir/nemoRepoCore" + echo "Linked nemoRepoCore -> $installDir/nemoRepoCore" +fi + +if ! PYTHONPATH="$installDir" python3 -c "import nemoRepoCore" 2>/dev/null; then + echo + echo "WARNING: the package was installed but could not be imported." + echo "Check that python3-gi and gir1.2-nemo-3.0 are installed:" + echo " sudo apt install python3-gi gir1.2-nemo-3.0 python-nemo" + exit 1 +fi + +coreVersion="$(PYTHONPATH="$installDir" python3 -c 'import nemoRepoCore; print(nemoRepoCore.coreVersion)')" +echo "Import check passed, core version $coreVersion" + +legacyConfig="$configDir/nemo-repo-emblems.json" +newConfig="$configDir/nemo-repo-core.json" +if [ -f "$legacyConfig" ] && [ ! -f "$newConfig" ]; then + echo + echo "Found a config from the pre-split extension:" + echo " $legacyConfig" + echo "It will be copied to $newConfig the first time a plugin loads." + echo "The old file is left untouched." +fi + +legacyExtension="$extensionsDir/nemo_repo_emblems.py" +if [ -e "$legacyExtension" ]; then + echo + echo "WARNING: the pre-split single file extension is still installed:" + echo " $legacyExtension" + echo "Running it alongside the new plugins duplicates emblems, columns and" + echo "menu entries. Remove it when you are ready to switch over:" + echo " rm \"$legacyExtension\"" +fi + +echo +echo "Core installed. It does nothing on its own: install at least one plugin." diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..e023b70 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,27 @@ +[build-system] +requires = ["setuptools>=61"] +build-backend = "setuptools.build_meta" + +[project] +name = "nemo-repo-core" +version = "1.0.0" +description = "Shared foundation for repository-aware Nemo file manager plugins" +readme = "README.md" +requires-python = ">=3.8" +license = { text = "MIT" } +authors = [{ name = "DualFroz" }] +keywords = ["nemo", "cinnamon", "git", "file-manager", "nemo-python"] +classifiers = [ + "Environment :: X11 Applications :: GTK", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Operating System :: POSIX :: Linux", + "Programming Language :: Python :: 3", + "Topic :: Desktop Environment :: File Managers", +] + +[project.urls] +Homepage = "https://github.com/dualfroz/nemo-repo-core" + +[tool.setuptools] +packages = ["nemoRepoCore"] diff --git a/uninstall.sh b/uninstall.sh new file mode 100755 index 0000000..382f927 --- /dev/null +++ b/uninstall.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# Removes the installed nemoRepoCore library. The checkout and the user's +# config file are left alone. +set -euo pipefail + +dataHome="${XDG_DATA_HOME:-$HOME/.local/share}" +installDir="$dataHome/nemo-repo-core" +extensionsDir="$dataHome/nemo-python/extensions" + +if [ -e "$installDir/nemoRepoCore" ]; then + rm -rf "$installDir/nemoRepoCore" + echo "Removed $installDir/nemoRepoCore" +else + echo "Nothing installed at $installDir/nemoRepoCore" +fi + +rmdir "$installDir" 2>/dev/null || true + +remaining="$(find "$extensionsDir" -maxdepth 1 -name 'nemoRepo*.py' -o -maxdepth 1 -name 'nemoFolderTools.py' 2>/dev/null || true)" +if [ -n "$remaining" ]; then + echo + echo "WARNING: these plugins still expect the core and will now fail to load:" + echo "$remaining" + echo "Uninstall them too, then restart Nemo with: nemo -q" +fi