projscan 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,93 @@
1
+ name: CI & Release
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+
8
+ jobs:
9
+ test-build-publish:
10
+ # To avoid infinite loops, skip this workflow if the commit message contains 'release v'
11
+ if: "!contains(github.event.head_commit.message, 'release v')"
12
+ runs-on: ubuntu-latest
13
+ permissions:
14
+ contents: write # to be able to push commit + tag
15
+
16
+ steps:
17
+ - name: Checkout
18
+ uses: actions/checkout@v5
19
+ with:
20
+ fetch-depth: 0 # necessary to create / push tags properly
21
+
22
+ - name: Install uv
23
+ uses: astral-sh/setup-uv@v6
24
+ with:
25
+ enable-cache: true
26
+
27
+ - name: Set up Python (from requires-python)
28
+ run: uv python install
29
+
30
+ - name: Install project
31
+ run: uv sync
32
+
33
+ - name: Detect version bump from commit message
34
+ id: bump
35
+ run: |
36
+ msg="${{ github.event.head_commit.message }}"
37
+ bump="none"
38
+
39
+ if echo "$msg" | grep -q "FIX"; then
40
+ bump="patch"
41
+ fi
42
+ if echo "$msg" | grep -q "MINOR"; then
43
+ bump="minor"
44
+ fi
45
+ if echo "$msg" | grep -q "MAJOR"; then
46
+ bump="major"
47
+ fi
48
+
49
+ echo "bump=$bump" >> "$GITHUB_OUTPUT"
50
+ echo "Bump type: $bump"
51
+
52
+ - name: Bump version with uv
53
+ if: steps.bump.outputs.bump != 'none'
54
+ id: bump_version
55
+ run: |
56
+ uv version --bump "${{ steps.bump.outputs.bump }}" --frozen
57
+
58
+ version=$(uv version --short)
59
+ echo "version=$version" >> "$GITHUB_OUTPUT"
60
+ echo "New version: $version"
61
+
62
+ - name: Commit version bump and tag
63
+ if: steps.bump.outputs.bump != 'none'
64
+ env:
65
+ VERSION: ${{ steps.bump_version.outputs.version }}
66
+ run: |
67
+ git config user.name "github-actions[bot]"
68
+ git config user.email "github-actions[bot]@users.noreply.github.com"
69
+
70
+ git add pyproject.toml
71
+ if [ -f uv.lock ]; then
72
+ git add uv.lock
73
+ fi
74
+
75
+ if git diff --cached --quiet; then
76
+ echo "No changes to commit"
77
+ else
78
+ git commit -m "release v$VERSION"
79
+ git tag "v$VERSION"
80
+ git push origin HEAD
81
+ git push origin "v$VERSION"
82
+ fi
83
+
84
+ - name: Build distributions
85
+ if: steps.bump.outputs.bump != 'none'
86
+ run: uv build
87
+
88
+ - name: Publish to PyPI
89
+ if: steps.bump.outputs.bump != 'none'
90
+ env:
91
+ # PyPI token (API token) stored in secret
92
+ UV_PUBLISH_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
93
+ run: uv publish
@@ -0,0 +1,11 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+ .claude/
9
+
10
+ # Virtual environments
11
+ .venv
@@ -0,0 +1 @@
1
+ 3.11
@@ -0,0 +1,176 @@
1
+ Metadata-Version: 2.4
2
+ Name: projscan
3
+ Version: 0.1.0
4
+ Summary: Scanner de dépôts git locaux et de tools uv installés.
5
+ Requires-Python: >=3.11
6
+ Requires-Dist: click>=8
7
+ Requires-Dist: rich>=13
8
+ Description-Content-Type: text/markdown
9
+
10
+ # projscan
11
+
12
+ Scanner de dépôts git locaux et de tools `uv` installés depuis git.
13
+ Répond à une question simple : **suis-je à jour ?**
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ uv tool install git+https://github.com/Caymaar/projscan.git
19
+ ```
20
+
21
+ Ou en mode développement :
22
+
23
+ ```bash
24
+ git clone ...
25
+ cd projscan
26
+ uv sync
27
+ uv run projscan --help
28
+ ```
29
+
30
+ ## Commandes
31
+
32
+ ### `projscan repos [PATHS]...`
33
+
34
+ Scanne récursivement un ou plusieurs dossiers et affiche l'état de chaque dépôt git.
35
+
36
+ ```
37
+ ╭──────────────────────┬─────────┬─────────┬─────────────────────┬────────┬────────────────────┬─────────────╮
38
+ │ Nom │ Version │ Branche │ Statut │ Commit │ Message │ Quand │
39
+ ├──────────────────────┼─────────┼─────────┼─────────────────────┼────────┼────────────────────┼─────────────┤
40
+ │ mon-api │ 2.1.0 │ main │ en retard (↓3) │ a4f2c1 │ fix: timeout retry │ 2 days ago │
41
+ │ mon-dashboard │ 1.0.0 │ main │ divergé (↓1 ↑2) │ b8e3d9 │ feat: dark mode │ 5 hours ago │
42
+ │ projscan │ 0.1.0 │ main │ à jour │ c1a2b3 │ initial commit │ 1 hour ago │
43
+ ╰──────────────────────┴─────────┴─────────┴─────────────────────┴────────┴────────────────────┴─────────────╯
44
+ 3 repos, 1 à jour, 2 à mettre à jour
45
+ ```
46
+
47
+ **Options :**
48
+
49
+ | Option | Description |
50
+ |--------|-------------|
51
+ | `--no-fetch` | Ne pas faire `git fetch` — utilise le cache local (rapide) |
52
+ | `--depth INTEGER` | Profondeur max de récursion (défaut : 3) |
53
+ | `--json` | Sortie JSON machine-readable |
54
+ | `--concurrency INTEGER` | Scans parallèles (défaut : 16) |
55
+
56
+ ```bash
57
+ # Scanne ~/code et ~/work
58
+ projscan repos ~/code ~/work
59
+
60
+ # Rapide, sans réseau
61
+ projscan repos --no-fetch ~/code
62
+
63
+ # Pipe JSON vers jq
64
+ projscan repos --no-fetch --json ~/code | jq '.[] | select(.status != "à jour")'
65
+ ```
66
+
67
+ ### `projscan tools`
68
+
69
+ Scanne les tools installés via `uv tool install git+https://...` et vérifie si des mises à jour sont disponibles (via `git ls-remote`, sans cloner).
70
+
71
+ ```
72
+ ╭────────────────┬─────────┬───────────────────────────────┬────────┬──────────┬───────────╮
73
+ │ Nom │ Version │ Source │ Ref │ Commit │ Statut │
74
+ ├────────────────┼─────────┼───────────────────────────────┼────────┼──────────┼───────────┤
75
+ │ mon-tool │ 1.2.0 │ github.com/org/mon-tool.git │ main │ f10c2123 │ MAJ dispo │
76
+ │ autre-tool │ 0.9.1 │ github.com/org/autre-tool.git │ main │ fc9e74c5 │ à jour │
77
+ ╰────────────────┴─────────┴───────────────────────────────┴────────┴──────────┴───────────╯
78
+ 2 tools, 1 à jour, 1 à mettre à jour
79
+ ```
80
+
81
+ **Options :**
82
+
83
+ | Option | Description |
84
+ |--------|-------------|
85
+ | `--tool-dir PATH` | Racine des tools uv (détection auto par défaut) |
86
+ | `--no-remote` | Liste locale sans vérifier les remotes |
87
+ | `--json` | Sortie JSON machine-readable |
88
+
89
+ ```bash
90
+ projscan tools
91
+ projscan tools --no-remote --json
92
+ projscan tools --tool-dir ~/.local/share/uv/tools
93
+ ```
94
+
95
+ ## Codes de sortie
96
+
97
+ | Code | Signification |
98
+ |------|---------------|
99
+ | `0` | Tout est à jour |
100
+ | `1` | Au moins un item à mettre à jour (ou en erreur) |
101
+ | `2` | Erreur d'exécution fatale |
102
+ | `130` | Interruption clavier (Ctrl-C) |
103
+
104
+ Utile en script ou en CI :
105
+
106
+ ```bash
107
+ projscan repos --no-fetch ~/code || echo "Des repos sont en retard !"
108
+ ```
109
+
110
+ ## Couleurs de statut
111
+
112
+ | Statut | Couleur | Signification |
113
+ |--------|---------|---------------|
114
+ | `à jour` | vert | Synchronisé avec l'upstream |
115
+ | `en retard (↓N)` | rouge | N commits distants non récupérés |
116
+ | `en avance (↑N)` | cyan | N commits locaux non poussés |
117
+ | `divergé (↓N ↑M)` | magenta | Branches divergées |
118
+ | `MAJ dispo` | jaune | Nouvelle version du tool disponible |
119
+ | `pas d'upstream` | gris | Pas de tracking branch configurée |
120
+ | `pas git` | gris | Tool installé depuis PyPI, pas git |
121
+ | `inconnu` | gris | Remote inaccessible (offline, auth...) |
122
+ | `erreur` | rouge gras | Erreur lors du scan |
123
+
124
+ ## Usage comme bibliothèque
125
+
126
+ `projscan` est conçu pour être importé dans une app Textual ou tout autre code async.
127
+
128
+ ```python
129
+ from pathlib import Path
130
+ from projscan import scan_paths, scan_tools, RepoInfo, ToolInfo
131
+
132
+ # Scanne des dépôts git
133
+ repos: list[RepoInfo] = await scan_paths(
134
+ [Path("~/code")],
135
+ fetch=True,
136
+ max_depth=3,
137
+ concurrency=16,
138
+ )
139
+
140
+ for repo in repos:
141
+ print(f"{repo.name}: {repo.status}")
142
+
143
+ # Scanne les tools uv
144
+ tools: list[ToolInfo] = await scan_tools(check_remote=True)
145
+
146
+ for tool in tools:
147
+ if tool.status == "MAJ dispo":
148
+ print(f"Update available: {tool.name}")
149
+ ```
150
+
151
+ Les dataclasses exposent une méthode `.as_dict()` pour la sérialisation JSON :
152
+
153
+ ```python
154
+ import json
155
+ print(json.dumps([r.as_dict() for r in repos], ensure_ascii=False))
156
+ ```
157
+
158
+ L'API publique complète :
159
+
160
+ ```python
161
+ from projscan import (
162
+ # Git
163
+ RepoInfo, scan_paths, scan_repo, find_repos,
164
+ # UV tools
165
+ ToolInfo, scan_tools, scan_tool, uv_tool_dir,
166
+ )
167
+ ```
168
+
169
+ ## Développement
170
+
171
+ ```bash
172
+ uv sync
173
+ uv run pytest # 52 tests, ~5s
174
+ uv run projscan repos . # test en réel
175
+ uv run projscan tools # test en réel
176
+ ```
@@ -0,0 +1,167 @@
1
+ # projscan
2
+
3
+ Scanner de dépôts git locaux et de tools `uv` installés depuis git.
4
+ Répond à une question simple : **suis-je à jour ?**
5
+
6
+ ## Installation
7
+
8
+ ```bash
9
+ uv tool install git+https://github.com/Caymaar/projscan.git
10
+ ```
11
+
12
+ Ou en mode développement :
13
+
14
+ ```bash
15
+ git clone ...
16
+ cd projscan
17
+ uv sync
18
+ uv run projscan --help
19
+ ```
20
+
21
+ ## Commandes
22
+
23
+ ### `projscan repos [PATHS]...`
24
+
25
+ Scanne récursivement un ou plusieurs dossiers et affiche l'état de chaque dépôt git.
26
+
27
+ ```
28
+ ╭──────────────────────┬─────────┬─────────┬─────────────────────┬────────┬────────────────────┬─────────────╮
29
+ │ Nom │ Version │ Branche │ Statut │ Commit │ Message │ Quand │
30
+ ├──────────────────────┼─────────┼─────────┼─────────────────────┼────────┼────────────────────┼─────────────┤
31
+ │ mon-api │ 2.1.0 │ main │ en retard (↓3) │ a4f2c1 │ fix: timeout retry │ 2 days ago │
32
+ │ mon-dashboard │ 1.0.0 │ main │ divergé (↓1 ↑2) │ b8e3d9 │ feat: dark mode │ 5 hours ago │
33
+ │ projscan │ 0.1.0 │ main │ à jour │ c1a2b3 │ initial commit │ 1 hour ago │
34
+ ╰──────────────────────┴─────────┴─────────┴─────────────────────┴────────┴────────────────────┴─────────────╯
35
+ 3 repos, 1 à jour, 2 à mettre à jour
36
+ ```
37
+
38
+ **Options :**
39
+
40
+ | Option | Description |
41
+ |--------|-------------|
42
+ | `--no-fetch` | Ne pas faire `git fetch` — utilise le cache local (rapide) |
43
+ | `--depth INTEGER` | Profondeur max de récursion (défaut : 3) |
44
+ | `--json` | Sortie JSON machine-readable |
45
+ | `--concurrency INTEGER` | Scans parallèles (défaut : 16) |
46
+
47
+ ```bash
48
+ # Scanne ~/code et ~/work
49
+ projscan repos ~/code ~/work
50
+
51
+ # Rapide, sans réseau
52
+ projscan repos --no-fetch ~/code
53
+
54
+ # Pipe JSON vers jq
55
+ projscan repos --no-fetch --json ~/code | jq '.[] | select(.status != "à jour")'
56
+ ```
57
+
58
+ ### `projscan tools`
59
+
60
+ Scanne les tools installés via `uv tool install git+https://...` et vérifie si des mises à jour sont disponibles (via `git ls-remote`, sans cloner).
61
+
62
+ ```
63
+ ╭────────────────┬─────────┬───────────────────────────────┬────────┬──────────┬───────────╮
64
+ │ Nom │ Version │ Source │ Ref │ Commit │ Statut │
65
+ ├────────────────┼─────────┼───────────────────────────────┼────────┼──────────┼───────────┤
66
+ │ mon-tool │ 1.2.0 │ github.com/org/mon-tool.git │ main │ f10c2123 │ MAJ dispo │
67
+ │ autre-tool │ 0.9.1 │ github.com/org/autre-tool.git │ main │ fc9e74c5 │ à jour │
68
+ ╰────────────────┴─────────┴───────────────────────────────┴────────┴──────────┴───────────╯
69
+ 2 tools, 1 à jour, 1 à mettre à jour
70
+ ```
71
+
72
+ **Options :**
73
+
74
+ | Option | Description |
75
+ |--------|-------------|
76
+ | `--tool-dir PATH` | Racine des tools uv (détection auto par défaut) |
77
+ | `--no-remote` | Liste locale sans vérifier les remotes |
78
+ | `--json` | Sortie JSON machine-readable |
79
+
80
+ ```bash
81
+ projscan tools
82
+ projscan tools --no-remote --json
83
+ projscan tools --tool-dir ~/.local/share/uv/tools
84
+ ```
85
+
86
+ ## Codes de sortie
87
+
88
+ | Code | Signification |
89
+ |------|---------------|
90
+ | `0` | Tout est à jour |
91
+ | `1` | Au moins un item à mettre à jour (ou en erreur) |
92
+ | `2` | Erreur d'exécution fatale |
93
+ | `130` | Interruption clavier (Ctrl-C) |
94
+
95
+ Utile en script ou en CI :
96
+
97
+ ```bash
98
+ projscan repos --no-fetch ~/code || echo "Des repos sont en retard !"
99
+ ```
100
+
101
+ ## Couleurs de statut
102
+
103
+ | Statut | Couleur | Signification |
104
+ |--------|---------|---------------|
105
+ | `à jour` | vert | Synchronisé avec l'upstream |
106
+ | `en retard (↓N)` | rouge | N commits distants non récupérés |
107
+ | `en avance (↑N)` | cyan | N commits locaux non poussés |
108
+ | `divergé (↓N ↑M)` | magenta | Branches divergées |
109
+ | `MAJ dispo` | jaune | Nouvelle version du tool disponible |
110
+ | `pas d'upstream` | gris | Pas de tracking branch configurée |
111
+ | `pas git` | gris | Tool installé depuis PyPI, pas git |
112
+ | `inconnu` | gris | Remote inaccessible (offline, auth...) |
113
+ | `erreur` | rouge gras | Erreur lors du scan |
114
+
115
+ ## Usage comme bibliothèque
116
+
117
+ `projscan` est conçu pour être importé dans une app Textual ou tout autre code async.
118
+
119
+ ```python
120
+ from pathlib import Path
121
+ from projscan import scan_paths, scan_tools, RepoInfo, ToolInfo
122
+
123
+ # Scanne des dépôts git
124
+ repos: list[RepoInfo] = await scan_paths(
125
+ [Path("~/code")],
126
+ fetch=True,
127
+ max_depth=3,
128
+ concurrency=16,
129
+ )
130
+
131
+ for repo in repos:
132
+ print(f"{repo.name}: {repo.status}")
133
+
134
+ # Scanne les tools uv
135
+ tools: list[ToolInfo] = await scan_tools(check_remote=True)
136
+
137
+ for tool in tools:
138
+ if tool.status == "MAJ dispo":
139
+ print(f"Update available: {tool.name}")
140
+ ```
141
+
142
+ Les dataclasses exposent une méthode `.as_dict()` pour la sérialisation JSON :
143
+
144
+ ```python
145
+ import json
146
+ print(json.dumps([r.as_dict() for r in repos], ensure_ascii=False))
147
+ ```
148
+
149
+ L'API publique complète :
150
+
151
+ ```python
152
+ from projscan import (
153
+ # Git
154
+ RepoInfo, scan_paths, scan_repo, find_repos,
155
+ # UV tools
156
+ ToolInfo, scan_tools, scan_tool, uv_tool_dir,
157
+ )
158
+ ```
159
+
160
+ ## Développement
161
+
162
+ ```bash
163
+ uv sync
164
+ uv run pytest # 52 tests, ~5s
165
+ uv run projscan repos . # test en réel
166
+ uv run projscan tools # test en réel
167
+ ```
@@ -0,0 +1,30 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "projscan"
7
+ version = "0.1.0"
8
+ description = "Scanner de dépôts git locaux et de tools uv installés."
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ dependencies = [
12
+ "rich>=13",
13
+ "click>=8",
14
+ ]
15
+
16
+ [project.scripts]
17
+ projscan = "projscan.cli:main"
18
+
19
+ [tool.hatch.build.targets.wheel]
20
+ packages = ["src/projscan"]
21
+
22
+ [dependency-groups]
23
+ dev = [
24
+ "pytest>=8",
25
+ "pytest-asyncio>=0.23",
26
+ ]
27
+
28
+ [tool.pytest.ini_options]
29
+ asyncio_mode = "auto"
30
+ testpaths = ["tests"]
@@ -0,0 +1,26 @@
1
+ """projscan — scanner de dépôts git locaux et de tools uv installés.
2
+
3
+ API publique importable depuis une app Textual ou tout autre code async :
4
+
5
+ from projscan import scan_paths, scan_tools, RepoInfo, ToolInfo
6
+
7
+ repos = await scan_paths([Path("~/code")], fetch=True)
8
+ tools = await scan_tools(check_remote=True)
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from projscan.git import RepoInfo, scan_paths, scan_repo, find_repos
14
+ from projscan.uv import ToolInfo, scan_tools, scan_tool, uv_tool_dir
15
+
16
+ __all__ = [
17
+ "RepoInfo",
18
+ "scan_paths",
19
+ "scan_repo",
20
+ "find_repos",
21
+ "ToolInfo",
22
+ "scan_tools",
23
+ "scan_tool",
24
+ "uv_tool_dir",
25
+ ]
26
+ __version__ = "0.1.0"