gitwebhooks 2.1.3__tar.gz → 2.1.4__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.
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/CLAUDE.md +12 -12
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/PKG-INFO +5 -5
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/cli/init_wizard.py +3 -3
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/cli/service.py +7 -7
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/config/loader.py +9 -3
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/handlers/request.py +16 -11
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/logging/setup.py +17 -6
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/main/__init__.py +1 -1
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/utils/systemd.py +3 -3
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks.egg-info/PKG-INFO +5 -5
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks.egg-info/SOURCES.txt +1 -1
- gitwebhooks-2.1.3/git-webhooks-server.ini.sample → gitwebhooks-2.1.4/gitwebhooks.ini.sample +1 -1
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/pyproject.toml +5 -5
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/tests/__init__.py +2 -2
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/tests/__main__.py +2 -2
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/tests/conftest.py +1 -1
- gitwebhooks-2.1.4/tests/integration/__init__.py +3 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/tests/integration/test_config_view_integration.py +1 -1
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/tests/integration/test_custom_webhook.py +1 -1
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/tests/integration/test_edge_cases.py +6 -2
- gitwebhooks-2.1.4/tests/unit/__init__.py +3 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/tests/unit/test_config_loading.py +4 -11
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/tests/unit/test_signature_verification.py +6 -9
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/tests/utils/certificate_generator.py +1 -1
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/tests/utils/coverage_runner.py +2 -2
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/tests/utils/http_client.py +1 -1
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/tests/utils/server_manager.py +3 -3
- gitwebhooks-2.1.3/tests/integration/__init__.py +0 -3
- gitwebhooks-2.1.3/tests/unit/__init__.py +0 -3
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/.editorconfig +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/LICENSE +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/MANIFEST.in +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/README.md +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/README.zh.md +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/doc/custom-body.png +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/doc/custom-header.png +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/doc/custom-response.png +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/doc/gitee-success.png +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/doc/gitee.png +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/doc/github-success.png +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/doc/github.png +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/doc/gitlab-success.png +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/doc/gitlab.png +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/doc/install.png +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/doc/uninstall.png +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/__init__.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/__main__.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/auth/__init__.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/auth/custom.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/auth/factory.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/auth/gitee.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/auth/github.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/auth/gitlab.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/auth/verifier.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/cli/__init__.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/cli/config.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/cli/prompts.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/config/__init__.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/config/models.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/config/registry.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/config/server.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/handlers/__init__.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/handlers/base.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/handlers/custom.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/handlers/factory.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/handlers/gitee.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/handlers/github.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/handlers/gitlab.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/logging/__init__.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/main/__main__.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/models/__init__.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/models/provider.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/models/request.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/models/result.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/server.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/utils/__init__.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/utils/constants.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/utils/exceptions.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks/utils/executor.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks.egg-info/dependency_links.txt +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks.egg-info/entry_points.txt +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks.egg-info/requires.txt +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/gitwebhooks.egg-info/top_level.txt +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/setup.cfg +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/tests/fixtures/__init__.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/tests/fixtures/custom_payloads.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/tests/fixtures/gitee_payloads.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/tests/fixtures/github_payloads.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/tests/fixtures/gitlab_payloads.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/tests/fixtures/signature_builder.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/tests/integration/test_command_execution.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/tests/integration/test_gitee_webhook.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/tests/integration/test_github_webhook.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/tests/integration/test_gitlab_webhook.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/tests/test_auth/__init__.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/tests/test_config/__init__.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/tests/test_handlers/__init__.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/tests/test_integration/__init__.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/tests/unit/cli/__init__.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/tests/unit/cli/test_config_view.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/tests/unit/cli/test_init_wizard.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/tests/unit/test_data_parsing.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/tests/unit/test_provider_detection.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/tests/unit/test_request_handler.py +0 -0
- {gitwebhooks-2.1.3 → gitwebhooks-2.1.4}/tests/utils/__init__.py +0 -0
|
@@ -49,19 +49,19 @@ gitwebhooks-cli
|
|
|
49
49
|
gitwebhooks-cli -c /path/to/config.ini
|
|
50
50
|
|
|
51
51
|
# systemd 服务管理
|
|
52
|
-
systemctl start
|
|
53
|
-
systemctl stop
|
|
54
|
-
systemctl restart
|
|
55
|
-
systemctl status
|
|
52
|
+
systemctl start gitwebhooks
|
|
53
|
+
systemctl stop gitwebhooks
|
|
54
|
+
systemctl restart gitwebhooks
|
|
55
|
+
systemctl status gitwebhooks
|
|
56
56
|
```
|
|
57
57
|
|
|
58
58
|
### 测试开发
|
|
59
59
|
```bash
|
|
60
60
|
# 直接运行(开发模式)
|
|
61
|
-
./gitwebhooks-cli -c
|
|
61
|
+
./gitwebhooks-cli -c gitwebhooks.ini.sample
|
|
62
62
|
|
|
63
63
|
# 或使用模块方式
|
|
64
|
-
python3 -m gitwebhooks.cli -c
|
|
64
|
+
python3 -m gitwebhooks.cli -c gitwebhooks.ini.sample
|
|
65
65
|
|
|
66
66
|
# 运行测试套件
|
|
67
67
|
python3 -m pytest tests/
|
|
@@ -204,7 +204,7 @@ python3 -m pytest tests/
|
|
|
204
204
|
## 文件结构
|
|
205
205
|
|
|
206
206
|
```
|
|
207
|
-
|
|
207
|
+
gitwebhooks/
|
|
208
208
|
├── gitwebhooks-cli # CLI 命令行工具
|
|
209
209
|
├── gitwebhooks/ # 主包目录
|
|
210
210
|
│ ├── __init__.py # 包入口
|
|
@@ -250,8 +250,8 @@ git-webhooks-server/
|
|
|
250
250
|
│ ├── integration/ # 集成测试
|
|
251
251
|
│ ├── utils/ # 测试工具
|
|
252
252
|
│ └── conftest.py # pytest 配置
|
|
253
|
-
├──
|
|
254
|
-
├──
|
|
253
|
+
├── gitwebhooks.ini.sample # 配置文件模板
|
|
254
|
+
├── gitwebhooks.service.sample # systemd 服务模板
|
|
255
255
|
├── install.sh # 安装/卸载脚本
|
|
256
256
|
├── message.sh # 颜色输出辅助脚本
|
|
257
257
|
├── README.md # 项目说明(英文)
|
|
@@ -268,10 +268,10 @@ git-webhooks-server/
|
|
|
268
268
|
|
|
269
269
|
## 配置文件位置
|
|
270
270
|
|
|
271
|
-
- 默认配置路径: `/usr/local/etc/
|
|
272
|
-
- 日志文件: `/var/log/
|
|
271
|
+
- 默认配置路径: `/usr/local/etc/gitwebhooks.ini`
|
|
272
|
+
- 日志文件: `/var/log/gitwebhooks.log`
|
|
273
273
|
- 安装后的 CLI: `/usr/local/bin/gitwebhooks-cli`
|
|
274
|
-
- systemd 服务: `/usr/lib/systemd/system/
|
|
274
|
+
- systemd 服务: `/usr/lib/systemd/system/gitwebhooks.service`
|
|
275
275
|
|
|
276
276
|
## Active Technologies
|
|
277
277
|
- Python 3.6+ + Python 标准库(无外部依赖)
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gitwebhooks
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.4
|
|
4
4
|
Summary: A lightweight Git webhook server for automated deployment supporting GitHub, Gitee, GitLab, and custom webhooks
|
|
5
5
|
Author-email: Troy Tse <troy@holyhi.net>
|
|
6
6
|
Maintainer-email: Troy Tse <troy@holyhi.net>
|
|
7
7
|
License-Expression: MIT
|
|
8
|
-
Project-URL: Homepage, https://github.com/troytse/
|
|
9
|
-
Project-URL: Documentation, https://github.com/troytse/
|
|
10
|
-
Project-URL: Repository, https://github.com/troytse/
|
|
11
|
-
Project-URL: Issues, https://github.com/troytse/
|
|
8
|
+
Project-URL: Homepage, https://github.com/troytse/gitwebhooks
|
|
9
|
+
Project-URL: Documentation, https://github.com/troytse/gitwebhooks#readme
|
|
10
|
+
Project-URL: Repository, https://github.com/troytse/gitwebhooks.git
|
|
11
|
+
Project-URL: Issues, https://github.com/troytse/gitwebhooks/issues
|
|
12
12
|
Keywords: webhook,git,github,gitee,gitlab,deployment,automation,server,devops
|
|
13
13
|
Classifier: Development Status :: 5 - Production/Stable
|
|
14
14
|
Classifier: Environment :: No Input/Output (Daemon)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Interactive configuration initialization wizard.
|
|
3
3
|
|
|
4
|
-
This module provides a user-friendly wizard for creating
|
|
4
|
+
This module provides a user-friendly wizard for creating gitwebhooks
|
|
5
5
|
configuration files through an interactive command-line interface.
|
|
6
6
|
|
|
7
7
|
Features:
|
|
@@ -310,7 +310,7 @@ class Wizard:
|
|
|
310
310
|
"""
|
|
311
311
|
Interactive configuration wizard.
|
|
312
312
|
|
|
313
|
-
Guides users through creating a
|
|
313
|
+
Guides users through creating a gitwebhooks configuration file
|
|
314
314
|
by collecting server, platform, and repository configuration parameters.
|
|
315
315
|
"""
|
|
316
316
|
|
|
@@ -487,7 +487,7 @@ class Wizard:
|
|
|
487
487
|
print("端口号必须在 1-65535 之间")
|
|
488
488
|
|
|
489
489
|
# Log file
|
|
490
|
-
default_log = "/var/log/
|
|
490
|
+
default_log = "/var/log/gitwebhooks.log"
|
|
491
491
|
log_file = input(f"日志文件路径 [默认: {default_log}]: ").strip()
|
|
492
492
|
if not log_file:
|
|
493
493
|
log_file = default_log
|
|
@@ -111,7 +111,7 @@ def install_service(force: bool = False, verbose: int = 0, dry_run: bool = False
|
|
|
111
111
|
print()
|
|
112
112
|
print('For migration from conda:', file=sys.stderr)
|
|
113
113
|
print(' 1. Deactivate your conda environment: conda deactivate', file=sys.stderr)
|
|
114
|
-
print(' 2. Install using pipx: pipx install
|
|
114
|
+
print(' 2. Install using pipx: pipx install gitwebhooks', file=sys.stderr)
|
|
115
115
|
print(' OR create a venv: python3 -m venv ~/venv/gitwebhooks', file=sys.stderr)
|
|
116
116
|
return 1
|
|
117
117
|
|
|
@@ -205,14 +205,14 @@ def enable_and_start_service() -> int:
|
|
|
205
205
|
# Ask if user wants to enable and start
|
|
206
206
|
if not ask_yes_no('Enable and start service? (Y/n) ', default=True):
|
|
207
207
|
print('Service installed but not started')
|
|
208
|
-
print('Start manually: sudo systemctl start
|
|
208
|
+
print('Start manually: sudo systemctl start gitwebhooks')
|
|
209
209
|
return 0
|
|
210
210
|
|
|
211
211
|
# Enable service
|
|
212
212
|
print('Enabling service...')
|
|
213
213
|
try:
|
|
214
214
|
subprocess.run(
|
|
215
|
-
['systemctl', 'enable', '
|
|
215
|
+
['systemctl', 'enable', 'gitwebhooks'],
|
|
216
216
|
check=True,
|
|
217
217
|
capture_output=True
|
|
218
218
|
)
|
|
@@ -224,7 +224,7 @@ def enable_and_start_service() -> int:
|
|
|
224
224
|
print('Starting service...')
|
|
225
225
|
try:
|
|
226
226
|
subprocess.run(
|
|
227
|
-
['systemctl', 'start', '
|
|
227
|
+
['systemctl', 'start', 'gitwebhooks'],
|
|
228
228
|
check=True,
|
|
229
229
|
capture_output=True
|
|
230
230
|
)
|
|
@@ -234,7 +234,7 @@ def enable_and_start_service() -> int:
|
|
|
234
234
|
|
|
235
235
|
print()
|
|
236
236
|
print('Installation complete!')
|
|
237
|
-
print('Service status: systemctl status
|
|
237
|
+
print('Service status: systemctl status gitwebhooks')
|
|
238
238
|
|
|
239
239
|
return 0
|
|
240
240
|
|
|
@@ -302,7 +302,7 @@ def stop_and_disable_service() -> None:
|
|
|
302
302
|
print('Stopping service...')
|
|
303
303
|
try:
|
|
304
304
|
subprocess.run(
|
|
305
|
-
['systemctl', 'stop', '
|
|
305
|
+
['systemctl', 'stop', 'gitwebhooks'],
|
|
306
306
|
check=False,
|
|
307
307
|
capture_output=True
|
|
308
308
|
)
|
|
@@ -314,7 +314,7 @@ def stop_and_disable_service() -> None:
|
|
|
314
314
|
print('Disabling service...')
|
|
315
315
|
try:
|
|
316
316
|
subprocess.run(
|
|
317
|
-
['systemctl', 'disable', '
|
|
317
|
+
['systemctl', 'disable', 'gitwebhooks'],
|
|
318
318
|
check=False,
|
|
319
319
|
capture_output=True
|
|
320
320
|
)
|
|
@@ -98,15 +98,21 @@ class ConfigLoader:
|
|
|
98
98
|
|
|
99
99
|
Note:
|
|
100
100
|
Skips reserved section names: server, ssl, github, gitee, gitlab, custom
|
|
101
|
+
Skips repository sections with missing required options (logs warning)
|
|
101
102
|
"""
|
|
102
103
|
configs = {}
|
|
103
104
|
reserved_sections = self._get_reserved_sections()
|
|
104
105
|
|
|
105
106
|
for section in self.parser.sections():
|
|
106
107
|
if section not in reserved_sections:
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
108
|
+
try:
|
|
109
|
+
repo_config = self.load_repository_config(section)
|
|
110
|
+
if repo_config is not None:
|
|
111
|
+
configs[section] = repo_config
|
|
112
|
+
except (configparser.NoOptionError, ConfigurationError) as e:
|
|
113
|
+
# Skip incomplete repository configurations
|
|
114
|
+
import logging
|
|
115
|
+
logging.warning('跳过不完整的仓库配置 [%s]: %s', section, e)
|
|
110
116
|
|
|
111
117
|
return configs
|
|
112
118
|
|
|
@@ -185,27 +185,32 @@ class WebhookRequestHandler(BaseHTTPRequestHandler):
|
|
|
185
185
|
|
|
186
186
|
Returns:
|
|
187
187
|
Tuple of (provider, event)
|
|
188
|
-
"""
|
|
189
|
-
headers_dict = dict(self.headers)
|
|
190
188
|
|
|
189
|
+
Note:
|
|
190
|
+
HTTP headers are case-insensitive per RFC 2616.
|
|
191
|
+
Uses get() method which is case-insensitive for HTTPMessage.
|
|
192
|
+
"""
|
|
191
193
|
# GitHub
|
|
192
|
-
|
|
193
|
-
|
|
194
|
+
github_event = self.headers.get(HEADER_GITHUB_EVENT)
|
|
195
|
+
if github_event is not None:
|
|
196
|
+
return Provider.GITHUB, github_event
|
|
194
197
|
|
|
195
198
|
# Gitee
|
|
196
|
-
|
|
197
|
-
|
|
199
|
+
gitee_event = self.headers.get(HEADER_GITEE_EVENT)
|
|
200
|
+
if gitee_event is not None:
|
|
201
|
+
return Provider.GITEE, gitee_event
|
|
198
202
|
|
|
199
203
|
# GitLab
|
|
200
|
-
|
|
201
|
-
|
|
204
|
+
gitlab_event = self.headers.get(HEADER_GITLAB_EVENT)
|
|
205
|
+
if gitlab_event is not None:
|
|
206
|
+
return Provider.GITLAB, gitlab_event
|
|
202
207
|
|
|
203
208
|
# Custom
|
|
204
209
|
custom_config = self._provider_configs.get(Provider.CUSTOM)
|
|
205
210
|
if custom_config and custom_config.header_name:
|
|
206
|
-
header_value =
|
|
207
|
-
if header_value.startswith(custom_config.header_value):
|
|
208
|
-
event =
|
|
211
|
+
header_value = self.headers.get(custom_config.header_name, '')
|
|
212
|
+
if header_value and header_value.startswith(custom_config.header_value):
|
|
213
|
+
event = self.headers.get(custom_config.header_event) if custom_config.header_event else None
|
|
209
214
|
return Provider.CUSTOM, event
|
|
210
215
|
|
|
211
216
|
# Unable to identify
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import logging
|
|
7
7
|
import sys
|
|
8
|
+
from pathlib import Path
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
def setup_logging(log_file: str = None):
|
|
@@ -16,6 +17,7 @@ def setup_logging(log_file: str = None):
|
|
|
16
17
|
根据配置设置日志记录:
|
|
17
18
|
- 如果 log_file 为空,只输出到 stdout
|
|
18
19
|
- 如果 log_file 非空,同时输出到文件和 stdout
|
|
20
|
+
- 如果日志目录不存在,只输出到 stdout(记录警告)
|
|
19
21
|
|
|
20
22
|
Note:
|
|
21
23
|
Python 3.8+ 使用 force 参数确保重新配置,
|
|
@@ -25,12 +27,21 @@ def setup_logging(log_file: str = None):
|
|
|
25
27
|
|
|
26
28
|
# 文件处理器
|
|
27
29
|
if log_file:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
try:
|
|
31
|
+
log_path = Path(log_file)
|
|
32
|
+
# 确保日志目录存在
|
|
33
|
+
log_path.parent.mkdir(parents=True, exist_ok=True)
|
|
34
|
+
|
|
35
|
+
file_handler = logging.FileHandler(log_file, encoding='utf-8')
|
|
36
|
+
file_handler.setFormatter(logging.Formatter(
|
|
37
|
+
'%(asctime)s %(message)s',
|
|
38
|
+
datefmt='%Y-%m-%d %H:%M:%S'
|
|
39
|
+
))
|
|
40
|
+
handlers.append(file_handler)
|
|
41
|
+
except (OSError, IOError) as e:
|
|
42
|
+
# 如果无法创建日志文件,回退到仅控制台输出
|
|
43
|
+
import warnings
|
|
44
|
+
warnings.warn(f'无法创建日志文件 {log_file}: {e},将仅输出到控制台')
|
|
34
45
|
|
|
35
46
|
# 控制台处理器
|
|
36
47
|
console_handler = logging.StreamHandler(sys.stdout)
|
|
@@ -16,7 +16,7 @@ from typing import Optional, List
|
|
|
16
16
|
class InstallationType(Enum):
|
|
17
17
|
"""Installation type enumeration
|
|
18
18
|
|
|
19
|
-
Represents the method used to install
|
|
19
|
+
Represents the method used to install gitwebhooks.
|
|
20
20
|
"""
|
|
21
21
|
PIPX = "pipx" # Installed via pipx (recommended)
|
|
22
22
|
VENV = "venv" # Python standard library venv
|
|
@@ -49,7 +49,7 @@ class InstallationEnvironment:
|
|
|
49
49
|
# Service file template
|
|
50
50
|
SERVICE_TEMPLATE = """[Unit]
|
|
51
51
|
Description=Git Webhooks Server
|
|
52
|
-
Documentation=https://github.com/troytse/
|
|
52
|
+
Documentation=https://github.com/troytse/gitwebhooks
|
|
53
53
|
After=network-online.target
|
|
54
54
|
Wants=network-online.target
|
|
55
55
|
|
|
@@ -244,7 +244,7 @@ def get_service_path() -> Path:
|
|
|
244
244
|
Returns:
|
|
245
245
|
Path to service file location
|
|
246
246
|
"""
|
|
247
|
-
return Path('/etc/systemd/system/
|
|
247
|
+
return Path('/etc/systemd/system/gitwebhooks.service')
|
|
248
248
|
|
|
249
249
|
|
|
250
250
|
def get_cli_path() -> str:
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gitwebhooks
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.4
|
|
4
4
|
Summary: A lightweight Git webhook server for automated deployment supporting GitHub, Gitee, GitLab, and custom webhooks
|
|
5
5
|
Author-email: Troy Tse <troy@holyhi.net>
|
|
6
6
|
Maintainer-email: Troy Tse <troy@holyhi.net>
|
|
7
7
|
License-Expression: MIT
|
|
8
|
-
Project-URL: Homepage, https://github.com/troytse/
|
|
9
|
-
Project-URL: Documentation, https://github.com/troytse/
|
|
10
|
-
Project-URL: Repository, https://github.com/troytse/
|
|
11
|
-
Project-URL: Issues, https://github.com/troytse/
|
|
8
|
+
Project-URL: Homepage, https://github.com/troytse/gitwebhooks
|
|
9
|
+
Project-URL: Documentation, https://github.com/troytse/gitwebhooks#readme
|
|
10
|
+
Project-URL: Repository, https://github.com/troytse/gitwebhooks.git
|
|
11
|
+
Project-URL: Issues, https://github.com/troytse/gitwebhooks/issues
|
|
12
12
|
Keywords: webhook,git,github,gitee,gitlab,deployment,automation,server,devops
|
|
13
13
|
Classifier: Development Status :: 5 - Production/Stable
|
|
14
14
|
Classifier: Environment :: No Input/Output (Daemon)
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "gitwebhooks"
|
|
7
|
-
version = "2.1.
|
|
7
|
+
version = "2.1.4"
|
|
8
8
|
description = "A lightweight Git webhook server for automated deployment supporting GitHub, Gitee, GitLab, and custom webhooks"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -58,10 +58,10 @@ dev = [
|
|
|
58
58
|
]
|
|
59
59
|
|
|
60
60
|
[project.urls]
|
|
61
|
-
Homepage = "https://github.com/troytse/
|
|
62
|
-
Documentation = "https://github.com/troytse/
|
|
63
|
-
Repository = "https://github.com/troytse/
|
|
64
|
-
Issues = "https://github.com/troytse/
|
|
61
|
+
Homepage = "https://github.com/troytse/gitwebhooks"
|
|
62
|
+
Documentation = "https://github.com/troytse/gitwebhooks#readme"
|
|
63
|
+
Repository = "https://github.com/troytse/gitwebhooks.git"
|
|
64
|
+
Issues = "https://github.com/troytse/gitwebhooks/issues"
|
|
65
65
|
|
|
66
66
|
[project.scripts]
|
|
67
67
|
gitwebhooks-cli = "gitwebhooks.main:main"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# Test Suite for
|
|
1
|
+
# Test Suite for gitwebhooks
|
|
2
2
|
#
|
|
3
|
-
# This package contains the complete test suite for the
|
|
3
|
+
# This package contains the complete test suite for the gitwebhooks project.
|
|
4
4
|
#
|
|
5
5
|
# Running Tests:
|
|
6
6
|
# python -m tests # Run all tests
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
"""
|
|
3
|
-
Test Suite Entry Point for
|
|
3
|
+
Test Suite Entry Point for gitwebhooks
|
|
4
4
|
|
|
5
5
|
This module provides the main entry point for running the test suite.
|
|
6
6
|
It supports test discovery, execution, and reporting.
|
|
@@ -29,7 +29,7 @@ def main(argv=None) -> int:
|
|
|
29
29
|
int: 0 if all tests pass, 1 otherwise
|
|
30
30
|
"""
|
|
31
31
|
parser = argparse.ArgumentParser(
|
|
32
|
-
description="Run
|
|
32
|
+
description="Run gitwebhooks test suite"
|
|
33
33
|
)
|
|
34
34
|
parser.add_argument(
|
|
35
35
|
"-v", "--verbose",
|
|
@@ -109,7 +109,7 @@ class TestCustomWebhook(WebhookTestCase):
|
|
|
109
109
|
Test that wrong header_token returns 401.
|
|
110
110
|
"""
|
|
111
111
|
config_builder = TestConfigBuilder(self.temp_dir)
|
|
112
|
-
config_builder.set_platform_verify('custom', verify=
|
|
112
|
+
config_builder.set_platform_verify('custom', verify=True, secret="correct_secret")
|
|
113
113
|
config_builder.add_repository("team/repo", self.temp_dir,
|
|
114
114
|
"echo 'test' > /dev/null")
|
|
115
115
|
config_path = config_builder.build()
|
|
@@ -76,6 +76,9 @@ class TestEdgeCases(WebhookTestCase):
|
|
|
76
76
|
def test_missing_repo_config_section_handled(self):
|
|
77
77
|
"""
|
|
78
78
|
Test when repo is found but cwd or cmd is missing.
|
|
79
|
+
|
|
80
|
+
Incomplete repository configurations are skipped with a warning.
|
|
81
|
+
Webhook requests for skipped repos return 404 Not Found.
|
|
79
82
|
"""
|
|
80
83
|
import tempfile
|
|
81
84
|
with tempfile.NamedTemporaryFile(mode='w', suffix='.ini', delete=False) as f:
|
|
@@ -85,6 +88,7 @@ class TestEdgeCases(WebhookTestCase):
|
|
|
85
88
|
config_path = f.name
|
|
86
89
|
|
|
87
90
|
try:
|
|
91
|
+
# Server should start (incomplete config is skipped)
|
|
88
92
|
with TestServer(config_path) as server:
|
|
89
93
|
server.wait_for_ready()
|
|
90
94
|
|
|
@@ -95,8 +99,8 @@ class TestEdgeCases(WebhookTestCase):
|
|
|
95
99
|
payload=PayloadBuilder.github_push_event(repo="incomplete/repo")
|
|
96
100
|
)
|
|
97
101
|
|
|
98
|
-
# Should
|
|
99
|
-
self.
|
|
102
|
+
# Should return 404 because incomplete config was skipped
|
|
103
|
+
self.assertStatusCode(response, 404)
|
|
100
104
|
finally:
|
|
101
105
|
Path(config_path).unlink(missing_ok=True)
|
|
102
106
|
|
|
@@ -58,10 +58,8 @@ class TestConfigLoading(unittest.TestCase):
|
|
|
58
58
|
nonexistent_config = f"{temp_dir}/nonexistent.ini"
|
|
59
59
|
|
|
60
60
|
# Run server with nonexistent config
|
|
61
|
-
server_script = Path(__file__).parent.parent.parent / "git-webhooks-server.py"
|
|
62
|
-
|
|
63
61
|
result = subprocess.run(
|
|
64
|
-
[sys.executable,
|
|
62
|
+
[sys.executable, '-m', 'gitwebhooks.cli', '-c', nonexistent_config],
|
|
65
63
|
capture_output=True,
|
|
66
64
|
timeout=5
|
|
67
65
|
)
|
|
@@ -82,10 +80,8 @@ class TestConfigLoading(unittest.TestCase):
|
|
|
82
80
|
invalid_config = f.name
|
|
83
81
|
|
|
84
82
|
try:
|
|
85
|
-
server_script = Path(__file__).parent.parent.parent / "git-webhooks-server.py"
|
|
86
|
-
|
|
87
83
|
result = subprocess.run(
|
|
88
|
-
[sys.executable,
|
|
84
|
+
[sys.executable, '-m', 'gitwebhooks.cli', '-c', invalid_config],
|
|
89
85
|
capture_output=True,
|
|
90
86
|
timeout=5
|
|
91
87
|
)
|
|
@@ -126,17 +122,14 @@ class TestConfigLoading(unittest.TestCase):
|
|
|
126
122
|
The server should display usage help when -h or --help
|
|
127
123
|
is provided.
|
|
128
124
|
"""
|
|
129
|
-
server_script = Path(__file__).parent.parent.parent / "git-webhooks-server.py"
|
|
130
|
-
|
|
131
125
|
result = subprocess.run(
|
|
132
|
-
[sys.executable,
|
|
126
|
+
[sys.executable, '-m', 'gitwebhooks.main', '-h'],
|
|
133
127
|
capture_output=True,
|
|
134
128
|
timeout=5
|
|
135
129
|
)
|
|
136
130
|
|
|
137
131
|
# Should exit with code 0 (help displayed)
|
|
138
|
-
|
|
139
|
-
self.assertIn(result.returncode, [0, 1])
|
|
132
|
+
self.assertEqual(result.returncode, 0)
|
|
140
133
|
|
|
141
134
|
# Output should contain usage information
|
|
142
135
|
output = result.stdout.decode('utf-8') + result.stderr.decode('utf-8')
|
|
@@ -59,11 +59,10 @@ class TestSignatureVerification(unittest.TestCase):
|
|
|
59
59
|
"""
|
|
60
60
|
Test Gitee HMAC-SHA256 signature calculation.
|
|
61
61
|
"""
|
|
62
|
-
payload = b'{"test": "data"}'
|
|
63
62
|
secret = "test_secret"
|
|
64
63
|
timestamp = 1705000000
|
|
65
64
|
|
|
66
|
-
signature = SignatureBuilder.gitee_signature(
|
|
65
|
+
signature = SignatureBuilder.gitee_signature(secret, timestamp)
|
|
67
66
|
|
|
68
67
|
# Should be Base64 encoded
|
|
69
68
|
import base64
|
|
@@ -79,21 +78,20 @@ class TestSignatureVerification(unittest.TestCase):
|
|
|
79
78
|
"""
|
|
80
79
|
Test Gitee signature verification.
|
|
81
80
|
"""
|
|
82
|
-
payload = b'{"test": "data"}'
|
|
83
81
|
secret = "test_secret"
|
|
84
82
|
timestamp = 1705000000
|
|
85
83
|
|
|
86
|
-
correct_sig = SignatureBuilder.gitee_signature(
|
|
84
|
+
correct_sig = SignatureBuilder.gitee_signature(secret, timestamp)
|
|
87
85
|
|
|
88
86
|
# Correct signature should verify
|
|
89
87
|
self.assertTrue(
|
|
90
|
-
SignatureBuilder.verify_gitee_signature(
|
|
88
|
+
SignatureBuilder.verify_gitee_signature(secret, timestamp, correct_sig)
|
|
91
89
|
)
|
|
92
90
|
|
|
93
91
|
# Wrong signature should fail
|
|
94
92
|
wrong_sig = base64.b64encode(b"wrong_signature").decode()
|
|
95
93
|
self.assertFalse(
|
|
96
|
-
SignatureBuilder.verify_gitee_signature(
|
|
94
|
+
SignatureBuilder.verify_gitee_signature(secret, timestamp, wrong_sig)
|
|
97
95
|
)
|
|
98
96
|
|
|
99
97
|
def test_gitlab_token_comparison(self):
|
|
@@ -207,11 +205,10 @@ class TestSignatureVerification(unittest.TestCase):
|
|
|
207
205
|
"""
|
|
208
206
|
Test that different timestamps produce different Gitee signatures.
|
|
209
207
|
"""
|
|
210
|
-
payload = b'{"test": "data"}'
|
|
211
208
|
secret = "test_secret"
|
|
212
209
|
|
|
213
|
-
sig1 = SignatureBuilder.gitee_signature(
|
|
214
|
-
sig2 = SignatureBuilder.gitee_signature(
|
|
210
|
+
sig1 = SignatureBuilder.gitee_signature(secret, 1705000000)
|
|
211
|
+
sig2 = SignatureBuilder.gitee_signature(secret, 1705000001)
|
|
215
212
|
|
|
216
213
|
self.assertNotEqual(sig1, sig2)
|
|
217
214
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Coverage Runner for
|
|
2
|
+
Coverage Runner for gitwebhooks Test Suite
|
|
3
3
|
|
|
4
4
|
This module provides a CoverageRunner class for generating code coverage
|
|
5
5
|
reports using Python's trace module.
|
|
@@ -110,7 +110,7 @@ class CoverageRunner:
|
|
|
110
110
|
Code coverage runner using Python's trace module.
|
|
111
111
|
|
|
112
112
|
This class runs tests and generates coverage reports for the
|
|
113
|
-
|
|
113
|
+
gitwebhooks codebase.
|
|
114
114
|
|
|
115
115
|
Usage:
|
|
116
116
|
runner = CoverageRunner(cover_dir="coverage")
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
|
-
Server Manager for
|
|
2
|
+
Server Manager for gitwebhooks Testing
|
|
3
3
|
|
|
4
|
-
This module provides a TestServer class for managing the
|
|
4
|
+
This module provides a TestServer class for managing the gitwebhooks
|
|
5
5
|
in test environments. It handles starting, stopping, and monitoring the server.
|
|
6
6
|
"""
|
|
7
7
|
|
|
@@ -25,7 +25,7 @@ from gitwebhooks.handlers.request import WebhookRequestHandler
|
|
|
25
25
|
|
|
26
26
|
class TestServer:
|
|
27
27
|
"""
|
|
28
|
-
Test server manager for
|
|
28
|
+
Test server manager for gitwebhooks.
|
|
29
29
|
|
|
30
30
|
This class manages the HTTP server in a background thread for testing.
|
|
31
31
|
It provides methods to start, stop, and monitor the server state.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|