gitwebhooks 2.2.0__py3-none-any.whl → 2.3.0__py3-none-any.whl
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/__init__.py +1 -1
- gitwebhooks/cli/__init__.py +103 -7
- gitwebhooks/cli/help.py +155 -0
- gitwebhooks/cli/prompts.py +48 -1
- gitwebhooks/cli/serve.py +151 -0
- gitwebhooks/cli/service.py +81 -7
- gitwebhooks/main/__init__.py +100 -28
- gitwebhooks/main/version.py +24 -0
- gitwebhooks/models/__init__.py +2 -0
- gitwebhooks/models/config.py +74 -0
- gitwebhooks/utils/constants.py +49 -0
- gitwebhooks/utils/systemd.py +3 -3
- {gitwebhooks-2.2.0.dist-info → gitwebhooks-2.3.0.dist-info}/METADATA +31 -12
- {gitwebhooks-2.2.0.dist-info → gitwebhooks-2.3.0.dist-info}/RECORD +18 -14
- {gitwebhooks-2.2.0.dist-info → gitwebhooks-2.3.0.dist-info}/WHEEL +0 -0
- {gitwebhooks-2.2.0.dist-info → gitwebhooks-2.3.0.dist-info}/entry_points.txt +0 -0
- {gitwebhooks-2.2.0.dist-info → gitwebhooks-2.3.0.dist-info}/licenses/LICENSE +0 -0
- {gitwebhooks-2.2.0.dist-info → gitwebhooks-2.3.0.dist-info}/top_level.txt +0 -0
gitwebhooks/__init__.py
CHANGED
gitwebhooks/cli/__init__.py
CHANGED
|
@@ -5,6 +5,8 @@ Provides subcommand registration and implementation for gitwebhooks-cli.
|
|
|
5
5
|
|
|
6
6
|
from gitwebhooks.cli.service import cmd_install, cmd_uninstall
|
|
7
7
|
from gitwebhooks.cli.config import cmd_init, cmd_view
|
|
8
|
+
from gitwebhooks.cli.help import cmd_help
|
|
9
|
+
from gitwebhooks.cli.serve import cmd_serve
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
def register_subparsers(subparsers):
|
|
@@ -16,15 +18,101 @@ def register_subparsers(subparsers):
|
|
|
16
18
|
Returns:
|
|
17
19
|
None
|
|
18
20
|
"""
|
|
19
|
-
# Register
|
|
20
|
-
|
|
21
|
+
# Register help subcommand
|
|
22
|
+
register_help_subparser(subparsers)
|
|
23
|
+
|
|
24
|
+
# Register serve subcommand
|
|
25
|
+
register_serve_subparser(subparsers)
|
|
21
26
|
|
|
22
27
|
# Register config subcommand
|
|
23
28
|
register_config_subparser(subparsers)
|
|
24
29
|
|
|
30
|
+
# Register service subcommand (deprecated, for backward compatibility)
|
|
31
|
+
register_service_subparser(subparsers)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def register_serve_subparser(subparsers):
|
|
35
|
+
"""Register serve subcommand and its actions
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
subparsers: argparse subparsers object
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
None
|
|
42
|
+
"""
|
|
43
|
+
serve_parser = subparsers.add_parser(
|
|
44
|
+
'serve',
|
|
45
|
+
add_help=False,
|
|
46
|
+
help='Start webhook server or manage systemd service'
|
|
47
|
+
)
|
|
48
|
+
serve_parser.add_argument(
|
|
49
|
+
'-c', '--config',
|
|
50
|
+
dest='config',
|
|
51
|
+
help='Path to configuration file (default: auto-discover)'
|
|
52
|
+
)
|
|
53
|
+
serve_subparsers = serve_parser.add_subparsers(dest='serve_action')
|
|
54
|
+
|
|
55
|
+
# install action
|
|
56
|
+
install_parser = serve_subparsers.add_parser('install', add_help=False)
|
|
57
|
+
install_parser.add_argument(
|
|
58
|
+
'--force',
|
|
59
|
+
action='store_true',
|
|
60
|
+
help='Force overwrite existing service'
|
|
61
|
+
)
|
|
62
|
+
install_parser.add_argument(
|
|
63
|
+
'--verbose',
|
|
64
|
+
action='count',
|
|
65
|
+
default=0,
|
|
66
|
+
help='Increase verbosity'
|
|
67
|
+
)
|
|
68
|
+
install_parser.add_argument(
|
|
69
|
+
'--dry-run',
|
|
70
|
+
action='store_true',
|
|
71
|
+
help='Preview mode without making changes'
|
|
72
|
+
)
|
|
73
|
+
install_parser.add_argument(
|
|
74
|
+
'--config-level',
|
|
75
|
+
choices=['user', 'local', 'system'],
|
|
76
|
+
help='Configuration file level (user/local/system)'
|
|
77
|
+
)
|
|
78
|
+
install_parser.set_defaults(func=cmd_serve)
|
|
79
|
+
|
|
80
|
+
# uninstall action
|
|
81
|
+
uninstall_parser = serve_subparsers.add_parser('uninstall', add_help=False)
|
|
82
|
+
uninstall_parser.add_argument(
|
|
83
|
+
'--purge',
|
|
84
|
+
action='store_true',
|
|
85
|
+
help='Also remove configuration file'
|
|
86
|
+
)
|
|
87
|
+
uninstall_parser.set_defaults(func=cmd_serve)
|
|
88
|
+
|
|
89
|
+
# Set default function for serve (when no action specified)
|
|
90
|
+
serve_parser.set_defaults(func=cmd_serve)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def register_help_subparser(subparsers):
|
|
94
|
+
"""Register help subcommand
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
subparsers: argparse subparsers object
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
None
|
|
101
|
+
"""
|
|
102
|
+
help_parser = subparsers.add_parser(
|
|
103
|
+
'help',
|
|
104
|
+
add_help=False
|
|
105
|
+
)
|
|
106
|
+
help_parser.add_argument(
|
|
107
|
+
'topic',
|
|
108
|
+
nargs='?',
|
|
109
|
+
help='Subcommand to show help for (serve, config)'
|
|
110
|
+
)
|
|
111
|
+
help_parser.set_defaults(func=cmd_help)
|
|
112
|
+
|
|
25
113
|
|
|
26
114
|
def register_service_subparser(subparsers):
|
|
27
|
-
"""Register service subcommand and its actions
|
|
115
|
+
"""Register service subcommand and its actions (deprecated)
|
|
28
116
|
|
|
29
117
|
Args:
|
|
30
118
|
subparsers: argparse subparsers object
|
|
@@ -34,12 +122,13 @@ def register_service_subparser(subparsers):
|
|
|
34
122
|
"""
|
|
35
123
|
service_parser = subparsers.add_parser(
|
|
36
124
|
'service',
|
|
37
|
-
|
|
125
|
+
add_help=False,
|
|
126
|
+
help='Manage systemd service installation (deprecated, use "serve" instead)'
|
|
38
127
|
)
|
|
39
128
|
service_subparsers = service_parser.add_subparsers(dest='service_action')
|
|
40
129
|
|
|
41
130
|
# install action
|
|
42
|
-
install_parser = service_subparsers.add_parser('install')
|
|
131
|
+
install_parser = service_subparsers.add_parser('install', add_help=False)
|
|
43
132
|
install_parser.add_argument(
|
|
44
133
|
'--force',
|
|
45
134
|
action='store_true',
|
|
@@ -56,10 +145,15 @@ def register_service_subparser(subparsers):
|
|
|
56
145
|
action='store_true',
|
|
57
146
|
help='Preview service file without installing'
|
|
58
147
|
)
|
|
148
|
+
install_parser.add_argument(
|
|
149
|
+
'--config-level',
|
|
150
|
+
choices=['user', 'local', 'system'],
|
|
151
|
+
help='Configuration file level (user/local/system). If not specified, will be prompted.'
|
|
152
|
+
)
|
|
59
153
|
install_parser.set_defaults(func=cmd_install)
|
|
60
154
|
|
|
61
155
|
# uninstall action
|
|
62
|
-
uninstall_parser = service_subparsers.add_parser('uninstall')
|
|
156
|
+
uninstall_parser = service_subparsers.add_parser('uninstall', add_help=False)
|
|
63
157
|
uninstall_parser.add_argument(
|
|
64
158
|
'--purge',
|
|
65
159
|
action='store_true',
|
|
@@ -79,6 +173,7 @@ def register_config_subparser(subparsers):
|
|
|
79
173
|
"""
|
|
80
174
|
config_parser = subparsers.add_parser(
|
|
81
175
|
'config',
|
|
176
|
+
add_help=False,
|
|
82
177
|
help='Manage gitwebhooks configuration'
|
|
83
178
|
)
|
|
84
179
|
config_subparsers = config_parser.add_subparsers(dest='config_action')
|
|
@@ -86,6 +181,7 @@ def register_config_subparser(subparsers):
|
|
|
86
181
|
# init action
|
|
87
182
|
init_parser = config_subparsers.add_parser(
|
|
88
183
|
'init',
|
|
184
|
+
add_help=False,
|
|
89
185
|
help='Initialize configuration file using interactive wizard'
|
|
90
186
|
)
|
|
91
187
|
init_parser.add_argument(
|
|
@@ -97,7 +193,7 @@ def register_config_subparser(subparsers):
|
|
|
97
193
|
init_parser.set_defaults(func=cmd_init)
|
|
98
194
|
|
|
99
195
|
# view action
|
|
100
|
-
view_parser = config_subparsers.add_parser('view')
|
|
196
|
+
view_parser = config_subparsers.add_parser('view', add_help=False)
|
|
101
197
|
view_parser.add_argument(
|
|
102
198
|
'-c',
|
|
103
199
|
'--config',
|
gitwebhooks/cli/help.py
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"""Help command module
|
|
2
|
+
|
|
3
|
+
Provides help information display functionality for gitwebhooks-cli.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def cmd_help(args: argparse.Namespace) -> int:
|
|
11
|
+
"""Handle help subcommand.
|
|
12
|
+
|
|
13
|
+
Displays main help or subcommand-specific help depending on the topic.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
args: Parsed command-line arguments
|
|
17
|
+
- topic: Optional subcommand name to show help for
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
Exit code (0 = success)
|
|
21
|
+
|
|
22
|
+
Examples:
|
|
23
|
+
>>> args = argparse.Namespace(topic=None)
|
|
24
|
+
>>> cmd_help(args) # doctest: +SKIP
|
|
25
|
+
0
|
|
26
|
+
>>> args = argparse.Namespace(topic='serve')
|
|
27
|
+
>>> cmd_help(args) # doctest: +SKIP
|
|
28
|
+
0
|
|
29
|
+
"""
|
|
30
|
+
# Import here to avoid circular dependency
|
|
31
|
+
from gitwebhooks.main import get_parser
|
|
32
|
+
|
|
33
|
+
parser = get_parser()
|
|
34
|
+
|
|
35
|
+
if hasattr(args, 'topic') and args.topic:
|
|
36
|
+
# Show subcommand help
|
|
37
|
+
show_subcommand_help(parser, args.topic)
|
|
38
|
+
else:
|
|
39
|
+
# Show main help
|
|
40
|
+
show_main_help(parser)
|
|
41
|
+
|
|
42
|
+
return 0
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def show_main_help(parser: argparse.ArgumentParser) -> None:
|
|
46
|
+
"""Display main help information.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
parser: The main argument parser
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
None
|
|
53
|
+
"""
|
|
54
|
+
# Custom help output
|
|
55
|
+
help_text = """Git Webhooks Server - Automated deployment webhook handler
|
|
56
|
+
|
|
57
|
+
Usage:
|
|
58
|
+
gitwebhooks-cli [-v] [-c CONFIG] <command> [<args>]
|
|
59
|
+
|
|
60
|
+
Commands:
|
|
61
|
+
serve Start webhook server or manage systemd service
|
|
62
|
+
config Manage configuration file
|
|
63
|
+
help Show help information
|
|
64
|
+
|
|
65
|
+
Options:
|
|
66
|
+
-v, --version Show version information
|
|
67
|
+
-c, --config Path to configuration file (default: auto-discover)
|
|
68
|
+
|
|
69
|
+
Examples:
|
|
70
|
+
gitwebhooks-cli serve # Start server
|
|
71
|
+
gitwebhooks-cli serve install # Install systemd service
|
|
72
|
+
gitwebhooks-cli config init # Initialize configuration
|
|
73
|
+
gitwebhooks-cli help serve # Show serve command help
|
|
74
|
+
|
|
75
|
+
For more information on a specific command:
|
|
76
|
+
gitwebhooks-cli help <command>
|
|
77
|
+
"""
|
|
78
|
+
print(help_text)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def show_subcommand_help(parser: argparse.ArgumentParser, topic: str) -> None:
|
|
82
|
+
"""Display help for a specific subcommand.
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
parser: The main argument parser
|
|
86
|
+
topic: Subcommand name
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
None
|
|
90
|
+
"""
|
|
91
|
+
# Map topic to help text
|
|
92
|
+
help_texts = {
|
|
93
|
+
'serve': """Command: serve
|
|
94
|
+
Start webhook server or manage systemd service
|
|
95
|
+
|
|
96
|
+
Usage:
|
|
97
|
+
gitwebhooks-cli serve [-c CONFIG]
|
|
98
|
+
gitwebhooks-cli serve install [--force] [--dry-run] [--verbose]
|
|
99
|
+
gitwebhooks-cli serve uninstall [--purge]
|
|
100
|
+
|
|
101
|
+
Subcommands:
|
|
102
|
+
(none) Start the webhook server
|
|
103
|
+
install Install systemd service
|
|
104
|
+
uninstall Uninstall systemd service
|
|
105
|
+
|
|
106
|
+
Options:
|
|
107
|
+
-c, --config Path to configuration file
|
|
108
|
+
|
|
109
|
+
Install options:
|
|
110
|
+
--force Force overwrite existing service
|
|
111
|
+
--dry-run Preview mode, don't make changes
|
|
112
|
+
--verbose Increase verbosity
|
|
113
|
+
|
|
114
|
+
Uninstall options:
|
|
115
|
+
--purge Also remove configuration file
|
|
116
|
+
|
|
117
|
+
Examples:
|
|
118
|
+
gitwebhooks-cli serve # Start server
|
|
119
|
+
gitwebhooks-cli serve -c /etc/webhooks.ini # Start with custom config
|
|
120
|
+
gitwebhooks-cli serve install # Install service
|
|
121
|
+
gitwebhooks-cli serve install --force # Force overwrite
|
|
122
|
+
gitwebhooks-cli serve uninstall # Uninstall service
|
|
123
|
+
""",
|
|
124
|
+
'config': """Command: config
|
|
125
|
+
Manage configuration file
|
|
126
|
+
|
|
127
|
+
Usage:
|
|
128
|
+
gitwebhooks-cli config init [level]
|
|
129
|
+
gitwebhooks-cli config view [-c CONFIG]
|
|
130
|
+
|
|
131
|
+
Subcommands:
|
|
132
|
+
init Initialize configuration file
|
|
133
|
+
view View configuration file
|
|
134
|
+
|
|
135
|
+
Init arguments:
|
|
136
|
+
level Configuration level (system, local, user)
|
|
137
|
+
|
|
138
|
+
View options:
|
|
139
|
+
-c, --config Path to configuration file
|
|
140
|
+
|
|
141
|
+
Examples:
|
|
142
|
+
gitwebhooks-cli config init # Interactive init
|
|
143
|
+
gitwebhooks-cli config init user # User-level config
|
|
144
|
+
gitwebhooks-cli config init system # System-level config (requires sudo)
|
|
145
|
+
gitwebhooks-cli config view # View current config
|
|
146
|
+
""",
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if topic in help_texts:
|
|
150
|
+
print(help_texts[topic])
|
|
151
|
+
else:
|
|
152
|
+
print(f"Error: Unknown command '{topic}'", file=sys.stderr)
|
|
153
|
+
print("Available commands: serve, config", file=sys.stderr)
|
|
154
|
+
print()
|
|
155
|
+
print("Use 'gitwebhooks-cli help' to see main help", file=sys.stderr)
|
gitwebhooks/cli/prompts.py
CHANGED
|
@@ -4,7 +4,9 @@ Provides functions for interactive user input and validation.
|
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
6
|
import sys
|
|
7
|
-
from typing import Any, Callable, Optional, Dict, List
|
|
7
|
+
from typing import Any, Callable, Optional, Dict, List, Tuple
|
|
8
|
+
|
|
9
|
+
from gitwebhooks.utils.constants import ConfigLevel
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
# Question template for configuration initialization
|
|
@@ -273,3 +275,48 @@ def validate_text(value: str) -> str:
|
|
|
273
275
|
if not value:
|
|
274
276
|
raise ValueError('Input cannot be empty')
|
|
275
277
|
return value
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def ask_config_level() -> ConfigLevel:
|
|
281
|
+
"""Ask user to select configuration file level
|
|
282
|
+
|
|
283
|
+
Displays an interactive menu for selecting configuration file level
|
|
284
|
+
with descriptions of each option.
|
|
285
|
+
|
|
286
|
+
Returns:
|
|
287
|
+
Selected ConfigLevel enum value
|
|
288
|
+
|
|
289
|
+
Raises:
|
|
290
|
+
KeyboardInterrupt: If user confirms exit on Ctrl+C
|
|
291
|
+
"""
|
|
292
|
+
print('Select configuration file level for the service:')
|
|
293
|
+
print(' 1. User level (~/.gitwebhooks.ini) - Single user, highest priority')
|
|
294
|
+
print(' 2. Local level (/usr/local/etc/gitwebhooks.ini) - Local system, medium priority')
|
|
295
|
+
print(' 3. System level (/etc/gitwebhooks.ini) - Global system, lowest priority')
|
|
296
|
+
print()
|
|
297
|
+
|
|
298
|
+
choice_map = {
|
|
299
|
+
'1': ConfigLevel.USER,
|
|
300
|
+
'2': ConfigLevel.LOCAL,
|
|
301
|
+
'3': ConfigLevel.SYSTEM,
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
while True:
|
|
305
|
+
try:
|
|
306
|
+
response = input('Enter choice [1-3] (default: 1): ').strip()
|
|
307
|
+
|
|
308
|
+
# Use default if empty input
|
|
309
|
+
if not response:
|
|
310
|
+
return ConfigLevel.USER
|
|
311
|
+
|
|
312
|
+
# Validate and map response
|
|
313
|
+
if response in choice_map:
|
|
314
|
+
return choice_map[response]
|
|
315
|
+
|
|
316
|
+
# Invalid input
|
|
317
|
+
print('Invalid choice. Please enter a number between 1 and 3.')
|
|
318
|
+
|
|
319
|
+
except KeyboardInterrupt:
|
|
320
|
+
if ask_yes_no('Confirm exit? (y/N) ', default=False):
|
|
321
|
+
print('\nService installation cancelled')
|
|
322
|
+
sys.exit(0)
|
gitwebhooks/cli/serve.py
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"""Serve command module
|
|
2
|
+
|
|
3
|
+
Provides server startup and systemd service management functionality.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Optional
|
|
10
|
+
|
|
11
|
+
from gitwebhooks.server import WebhookServer
|
|
12
|
+
from gitwebhooks.utils.exceptions import ConfigurationError
|
|
13
|
+
from gitwebhooks.utils.constants import CONFIG_SEARCH_PATHS
|
|
14
|
+
|
|
15
|
+
# Import service management functions
|
|
16
|
+
from gitwebhooks.cli.service import (
|
|
17
|
+
cmd_install as service_cmd_install,
|
|
18
|
+
cmd_uninstall as service_cmd_uninstall,
|
|
19
|
+
install_service,
|
|
20
|
+
uninstall_service,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
# Import version display
|
|
24
|
+
from gitwebhooks import __version__
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def cmd_serve(args: argparse.Namespace) -> int:
|
|
28
|
+
"""Handle serve subcommand.
|
|
29
|
+
|
|
30
|
+
This function dispatches to the appropriate serve action:
|
|
31
|
+
- No action: Start the webhook server
|
|
32
|
+
- install: Install systemd service
|
|
33
|
+
- uninstall: Uninstall systemd service
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
args: Parsed command-line arguments
|
|
37
|
+
- serve_action: The serve sub-action (install, uninstall, or None)
|
|
38
|
+
- config: Configuration file path (from global -c argument)
|
|
39
|
+
- force: Force overwrite existing service (install action)
|
|
40
|
+
- dry_run: Preview mode (install action)
|
|
41
|
+
- verbose: Verbosity level (install action)
|
|
42
|
+
- purge: Also remove config file (uninstall action)
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
Exit code (0 = success, 1 = error)
|
|
46
|
+
"""
|
|
47
|
+
# Get serve_action from args
|
|
48
|
+
serve_action = getattr(args, 'serve_action', None)
|
|
49
|
+
|
|
50
|
+
if serve_action == 'install':
|
|
51
|
+
# Delegate to service install command
|
|
52
|
+
return service_cmd_install(args)
|
|
53
|
+
elif serve_action == 'uninstall':
|
|
54
|
+
# Delegate to service uninstall command
|
|
55
|
+
return service_cmd_uninstall(args)
|
|
56
|
+
else:
|
|
57
|
+
# Start server
|
|
58
|
+
return start_server(args)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def start_server(args: argparse.Namespace) -> int:
|
|
62
|
+
"""Start the webhook server.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
args: Parsed command-line arguments
|
|
66
|
+
- config: Configuration file path (from global -c argument)
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
Exit code (0 = success, 1 = error)
|
|
70
|
+
"""
|
|
71
|
+
# Get config path from args
|
|
72
|
+
config_file = getattr(args, 'config', None)
|
|
73
|
+
|
|
74
|
+
# Print version
|
|
75
|
+
print(f'gitwebhooks-cli {__version__}')
|
|
76
|
+
|
|
77
|
+
# Auto-discover config file if not specified
|
|
78
|
+
if config_file is None:
|
|
79
|
+
config_file = find_config_file()
|
|
80
|
+
if config_file is None:
|
|
81
|
+
# No config found - format error with all search paths
|
|
82
|
+
searched_paths = [Path(p).expanduser().resolve() for p in CONFIG_SEARCH_PATHS]
|
|
83
|
+
error_msg = format_config_error(searched_paths)
|
|
84
|
+
print(error_msg, file=sys.stderr)
|
|
85
|
+
return 1
|
|
86
|
+
|
|
87
|
+
# Expand user path
|
|
88
|
+
config_path = Path(config_file).expanduser()
|
|
89
|
+
|
|
90
|
+
# Check configuration file exists
|
|
91
|
+
if not config_path.exists():
|
|
92
|
+
print(f'Error: Configuration file not found: {config_path}', file=sys.stderr)
|
|
93
|
+
return 1
|
|
94
|
+
|
|
95
|
+
# Print configuration file being used
|
|
96
|
+
print(f'Using configuration file: {config_path}')
|
|
97
|
+
print('Starting server...')
|
|
98
|
+
|
|
99
|
+
# Create and run server
|
|
100
|
+
try:
|
|
101
|
+
server = WebhookServer(config_path=str(config_path))
|
|
102
|
+
server.run()
|
|
103
|
+
return 0
|
|
104
|
+
except ConfigurationError as e:
|
|
105
|
+
print(f'Configuration error: {e}', file=sys.stderr)
|
|
106
|
+
return 1
|
|
107
|
+
except KeyboardInterrupt:
|
|
108
|
+
print('\nServer stopped by user')
|
|
109
|
+
return 0
|
|
110
|
+
except Exception as e:
|
|
111
|
+
print(f'Error: {e}', file=sys.stderr)
|
|
112
|
+
return 1
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def find_config_file() -> Optional[str]:
|
|
116
|
+
"""Find configuration file by priority order.
|
|
117
|
+
|
|
118
|
+
Searches for configuration files in the following order:
|
|
119
|
+
1. User level: ~/.gitwebhooks.ini
|
|
120
|
+
2. Local level: /usr/local/etc/gitwebhooks.ini
|
|
121
|
+
3. System level: /etc/gitwebhooks.ini
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
Path string of the first existing configuration file, or None if not found.
|
|
125
|
+
"""
|
|
126
|
+
for config_path in CONFIG_SEARCH_PATHS:
|
|
127
|
+
expanded_path = Path(config_path).expanduser().resolve()
|
|
128
|
+
if expanded_path.exists():
|
|
129
|
+
return str(expanded_path)
|
|
130
|
+
return None
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def format_config_error(searched_paths: list) -> str:
|
|
134
|
+
"""Format user-friendly error message for missing configuration file.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
searched_paths: List of paths that were searched.
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
Formatted error message string.
|
|
141
|
+
"""
|
|
142
|
+
lines = [
|
|
143
|
+
'Error: Configuration file not found.',
|
|
144
|
+
'Searched paths:'
|
|
145
|
+
]
|
|
146
|
+
for i, path in enumerate(searched_paths, 1):
|
|
147
|
+
lines.append(f' {i}. {path}')
|
|
148
|
+
lines.append('')
|
|
149
|
+
lines.append('You can create a configuration file using:')
|
|
150
|
+
lines.append(' gitwebhooks-cli config init')
|
|
151
|
+
return '\n'.join(lines)
|
gitwebhooks/cli/service.py
CHANGED
|
@@ -7,6 +7,7 @@ import argparse
|
|
|
7
7
|
import subprocess
|
|
8
8
|
import sys
|
|
9
9
|
from pathlib import Path
|
|
10
|
+
from typing import Optional
|
|
10
11
|
|
|
11
12
|
from gitwebhooks.utils.systemd import (
|
|
12
13
|
check_systemd,
|
|
@@ -16,7 +17,8 @@ from gitwebhooks.utils.systemd import (
|
|
|
16
17
|
detect_installation_type,
|
|
17
18
|
InstallationType,
|
|
18
19
|
)
|
|
19
|
-
from gitwebhooks.
|
|
20
|
+
from gitwebhooks.utils.constants import ConfigLevel
|
|
21
|
+
from gitwebhooks.cli.prompts import ask_yes_no, ask_config_level
|
|
20
22
|
|
|
21
23
|
|
|
22
24
|
# Error codes
|
|
@@ -26,21 +28,39 @@ E_EXISTS = 1
|
|
|
26
28
|
|
|
27
29
|
|
|
28
30
|
def cmd_install(args: argparse.Namespace) -> int:
|
|
29
|
-
"""Install systemd service
|
|
31
|
+
"""Install systemd service (deprecated, use 'serve install' instead)
|
|
30
32
|
|
|
31
33
|
Args:
|
|
32
34
|
args: Parsed command-line arguments
|
|
33
35
|
- force: Force overwrite existing service
|
|
34
36
|
- verbose: Verbosity level (0=normal, 1=verbose, 2=extra verbose)
|
|
35
37
|
- dry_run: Preview mode without actual installation
|
|
38
|
+
- config: Path to configuration file (from global -c argument)
|
|
39
|
+
- config_level: Config level from --config-level argument
|
|
36
40
|
|
|
37
41
|
Returns:
|
|
38
42
|
Exit code (0 = success, 1 = error)
|
|
39
43
|
"""
|
|
44
|
+
# Show deprecation warning
|
|
45
|
+
print('Warning: "service install" is deprecated. Use "serve install" instead.', file=sys.stderr)
|
|
46
|
+
print()
|
|
40
47
|
# Get verbose and dry_run from args
|
|
41
48
|
verbose = getattr(args, 'verbose', 0)
|
|
42
49
|
dry_run = getattr(args, 'dry_run', False)
|
|
43
50
|
|
|
51
|
+
# Get config path from global -c argument (backward compatibility)
|
|
52
|
+
config_path = getattr(args, 'config', None)
|
|
53
|
+
config_level = getattr(args, 'config_level', None)
|
|
54
|
+
|
|
55
|
+
# Check for conflicting options
|
|
56
|
+
if config_path and config_level:
|
|
57
|
+
print('Error: Conflicting options: --config-level and -c cannot be used together.', file=sys.stderr)
|
|
58
|
+
print()
|
|
59
|
+
print('Please use either:', file=sys.stderr)
|
|
60
|
+
print(' - gitwebhooks-cli service install --config-level <level>', file=sys.stderr)
|
|
61
|
+
print(' - gitwebhooks-cli service install -c <config-file>', file=sys.stderr)
|
|
62
|
+
return 1
|
|
63
|
+
|
|
44
64
|
# For dry-run mode, skip root check
|
|
45
65
|
if not dry_run:
|
|
46
66
|
# Check root permission
|
|
@@ -76,16 +96,30 @@ def cmd_install(args: argparse.Namespace) -> int:
|
|
|
76
96
|
print()
|
|
77
97
|
|
|
78
98
|
# Perform installation
|
|
79
|
-
return install_service(
|
|
99
|
+
return install_service(
|
|
100
|
+
force=args.force,
|
|
101
|
+
verbose=verbose,
|
|
102
|
+
dry_run=dry_run,
|
|
103
|
+
config_level_override=config_level,
|
|
104
|
+
config_path_override=config_path
|
|
105
|
+
)
|
|
80
106
|
|
|
81
107
|
|
|
82
|
-
def install_service(
|
|
108
|
+
def install_service(
|
|
109
|
+
force: bool = False,
|
|
110
|
+
verbose: int = 0,
|
|
111
|
+
dry_run: bool = False,
|
|
112
|
+
config_level_override: Optional[str] = None,
|
|
113
|
+
config_path_override: Optional[str] = None
|
|
114
|
+
) -> int:
|
|
83
115
|
"""Execute service installation logic
|
|
84
116
|
|
|
85
117
|
Args:
|
|
86
118
|
force: Force overwrite existing service
|
|
87
119
|
verbose: Verbosity level (0=normal, 1=verbose, 2=extra verbose)
|
|
88
120
|
dry_run: Preview mode without actual installation
|
|
121
|
+
config_level_override: Config level from command line argument (user/local/system)
|
|
122
|
+
config_path_override: Config path from -c argument (for backward compatibility)
|
|
89
123
|
|
|
90
124
|
Returns:
|
|
91
125
|
Exit code (0 = success, 1 = error)
|
|
@@ -124,13 +158,43 @@ def install_service(force: bool = False, verbose: int = 0, dry_run: bool = False
|
|
|
124
158
|
print('Consider using pipx or venv for better isolation.', file=sys.stderr)
|
|
125
159
|
print()
|
|
126
160
|
|
|
161
|
+
# Determine configuration level and path
|
|
162
|
+
config_level: ConfigLevel
|
|
163
|
+
config_path: str
|
|
164
|
+
|
|
165
|
+
if config_path_override:
|
|
166
|
+
# Backward compatibility: -c parameter takes precedence
|
|
167
|
+
config_path = config_path_override
|
|
168
|
+
# Map path to config level for display purposes
|
|
169
|
+
expanded_path = Path(config_path).expanduser()
|
|
170
|
+
if str(expanded_path) == str(Path('~/.gitwebhooks.ini').expanduser()):
|
|
171
|
+
config_level = ConfigLevel.USER
|
|
172
|
+
elif str(expanded_path) == '/usr/local/etc/gitwebhooks.ini':
|
|
173
|
+
config_level = ConfigLevel.LOCAL
|
|
174
|
+
elif str(expanded_path) == '/etc/gitwebhooks.ini':
|
|
175
|
+
config_level = ConfigLevel.SYSTEM
|
|
176
|
+
else:
|
|
177
|
+
# Custom path, default to USER for display
|
|
178
|
+
config_level = ConfigLevel.USER
|
|
179
|
+
elif config_level_override:
|
|
180
|
+
# Command-line --config-level parameter
|
|
181
|
+
try:
|
|
182
|
+
config_level = ConfigLevel.from_string(config_level_override)
|
|
183
|
+
except ValueError as e:
|
|
184
|
+
print(f'Error: {e}', file=sys.stderr)
|
|
185
|
+
return 1
|
|
186
|
+
config_path = str(config_level.get_config_path())
|
|
187
|
+
else:
|
|
188
|
+
# Interactive selection
|
|
189
|
+
config_level = ask_config_level()
|
|
190
|
+
config_path = str(config_level.get_config_path())
|
|
191
|
+
|
|
127
192
|
# Get paths
|
|
128
193
|
service_path = get_service_path()
|
|
129
|
-
config_path = '~/.gitwebhook.ini'
|
|
130
194
|
|
|
131
195
|
# Generate service file based on installation type
|
|
132
196
|
# For pipx: use cli_path directly
|
|
133
|
-
# For others: use python -m gitwebhooks.
|
|
197
|
+
# For others: use python -m gitwebhooks.main
|
|
134
198
|
use_python_module = env.type != InstallationType.PIPX
|
|
135
199
|
|
|
136
200
|
service_content = generate_service_file(
|
|
@@ -147,6 +211,9 @@ def install_service(force: bool = False, verbose: int = 0, dry_run: bool = False
|
|
|
147
211
|
print(service_content)
|
|
148
212
|
print('---')
|
|
149
213
|
print()
|
|
214
|
+
if config_level_override or not config_path_override:
|
|
215
|
+
print(f'Configuration level: {config_level.value}')
|
|
216
|
+
print(f'Configuration path: {config_path}')
|
|
150
217
|
print(f'Service file would be written to: {service_path}')
|
|
151
218
|
print('Dry-run complete: No changes made.')
|
|
152
219
|
return 0
|
|
@@ -174,6 +241,10 @@ def install_service(force: bool = False, verbose: int = 0, dry_run: bool = False
|
|
|
174
241
|
print(f'Warning: systemctl daemon-reload failed: {e}', file=sys.stderr)
|
|
175
242
|
# Continue anyway
|
|
176
243
|
|
|
244
|
+
# Display configuration file information
|
|
245
|
+
print()
|
|
246
|
+
print(f'Using configuration file: {config_path}')
|
|
247
|
+
|
|
177
248
|
# Enable and start service
|
|
178
249
|
return enable_and_start_service()
|
|
179
250
|
|
|
@@ -240,7 +311,7 @@ def enable_and_start_service() -> int:
|
|
|
240
311
|
|
|
241
312
|
|
|
242
313
|
def cmd_uninstall(args: argparse.Namespace) -> int:
|
|
243
|
-
"""Uninstall systemd service
|
|
314
|
+
"""Uninstall systemd service (deprecated, use 'serve uninstall' instead)
|
|
244
315
|
|
|
245
316
|
Args:
|
|
246
317
|
args: Parsed command-line arguments
|
|
@@ -249,6 +320,9 @@ def cmd_uninstall(args: argparse.Namespace) -> int:
|
|
|
249
320
|
Returns:
|
|
250
321
|
Exit code (0 = success, 1 = error)
|
|
251
322
|
"""
|
|
323
|
+
# Show deprecation warning
|
|
324
|
+
print('Warning: "service uninstall" is deprecated. Use "serve uninstall" instead.', file=sys.stderr)
|
|
325
|
+
print()
|
|
252
326
|
# Check root permission
|
|
253
327
|
if not check_root_permission():
|
|
254
328
|
print('Error: This operation requires root privileges', file=sys.stderr)
|
gitwebhooks/main/__init__.py
CHANGED
|
@@ -19,6 +19,57 @@ try:
|
|
|
19
19
|
except ImportError:
|
|
20
20
|
HAS_SUBCOMMANDS = False
|
|
21
21
|
|
|
22
|
+
# Global parser reference for help command
|
|
23
|
+
_parser = None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def get_parser() -> argparse.ArgumentParser:
|
|
27
|
+
"""Get the main argument parser instance.
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
The main ArgumentParser instance
|
|
31
|
+
|
|
32
|
+
Examples:
|
|
33
|
+
>>> parser = get_parser()
|
|
34
|
+
>>> isinstance(parser, argparse.ArgumentParser)
|
|
35
|
+
True
|
|
36
|
+
"""
|
|
37
|
+
global _parser
|
|
38
|
+
if _parser is None:
|
|
39
|
+
_parser = create_parser()
|
|
40
|
+
return _parser
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def create_parser() -> argparse.ArgumentParser:
|
|
44
|
+
"""Create and configure the main argument parser.
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
Configured ArgumentParser instance
|
|
48
|
+
|
|
49
|
+
Examples:
|
|
50
|
+
>>> parser = create_parser()
|
|
51
|
+
>>> isinstance(parser, argparse.ArgumentParser)
|
|
52
|
+
True
|
|
53
|
+
"""
|
|
54
|
+
# Create main argument parser (help disabled)
|
|
55
|
+
parser = argparse.ArgumentParser(
|
|
56
|
+
prog='gitwebhooks-cli',
|
|
57
|
+
description='Git Webhooks Server - Automated deployment webhook handler',
|
|
58
|
+
add_help=False
|
|
59
|
+
)
|
|
60
|
+
parser.add_argument(
|
|
61
|
+
'-c', '--config',
|
|
62
|
+
default=None,
|
|
63
|
+
help='Path to INI configuration file (default: auto-discover)'
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# Add subcommands if available
|
|
67
|
+
if HAS_SUBCOMMANDS:
|
|
68
|
+
subparsers = parser.add_subparsers(dest='command', help='Subcommands')
|
|
69
|
+
register_subparsers(subparsers)
|
|
70
|
+
|
|
71
|
+
return parser
|
|
72
|
+
|
|
22
73
|
|
|
23
74
|
def find_config_file() -> Optional[str]:
|
|
24
75
|
"""Find configuration file by priority order.
|
|
@@ -83,10 +134,11 @@ def main(argv: List[str] = None) -> int:
|
|
|
83
134
|
SystemExit: Exit on error
|
|
84
135
|
|
|
85
136
|
Command Line Arguments:
|
|
86
|
-
-
|
|
87
|
-
-c, --config
|
|
88
|
-
|
|
89
|
-
config
|
|
137
|
+
-v, --version Show version information and exit
|
|
138
|
+
-c, --config Specify configuration file path (optional)
|
|
139
|
+
serve Start webhook server or manage systemd service
|
|
140
|
+
config Configuration management subcommands
|
|
141
|
+
help Show help information
|
|
90
142
|
|
|
91
143
|
Config Discovery:
|
|
92
144
|
When -c is not specified, searches in order:
|
|
@@ -95,38 +147,58 @@ def main(argv: List[str] = None) -> int:
|
|
|
95
147
|
3. /etc/gitwebhooks.ini (system level)
|
|
96
148
|
|
|
97
149
|
Examples:
|
|
98
|
-
python3 -m gitwebhooks.main
|
|
99
|
-
python3 -m gitwebhooks.main
|
|
100
|
-
gitwebhooks-
|
|
101
|
-
gitwebhooks-cli
|
|
150
|
+
python3 -m gitwebhooks.main -v
|
|
151
|
+
python3 -m gitwebhooks.main serve
|
|
152
|
+
python3 -m gitwebhooks.main serve -c /path/to/config.ini
|
|
153
|
+
gitwebhooks-cli serve install
|
|
102
154
|
gitwebhooks-cli config init
|
|
155
|
+
gitwebhooks-cli help
|
|
103
156
|
"""
|
|
104
157
|
if argv is None:
|
|
105
158
|
argv = sys.argv[1:]
|
|
106
159
|
|
|
107
|
-
#
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
)
|
|
113
|
-
parser.add_argument(
|
|
114
|
-
'-c', '--config',
|
|
115
|
-
default=None,
|
|
116
|
-
help='Path to INI configuration file (default: auto-discover)'
|
|
117
|
-
)
|
|
160
|
+
# Pre-check for -v/--version parameters (takes priority over everything)
|
|
161
|
+
if '-v' in argv or '--version' in argv:
|
|
162
|
+
from gitwebhooks.main.version import print_version
|
|
163
|
+
print_version()
|
|
164
|
+
return 0
|
|
118
165
|
|
|
119
|
-
#
|
|
120
|
-
if
|
|
121
|
-
|
|
122
|
-
|
|
166
|
+
# Check for -h/--help parameters and show error
|
|
167
|
+
if '-h' in argv or '--help' in argv:
|
|
168
|
+
print('Error: -h/--help parameters are not supported.', file=sys.stderr)
|
|
169
|
+
print('Use \'gitwebhooks-cli help\' or \'gitwebhooks-cli help <subcommand>\' instead.', file=sys.stderr)
|
|
170
|
+
return 1
|
|
171
|
+
|
|
172
|
+
# Create parser and parse arguments
|
|
173
|
+
parser = create_parser()
|
|
174
|
+
|
|
175
|
+
# Check if user provided a command (first argument without leading dash)
|
|
176
|
+
command = None
|
|
177
|
+
for i, arg in enumerate(argv):
|
|
178
|
+
if not arg.startswith('-'):
|
|
179
|
+
command = arg
|
|
180
|
+
break
|
|
123
181
|
|
|
124
|
-
#
|
|
125
|
-
|
|
182
|
+
# Try to parse arguments
|
|
183
|
+
try:
|
|
184
|
+
args = parser.parse_args(argv)
|
|
185
|
+
except SystemExit as e:
|
|
186
|
+
# argparse will call sys.exit on error, catch it to provide better error
|
|
187
|
+
if e.code != 0:
|
|
188
|
+
# Parsing failed, check if it's an unknown command
|
|
189
|
+
if command and command not in ['serve', 'config', 'help', 'service']:
|
|
190
|
+
print(f"Error: Unknown command '{command}'", file=sys.stderr)
|
|
191
|
+
print("Available commands: serve, config, help", file=sys.stderr)
|
|
192
|
+
print()
|
|
193
|
+
print("Use 'gitwebhooks-cli help' to see usage information", file=sys.stderr)
|
|
194
|
+
return 1
|
|
195
|
+
raise
|
|
126
196
|
|
|
127
|
-
# If no subcommand,
|
|
128
|
-
if not hasattr(args, 'func'):
|
|
129
|
-
|
|
197
|
+
# If no subcommand specified, show help (new default behavior)
|
|
198
|
+
if not hasattr(args, 'func') or args.func is None:
|
|
199
|
+
from gitwebhooks.cli.help import show_main_help
|
|
200
|
+
show_main_help(parser)
|
|
201
|
+
return 0
|
|
130
202
|
|
|
131
203
|
# Execute subcommand
|
|
132
204
|
return args.func(args)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Version display module
|
|
2
|
+
|
|
3
|
+
Provides version information display functionality for gitwebhooks-cli.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def print_version() -> None:
|
|
10
|
+
"""Print version information and exit.
|
|
11
|
+
|
|
12
|
+
Displays version in the format: gitwebhooks-cli x.x.x
|
|
13
|
+
|
|
14
|
+
Returns:
|
|
15
|
+
None
|
|
16
|
+
|
|
17
|
+
Examples:
|
|
18
|
+
>>> print_version() # doctest: +SKIP
|
|
19
|
+
gitwebhooks-cli 2.2.0
|
|
20
|
+
"""
|
|
21
|
+
# Import __version__ from package
|
|
22
|
+
from gitwebhooks import __version__
|
|
23
|
+
|
|
24
|
+
print(f'gitwebhooks-cli {__version__}')
|
gitwebhooks/models/__init__.py
CHANGED
|
@@ -6,9 +6,11 @@ Defines core data types and enumerations.
|
|
|
6
6
|
from .provider import Provider
|
|
7
7
|
from .request import WebhookRequest
|
|
8
8
|
from .result import SignatureVerificationResult
|
|
9
|
+
from .config import ServiceInstallContext
|
|
9
10
|
|
|
10
11
|
__all__ = [
|
|
11
12
|
'Provider',
|
|
12
13
|
'WebhookRequest',
|
|
13
14
|
'SignatureVerificationResult',
|
|
15
|
+
'ServiceInstallContext',
|
|
14
16
|
]
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""Configuration-related data models
|
|
2
|
+
|
|
3
|
+
Provides data classes for configuration and service installation contexts.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Optional
|
|
9
|
+
|
|
10
|
+
from gitwebhooks.utils.constants import ConfigLevel
|
|
11
|
+
from gitwebhooks.utils.systemd import InstallationType
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass
|
|
15
|
+
class ServiceInstallContext:
|
|
16
|
+
"""Service installation context
|
|
17
|
+
|
|
18
|
+
Contains all information needed for service installation,
|
|
19
|
+
including configuration level, paths, and installation type.
|
|
20
|
+
|
|
21
|
+
Attributes:
|
|
22
|
+
config_level: Configuration file level (user/local/system)
|
|
23
|
+
config_path: Full path to configuration file
|
|
24
|
+
install_type: Installation type (pipx/venv/virtualenv/system)
|
|
25
|
+
use_python_module: Whether to use 'python -m' format
|
|
26
|
+
cli_path: Path to gitwebhooks-cli executable
|
|
27
|
+
python_path: Python interpreter path (required when use_python_module=True)
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
config_level: ConfigLevel
|
|
31
|
+
config_path: Path
|
|
32
|
+
install_type: InstallationType
|
|
33
|
+
use_python_module: bool
|
|
34
|
+
cli_path: str
|
|
35
|
+
python_path: Optional[str] = None
|
|
36
|
+
|
|
37
|
+
def __post_init__(self):
|
|
38
|
+
"""Validate the service installation context
|
|
39
|
+
|
|
40
|
+
Raises:
|
|
41
|
+
ValueError: If validation fails
|
|
42
|
+
"""
|
|
43
|
+
# Ensure config_path is a Path object
|
|
44
|
+
if isinstance(self.config_path, str):
|
|
45
|
+
self.config_path = Path(self.config_path)
|
|
46
|
+
|
|
47
|
+
# Validate use_python_module requires python_path
|
|
48
|
+
if self.use_python_module and not self.python_path:
|
|
49
|
+
raise ValueError(
|
|
50
|
+
"python_path is required when use_python_module is True"
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# Validate config_path is not empty
|
|
54
|
+
if not self.config_path or str(self.config_path) == '.':
|
|
55
|
+
raise ValueError("config_path cannot be empty")
|
|
56
|
+
|
|
57
|
+
def get_exec_start_command(self) -> str:
|
|
58
|
+
"""Generate the ExecStart command for systemd service file
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
Complete ExecStart command string
|
|
62
|
+
"""
|
|
63
|
+
if self.use_python_module and self.python_path:
|
|
64
|
+
return f"{self.python_path} -m gitwebhooks.main -c {self.config_path}"
|
|
65
|
+
else:
|
|
66
|
+
return f"{self.cli_path} -c {self.config_path}"
|
|
67
|
+
|
|
68
|
+
def get_config_path_str(self) -> str:
|
|
69
|
+
"""Get configuration file path as string
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
Configuration file path as string
|
|
73
|
+
"""
|
|
74
|
+
return str(self.config_path)
|
gitwebhooks/utils/constants.py
CHANGED
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
Defines all constants used in the project.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
6
9
|
# HTTP Status Codes
|
|
7
10
|
HTTP_OK = 200
|
|
8
11
|
HTTP_BAD_REQUEST = 400
|
|
@@ -53,3 +56,49 @@ SENSITIVE_KEYWORDS = {"secret", "password", "token", "key", "passphrase"}
|
|
|
53
56
|
# ANSI color codes for sensitive field highlighting
|
|
54
57
|
COLOR_SENSITIVE = "\033[33m" # Yellow
|
|
55
58
|
COLOR_RESET = "\033[0m"
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class ConfigLevel(Enum):
|
|
62
|
+
"""Configuration file level for service installation
|
|
63
|
+
|
|
64
|
+
Represents the three configuration file levels supported by gitwebhooks,
|
|
65
|
+
ordered by priority (highest to lowest).
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
USER = "user" # ~/.gitwebhooks.ini (highest priority)
|
|
69
|
+
LOCAL = "local" # /usr/local/etc/gitwebhooks.ini
|
|
70
|
+
SYSTEM = "system" # /etc/gitwebhooks.ini (lowest priority)
|
|
71
|
+
|
|
72
|
+
def get_config_path(self) -> Path:
|
|
73
|
+
"""Get the configuration file path for this level
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
Path object pointing to the configuration file
|
|
77
|
+
"""
|
|
78
|
+
paths = {
|
|
79
|
+
ConfigLevel.USER: Path(CONFIG_PATH_USER).expanduser(),
|
|
80
|
+
ConfigLevel.LOCAL: Path(CONFIG_PATH_LOCAL),
|
|
81
|
+
ConfigLevel.SYSTEM: Path(CONFIG_PATH_SYSTEM),
|
|
82
|
+
}
|
|
83
|
+
return paths[self]
|
|
84
|
+
|
|
85
|
+
@classmethod
|
|
86
|
+
def from_string(cls, value: str) -> 'ConfigLevel':
|
|
87
|
+
"""Create ConfigLevel from string value
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
value: String value ('user', 'local', or 'system')
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
ConfigLevel enum value
|
|
94
|
+
|
|
95
|
+
Raises:
|
|
96
|
+
ValueError: If value is not a valid config level
|
|
97
|
+
"""
|
|
98
|
+
try:
|
|
99
|
+
return cls(value.lower())
|
|
100
|
+
except ValueError:
|
|
101
|
+
valid_values = ', '.join([e.value for e in cls])
|
|
102
|
+
raise ValueError(
|
|
103
|
+
f"Invalid config level '{value}'. Valid values are: {valid_values}"
|
|
104
|
+
)
|
gitwebhooks/utils/systemd.py
CHANGED
|
@@ -223,15 +223,15 @@ def generate_service_file(
|
|
|
223
223
|
cli_path: Path to gitwebhooks-cli executable
|
|
224
224
|
config_path: Path to configuration file
|
|
225
225
|
python_path: Python interpreter path (if using python -m mode)
|
|
226
|
-
use_python_module: If True, use 'python -m gitwebhooks.
|
|
226
|
+
use_python_module: If True, use 'python -m gitwebhooks.main' format
|
|
227
227
|
|
|
228
228
|
Returns:
|
|
229
229
|
Service file content as string
|
|
230
230
|
"""
|
|
231
231
|
if use_python_module and python_path:
|
|
232
|
-
exec_start = f"{python_path} -m gitwebhooks.
|
|
232
|
+
exec_start = f"{python_path} -m gitwebhooks.main serve -c {config_path}"
|
|
233
233
|
else:
|
|
234
|
-
exec_start = f"{cli_path} -c {config_path}"
|
|
234
|
+
exec_start = f"{cli_path} serve -c {config_path}"
|
|
235
235
|
|
|
236
236
|
return SERVICE_TEMPLATE.format(
|
|
237
237
|
exec_start=exec_start
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gitwebhooks
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
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>
|
|
@@ -169,28 +169,40 @@ This will create a configuration file with secure permissions (0600).
|
|
|
169
169
|
The service installation command automatically detects your installation type (pipx, venv, or system) and generates the appropriate service file.
|
|
170
170
|
|
|
171
171
|
```bash
|
|
172
|
-
# Install and start as a systemd service
|
|
173
|
-
sudo gitwebhooks-cli
|
|
172
|
+
# Install and start as a systemd service (interactive config level selection)
|
|
173
|
+
sudo gitwebhooks-cli serve install
|
|
174
|
+
|
|
175
|
+
# Install with specific config level (skip interactive selection)
|
|
176
|
+
sudo gitwebhooks-cli serve install --config-level user # User level (~/.gitwebhooks.ini)
|
|
177
|
+
sudo gitwebhooks-cli serve install --config-level local # Local level (/usr/local/etc/gitwebhooks.ini)
|
|
178
|
+
sudo gitwebhooks-cli serve install --config-level system # System level (/etc/gitwebhooks.ini)
|
|
179
|
+
|
|
180
|
+
# Use custom config file (backward compatibility)
|
|
181
|
+
sudo gitwebhooks-cli serve install -c /path/to/config.ini
|
|
174
182
|
|
|
175
183
|
# Preview service file without installing (dry-run mode)
|
|
176
|
-
sudo gitwebhooks-cli
|
|
184
|
+
sudo gitwebhooks-cli serve install --dry-run
|
|
185
|
+
sudo gitwebhooks-cli serve install --config-level local --dry-run # Preview specific level
|
|
177
186
|
|
|
178
187
|
# Install with verbose output
|
|
179
|
-
sudo gitwebhooks-cli
|
|
180
|
-
sudo gitwebhooks-cli service install -v # Basic verbosity
|
|
181
|
-
sudo gitwebhooks-cli service install -vv # Extra verbosity
|
|
188
|
+
sudo gitwebhooks-cli serve install --verbose
|
|
182
189
|
|
|
183
190
|
# Force overwrite existing service
|
|
184
|
-
sudo gitwebhooks-cli
|
|
191
|
+
sudo gitwebhooks-cli serve install --force
|
|
185
192
|
|
|
186
193
|
# Uninstall the service
|
|
187
|
-
sudo gitwebhooks-cli
|
|
194
|
+
sudo gitwebhooks-cli serve uninstall
|
|
188
195
|
```
|
|
189
196
|
|
|
197
|
+
**Configuration levels**:
|
|
198
|
+
- **user**: `~/.gitwebhooks.ini` - Single user configuration, highest priority
|
|
199
|
+
- **local**: `/usr/local/etc/gitwebhooks.ini` - Local system configuration, medium priority
|
|
200
|
+
- **system**: `/etc/gitwebhooks.ini` - Global system configuration, lowest priority
|
|
201
|
+
|
|
190
202
|
**Service file auto-detection**:
|
|
191
203
|
- **pipx installation**: Uses `gitwebhooks-cli` command directly
|
|
192
|
-
- **venv/virtualenv**: Uses `python -m gitwebhooks.
|
|
193
|
-
- **System pip**: Uses system Python with `python -m gitwebhooks.
|
|
204
|
+
- **venv/virtualenv**: Uses `python -m gitwebhooks.main` with environment's Python
|
|
205
|
+
- **System pip**: Uses system Python with `python -m gitwebhooks.main`
|
|
194
206
|
- **conda**: Not supported - installation will be refused with clear error message
|
|
195
207
|
|
|
196
208
|
### Manual Installation
|
|
@@ -208,7 +220,14 @@ sudo ln "$(pwd)/gitwebhooks-cli" /usr/local/bin/gitwebhooks-cli
|
|
|
208
220
|
### Verify Installation
|
|
209
221
|
|
|
210
222
|
```bash
|
|
211
|
-
|
|
223
|
+
# Show version
|
|
224
|
+
gitwebhooks-cli -v
|
|
225
|
+
|
|
226
|
+
# Show help
|
|
227
|
+
gitwebhooks-cli help
|
|
228
|
+
|
|
229
|
+
# View configuration
|
|
230
|
+
gitwebhooks-cli config view
|
|
212
231
|
```
|
|
213
232
|
|
|
214
233
|
## Migrating from System pip Installation
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
gitwebhooks/__init__.py,sha256=
|
|
1
|
+
gitwebhooks/__init__.py,sha256=mwkwwmLU4XQc5li5wJQkU4XRdOwlNvqcgi3dmdWwxuo,422
|
|
2
2
|
gitwebhooks/__main__.py,sha256=DW1lmngPDSFHJKSyaG2Dd5k3UguGSuYPUM2xKR8DhTo,189
|
|
3
3
|
gitwebhooks/server.py,sha256=keQcfOysoitx7cINcTkaDvhe6nScIFL2K8dCCViFF-o,3986
|
|
4
4
|
gitwebhooks/auth/__init__.py,sha256=Hu88BS6aOickrWBBWM2ppTzy2xOJfWODYkp752bPTPM,532
|
|
@@ -8,11 +8,13 @@ gitwebhooks/auth/gitee.py,sha256=8o_tKugZrW2or-uoRzjCo3h-WRlm3zbYmkTNbf5zL6s,324
|
|
|
8
8
|
gitwebhooks/auth/github.py,sha256=sY3xPwfYvD3tID5b26qHhTI1E9EW2VNHaFEygmsZYCg,1619
|
|
9
9
|
gitwebhooks/auth/gitlab.py,sha256=Aoz77VbYRASxNXqOAnK5__9hkGgaf9kLMoCgOnvFMNE,862
|
|
10
10
|
gitwebhooks/auth/verifier.py,sha256=onIvbmFVWpk7vkdEiyF48S4KrtbP48NH3FLz0fE24XE,971
|
|
11
|
-
gitwebhooks/cli/__init__.py,sha256=
|
|
11
|
+
gitwebhooks/cli/__init__.py,sha256=A8GyUtejo5qtzD9K_0mvuWGcs1WWYmeSQrJwoLz4b70,5657
|
|
12
12
|
gitwebhooks/cli/config.py,sha256=rVq7qh2KB9Z4Up2bc9zWDvGpf28iALdnu1pGIJ0p6-8,6373
|
|
13
|
+
gitwebhooks/cli/help.py,sha256=zjWrpKfs4GdvoxR9awy4Hc-pc6Lgyb-RFMV-bO1_TG8,4284
|
|
13
14
|
gitwebhooks/cli/init_wizard.py,sha256=7nJHFrXsqC5FS79CE3xyftP9_KW9Q5FO2Duuj8nuO4M,23926
|
|
14
|
-
gitwebhooks/cli/prompts.py,sha256=
|
|
15
|
-
gitwebhooks/cli/
|
|
15
|
+
gitwebhooks/cli/prompts.py,sha256=8XDUyplQqvcqDEARI3XI5qGh5PpJLYwHGCNuqERn4UI,8655
|
|
16
|
+
gitwebhooks/cli/serve.py,sha256=MP58IuuZpva0soMR34zxv-856XsMBkHJnF-VLETSK6w,4665
|
|
17
|
+
gitwebhooks/cli/service.py,sha256=cpMOTwjU58Nurjo5zBpsMJTNLo7GHixHe4CSPpmifdc,14175
|
|
16
18
|
gitwebhooks/config/__init__.py,sha256=CnU2ybNi8Yf5YqGWe7uls98ditREEuxuj5mW0t1ti0s,413
|
|
17
19
|
gitwebhooks/config/loader.py,sha256=fbuIPeDFx5f6-HjPuo35U5CMYxeH1ZWPD18vZJRpSiE,5774
|
|
18
20
|
gitwebhooks/config/models.py,sha256=oZ0ISLxh0zq5U7fqpAk2as7qHsOcbwj257BZEaK1zO8,4758
|
|
@@ -28,20 +30,22 @@ gitwebhooks/handlers/gitlab.py,sha256=UkK3R_KSKSt0s7XAtH1_G_uid85tqiL12qND8_Pnos
|
|
|
28
30
|
gitwebhooks/handlers/request.py,sha256=GsKH-R7L7j7h9GKEAse-O4tnmXeQMkbsT8WK_J8ACl8,9184
|
|
29
31
|
gitwebhooks/logging/__init__.py,sha256=CmL5WD2RgZ76uhrxzwnIfOEd_HgTZMTtTjjqYSY06GA,146
|
|
30
32
|
gitwebhooks/logging/setup.py,sha256=scu4nfEJTLR6QpmO4exXbKaMcTU11qHR2Jv8fEpEMD8,2095
|
|
31
|
-
gitwebhooks/main/__init__.py,sha256=
|
|
33
|
+
gitwebhooks/main/__init__.py,sha256=WV4io9b8qJ4_v6GExq_w-fF0v0eoEeL8fCpdT-aYAgk,7920
|
|
32
34
|
gitwebhooks/main/__main__.py,sha256=qE-72BSpj15NeV6-oWpMXR3ARmATeSGUO8VUB98sxF8,223
|
|
33
|
-
gitwebhooks/
|
|
35
|
+
gitwebhooks/main/version.py,sha256=u0iDPU2FE132Bq2s-fManXjDcoBxJ0jnRP77flN5Mug,497
|
|
36
|
+
gitwebhooks/models/__init__.py,sha256=3uS3rYD5EG2F8Vc-pJ02ufmMreL6sIcJ_fTjRh7V398,344
|
|
37
|
+
gitwebhooks/models/config.py,sha256=7T8vE-eNMRcd4dNPDwNNmB0NkTlh_Rk00XcgMIAy7bo,2419
|
|
34
38
|
gitwebhooks/models/provider.py,sha256=lmjJvGjklD1VTKB6UF9V-gZCPwXhiip8DR8qXs_A-F8,451
|
|
35
39
|
gitwebhooks/models/request.py,sha256=UkdTiBpX4-XipeOnhE0SvAaR53bFFh8EKftSVFjNBLM,1838
|
|
36
40
|
gitwebhooks/models/result.py,sha256=0M3fjeUH3CM4Mjpc9e4ZxBdfPO9xlODRRdlshVomQYQ,1020
|
|
37
41
|
gitwebhooks/utils/__init__.py,sha256=qZcrINToHF8qAtIIDAjOlzfKcohCVyYe2ZaKYsWbOxY,219
|
|
38
|
-
gitwebhooks/utils/constants.py,sha256=
|
|
42
|
+
gitwebhooks/utils/constants.py,sha256=PGmwV9lkH8VP8XblEopdm2lr71ZaNISXzuWcU0KfcAU,3220
|
|
39
43
|
gitwebhooks/utils/exceptions.py,sha256=Mfz-FOCiHZiYmpSQDw36S9HarLE2wPLfFbkeHfWJnkk,627
|
|
40
44
|
gitwebhooks/utils/executor.py,sha256=-zQUV5t-eq0JF6T4EvsUlvg4lnBtahRGrV8N3iVgx_c,923
|
|
41
|
-
gitwebhooks/utils/systemd.py,sha256=
|
|
42
|
-
gitwebhooks-2.
|
|
43
|
-
gitwebhooks-2.
|
|
44
|
-
gitwebhooks-2.
|
|
45
|
-
gitwebhooks-2.
|
|
46
|
-
gitwebhooks-2.
|
|
47
|
-
gitwebhooks-2.
|
|
45
|
+
gitwebhooks/utils/systemd.py,sha256=1g2-oByu-XTgZZI_FzsyLl7A1b-NKcwWqtzapvoVGUs,7915
|
|
46
|
+
gitwebhooks-2.3.0.dist-info/licenses/LICENSE,sha256=NV5F0xpN-DVJuhc0qzOsXtLjf3l8X18zehW_7F4qI50,1083
|
|
47
|
+
gitwebhooks-2.3.0.dist-info/METADATA,sha256=v521rAHS_nift5U89wwgElSumqjxcjAHnMV50_N6E58,15188
|
|
48
|
+
gitwebhooks-2.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
49
|
+
gitwebhooks-2.3.0.dist-info/entry_points.txt,sha256=kcbGwiZPNGnfEYlsrOaNQinGC1rTkiaDOF2NJ6S_5MU,58
|
|
50
|
+
gitwebhooks-2.3.0.dist-info/top_level.txt,sha256=9a6CxnHxBD53HL38GUeZETIvnUhUKcgDRIGJkCqwc3o,12
|
|
51
|
+
gitwebhooks-2.3.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|