pygitweb 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.
- pygitweb-0.1.0/LICENSE +21 -0
- pygitweb-0.1.0/PKG-INFO +165 -0
- pygitweb-0.1.0/README.md +145 -0
- pygitweb-0.1.0/__init__.py +22 -0
- pygitweb-0.1.0/__meta__.py +9 -0
- pygitweb-0.1.0/actions.py +1303 -0
- pygitweb-0.1.0/addproject_permissions_test.py +114 -0
- pygitweb-0.1.0/api/__init__.py +0 -0
- pygitweb-0.1.0/api/action.py +20 -0
- pygitweb-0.1.0/api/plugins/__init__.py +0 -0
- pygitweb-0.1.0/api/plugins/project_zip.py +71 -0
- pygitweb-0.1.0/api/project.py +67 -0
- pygitweb-0.1.0/api/subpage.py +22 -0
- pygitweb-0.1.0/auth.py +387 -0
- pygitweb-0.1.0/auth.schema.json +106 -0
- pygitweb-0.1.0/auth_config.py +243 -0
- pygitweb-0.1.0/auth_oauth.py +343 -0
- pygitweb-0.1.0/auth_test.py +390 -0
- pygitweb-0.1.0/boards_permissions_test.py +113 -0
- pygitweb-0.1.0/change_queue.py +110 -0
- pygitweb-0.1.0/change_queue_routes_test.py +146 -0
- pygitweb-0.1.0/change_queue_test.py +91 -0
- pygitweb-0.1.0/comments_permissions_test.py +140 -0
- pygitweb-0.1.0/config.py +185 -0
- pygitweb-0.1.0/conftest.py +43 -0
- pygitweb-0.1.0/dependencies.py +87 -0
- pygitweb-0.1.0/formatting.py +182 -0
- pygitweb-0.1.0/formatting_test.py +68 -0
- pygitweb-0.1.0/git_helpers.py +454 -0
- pygitweb-0.1.0/git_helpers_test.py +223 -0
- pygitweb-0.1.0/gravatar.py +16 -0
- pygitweb-0.1.0/gravatar_test.py +10 -0
- pygitweb-0.1.0/hooks_install.py +278 -0
- pygitweb-0.1.0/hooks_install_test.py +363 -0
- pygitweb-0.1.0/hooks_permissions_test.py +116 -0
- pygitweb-0.1.0/main.py +887 -0
- pygitweb-0.1.0/merge_requests.py +230 -0
- pygitweb-0.1.0/merge_requests_test.py +85 -0
- pygitweb-0.1.0/mr_permissions_test.py +148 -0
- pygitweb-0.1.0/password_hash.py +74 -0
- pygitweb-0.1.0/password_hash_test.py +20 -0
- pygitweb-0.1.0/permissions.py +209 -0
- pygitweb-0.1.0/permissions_test.py +86 -0
- pygitweb-0.1.0/plugin_loader.py +83 -0
- pygitweb-0.1.0/projects.py +213 -0
- pygitweb-0.1.0/projects_test.py +186 -0
- pygitweb-0.1.0/pygitweb.egg-info/PKG-INFO +165 -0
- pygitweb-0.1.0/pygitweb.egg-info/SOURCES.txt +188 -0
- pygitweb-0.1.0/pygitweb.egg-info/dependency_links.txt +1 -0
- pygitweb-0.1.0/pygitweb.egg-info/requires.txt +10 -0
- pygitweb-0.1.0/pygitweb.egg-info/top_level.txt +1 -0
- pygitweb-0.1.0/pyproject.toml +36 -0
- pygitweb-0.1.0/sessions.py +73 -0
- pygitweb-0.1.0/settings.py +412 -0
- pygitweb-0.1.0/settings.schema.json +73 -0
- pygitweb-0.1.0/settings_config.py +122 -0
- pygitweb-0.1.0/settings_config_test.py +101 -0
- pygitweb-0.1.0/settings_permissions_test.py +76 -0
- pygitweb-0.1.0/setup.cfg +4 -0
- pygitweb-0.1.0/static/blob-view.js +133 -0
- pygitweb-0.1.0/static/board.js +839 -0
- pygitweb-0.1.0/static/hook-install.js +53 -0
- pygitweb-0.1.0/static/icons/cloud-down.svg +1 -0
- pygitweb-0.1.0/static/icons/cloud-exclamation.svg +1 -0
- pygitweb-0.1.0/static/icons/cloud-filled.svg +1 -0
- pygitweb-0.1.0/static/icons/cloud-storm.svg +1 -0
- pygitweb-0.1.0/static/icons/cloud-up.svg +1 -0
- pygitweb-0.1.0/static/icons/cloud.svg +1 -0
- pygitweb-0.1.0/static/logo.svg +44 -0
- pygitweb-0.1.0/static/main.css +1210 -0
- pygitweb-0.1.0/static/navbar-auth.js +82 -0
- pygitweb-0.1.0/static/readme-render.js +174 -0
- pygitweb-0.1.0/static/search-page.js +139 -0
- pygitweb-0.1.0/static/summary-ref-switcher.js +130 -0
- pygitweb-0.1.0/static/themes/solarized.css +34 -0
- pygitweb-0.1.0/tasks.py +595 -0
- pygitweb-0.1.0/tasks_permissions_test.py +137 -0
- pygitweb-0.1.0/tasks_routes_test.py +430 -0
- pygitweb-0.1.0/templates/addproject.html +174 -0
- pygitweb-0.1.0/templates/blobdiff.html +57 -0
- pygitweb-0.1.0/templates/board.html +31 -0
- pygitweb-0.1.0/templates/board_card.html +44 -0
- pygitweb-0.1.0/templates/commitdiff.html +57 -0
- pygitweb-0.1.0/templates/hooks_summary_cell.html +37 -0
- pygitweb-0.1.0/templates/login.html +37 -0
- pygitweb-0.1.0/templates/merge_request_summary_cell.html +18 -0
- pygitweb-0.1.0/templates/merge_request_tag.html +90 -0
- pygitweb-0.1.0/templates/pagination.html +44 -0
- pygitweb-0.1.0/templates/preamble.html +89 -0
- pygitweb-0.1.0/templates/project_settings.html +13 -0
- pygitweb-0.1.0/templates/pygit2_settings.html +12 -0
- pygitweb-0.1.0/templates/pygitweb_settings.html +12 -0
- pygitweb-0.1.0/templates/search.html +77 -0
- pygitweb-0.1.0/templates/settings_form_field.html +27 -0
- pygitweb-0.1.0/templates/table.html +15 -0
- pygitweb-0.1.0/templates/user_profile.html +10 -0
- pygitweb-0.1.0/templates_env.py +21 -0
- pygitweb-0.1.0/timeline_cache.py +243 -0
- pygitweb-0.1.0/validation.py +75 -0
- pygitweb-0.1.0/validation_test.py +167 -0
pygitweb-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Will Bowers
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
pygitweb-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pygitweb
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Gitweb reimplementation with FastAPI and Pygit2
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Requires-Python: >=3.11
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Dist: pygittools>=0.1.0
|
|
10
|
+
Requires-Dist: fastapi[standard-no-fastapi-cloud-cli]>=0.104.0
|
|
11
|
+
Requires-Dist: uvicorn[standard]>=0.24.0
|
|
12
|
+
Requires-Dist: python-multipart>=0.0.6
|
|
13
|
+
Requires-Dist: jinja2>=3.1.0
|
|
14
|
+
Requires-Dist: orjson>=0.19.0
|
|
15
|
+
Requires-Dist: pygit2>=1.12.0
|
|
16
|
+
Requires-Dist: pydantic-settings>=2.13.0
|
|
17
|
+
Requires-Dist: httpx>=0.27.0
|
|
18
|
+
Requires-Dist: python-ripgrep==0.0.9
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
# Pygitweb
|
|
22
|
+
|
|
23
|
+
Gitweb reimplementation using **Python**, **FastAPI**, and **Pygit2**, ported from `git/gitweb/gitweb.perl`.
|
|
24
|
+
|
|
25
|
+
## Setup
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
uv sync --package pygitweb
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Or install everything in the workspace (all members + dev tools):
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
uv sync --all-packages --group dev
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Run
|
|
38
|
+
|
|
39
|
+
From the repository root (after `uv sync`), with the workspace `.venv` activated:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
uvicorn pygitweb.main:app --reload --host 0.0.0.0 --port 8000
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Or:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
python -m pygitweb.main
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Config
|
|
52
|
+
|
|
53
|
+
Settings load from `~/.pygitweb/settings.json` (or `PYGITWEB_SETTINGS_CONFIG`), `PYGITWEB_*`
|
|
54
|
+
environment variables, and optionally a `.env` file in the working directory via
|
|
55
|
+
`pydantic-settings`. See `pygitweb/config.py` and `pygitweb/settings.schema.json` for the full schema. Common ones:
|
|
56
|
+
|
|
57
|
+
- `PYGITWEB_PROJECTROOT` — absolute path to directory containing git repositories (default: `$HOME`)
|
|
58
|
+
- `PYGITWEB_PROJECTS_LIST` — directory to scan, or path to a project-list file (default: `PROJECTROOT`)
|
|
59
|
+
- `PYGITWEB_EXPORT_OK` — filename that must exist to allow export (e.g. `git-daemon-export-ok`); empty = no check
|
|
60
|
+
- `PYGITWEB_SITE_NAME` — site name in titles (default: `PyGitWeb`)
|
|
61
|
+
- `PYGITWEB_GIT` — path to the git executable (default: `git`)
|
|
62
|
+
- `PYGITWEB_AUTH` — `0` / `false` / `off` disables auth; `1` / `true` / `on` enables it. If **unset or empty**, auth defaults to **on**.
|
|
63
|
+
- `PYGITWEB_AUTH_CONFIG` — path to the auth JSON file (default: `~/.pygitweb/auth.json`). Generated to a reasonable default if not present.
|
|
64
|
+
- `PYGITWEB_SETTINGS_CONFIG` — path to the settings JSON file (default: `~/.pygitweb/settings.json`). Created from the environment on first run if missing.
|
|
65
|
+
- Browser login: `GET /login` (optional `?next=/path`), `POST /login` with form fields `username`, `password`, `next`; sets an HttpOnly cookie with an opaque in-process session id (also accepted by protected routes via `Authorization: Bearer`). Sessions are wiped when the process exits. `GET /logout?next=/` revokes the session and clears the cookie.
|
|
66
|
+
|
|
67
|
+
## Routes
|
|
68
|
+
|
|
69
|
+
Load the `/docs` page for a detailed view of routes (below the readme) if in debug mode.
|
|
70
|
+
|
|
71
|
+
**No-project routes**
|
|
72
|
+
|
|
73
|
+
- `GET /`: project list
|
|
74
|
+
- `GET /index`: plain text project index (path, owner)
|
|
75
|
+
- `GET /opml`: OPML feed list
|
|
76
|
+
- `GET /project/{name}`: Project dispatch (See actions table)
|
|
77
|
+
|
|
78
|
+
**Hook management routes**
|
|
79
|
+
|
|
80
|
+
- `GET /project/{name}/hooks`: JSON `{hooks: [...samples...], bundles: [...]}` with current status (`installed`, `not_installed`, `different`) for every pygittools sample and bundle
|
|
81
|
+
- `POST /project/{name}/hook?name=<sample-or-bundle>&op=<add|remove|check>`: install, remove, or check one sample or bundle. `name` may be a sample filename (e.g. `post-receive.notify`) or a bundle name (e.g. `update`, which expands to `post-commit.notify` + `post-receive.notify`). `add`/`remove` refuse to clobber a custom hook with different content (returns `409`)
|
|
82
|
+
|
|
83
|
+
**Actions**
|
|
84
|
+
|
|
85
|
+
| Action | Query parameters | URL | Description |
|
|
86
|
+
|--------|------------------|-----|-------------|
|
|
87
|
+
| summary | *(default)* or `a` | `GET /project/{project}` | Project summary (description, owner, HEAD, tree link). |
|
|
88
|
+
| tree | `h`, `f` | `GET /project/{project}?a=tree&h=...&f=...` | Directory listing (tree). |
|
|
89
|
+
| blob | `h`, `f` | `GET /project/{project}?a=blob&h=...&f=...` | File view (HTML). |
|
|
90
|
+
| blob_plain | `h`, `f` | `GET /project/{project}?a=blob_plain&h=...&f=...` | Raw file download. |
|
|
91
|
+
| log | `h`, `updates`, `pf` | `GET /project/{project}?a=log&h=...` | Commit log. Supports long-poll subscribe. |
|
|
92
|
+
| shortlog | `h`, `updates`, `pf` | `GET /project/{project}?a=shortlog&h=...` | Shortlog. Supports long-poll subscribe. |
|
|
93
|
+
| history | `h`, `f`, `updates`, `pf` | `GET /project/{project}?a=history&h=...&f=...` | History of a file or path. Supports long-poll subscribe. |
|
|
94
|
+
| heads | `updates`, `pf` | `GET /project/{project}?a=heads` | List branch heads. Supports long-poll subscribe. |
|
|
95
|
+
| tags | `updates`, `pf` | `GET /project/{project}?a=tags` | List all tags. Supports long-poll subscribe. |
|
|
96
|
+
| tag | `h` | `GET /project/{project}?a=tag&h=...` | Single tag view (tag ref or hash). |
|
|
97
|
+
| commit | — | `GET /project/{project}?a=commit` | Commit information. |
|
|
98
|
+
| commitdiff | — | `GET /project/{project}?a=commitdiff` | Commit diff (unified diff rendered with [diff2html](https://github.com/rtfpessoa/diff2html)). |
|
|
99
|
+
| patch | `h` | `GET /project/{project}?a=patch&h=...` | Single-commit patch (plain text). |
|
|
100
|
+
| patches | `h`, `hb` | `GET /project/{project}?a=patches&h=...&hb=...` | Multi-commit patches for range `hb..h` (plain text). |
|
|
101
|
+
| blobdiff | `h`, `hb`, `f`, `fp` | `GET /project/{project}?a=blobdiff&h=...&hb=...&f=...&fp=...` | Blob diff (two versions of a file) rendered with diff2html. |
|
|
102
|
+
| blobpatch | `h`, `hb`, `f`, `fp` | `GET /project/{project}?a=blobpatch&h=...&hb=...&f=...&fp=...` | Blob diff as plain unified diff. |
|
|
103
|
+
| remotes | — | `GET /project/{project}?a=remotes` | List repo remotes. |
|
|
104
|
+
| object | `h` | `GET /project/{project}?a=object&h=...` | Show object by type (commit, tree, tag, or blob). |
|
|
105
|
+
| blame_raw | `h`, `f` | `GET /project/{project}?a=blame_raw&h=...&f=...` | File blame as JSON: array of `{commit_id, commit_msg, commit_author, time, line_range}`. Contiguous lines from the same commit are merged. |
|
|
106
|
+
| blame | `h`, `f` | `GET /project/{project}?a=blame&h=...&f=...` | File blame HTML view (syntax-highlighted source, line numbers, commit links per line range; hover shows author and date). |
|
|
107
|
+
| blame_incremental | — | | TODO |
|
|
108
|
+
| blame_data | — | | TODO |
|
|
109
|
+
| rss | — | | TODO |
|
|
110
|
+
| atom | — | | TODO |
|
|
111
|
+
| search | `patterns`, `paths`, `globs`, `heading`, `sort`, `max_count`, `multiline` | `GET /project/{project}?a=search&patterns=...` | Ripgrep search (via `python-ripgrep`); returns JSON. `paths` are relative to the project tree and cannot escape it. |
|
|
112
|
+
| search (page) | — | `GET /project/{project}/search` | Per-project search UI page. |
|
|
113
|
+
| search_help | — | | TODO |
|
|
114
|
+
|
|
115
|
+
## Query parameter short names (CGI mapping)
|
|
116
|
+
|
|
117
|
+
- `p` → project
|
|
118
|
+
- `a` → action
|
|
119
|
+
- `f` → file_name
|
|
120
|
+
- `fp` → file_parent
|
|
121
|
+
- `h` → hash
|
|
122
|
+
- `hp` → hash_parent
|
|
123
|
+
- `hb` → hash_base
|
|
124
|
+
- `hpb` → hash_parent_base
|
|
125
|
+
- `pg` → page
|
|
126
|
+
- `o` → order
|
|
127
|
+
- `s` → searchtext
|
|
128
|
+
- `st` → searchtype
|
|
129
|
+
- `sf` → snapshot_format
|
|
130
|
+
- `opt` → extra_options
|
|
131
|
+
- `sr` → search_use_regexp
|
|
132
|
+
- `by_tag` → ctag
|
|
133
|
+
- `ds` → diff_style
|
|
134
|
+
- `pf` → project_filter
|
|
135
|
+
- `updates` → long-poll subscription flag (`true` parks the request up to 30s; returns `200 OK` with an empty body on timeout/shutdown, or the action data when the queue is notified)
|
|
136
|
+
|
|
137
|
+
## Live updates (long polling)
|
|
138
|
+
|
|
139
|
+
Supported actions (`history`, `log`, `shortlog`, `heads`, `tags`) accept `updates=true` to
|
|
140
|
+
subscribe to the project's change queue. The request is held for up to 30 seconds:
|
|
141
|
+
|
|
142
|
+
- A `POST /_internal/notify?project=<name>` (typically from a server-side git hook) wakes
|
|
143
|
+
matching subscribers, who then receive the freshly-rendered action response.
|
|
144
|
+
- If no notification arrives in 30 seconds (or the server is shutting down), the response is `200 OK` with an empty body.
|
|
145
|
+
- Combine with `pf=<prefix>` to subscribe to every project under a path prefix instead of
|
|
146
|
+
just the URL project (the action is still rendered for the URL project).
|
|
147
|
+
|
|
148
|
+
The notify endpoint is intended for loopback use by `pygittools` post-receive hooks (see
|
|
149
|
+
`pygittools/hook_samples/post-receive.notify`); production deployments should restrict it
|
|
150
|
+
at the reverse proxy.
|
|
151
|
+
|
|
152
|
+
## Hook management
|
|
153
|
+
|
|
154
|
+
The project summary page exposes an **Update Hook** row that installs / removes the
|
|
155
|
+
`update` bundle: `post-receive.notify` (feeds the change queue when refs are pushed) and
|
|
156
|
+
`post-commit.notify` (feeds the change queue when a working clone of this repo records
|
|
157
|
+
a local commit). Either is sufficient to wake long-poll subscribers; installing both
|
|
158
|
+
covers server-side and client-side commit paths.
|
|
159
|
+
|
|
160
|
+
The same operations are available programmatically via
|
|
161
|
+
`POST /project/{name}/hook?name=<sample-or-bundle>&op=<...>`. Bundle status is
|
|
162
|
+
`INSTALLED` only when every member is installed, `DIFFERENT` if any member's path holds
|
|
163
|
+
a custom hook (the bundle then refuses to install or remove anything to preserve the
|
|
164
|
+
custom hook), otherwise `NOT_INSTALLED`. When `PYGITWEB_AUTH` is enabled, `add` and `remove`
|
|
165
|
+
require a valid access token (`Authorization: Bearer …` from `POST /token`, or the cookie from `POST /login`); `check` is always allowed.
|
pygitweb-0.1.0/README.md
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# Pygitweb
|
|
2
|
+
|
|
3
|
+
Gitweb reimplementation using **Python**, **FastAPI**, and **Pygit2**, ported from `git/gitweb/gitweb.perl`.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
uv sync --package pygitweb
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or install everything in the workspace (all members + dev tools):
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
uv sync --all-packages --group dev
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Run
|
|
18
|
+
|
|
19
|
+
From the repository root (after `uv sync`), with the workspace `.venv` activated:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
uvicorn pygitweb.main:app --reload --host 0.0.0.0 --port 8000
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Or:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
python -m pygitweb.main
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Config
|
|
32
|
+
|
|
33
|
+
Settings load from `~/.pygitweb/settings.json` (or `PYGITWEB_SETTINGS_CONFIG`), `PYGITWEB_*`
|
|
34
|
+
environment variables, and optionally a `.env` file in the working directory via
|
|
35
|
+
`pydantic-settings`. See `pygitweb/config.py` and `pygitweb/settings.schema.json` for the full schema. Common ones:
|
|
36
|
+
|
|
37
|
+
- `PYGITWEB_PROJECTROOT` — absolute path to directory containing git repositories (default: `$HOME`)
|
|
38
|
+
- `PYGITWEB_PROJECTS_LIST` — directory to scan, or path to a project-list file (default: `PROJECTROOT`)
|
|
39
|
+
- `PYGITWEB_EXPORT_OK` — filename that must exist to allow export (e.g. `git-daemon-export-ok`); empty = no check
|
|
40
|
+
- `PYGITWEB_SITE_NAME` — site name in titles (default: `PyGitWeb`)
|
|
41
|
+
- `PYGITWEB_GIT` — path to the git executable (default: `git`)
|
|
42
|
+
- `PYGITWEB_AUTH` — `0` / `false` / `off` disables auth; `1` / `true` / `on` enables it. If **unset or empty**, auth defaults to **on**.
|
|
43
|
+
- `PYGITWEB_AUTH_CONFIG` — path to the auth JSON file (default: `~/.pygitweb/auth.json`). Generated to a reasonable default if not present.
|
|
44
|
+
- `PYGITWEB_SETTINGS_CONFIG` — path to the settings JSON file (default: `~/.pygitweb/settings.json`). Created from the environment on first run if missing.
|
|
45
|
+
- Browser login: `GET /login` (optional `?next=/path`), `POST /login` with form fields `username`, `password`, `next`; sets an HttpOnly cookie with an opaque in-process session id (also accepted by protected routes via `Authorization: Bearer`). Sessions are wiped when the process exits. `GET /logout?next=/` revokes the session and clears the cookie.
|
|
46
|
+
|
|
47
|
+
## Routes
|
|
48
|
+
|
|
49
|
+
Load the `/docs` page for a detailed view of routes (below the readme) if in debug mode.
|
|
50
|
+
|
|
51
|
+
**No-project routes**
|
|
52
|
+
|
|
53
|
+
- `GET /`: project list
|
|
54
|
+
- `GET /index`: plain text project index (path, owner)
|
|
55
|
+
- `GET /opml`: OPML feed list
|
|
56
|
+
- `GET /project/{name}`: Project dispatch (See actions table)
|
|
57
|
+
|
|
58
|
+
**Hook management routes**
|
|
59
|
+
|
|
60
|
+
- `GET /project/{name}/hooks`: JSON `{hooks: [...samples...], bundles: [...]}` with current status (`installed`, `not_installed`, `different`) for every pygittools sample and bundle
|
|
61
|
+
- `POST /project/{name}/hook?name=<sample-or-bundle>&op=<add|remove|check>`: install, remove, or check one sample or bundle. `name` may be a sample filename (e.g. `post-receive.notify`) or a bundle name (e.g. `update`, which expands to `post-commit.notify` + `post-receive.notify`). `add`/`remove` refuse to clobber a custom hook with different content (returns `409`)
|
|
62
|
+
|
|
63
|
+
**Actions**
|
|
64
|
+
|
|
65
|
+
| Action | Query parameters | URL | Description |
|
|
66
|
+
|--------|------------------|-----|-------------|
|
|
67
|
+
| summary | *(default)* or `a` | `GET /project/{project}` | Project summary (description, owner, HEAD, tree link). |
|
|
68
|
+
| tree | `h`, `f` | `GET /project/{project}?a=tree&h=...&f=...` | Directory listing (tree). |
|
|
69
|
+
| blob | `h`, `f` | `GET /project/{project}?a=blob&h=...&f=...` | File view (HTML). |
|
|
70
|
+
| blob_plain | `h`, `f` | `GET /project/{project}?a=blob_plain&h=...&f=...` | Raw file download. |
|
|
71
|
+
| log | `h`, `updates`, `pf` | `GET /project/{project}?a=log&h=...` | Commit log. Supports long-poll subscribe. |
|
|
72
|
+
| shortlog | `h`, `updates`, `pf` | `GET /project/{project}?a=shortlog&h=...` | Shortlog. Supports long-poll subscribe. |
|
|
73
|
+
| history | `h`, `f`, `updates`, `pf` | `GET /project/{project}?a=history&h=...&f=...` | History of a file or path. Supports long-poll subscribe. |
|
|
74
|
+
| heads | `updates`, `pf` | `GET /project/{project}?a=heads` | List branch heads. Supports long-poll subscribe. |
|
|
75
|
+
| tags | `updates`, `pf` | `GET /project/{project}?a=tags` | List all tags. Supports long-poll subscribe. |
|
|
76
|
+
| tag | `h` | `GET /project/{project}?a=tag&h=...` | Single tag view (tag ref or hash). |
|
|
77
|
+
| commit | — | `GET /project/{project}?a=commit` | Commit information. |
|
|
78
|
+
| commitdiff | — | `GET /project/{project}?a=commitdiff` | Commit diff (unified diff rendered with [diff2html](https://github.com/rtfpessoa/diff2html)). |
|
|
79
|
+
| patch | `h` | `GET /project/{project}?a=patch&h=...` | Single-commit patch (plain text). |
|
|
80
|
+
| patches | `h`, `hb` | `GET /project/{project}?a=patches&h=...&hb=...` | Multi-commit patches for range `hb..h` (plain text). |
|
|
81
|
+
| blobdiff | `h`, `hb`, `f`, `fp` | `GET /project/{project}?a=blobdiff&h=...&hb=...&f=...&fp=...` | Blob diff (two versions of a file) rendered with diff2html. |
|
|
82
|
+
| blobpatch | `h`, `hb`, `f`, `fp` | `GET /project/{project}?a=blobpatch&h=...&hb=...&f=...&fp=...` | Blob diff as plain unified diff. |
|
|
83
|
+
| remotes | — | `GET /project/{project}?a=remotes` | List repo remotes. |
|
|
84
|
+
| object | `h` | `GET /project/{project}?a=object&h=...` | Show object by type (commit, tree, tag, or blob). |
|
|
85
|
+
| blame_raw | `h`, `f` | `GET /project/{project}?a=blame_raw&h=...&f=...` | File blame as JSON: array of `{commit_id, commit_msg, commit_author, time, line_range}`. Contiguous lines from the same commit are merged. |
|
|
86
|
+
| blame | `h`, `f` | `GET /project/{project}?a=blame&h=...&f=...` | File blame HTML view (syntax-highlighted source, line numbers, commit links per line range; hover shows author and date). |
|
|
87
|
+
| blame_incremental | — | | TODO |
|
|
88
|
+
| blame_data | — | | TODO |
|
|
89
|
+
| rss | — | | TODO |
|
|
90
|
+
| atom | — | | TODO |
|
|
91
|
+
| search | `patterns`, `paths`, `globs`, `heading`, `sort`, `max_count`, `multiline` | `GET /project/{project}?a=search&patterns=...` | Ripgrep search (via `python-ripgrep`); returns JSON. `paths` are relative to the project tree and cannot escape it. |
|
|
92
|
+
| search (page) | — | `GET /project/{project}/search` | Per-project search UI page. |
|
|
93
|
+
| search_help | — | | TODO |
|
|
94
|
+
|
|
95
|
+
## Query parameter short names (CGI mapping)
|
|
96
|
+
|
|
97
|
+
- `p` → project
|
|
98
|
+
- `a` → action
|
|
99
|
+
- `f` → file_name
|
|
100
|
+
- `fp` → file_parent
|
|
101
|
+
- `h` → hash
|
|
102
|
+
- `hp` → hash_parent
|
|
103
|
+
- `hb` → hash_base
|
|
104
|
+
- `hpb` → hash_parent_base
|
|
105
|
+
- `pg` → page
|
|
106
|
+
- `o` → order
|
|
107
|
+
- `s` → searchtext
|
|
108
|
+
- `st` → searchtype
|
|
109
|
+
- `sf` → snapshot_format
|
|
110
|
+
- `opt` → extra_options
|
|
111
|
+
- `sr` → search_use_regexp
|
|
112
|
+
- `by_tag` → ctag
|
|
113
|
+
- `ds` → diff_style
|
|
114
|
+
- `pf` → project_filter
|
|
115
|
+
- `updates` → long-poll subscription flag (`true` parks the request up to 30s; returns `200 OK` with an empty body on timeout/shutdown, or the action data when the queue is notified)
|
|
116
|
+
|
|
117
|
+
## Live updates (long polling)
|
|
118
|
+
|
|
119
|
+
Supported actions (`history`, `log`, `shortlog`, `heads`, `tags`) accept `updates=true` to
|
|
120
|
+
subscribe to the project's change queue. The request is held for up to 30 seconds:
|
|
121
|
+
|
|
122
|
+
- A `POST /_internal/notify?project=<name>` (typically from a server-side git hook) wakes
|
|
123
|
+
matching subscribers, who then receive the freshly-rendered action response.
|
|
124
|
+
- If no notification arrives in 30 seconds (or the server is shutting down), the response is `200 OK` with an empty body.
|
|
125
|
+
- Combine with `pf=<prefix>` to subscribe to every project under a path prefix instead of
|
|
126
|
+
just the URL project (the action is still rendered for the URL project).
|
|
127
|
+
|
|
128
|
+
The notify endpoint is intended for loopback use by `pygittools` post-receive hooks (see
|
|
129
|
+
`pygittools/hook_samples/post-receive.notify`); production deployments should restrict it
|
|
130
|
+
at the reverse proxy.
|
|
131
|
+
|
|
132
|
+
## Hook management
|
|
133
|
+
|
|
134
|
+
The project summary page exposes an **Update Hook** row that installs / removes the
|
|
135
|
+
`update` bundle: `post-receive.notify` (feeds the change queue when refs are pushed) and
|
|
136
|
+
`post-commit.notify` (feeds the change queue when a working clone of this repo records
|
|
137
|
+
a local commit). Either is sufficient to wake long-poll subscribers; installing both
|
|
138
|
+
covers server-side and client-side commit paths.
|
|
139
|
+
|
|
140
|
+
The same operations are available programmatically via
|
|
141
|
+
`POST /project/{name}/hook?name=<sample-or-bundle>&op=<...>`. Bundle status is
|
|
142
|
+
`INSTALLED` only when every member is installed, `DIFFERENT` if any member's path holds
|
|
143
|
+
a custom hook (the bundle then refuses to install or remove anything to preserve the
|
|
144
|
+
custom hook), otherwise `NOT_INSTALLED`. When `PYGITWEB_AUTH` is enabled, `add` and `remove`
|
|
145
|
+
require a valid access token (`Authorization: Bearer …` from `POST /token`, or the cookie from `POST /login`); `check` is always allowed.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
if "pytest" in sys.modules:
|
|
6
|
+
_test_auth_config = Path(os.environ.get("TEMP", "/tmp")) / "pygitweb-test-auth.json"
|
|
7
|
+
_test_settings_config = Path(os.environ.get("TEMP", "/tmp")) / "pygitweb-test-settings.json"
|
|
8
|
+
os.environ.setdefault("PYGITWEB_AUTH", "0")
|
|
9
|
+
os.environ.setdefault("PYGITWEB_AUTH_CONFIG", str(_test_auth_config))
|
|
10
|
+
os.environ.setdefault("PYGITWEB_SETTINGS_CONFIG", str(_test_settings_config))
|
|
11
|
+
|
|
12
|
+
import pygitweb.__meta__
|
|
13
|
+
from pygitweb.main import app as app
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
Git Repo Browser built with FastAPI + Pygit2
|
|
17
|
+
"""
|
|
18
|
+
__version__ = pygitweb.__meta__.__version__
|
|
19
|
+
__author__ = pygitweb.__meta__.__author__
|
|
20
|
+
__description__ = pygitweb.__meta__.__description__
|
|
21
|
+
__url__ = pygitweb.__meta__.__url__
|
|
22
|
+
__license__ = pygitweb.__meta__.__license__
|