feat: port to Python and package as pip-installable distribution

Replaces main.sh with the 'cli-gnome-extension-manager' distribution in a src layout, shipping both the cli-gnome-extension-manager and the older goexma command.

sync_repository keeps the three states of the shell original apart: clone when the folder is missing, pull when it is a checkout, leave it alone otherwise, so a manually installed extension is never overwritten. build_extension still moves the checkout away before make install, but into a TemporaryDirectory instead of a fixed /tmp/<name>, which removes the collision between parallel runs and cleans up on abort.

Every step passes through the exit code of the failing tool, replacing the shell chain of || exit 1; a missing gnome-extensions aborts with exit 127. The e2e suite builds a real git repository, clones it with real git and runs real make install.

Adds --extensions-dir, unit, integration and container-based e2e tests, ruff and markdown/mermaid linting, CodeQL and Dependabot, and pins the core metadata to 2.4 so twine accepts the artifacts.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-14 14:41:17 +02:00
parent 59943dc029
commit ccac936015
23 changed files with 1149 additions and 72 deletions

View File

@@ -1,10 +1,35 @@
# CLI GNOME Extension Manager 🚀
[![GitHub Sponsors](https://img.shields.io/badge/Sponsor-GitHub%20Sponsors-blue?logo=github)](https://github.com/sponsors/kevinveenbirkenbach) [![Patreon](https://img.shields.io/badge/Support-Patreon-orange?logo=patreon)](https://www.patreon.com/c/kevinveenbirkenbach) [![Buy Me a Coffee](https://img.shields.io/badge/Buy%20me%20a%20Coffee-Funding-yellow?logo=buymeacoffee)](https://buymeacoffee.com/kevinveenbirkenbach) [![PayPal](https://img.shields.io/badge/Donate-PayPal-blue?logo=paypal)](https://s.veen.world/paypaldonate)
[![License: AGPL v3](https://img.shields.io/badge/License-AGPL%20v3-blue.svg)](./LICENSE) [![Python Version](https://img.shields.io/badge/Python-3.10%2B-blue.svg)](https://www.python.org) [![GitHub stars](https://img.shields.io/github/stars/kevinveenbirkenbach/cli-gnome-extension-manager.svg?style=social)](https://github.com/kevinveenbirkenbach/cli-gnome-extension-manager/stargazers)
[![License: AGPL v3](https://img.shields.io/badge/License-AGPL%20v3-blue.svg)](./LICENSE) [![Bash Version](https://img.shields.io/badge/Bash-4.x%2B-green.svg)](https://www.gnu.org/software/bash/) [![GitHub stars](https://img.shields.io/github/stars/kevinveenbirkenbach/cli-gnome-extension-manager.svg?style=social)](https://github.com/kevinveenbirkenbach/cli-gnome-extension-manager/stargazers)
Manage your GNOME extensions easily from the command line with **CLI GNOME Extension Manager**. This Python CLI lets you install, update, enable, and disable GNOME extensions directly from your terminal.
Manage your GNOME extensions easily from the command line with **CLI GNOME Extension Manager**. This Bash script lets you install, update, enable, and disable GNOME extensions directly from your terminal.
## How it works 🧭
```mermaid
flowchart TD
A["cli-gnome-extension-manager ACTION NAME [REPO]"] --> B{"action"}
B -- disable --> C["gnome-extensions disable"]
B -- enable --> D{"REPO argument given?"}
D -- no --> H["gnome-extensions enable"]
D -- yes --> E{"extension folder already there?"}
E -- no --> F["git clone into the extensions dir"]
E -- yes --> G{"is it a git checkout?"}
G -- yes --> G1["git pull"]
G -- no --> G2["leave it untouched"]
F --> I{"ships a Makefile?"}
G1 --> I
G2 --> I
I -- yes --> J["move to a scratch dir, make install, drop the scratch dir"]
I -- no --> H
J --> H
C --> Z["Installation complete"]
H --> Z
```
Any failing step - `git`, `make` or `gnome-extensions` - stops the run and its exit code is passed through.
## Features ✨
@@ -15,55 +40,84 @@ Manage your GNOME extensions easily from the command line with **CLI GNOME Exten
## Requirements 🔧
- **Python 3.10+** 🐍
- **GNOME Shell** (version 3.36+)
- **Bash** (version 4.x+)
- **Git** (for cloning repositories)
- **Make** (optional, for compiling extensions)
- **gnome-extensions** CLI tool
- **`gnome-extensions`** on `PATH` — always required
- **`git`** on `PATH` — only when a repository argument is given
- **`make`** on `PATH` — only when the extension ships a `Makefile`
If a required command is missing, the tool exits with code `127` and a oneline error instead of a traceback.
## Installation 📦
You can install **CLI GNOME Extension Manager** using [Kevin's Package Manager](https://github.com/kevinveenbirkenbach/pkgmgr):
```bash
pkgmgr install goexma
pip install cli-gnome-extension-manager
```
Alternatively, clone this repository:
pip is the single supported installation path.
```bash
git clone https://github.com/kevinveenbirkenbach/cli-gnome-extension-manager.git
cd cli-gnome-extension-manager
```
The package installs **two** identical commands: `cli-gnome-extension-manager` (primary) and `goexma` (kept for older documentation and scripts).
## Usage ⚙️
To **install and enable** an extension, run:
```bash
goexma enable <extension_name> <extension_repository_path>
cli-gnome-extension-manager enable <extension_name> <extension_repository_path>
```
To **enable** an already installed extension, drop the repository argument:
```bash
cli-gnome-extension-manager enable <extension_name>
```
To **disable** an extension, run:
```bash
goexma disable <extension_name>
cli-gnome-extension-manager disable <extension_name>
```
The script will:
The tool will:
- Clone the repository if the extension isn't installed.
- Pull updates if the extension is already a Git repository.
- Compile the extension if a Makefile is present.
- Compile the extension if a Makefile is present — the checkout is moved into a scratch directory, `make install` runs there, and the scratch directory is removed afterwards.
- Enable or disable the extension using `gnome-extensions`.
Extensions live in `~/.local/share/gnome-shell/extensions` by default; `--extensions-dir` points the tool elsewhere.
### Exit codes
| Code | Meaning |
| --- | --- |
| `0` | Finished. |
| `2` | Invalid command line arguments, including an action other than `enable` or `disable`. |
| `127` | A required command is not installed. |
| other | The exit code of the failing `git`, `make` or `gnome-extensions` call. |
## Development 🧪
```bash
make lint # ruff check + ruff format --check
make format # apply ruff format
make test # unit + integration tests
make test-unit
make test-integration
make test-e2e # install the package in a container and exercise the CLI
```
Tests run against the working tree — the `Makefile` puts `src/` on `PYTHONPATH`, so no install is needed. The integration tests build a real throwaway git repository and stub `gnome-extensions` on `PATH`, so no extension of yours is ever touched.
## License 📜
This project is licensed under the GNU Affero General Public License v3.0. See the [LICENSE](./LICENSE) file for details.
## Author 👨‍💻
**Kevin Veen-Birkenbach**
- 📧 [kevin@veen.world](mailto:kevin@veen.world)
Developed by **Kevin Veen-Birkenbach**
- 📧 [kevin@veen.world](mailto:kevin@veen.world)
- 🌐 [https://www.veen.world/](https://www.veen.world/)
---