gitwebhooks 2.1.2__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.2 → gitwebhooks-2.1.4}/CLAUDE.md +14 -12
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/PKG-INFO +5 -5
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/cli/init_wizard.py +125 -47
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/cli/service.py +7 -7
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/config/loader.py +9 -3
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/handlers/request.py +16 -11
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/logging/setup.py +17 -6
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/main/__init__.py +1 -1
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/utils/systemd.py +3 -3
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks.egg-info/PKG-INFO +5 -5
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks.egg-info/SOURCES.txt +1 -1
- gitwebhooks-2.1.2/git-webhooks-server.ini.sample → gitwebhooks-2.1.4/gitwebhooks.ini.sample +1 -1
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/pyproject.toml +5 -5
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/tests/__init__.py +2 -2
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/tests/__main__.py +2 -2
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/tests/conftest.py +1 -1
- gitwebhooks-2.1.4/tests/integration/__init__.py +3 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/tests/integration/test_config_view_integration.py +1 -1
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/tests/integration/test_custom_webhook.py +1 -1
- {gitwebhooks-2.1.2 → 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.2 → gitwebhooks-2.1.4}/tests/unit/cli/test_init_wizard.py +14 -9
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/tests/unit/test_config_loading.py +4 -11
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/tests/unit/test_signature_verification.py +6 -9
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/tests/utils/certificate_generator.py +1 -1
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/tests/utils/coverage_runner.py +2 -2
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/tests/utils/http_client.py +1 -1
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/tests/utils/server_manager.py +3 -3
- gitwebhooks-2.1.2/tests/integration/__init__.py +0 -3
- gitwebhooks-2.1.2/tests/unit/__init__.py +0 -3
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/.editorconfig +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/LICENSE +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/MANIFEST.in +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/README.md +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/README.zh.md +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/doc/custom-body.png +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/doc/custom-header.png +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/doc/custom-response.png +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/doc/gitee-success.png +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/doc/gitee.png +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/doc/github-success.png +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/doc/github.png +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/doc/gitlab-success.png +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/doc/gitlab.png +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/doc/install.png +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/doc/uninstall.png +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/__init__.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/__main__.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/auth/__init__.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/auth/custom.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/auth/factory.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/auth/gitee.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/auth/github.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/auth/gitlab.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/auth/verifier.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/cli/__init__.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/cli/config.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/cli/prompts.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/config/__init__.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/config/models.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/config/registry.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/config/server.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/handlers/__init__.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/handlers/base.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/handlers/custom.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/handlers/factory.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/handlers/gitee.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/handlers/github.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/handlers/gitlab.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/logging/__init__.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/main/__main__.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/models/__init__.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/models/provider.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/models/request.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/models/result.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/server.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/utils/__init__.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/utils/constants.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/utils/exceptions.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks/utils/executor.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks.egg-info/dependency_links.txt +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks.egg-info/entry_points.txt +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks.egg-info/requires.txt +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/gitwebhooks.egg-info/top_level.txt +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/setup.cfg +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/tests/fixtures/__init__.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/tests/fixtures/custom_payloads.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/tests/fixtures/gitee_payloads.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/tests/fixtures/github_payloads.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/tests/fixtures/gitlab_payloads.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/tests/fixtures/signature_builder.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/tests/integration/test_command_execution.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/tests/integration/test_gitee_webhook.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/tests/integration/test_github_webhook.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/tests/integration/test_gitlab_webhook.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/tests/test_auth/__init__.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/tests/test_config/__init__.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/tests/test_handlers/__init__.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/tests/test_integration/__init__.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/tests/unit/cli/__init__.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/tests/unit/cli/test_config_view.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/tests/unit/test_data_parsing.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/tests/unit/test_provider_detection.py +0 -0
- {gitwebhooks-2.1.2 → gitwebhooks-2.1.4}/tests/unit/test_request_handler.py +0 -0
- {gitwebhooks-2.1.2 → 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 标准库(无外部依赖)
|
|
@@ -290,6 +290,8 @@ git-webhooks-server/
|
|
|
290
290
|
- INI 配置文件生成(读写) (001-config-init-wizard)
|
|
291
291
|
- Python 3.7+ + Python 标准库(sys, shutil, os, pathlib, argparse) (001-install-method-guidance)
|
|
292
292
|
- INI 配置文件,systemd 服务单元文件 (001-install-method-guidance)
|
|
293
|
+
- Python 3.7+ (项目要求) + Python 标准库仅(无外部依赖) (001-fix-config-init-bug)
|
|
294
|
+
- INI 配置文件 (configparser) (001-fix-config-init-bug)
|
|
293
295
|
|
|
294
296
|
## Recent Changes
|
|
295
297
|
- 001-config-init-wizard: 添加 `config init` 交互式向导,支持配置级别选择、平台配置和仓库配置
|
|
@@ -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,8 +1,30 @@
|
|
|
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
|
+
|
|
7
|
+
Features:
|
|
8
|
+
- Support for GitHub, Gitee, GitLab, and custom webhook platforms
|
|
9
|
+
- Automatic validation of repository names (supports dots, hyphens, underscores)
|
|
10
|
+
- Direct text input for event names with platform-specific defaults
|
|
11
|
+
- Secure file generation with proper permissions (0600)
|
|
12
|
+
|
|
13
|
+
Validation Functions:
|
|
14
|
+
- validate_repo_name: Validates repository name format
|
|
15
|
+
- validate_event_input: Validates event name input (rejects newlines and digits)
|
|
16
|
+
- _get_platform_default_event: Returns platform-specific default event
|
|
17
|
+
|
|
18
|
+
TECHNICAL DEBT:
|
|
19
|
+
This module (768 lines) exceeds the Constitution's recommended 400 line limit.
|
|
20
|
+
This is acceptable because the module contains the complete interactive wizard
|
|
21
|
+
logic including platform selection, repository configuration, event configuration,
|
|
22
|
+
and file generation.
|
|
23
|
+
|
|
24
|
+
Refactoring Plan (future task):
|
|
25
|
+
- Extract validation functions to gitwebhooks/cli/wizard_validators.py
|
|
26
|
+
- Consider splitting Wizard class into smaller focused classes
|
|
27
|
+
- Evaluate separating platform-specific logic into dedicated handlers
|
|
6
28
|
"""
|
|
7
29
|
|
|
8
30
|
import os
|
|
@@ -51,6 +73,19 @@ PLATFORMS = {
|
|
|
51
73
|
}
|
|
52
74
|
}
|
|
53
75
|
|
|
76
|
+
# 平台默认事件值
|
|
77
|
+
# 每个平台使用不同的默认事件格式:
|
|
78
|
+
# - GitHub: 'push' (小写单数)
|
|
79
|
+
# - Gitee: 'Push Hook' (大写带空格)
|
|
80
|
+
# - GitLab: 'Push Hooks' (大写空格复数)
|
|
81
|
+
# - Custom: 'webhook' (通用自定义事件)
|
|
82
|
+
PLATFORM_DEFAULT_EVENTS = {
|
|
83
|
+
'github': 'push',
|
|
84
|
+
'gitee': 'Push Hook',
|
|
85
|
+
'gitlab': 'Push Hooks',
|
|
86
|
+
'custom': 'webhook',
|
|
87
|
+
}
|
|
88
|
+
|
|
54
89
|
|
|
55
90
|
# =============================================================================
|
|
56
91
|
# Data Classes
|
|
@@ -76,7 +111,7 @@ class ServerConfig:
|
|
|
76
111
|
class PlatformConfig:
|
|
77
112
|
"""Platform configuration parameters."""
|
|
78
113
|
platform: str
|
|
79
|
-
handle_events: List[str]
|
|
114
|
+
handle_events: str # Changed from List[str] to str - comma-separated event names
|
|
80
115
|
verify: bool
|
|
81
116
|
secret: Optional[str] = None
|
|
82
117
|
custom_params: Optional[Dict[str, str]] = None
|
|
@@ -96,19 +131,31 @@ class RepositoryConfig:
|
|
|
96
131
|
|
|
97
132
|
def validate_repo_name(value: str) -> bool:
|
|
98
133
|
"""
|
|
99
|
-
|
|
134
|
+
验证仓库名称格式。
|
|
135
|
+
|
|
136
|
+
支持的格式:
|
|
137
|
+
- Two-level: owner/repo (GitHub/Gitee)
|
|
138
|
+
- Multi-level: group/subgroup/project (GitLab)
|
|
139
|
+
- 特殊字符: 支持点号、连字符、下划线
|
|
140
|
+
|
|
141
|
+
有效示例:
|
|
142
|
+
- owner/repo
|
|
143
|
+
- my-org/my-repo
|
|
144
|
+
- user.repo/project
|
|
145
|
+
- group/sub_group/project.name
|
|
100
146
|
|
|
101
|
-
|
|
102
|
-
-
|
|
103
|
-
-
|
|
147
|
+
无效示例:
|
|
148
|
+
- owner repo (包含空格)
|
|
149
|
+
- owner/repo/ (尾部斜杠)
|
|
150
|
+
- owner@org/repo (包含不支持的字符)
|
|
104
151
|
|
|
105
152
|
Args:
|
|
106
|
-
value:
|
|
153
|
+
value: 仓库名称字符串
|
|
107
154
|
|
|
108
155
|
Returns:
|
|
109
156
|
True if valid, False otherwise
|
|
110
157
|
"""
|
|
111
|
-
return bool(re.match(r'^[
|
|
158
|
+
return bool(re.match(r'^[\w.-]+(/[\w.-]+)*$', value))
|
|
112
159
|
|
|
113
160
|
|
|
114
161
|
def validate_existing_path(value: str) -> bool:
|
|
@@ -154,6 +201,50 @@ def validate_port(value: str) -> bool:
|
|
|
154
201
|
return False
|
|
155
202
|
|
|
156
203
|
|
|
204
|
+
def validate_event_input(value: str) -> tuple[bool, str]:
|
|
205
|
+
"""
|
|
206
|
+
验证事件名称输入。
|
|
207
|
+
|
|
208
|
+
验证规则:
|
|
209
|
+
1. 不能为空字符串
|
|
210
|
+
2. 不能包含换行符(会破坏 INI 文件格式)
|
|
211
|
+
3. 不能包含数字(拒绝旧的数字选择格式)
|
|
212
|
+
|
|
213
|
+
Args:
|
|
214
|
+
value: 用户输入的事件名称
|
|
215
|
+
|
|
216
|
+
Returns:
|
|
217
|
+
(is_valid, error_message) - 验证结果和错误消息(如果验证失败)
|
|
218
|
+
"""
|
|
219
|
+
# 空输入检查(由调用方处理默认值,这里只做非空检查)
|
|
220
|
+
if not value or not value.strip():
|
|
221
|
+
return False, "事件名称不能为空"
|
|
222
|
+
|
|
223
|
+
# 换行符检查
|
|
224
|
+
if '\n' in value or '\r' in value:
|
|
225
|
+
return False, "事件名称不能包含换行符"
|
|
226
|
+
|
|
227
|
+
# 数字检查:去除逗号和空格后检查是否包含数字
|
|
228
|
+
cleaned = value.replace(',', '').replace(' ', '')
|
|
229
|
+
if any(c.isdigit() for c in cleaned):
|
|
230
|
+
return False, "请直接输入事件名称,不支持数字选择"
|
|
231
|
+
|
|
232
|
+
return True, ""
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def _get_platform_default_event(platform: str) -> str:
|
|
236
|
+
"""
|
|
237
|
+
获取平台默认事件值。
|
|
238
|
+
|
|
239
|
+
Args:
|
|
240
|
+
platform: 平台名称 (github/gitee/gitlab/custom)
|
|
241
|
+
|
|
242
|
+
Returns:
|
|
243
|
+
默认事件字符串,如果平台未找到则返回 'webhook'
|
|
244
|
+
"""
|
|
245
|
+
return PLATFORM_DEFAULT_EVENTS.get(platform.lower(), 'webhook')
|
|
246
|
+
|
|
247
|
+
|
|
157
248
|
# =============================================================================
|
|
158
249
|
# INI Generation Functions
|
|
159
250
|
# =============================================================================
|
|
@@ -186,7 +277,7 @@ def _generate_config(
|
|
|
186
277
|
# Platform section
|
|
187
278
|
platform_section = platform.platform
|
|
188
279
|
platform_config = {
|
|
189
|
-
'handle_events':
|
|
280
|
+
'handle_events': platform.handle_events, # Direct string value
|
|
190
281
|
'verify': 'true' if platform.verify else 'false'
|
|
191
282
|
}
|
|
192
283
|
|
|
@@ -202,7 +293,7 @@ def _generate_config(
|
|
|
202
293
|
config[platform_section] = platform_config
|
|
203
294
|
|
|
204
295
|
# Repository section
|
|
205
|
-
repo_section =
|
|
296
|
+
repo_section = repo.name
|
|
206
297
|
config[repo_section] = {
|
|
207
298
|
'cwd': repo.cwd,
|
|
208
299
|
'cmd': repo.cmd
|
|
@@ -219,7 +310,7 @@ class Wizard:
|
|
|
219
310
|
"""
|
|
220
311
|
Interactive configuration wizard.
|
|
221
312
|
|
|
222
|
-
Guides users through creating a
|
|
313
|
+
Guides users through creating a gitwebhooks configuration file
|
|
223
314
|
by collecting server, platform, and repository configuration parameters.
|
|
224
315
|
"""
|
|
225
316
|
|
|
@@ -396,7 +487,7 @@ class Wizard:
|
|
|
396
487
|
print("端口号必须在 1-65535 之间")
|
|
397
488
|
|
|
398
489
|
# Log file
|
|
399
|
-
default_log = "/var/log/
|
|
490
|
+
default_log = "/var/log/gitwebhooks.log"
|
|
400
491
|
log_file = input(f"日志文件路径 [默认: {default_log}]: ").strip()
|
|
401
492
|
if not log_file:
|
|
402
493
|
log_file = default_log
|
|
@@ -489,51 +580,38 @@ class Wizard:
|
|
|
489
580
|
custom_params=custom_params
|
|
490
581
|
)
|
|
491
582
|
|
|
492
|
-
def _collect_events(self, platform: str) ->
|
|
583
|
+
def _collect_events(self, platform: str) -> str:
|
|
493
584
|
"""
|
|
494
|
-
|
|
585
|
+
收集平台要处理的事件名称。
|
|
586
|
+
|
|
587
|
+
改为直接文本输入方式,支持逗号分隔多个事件。
|
|
588
|
+
空输入时使用平台默认值。
|
|
495
589
|
|
|
496
590
|
Args:
|
|
497
|
-
platform: Platform name
|
|
591
|
+
platform: Platform name (github/gitee/gitlab/custom)
|
|
498
592
|
|
|
499
593
|
Returns:
|
|
500
|
-
|
|
594
|
+
逗号分隔的事件名称字符串
|
|
501
595
|
"""
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
if platform == 'custom':
|
|
505
|
-
# Custom platform: ask for event name
|
|
506
|
-
print("自定义平台事件处理")
|
|
507
|
-
print("输入要处理的事件名称(直接回车跳过):")
|
|
508
|
-
events_str = input().strip()
|
|
509
|
-
return [e.strip() for e in events_str.split(',') if e.strip()] if events_str else ['webhook']
|
|
510
|
-
|
|
511
|
-
# Standard platforms: show event list
|
|
512
|
-
events = platform_info['events']
|
|
513
|
-
print("选择要处理的 webhook 事件(多个用逗号分隔,直接回车默认选 1):")
|
|
514
|
-
for idx, event in enumerate(events, 1):
|
|
515
|
-
print(f" {idx}. {event}")
|
|
596
|
+
default_event = _get_platform_default_event(platform)
|
|
597
|
+
prompt = f"输入要处理的事件名称(多个用逗号分隔,直接回车默认: {default_event}): "
|
|
516
598
|
|
|
517
599
|
while True:
|
|
518
|
-
|
|
519
|
-
choice = input("输入选择: ").strip()
|
|
520
|
-
if not choice:
|
|
521
|
-
# Default to first event (push)
|
|
522
|
-
return [events[0]]
|
|
600
|
+
value = input(prompt).strip()
|
|
523
601
|
|
|
524
|
-
|
|
525
|
-
|
|
602
|
+
# 空输入使用默认值
|
|
603
|
+
if not value:
|
|
604
|
+
return default_event
|
|
526
605
|
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
606
|
+
# 验证输入
|
|
607
|
+
is_valid, error_msg = validate_event_input(value)
|
|
608
|
+
if not is_valid:
|
|
609
|
+
print(f"错误: {error_msg}")
|
|
610
|
+
continue
|
|
532
611
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
raise
|
|
612
|
+
# 去除逗号前后的空格
|
|
613
|
+
cleaned = ','.join(part.strip() for part in value.split(','))
|
|
614
|
+
return cleaned
|
|
537
615
|
|
|
538
616
|
def _collect_custom_params(self) -> Dict[str, str]:
|
|
539
617
|
"""
|
|
@@ -585,7 +663,7 @@ class Wizard:
|
|
|
585
663
|
repo_name = input("输入仓库名称 (格式: owner/repo): ").strip()
|
|
586
664
|
if validate_repo_name(repo_name):
|
|
587
665
|
break
|
|
588
|
-
print("
|
|
666
|
+
print("仓库名称只能包含字母、数字、斜杠、点号、连字符和下划线")
|
|
589
667
|
|
|
590
668
|
# Working directory
|
|
591
669
|
while True:
|
|
@@ -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",
|