gitwebhooks 2.1.1__tar.gz → 2.1.3__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.1 → gitwebhooks-2.1.3}/CLAUDE.md +2 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/PKG-INFO +25 -1
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/README.md +24 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/README.zh.md +24 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/cli/init_wizard.py +122 -44
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks.egg-info/PKG-INFO +25 -1
- gitwebhooks-2.1.3/gitwebhooks.egg-info/entry_points.txt +2 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/pyproject.toml +2 -2
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/tests/unit/cli/test_init_wizard.py +14 -9
- gitwebhooks-2.1.1/gitwebhooks.egg-info/entry_points.txt +0 -2
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/.editorconfig +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/LICENSE +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/MANIFEST.in +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/doc/custom-body.png +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/doc/custom-header.png +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/doc/custom-response.png +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/doc/gitee-success.png +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/doc/gitee.png +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/doc/github-success.png +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/doc/github.png +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/doc/gitlab-success.png +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/doc/gitlab.png +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/doc/install.png +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/doc/uninstall.png +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/git-webhooks-server.ini.sample +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/__init__.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/__main__.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/auth/__init__.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/auth/custom.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/auth/factory.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/auth/gitee.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/auth/github.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/auth/gitlab.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/auth/verifier.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/cli/__init__.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/cli/config.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/cli/prompts.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/cli/service.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/config/__init__.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/config/loader.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/config/models.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/config/registry.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/config/server.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/handlers/__init__.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/handlers/base.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/handlers/custom.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/handlers/factory.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/handlers/gitee.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/handlers/github.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/handlers/gitlab.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/handlers/request.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/logging/__init__.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/logging/setup.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/main/__init__.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/main/__main__.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/models/__init__.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/models/provider.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/models/request.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/models/result.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/server.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/utils/__init__.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/utils/constants.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/utils/exceptions.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/utils/executor.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks/utils/systemd.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks.egg-info/SOURCES.txt +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks.egg-info/dependency_links.txt +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks.egg-info/requires.txt +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/gitwebhooks.egg-info/top_level.txt +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/setup.cfg +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/tests/__init__.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/tests/__main__.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/tests/conftest.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/tests/fixtures/__init__.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/tests/fixtures/custom_payloads.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/tests/fixtures/gitee_payloads.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/tests/fixtures/github_payloads.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/tests/fixtures/gitlab_payloads.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/tests/fixtures/signature_builder.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/tests/integration/__init__.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/tests/integration/test_command_execution.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/tests/integration/test_config_view_integration.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/tests/integration/test_custom_webhook.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/tests/integration/test_edge_cases.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/tests/integration/test_gitee_webhook.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/tests/integration/test_github_webhook.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/tests/integration/test_gitlab_webhook.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/tests/test_auth/__init__.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/tests/test_config/__init__.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/tests/test_handlers/__init__.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/tests/test_integration/__init__.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/tests/unit/__init__.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/tests/unit/cli/__init__.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/tests/unit/cli/test_config_view.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/tests/unit/test_config_loading.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/tests/unit/test_data_parsing.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/tests/unit/test_provider_detection.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/tests/unit/test_request_handler.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/tests/unit/test_signature_verification.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/tests/utils/__init__.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/tests/utils/certificate_generator.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/tests/utils/coverage_runner.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/tests/utils/http_client.py +0 -0
- {gitwebhooks-2.1.1 → gitwebhooks-2.1.3}/tests/utils/server_manager.py +0 -0
|
@@ -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,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gitwebhooks
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.3
|
|
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>
|
|
@@ -489,6 +489,30 @@ python3 -m pytest tests/
|
|
|
489
489
|
python3 -m unittest discover tests/
|
|
490
490
|
```
|
|
491
491
|
|
|
492
|
+
### Releasing to PyPI
|
|
493
|
+
|
|
494
|
+
**IMPORTANT**: Before releasing to PyPI, you MUST run the pre-release validation script:
|
|
495
|
+
|
|
496
|
+
```bash
|
|
497
|
+
./scripts/pre-release-check.sh
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
This script validates:
|
|
501
|
+
- Package builds successfully
|
|
502
|
+
- CLI entry point works (`gitwebhooks-cli`)
|
|
503
|
+
- All subcommands work (`service`, `config`)
|
|
504
|
+
- Module imports work correctly
|
|
505
|
+
|
|
506
|
+
Only after all checks pass, proceed with publishing:
|
|
507
|
+
|
|
508
|
+
```bash
|
|
509
|
+
# Build the package
|
|
510
|
+
python3 -m build
|
|
511
|
+
|
|
512
|
+
# Upload to PyPI
|
|
513
|
+
python3 -m twine upload dist/*
|
|
514
|
+
```
|
|
515
|
+
|
|
492
516
|
## License
|
|
493
517
|
|
|
494
518
|
MIT License
|
|
@@ -449,6 +449,30 @@ python3 -m pytest tests/
|
|
|
449
449
|
python3 -m unittest discover tests/
|
|
450
450
|
```
|
|
451
451
|
|
|
452
|
+
### Releasing to PyPI
|
|
453
|
+
|
|
454
|
+
**IMPORTANT**: Before releasing to PyPI, you MUST run the pre-release validation script:
|
|
455
|
+
|
|
456
|
+
```bash
|
|
457
|
+
./scripts/pre-release-check.sh
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
This script validates:
|
|
461
|
+
- Package builds successfully
|
|
462
|
+
- CLI entry point works (`gitwebhooks-cli`)
|
|
463
|
+
- All subcommands work (`service`, `config`)
|
|
464
|
+
- Module imports work correctly
|
|
465
|
+
|
|
466
|
+
Only after all checks pass, proceed with publishing:
|
|
467
|
+
|
|
468
|
+
```bash
|
|
469
|
+
# Build the package
|
|
470
|
+
python3 -m build
|
|
471
|
+
|
|
472
|
+
# Upload to PyPI
|
|
473
|
+
python3 -m twine upload dist/*
|
|
474
|
+
```
|
|
475
|
+
|
|
452
476
|
## License
|
|
453
477
|
|
|
454
478
|
MIT License
|
|
@@ -449,6 +449,30 @@ python3 -m pytest tests/
|
|
|
449
449
|
python3 -m unittest discover tests/
|
|
450
450
|
```
|
|
451
451
|
|
|
452
|
+
### 发布到 PyPI
|
|
453
|
+
|
|
454
|
+
**重要提示**:发布到 PyPI 前,必须运行发布前验证脚本:
|
|
455
|
+
|
|
456
|
+
```bash
|
|
457
|
+
./scripts/pre-release-check.sh
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
此脚本会验证:
|
|
461
|
+
- 包能成功构建
|
|
462
|
+
- CLI 入口点正常工作(`gitwebhooks-cli`)
|
|
463
|
+
- 所有子命令正常工作(`service`、`config`)
|
|
464
|
+
- 模块导入正常工作
|
|
465
|
+
|
|
466
|
+
只有在所有检查通过后,才能进行发布:
|
|
467
|
+
|
|
468
|
+
```bash
|
|
469
|
+
# 构建包
|
|
470
|
+
python3 -m build
|
|
471
|
+
|
|
472
|
+
# 上传到 PyPI
|
|
473
|
+
python3 -m twine upload dist/*
|
|
474
|
+
```
|
|
475
|
+
|
|
452
476
|
## 许可证
|
|
453
477
|
|
|
454
478
|
MIT License
|
|
@@ -3,6 +3,28 @@ Interactive configuration initialization wizard.
|
|
|
3
3
|
|
|
4
4
|
This module provides a user-friendly wizard for creating git-webhooks-server
|
|
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
|
|
@@ -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:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gitwebhooks
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.3
|
|
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>
|
|
@@ -489,6 +489,30 @@ python3 -m pytest tests/
|
|
|
489
489
|
python3 -m unittest discover tests/
|
|
490
490
|
```
|
|
491
491
|
|
|
492
|
+
### Releasing to PyPI
|
|
493
|
+
|
|
494
|
+
**IMPORTANT**: Before releasing to PyPI, you MUST run the pre-release validation script:
|
|
495
|
+
|
|
496
|
+
```bash
|
|
497
|
+
./scripts/pre-release-check.sh
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
This script validates:
|
|
501
|
+
- Package builds successfully
|
|
502
|
+
- CLI entry point works (`gitwebhooks-cli`)
|
|
503
|
+
- All subcommands work (`service`, `config`)
|
|
504
|
+
- Module imports work correctly
|
|
505
|
+
|
|
506
|
+
Only after all checks pass, proceed with publishing:
|
|
507
|
+
|
|
508
|
+
```bash
|
|
509
|
+
# Build the package
|
|
510
|
+
python3 -m build
|
|
511
|
+
|
|
512
|
+
# Upload to PyPI
|
|
513
|
+
python3 -m twine upload dist/*
|
|
514
|
+
```
|
|
515
|
+
|
|
492
516
|
## License
|
|
493
517
|
|
|
494
518
|
MIT License
|
|
@@ -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.3"
|
|
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"
|
|
@@ -64,7 +64,7 @@ Repository = "https://github.com/troytse/git-webhooks-server.git"
|
|
|
64
64
|
Issues = "https://github.com/troytse/git-webhooks-server/issues"
|
|
65
65
|
|
|
66
66
|
[project.scripts]
|
|
67
|
-
gitwebhooks-cli = "gitwebhooks.
|
|
67
|
+
gitwebhooks-cli = "gitwebhooks.main:main"
|
|
68
68
|
|
|
69
69
|
[tool.setuptools]
|
|
70
70
|
package-dir = {"" = "."}
|
|
@@ -76,16 +76,20 @@ class TestInputValidation:
|
|
|
76
76
|
assert validate_repo_name('org/team/sub-project') is True
|
|
77
77
|
assert validate_repo_name('group/subgroup/project') is True
|
|
78
78
|
|
|
79
|
+
# Single-level format (now supported per spec)
|
|
80
|
+
assert validate_repo_name('owner') is True
|
|
81
|
+
assert validate_repo_name('myrepo') is True
|
|
82
|
+
|
|
79
83
|
def test_validate_repo_name_invalid(self):
|
|
80
84
|
"""Test validate_repo_name rejects invalid formats."""
|
|
81
85
|
from gitwebhooks.cli.init_wizard import validate_repo_name
|
|
82
86
|
|
|
83
|
-
assert validate_repo_name('owner') is False
|
|
84
87
|
assert validate_repo_name('owner/') is False
|
|
85
88
|
assert validate_repo_name('/repo') is False
|
|
86
89
|
assert validate_repo_name('') is False
|
|
87
90
|
assert validate_repo_name('https://github.com/owner/repo') is False
|
|
88
91
|
assert validate_repo_name('owner//repo') is False
|
|
92
|
+
assert validate_repo_name('owner repo') is False # spaces not allowed
|
|
89
93
|
|
|
90
94
|
def test_validate_existing_path_valid(self):
|
|
91
95
|
"""Test validate_existing_path accepts existing directories."""
|
|
@@ -209,7 +213,7 @@ class TestINIGeneration:
|
|
|
209
213
|
server = MagicMock()
|
|
210
214
|
platform = PlatformConfig(
|
|
211
215
|
platform='github',
|
|
212
|
-
handle_events=
|
|
216
|
+
handle_events='push,release', # Changed from list to string
|
|
213
217
|
verify=True,
|
|
214
218
|
secret='test-secret',
|
|
215
219
|
custom_params=None
|
|
@@ -219,7 +223,7 @@ class TestINIGeneration:
|
|
|
219
223
|
config = _generate_config(server, platform, repo)
|
|
220
224
|
|
|
221
225
|
assert config.has_section('github')
|
|
222
|
-
assert config.get('github', 'handle_events') == 'push,
|
|
226
|
+
assert config.get('github', 'handle_events') == 'push,release' # No spaces
|
|
223
227
|
assert config.get('github', 'verify') == 'true'
|
|
224
228
|
assert config.get('github', 'secret') == 'test-secret'
|
|
225
229
|
|
|
@@ -237,9 +241,10 @@ class TestINIGeneration:
|
|
|
237
241
|
|
|
238
242
|
config = _generate_config(server, platform, repo)
|
|
239
243
|
|
|
240
|
-
|
|
241
|
-
assert config.
|
|
242
|
-
assert config.get('
|
|
244
|
+
# Section name no longer has 'repo/' prefix
|
|
245
|
+
assert config.has_section('owner/repo')
|
|
246
|
+
assert config.get('owner/repo', 'cwd') == '/path/to/repo'
|
|
247
|
+
assert config.get('owner/repo', 'cmd') == 'git pull && ./deploy.sh'
|
|
243
248
|
|
|
244
249
|
def test_generate_custom_platform_section(self):
|
|
245
250
|
"""Test generating [custom] section with custom parameters."""
|
|
@@ -248,7 +253,7 @@ class TestINIGeneration:
|
|
|
248
253
|
server = MagicMock()
|
|
249
254
|
platform = PlatformConfig(
|
|
250
255
|
platform='custom',
|
|
251
|
-
handle_events=
|
|
256
|
+
handle_events='webhook', # Changed from list to string
|
|
252
257
|
verify=False,
|
|
253
258
|
secret=None,
|
|
254
259
|
custom_params={
|
|
@@ -291,12 +296,12 @@ class TestDataClasses:
|
|
|
291
296
|
|
|
292
297
|
platform = PlatformConfig(
|
|
293
298
|
platform='github',
|
|
294
|
-
handle_events=
|
|
299
|
+
handle_events='push,release', # Changed from list to string
|
|
295
300
|
verify=True,
|
|
296
301
|
secret='my-secret'
|
|
297
302
|
)
|
|
298
303
|
assert platform.platform == 'github'
|
|
299
|
-
assert platform.handle_events ==
|
|
304
|
+
assert platform.handle_events == 'push,release'
|
|
300
305
|
assert platform.verify is True
|
|
301
306
|
assert platform.secret == 'my-secret'
|
|
302
307
|
|
|
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
|
|
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
|