cronbark 0.0.1__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.
- cronbark-0.0.1/LICENSE +21 -0
- cronbark-0.0.1/PKG-INFO +161 -0
- cronbark-0.0.1/README.md +130 -0
- cronbark-0.0.1/cronbark/__init__.py +50 -0
- cronbark-0.0.1/cronbark/cli.py +185 -0
- cronbark-0.0.1/cronbark/client.py +161 -0
- cronbark-0.0.1/cronbark.egg-info/PKG-INFO +161 -0
- cronbark-0.0.1/cronbark.egg-info/SOURCES.txt +13 -0
- cronbark-0.0.1/cronbark.egg-info/dependency_links.txt +1 -0
- cronbark-0.0.1/cronbark.egg-info/entry_points.txt +2 -0
- cronbark-0.0.1/cronbark.egg-info/requires.txt +2 -0
- cronbark-0.0.1/cronbark.egg-info/top_level.txt +1 -0
- cronbark-0.0.1/pyproject.toml +63 -0
- cronbark-0.0.1/setup.cfg +4 -0
- cronbark-0.0.1/tests/test_smoke.py +19 -0
cronbark-0.0.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 CronBark
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
cronbark-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cronbark
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: CronBark Python SDK & CLI — 크론잡 모니터링을 위한 간편 연동 도구
|
|
5
|
+
Author: CronBark Team
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/wookja-0/cronbark-sdk
|
|
8
|
+
Project-URL: Repository, https://github.com/wookja-0/cronbark-sdk
|
|
9
|
+
Project-URL: Issues, https://github.com/wookja-0/cronbark-sdk/issues
|
|
10
|
+
Project-URL: Documentation, https://cronbark.com/docs
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: System Administrators
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
24
|
+
Classifier: Topic :: System :: Monitoring
|
|
25
|
+
Requires-Python: >=3.9
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
Requires-Dist: httpx>=0.25.0
|
|
29
|
+
Requires-Dist: click>=8.0.0
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
|
|
32
|
+
# CronBark Python SDK
|
|
33
|
+
|
|
34
|
+
Python SDK and CLI for [CronBark](https://cronbark.com) — monitor any cron job
|
|
35
|
+
with a single decorator.
|
|
36
|
+
|
|
37
|
+
> **Preview / Alpha.** This SDK is in preview and the CronBark SaaS service is
|
|
38
|
+
> launching soon. The public API may change in backwards-incompatible ways
|
|
39
|
+
> before the 1.0 release.
|
|
40
|
+
|
|
41
|
+
## Install
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install cronbark
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Requires **Python 3.9+**.
|
|
48
|
+
|
|
49
|
+
## Quickstart
|
|
50
|
+
|
|
51
|
+
Get an API token from your CronBark dashboard, then pick whichever style fits
|
|
52
|
+
your job best.
|
|
53
|
+
|
|
54
|
+
### 1. Context manager
|
|
55
|
+
|
|
56
|
+
Wrap any block of code — `start` is sent on entry, and `success` or `fail`
|
|
57
|
+
(with the traceback) is sent on exit.
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
import cronbark
|
|
61
|
+
|
|
62
|
+
cronbark.configure(token="YOUR_TOKEN")
|
|
63
|
+
|
|
64
|
+
with cronbark.monitor():
|
|
65
|
+
run_backup()
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### 2. Decorator
|
|
69
|
+
|
|
70
|
+
Instrument an existing function in one line.
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
import cronbark
|
|
74
|
+
|
|
75
|
+
@cronbark.job("YOUR_TOKEN")
|
|
76
|
+
def generate_report():
|
|
77
|
+
...
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 3. CLI — wrap any command
|
|
81
|
+
|
|
82
|
+
The easiest way to monitor an existing cron entry without touching its source:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
cronbark exec --token YOUR_TOKEN "python backup.py"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Use it directly in `crontab`:
|
|
89
|
+
|
|
90
|
+
```crontab
|
|
91
|
+
# Before
|
|
92
|
+
0 * * * * /usr/bin/python3 /opt/scripts/backup.py
|
|
93
|
+
|
|
94
|
+
# After
|
|
95
|
+
0 * * * * cronbark exec --token YOUR_TOKEN "/usr/bin/python3 /opt/scripts/backup.py"
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The CLI captures stdout/stderr, forwards them to CronBark, and exits with the
|
|
99
|
+
same status code as the wrapped command.
|
|
100
|
+
|
|
101
|
+
## Configuration
|
|
102
|
+
|
|
103
|
+
The SDK reads configuration from environment variables by default:
|
|
104
|
+
|
|
105
|
+
| Variable | Purpose | Default |
|
|
106
|
+
|----------|---------|---------|
|
|
107
|
+
| `CRONBARK_TOKEN` | Your job's API token | _(none — required)_ |
|
|
108
|
+
| `CRONBARK_URL` | API base URL override (self-hosted, staging, etc.) | `https://api.cronbark.com` |
|
|
109
|
+
|
|
110
|
+
You can also configure the SDK programmatically:
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
import cronbark
|
|
114
|
+
|
|
115
|
+
cronbark.configure(
|
|
116
|
+
token="YOUR_TOKEN",
|
|
117
|
+
base_url="https://api.cronbark.com", # optional
|
|
118
|
+
timeout=10, # seconds, optional
|
|
119
|
+
)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Public API
|
|
123
|
+
|
|
124
|
+
Top-level functions exported from the `cronbark` package:
|
|
125
|
+
|
|
126
|
+
| Symbol | Description |
|
|
127
|
+
|--------|-------------|
|
|
128
|
+
| `configure(token, base_url, timeout)` | Set global SDK configuration. |
|
|
129
|
+
| `monitor(token=None)` | Context manager — auto `start` / `success` / `fail`. |
|
|
130
|
+
| `job(token=None)` | Decorator wrapping a function with `monitor`. |
|
|
131
|
+
| `ping(token=None)` | One-shot `start + success`. |
|
|
132
|
+
| `start(token=None)` | Report execution start. |
|
|
133
|
+
| `success(token=None, output=None, metrics=None)` | Report success. |
|
|
134
|
+
| `fail(token=None, error_message=None, output=None)` | Report failure. |
|
|
135
|
+
| `tick(token=None)` | Heartbeat — report success without a prior `start`. |
|
|
136
|
+
|
|
137
|
+
CLI commands (`cronbark --help`):
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
cronbark exec --token TOKEN "<command>" # wrap and run a command
|
|
141
|
+
cronbark ping TOKEN # one-shot start+success
|
|
142
|
+
cronbark start TOKEN
|
|
143
|
+
cronbark success TOKEN
|
|
144
|
+
cronbark fail TOKEN --msg "..."
|
|
145
|
+
cronbark tick TOKEN
|
|
146
|
+
cronbark discover # scan local crontab
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
All HTTP calls are best-effort: network errors are swallowed and returned as
|
|
150
|
+
`{"status": "error", "message": "..."}` so an unreachable monitoring service
|
|
151
|
+
will never break your job.
|
|
152
|
+
|
|
153
|
+
## Links
|
|
154
|
+
|
|
155
|
+
- Website: [cronbark.com](https://cronbark.com)
|
|
156
|
+
- Documentation: [cronbark.com/docs](https://cronbark.com/docs) _(coming soon)_
|
|
157
|
+
- Issues: [github.com/wookja-0/cronbark-sdk/issues](https://github.com/wookja-0/cronbark-sdk/issues)
|
|
158
|
+
|
|
159
|
+
## License
|
|
160
|
+
|
|
161
|
+
MIT — see [LICENSE](./LICENSE).
|
cronbark-0.0.1/README.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# CronBark Python SDK
|
|
2
|
+
|
|
3
|
+
Python SDK and CLI for [CronBark](https://cronbark.com) — monitor any cron job
|
|
4
|
+
with a single decorator.
|
|
5
|
+
|
|
6
|
+
> **Preview / Alpha.** This SDK is in preview and the CronBark SaaS service is
|
|
7
|
+
> launching soon. The public API may change in backwards-incompatible ways
|
|
8
|
+
> before the 1.0 release.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install cronbark
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Requires **Python 3.9+**.
|
|
17
|
+
|
|
18
|
+
## Quickstart
|
|
19
|
+
|
|
20
|
+
Get an API token from your CronBark dashboard, then pick whichever style fits
|
|
21
|
+
your job best.
|
|
22
|
+
|
|
23
|
+
### 1. Context manager
|
|
24
|
+
|
|
25
|
+
Wrap any block of code — `start` is sent on entry, and `success` or `fail`
|
|
26
|
+
(with the traceback) is sent on exit.
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
import cronbark
|
|
30
|
+
|
|
31
|
+
cronbark.configure(token="YOUR_TOKEN")
|
|
32
|
+
|
|
33
|
+
with cronbark.monitor():
|
|
34
|
+
run_backup()
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### 2. Decorator
|
|
38
|
+
|
|
39
|
+
Instrument an existing function in one line.
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
import cronbark
|
|
43
|
+
|
|
44
|
+
@cronbark.job("YOUR_TOKEN")
|
|
45
|
+
def generate_report():
|
|
46
|
+
...
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### 3. CLI — wrap any command
|
|
50
|
+
|
|
51
|
+
The easiest way to monitor an existing cron entry without touching its source:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
cronbark exec --token YOUR_TOKEN "python backup.py"
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Use it directly in `crontab`:
|
|
58
|
+
|
|
59
|
+
```crontab
|
|
60
|
+
# Before
|
|
61
|
+
0 * * * * /usr/bin/python3 /opt/scripts/backup.py
|
|
62
|
+
|
|
63
|
+
# After
|
|
64
|
+
0 * * * * cronbark exec --token YOUR_TOKEN "/usr/bin/python3 /opt/scripts/backup.py"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
The CLI captures stdout/stderr, forwards them to CronBark, and exits with the
|
|
68
|
+
same status code as the wrapped command.
|
|
69
|
+
|
|
70
|
+
## Configuration
|
|
71
|
+
|
|
72
|
+
The SDK reads configuration from environment variables by default:
|
|
73
|
+
|
|
74
|
+
| Variable | Purpose | Default |
|
|
75
|
+
|----------|---------|---------|
|
|
76
|
+
| `CRONBARK_TOKEN` | Your job's API token | _(none — required)_ |
|
|
77
|
+
| `CRONBARK_URL` | API base URL override (self-hosted, staging, etc.) | `https://api.cronbark.com` |
|
|
78
|
+
|
|
79
|
+
You can also configure the SDK programmatically:
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
import cronbark
|
|
83
|
+
|
|
84
|
+
cronbark.configure(
|
|
85
|
+
token="YOUR_TOKEN",
|
|
86
|
+
base_url="https://api.cronbark.com", # optional
|
|
87
|
+
timeout=10, # seconds, optional
|
|
88
|
+
)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Public API
|
|
92
|
+
|
|
93
|
+
Top-level functions exported from the `cronbark` package:
|
|
94
|
+
|
|
95
|
+
| Symbol | Description |
|
|
96
|
+
|--------|-------------|
|
|
97
|
+
| `configure(token, base_url, timeout)` | Set global SDK configuration. |
|
|
98
|
+
| `monitor(token=None)` | Context manager — auto `start` / `success` / `fail`. |
|
|
99
|
+
| `job(token=None)` | Decorator wrapping a function with `monitor`. |
|
|
100
|
+
| `ping(token=None)` | One-shot `start + success`. |
|
|
101
|
+
| `start(token=None)` | Report execution start. |
|
|
102
|
+
| `success(token=None, output=None, metrics=None)` | Report success. |
|
|
103
|
+
| `fail(token=None, error_message=None, output=None)` | Report failure. |
|
|
104
|
+
| `tick(token=None)` | Heartbeat — report success without a prior `start`. |
|
|
105
|
+
|
|
106
|
+
CLI commands (`cronbark --help`):
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
cronbark exec --token TOKEN "<command>" # wrap and run a command
|
|
110
|
+
cronbark ping TOKEN # one-shot start+success
|
|
111
|
+
cronbark start TOKEN
|
|
112
|
+
cronbark success TOKEN
|
|
113
|
+
cronbark fail TOKEN --msg "..."
|
|
114
|
+
cronbark tick TOKEN
|
|
115
|
+
cronbark discover # scan local crontab
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
All HTTP calls are best-effort: network errors are swallowed and returned as
|
|
119
|
+
`{"status": "error", "message": "..."}` so an unreachable monitoring service
|
|
120
|
+
will never break your job.
|
|
121
|
+
|
|
122
|
+
## Links
|
|
123
|
+
|
|
124
|
+
- Website: [cronbark.com](https://cronbark.com)
|
|
125
|
+
- Documentation: [cronbark.com/docs](https://cronbark.com/docs) _(coming soon)_
|
|
126
|
+
- Issues: [github.com/wookja-0/cronbark-sdk/issues](https://github.com/wookja-0/cronbark-sdk/issues)
|
|
127
|
+
|
|
128
|
+
## License
|
|
129
|
+
|
|
130
|
+
MIT — see [LICENSE](./LICENSE).
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CronBark Python SDK
|
|
3
|
+
크론잡 모니터링을 위한 간편 연동 라이브러리.
|
|
4
|
+
|
|
5
|
+
사용 예:
|
|
6
|
+
import cronbark
|
|
7
|
+
|
|
8
|
+
# 환경변수 또는 직접 설정
|
|
9
|
+
cronbark.configure(token="YOUR_TOKEN", base_url="https://api.cronbark.com")
|
|
10
|
+
|
|
11
|
+
# 컨텍스트 매니저 — 자동 start/success/fail
|
|
12
|
+
with cronbark.monitor("backup-daily"):
|
|
13
|
+
run_backup()
|
|
14
|
+
|
|
15
|
+
# 데코레이터
|
|
16
|
+
@cronbark.job("report-gen")
|
|
17
|
+
def generate_report():
|
|
18
|
+
...
|
|
19
|
+
|
|
20
|
+
# 수동 호출
|
|
21
|
+
cronbark.ping("YOUR_TOKEN")
|
|
22
|
+
cronbark.start("YOUR_TOKEN")
|
|
23
|
+
cronbark.success("YOUR_TOKEN", output="Done: 100 rows")
|
|
24
|
+
cronbark.fail("YOUR_TOKEN", error_message="Connection refused")
|
|
25
|
+
cronbark.tick("YOUR_TOKEN")
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from cronbark.client import (
|
|
29
|
+
configure,
|
|
30
|
+
fail,
|
|
31
|
+
job,
|
|
32
|
+
monitor,
|
|
33
|
+
ping,
|
|
34
|
+
start,
|
|
35
|
+
success,
|
|
36
|
+
tick,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
__version__ = "0.0.1"
|
|
40
|
+
|
|
41
|
+
__all__ = [
|
|
42
|
+
"configure",
|
|
43
|
+
"monitor",
|
|
44
|
+
"job",
|
|
45
|
+
"ping",
|
|
46
|
+
"start",
|
|
47
|
+
"success",
|
|
48
|
+
"fail",
|
|
49
|
+
"tick",
|
|
50
|
+
]
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CronBark CLI — 터미널에서 크론잡을 모니터링하는 명령줄 도구.
|
|
3
|
+
|
|
4
|
+
사용 예:
|
|
5
|
+
# 명령어 감싸기 (자동 start → 실행 → success/fail)
|
|
6
|
+
cronbark exec --token TOKEN "python backup.py"
|
|
7
|
+
|
|
8
|
+
# 수동 이벤트 전송
|
|
9
|
+
cronbark ping TOKEN
|
|
10
|
+
cronbark start TOKEN
|
|
11
|
+
cronbark success TOKEN
|
|
12
|
+
cronbark fail TOKEN --msg "Connection refused"
|
|
13
|
+
cronbark tick TOKEN
|
|
14
|
+
|
|
15
|
+
# crontab 자동 스캔
|
|
16
|
+
cronbark discover
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import os
|
|
20
|
+
import subprocess
|
|
21
|
+
import sys
|
|
22
|
+
|
|
23
|
+
import click
|
|
24
|
+
|
|
25
|
+
from cronbark.client import configure, fail, ping, start, success, tick
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@click.group()
|
|
29
|
+
@click.option(
|
|
30
|
+
"--url",
|
|
31
|
+
envvar="CRONBARK_URL",
|
|
32
|
+
default="https://api.cronbark.com",
|
|
33
|
+
help="CronBark API URL (기본: https://api.cronbark.com)",
|
|
34
|
+
)
|
|
35
|
+
def main(url: str):
|
|
36
|
+
"""CronBark CLI - 크론잡 모니터링 도구"""
|
|
37
|
+
configure(base_url=url)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@main.command(name="ping")
|
|
41
|
+
@click.argument("token")
|
|
42
|
+
def do_ping(token: str):
|
|
43
|
+
"""간단 ping (start + success 한 번에)"""
|
|
44
|
+
result = ping(token)
|
|
45
|
+
click.echo(f"[cronbark] {result.get('status', 'error')}: {result.get('message', '')}")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@main.command(name="start")
|
|
49
|
+
@click.argument("token")
|
|
50
|
+
def do_start(token: str):
|
|
51
|
+
"""실행 시작 알림"""
|
|
52
|
+
result = start(token)
|
|
53
|
+
click.echo(f"[cronbark] {result.get('status', 'error')}: {result.get('message', '')}")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@main.command(name="success")
|
|
57
|
+
@click.argument("token")
|
|
58
|
+
def do_success(token: str):
|
|
59
|
+
"""실행 성공 알림"""
|
|
60
|
+
result = success(token)
|
|
61
|
+
click.echo(f"[cronbark] {result.get('status', 'error')}: {result.get('message', '')}")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@main.command(name="fail")
|
|
65
|
+
@click.argument("token")
|
|
66
|
+
@click.option("--msg", default=None, help="에러 메시지")
|
|
67
|
+
def do_fail(token: str, msg: str):
|
|
68
|
+
"""실행 실패 알림"""
|
|
69
|
+
result = fail(token, error_message=msg)
|
|
70
|
+
click.echo(f"[cronbark] {result.get('status', 'error')}: {result.get('message', '')}")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@main.command(name="tick")
|
|
74
|
+
@click.argument("token")
|
|
75
|
+
def do_tick(token: str):
|
|
76
|
+
"""하트비트 전송"""
|
|
77
|
+
result = tick(token)
|
|
78
|
+
click.echo(f"[cronbark] {result.get('status', 'error')}: {result.get('message', '')}")
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@main.command(name="exec")
|
|
82
|
+
@click.option("--token", envvar="CRONBARK_TOKEN", required=True, help="API 토큰")
|
|
83
|
+
@click.argument("command", nargs=-1, required=True)
|
|
84
|
+
def do_exec(token: str, command: tuple):
|
|
85
|
+
"""
|
|
86
|
+
명령어를 실행하고 결과를 자동 보고한다.
|
|
87
|
+
|
|
88
|
+
사용: cronbark exec --token TOKEN "python backup.py"
|
|
89
|
+
"""
|
|
90
|
+
cmd_str = " ".join(command)
|
|
91
|
+
click.echo(f"[cronbark] 실행: {cmd_str}")
|
|
92
|
+
|
|
93
|
+
# start 전송
|
|
94
|
+
start(token)
|
|
95
|
+
|
|
96
|
+
try:
|
|
97
|
+
# 명령어 실행
|
|
98
|
+
result = subprocess.run(
|
|
99
|
+
cmd_str,
|
|
100
|
+
shell=True,
|
|
101
|
+
capture_output=True,
|
|
102
|
+
text=True,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
output = ""
|
|
106
|
+
if result.stdout:
|
|
107
|
+
output += result.stdout
|
|
108
|
+
if result.stderr:
|
|
109
|
+
output += "\n--- STDERR ---\n" + result.stderr
|
|
110
|
+
|
|
111
|
+
if result.returncode == 0:
|
|
112
|
+
# 성공
|
|
113
|
+
resp = success(token, output=output[:1_000_000] if output else None)
|
|
114
|
+
click.echo(f"[cronbark] 성공 (exit code 0)")
|
|
115
|
+
else:
|
|
116
|
+
# 실패
|
|
117
|
+
error_msg = f"Exit code {result.returncode}"
|
|
118
|
+
if result.stderr:
|
|
119
|
+
error_msg += f": {result.stderr[:500]}"
|
|
120
|
+
resp = fail(token, error_message=error_msg, output=output[:1_000_000] if output else None)
|
|
121
|
+
click.echo(f"[cronbark] 실패 (exit code {result.returncode})")
|
|
122
|
+
|
|
123
|
+
sys.exit(result.returncode)
|
|
124
|
+
|
|
125
|
+
except Exception as e:
|
|
126
|
+
fail(token, error_message=str(e))
|
|
127
|
+
click.echo(f"[cronbark] 에러: {e}")
|
|
128
|
+
sys.exit(1)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@main.command()
|
|
132
|
+
def discover():
|
|
133
|
+
"""
|
|
134
|
+
시스템의 crontab을 스캔하여 등록 가능한 크론잡 목록을 표시한다.
|
|
135
|
+
"""
|
|
136
|
+
click.echo("[cronbark] crontab 스캔 중...")
|
|
137
|
+
|
|
138
|
+
try:
|
|
139
|
+
# 현재 사용자의 crontab 조회
|
|
140
|
+
result = subprocess.run(
|
|
141
|
+
["crontab", "-l"],
|
|
142
|
+
capture_output=True,
|
|
143
|
+
text=True,
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
if result.returncode != 0:
|
|
147
|
+
click.echo("[cronbark] crontab이 비어있거나 접근할 수 없습니다.")
|
|
148
|
+
return
|
|
149
|
+
|
|
150
|
+
lines = result.stdout.strip().split("\n")
|
|
151
|
+
jobs = []
|
|
152
|
+
for line in lines:
|
|
153
|
+
line = line.strip()
|
|
154
|
+
# 주석과 빈 줄 건너뛰기
|
|
155
|
+
if not line or line.startswith("#"):
|
|
156
|
+
continue
|
|
157
|
+
# 환경변수 설정 건너뛰기
|
|
158
|
+
if "=" in line.split()[0]:
|
|
159
|
+
continue
|
|
160
|
+
jobs.append(line)
|
|
161
|
+
|
|
162
|
+
if not jobs:
|
|
163
|
+
click.echo("[cronbark] 등록된 crontab 작업이 없습니다.")
|
|
164
|
+
return
|
|
165
|
+
|
|
166
|
+
click.echo(f"\n발견된 크론잡 {len(jobs)}개:\n")
|
|
167
|
+
click.echo(f"{'#':<4} {'스케줄':<20} {'명령어'}")
|
|
168
|
+
click.echo("-" * 70)
|
|
169
|
+
for i, job_line in enumerate(jobs, 1):
|
|
170
|
+
parts = job_line.split(None, 5)
|
|
171
|
+
if len(parts) >= 6:
|
|
172
|
+
schedule = " ".join(parts[:5])
|
|
173
|
+
command = parts[5]
|
|
174
|
+
else:
|
|
175
|
+
schedule = "파싱 불가"
|
|
176
|
+
command = job_line
|
|
177
|
+
click.echo(f"{i:<4} {schedule:<20} {command}")
|
|
178
|
+
|
|
179
|
+
click.echo(f"\n[cronbark] 위 크론잡을 CronBark에 등록하려면 각 crontab 명령어를 다음처럼 감싸세요:")
|
|
180
|
+
click.echo(' cronbark exec --token YOUR_TOKEN "명령어"')
|
|
181
|
+
|
|
182
|
+
except FileNotFoundError:
|
|
183
|
+
click.echo("[cronbark] crontab 명령어를 찾을 수 없습니다. (Windows에서는 지원되지 않습니다)")
|
|
184
|
+
except Exception as e:
|
|
185
|
+
click.echo(f"[cronbark] 스캔 에러: {e}")
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CronBark Python SDK 클라이언트.
|
|
3
|
+
HTTP API를 래핑하여 간편한 크론잡 모니터링을 제공한다.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import functools
|
|
7
|
+
import os
|
|
8
|
+
import sys
|
|
9
|
+
import traceback
|
|
10
|
+
from contextlib import contextmanager
|
|
11
|
+
from typing import Any, Callable, Optional
|
|
12
|
+
|
|
13
|
+
import httpx
|
|
14
|
+
|
|
15
|
+
# ── 글로벌 설정 ────────────────────────────────────────────────
|
|
16
|
+
|
|
17
|
+
_config = {
|
|
18
|
+
"token": os.environ.get("CRONBARK_TOKEN", ""),
|
|
19
|
+
"base_url": os.environ.get("CRONBARK_URL", "https://api.cronbark.com"),
|
|
20
|
+
"timeout": 10,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def configure(
|
|
25
|
+
token: Optional[str] = None,
|
|
26
|
+
base_url: Optional[str] = None,
|
|
27
|
+
timeout: int = 10,
|
|
28
|
+
) -> None:
|
|
29
|
+
"""SDK 글로벌 설정을 변경한다."""
|
|
30
|
+
if token is not None:
|
|
31
|
+
_config["token"] = token
|
|
32
|
+
if base_url is not None:
|
|
33
|
+
_config["base_url"] = base_url
|
|
34
|
+
_config["timeout"] = timeout
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _get_token(token: Optional[str] = None) -> str:
|
|
38
|
+
"""토큰을 결정한다 (직접 전달 > 환경변수 > 글로벌 설정)."""
|
|
39
|
+
return token or _config["token"] or os.environ.get("CRONBARK_TOKEN", "")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _api_url(token: str, endpoint: str) -> str:
|
|
43
|
+
"""API URL을 구성한다."""
|
|
44
|
+
base = _config["base_url"].rstrip("/")
|
|
45
|
+
return f"{base}/api/v1/health/{token}/{endpoint}"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _send(token: str, endpoint: str, body: Optional[dict] = None) -> dict:
|
|
49
|
+
"""
|
|
50
|
+
텔레메트리 이벤트를 전송한다.
|
|
51
|
+
실패해도 예외를 발생시키지 않고 에러 dict를 반환한다.
|
|
52
|
+
"""
|
|
53
|
+
url = _api_url(token, endpoint)
|
|
54
|
+
try:
|
|
55
|
+
with httpx.Client(timeout=_config["timeout"]) as client:
|
|
56
|
+
if body:
|
|
57
|
+
resp = client.post(url, json=body)
|
|
58
|
+
else:
|
|
59
|
+
resp = client.post(url)
|
|
60
|
+
return resp.json()
|
|
61
|
+
except Exception as e:
|
|
62
|
+
return {"status": "error", "message": str(e)}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
# ── 공개 API ─────────────────────────────────────────────────
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def ping(token: Optional[str] = None) -> dict:
|
|
69
|
+
"""간단 ping — start+success를 한 번에."""
|
|
70
|
+
t = _get_token(token)
|
|
71
|
+
base = _config["base_url"].rstrip("/")
|
|
72
|
+
url = f"{base}/api/v1/ping/{t}"
|
|
73
|
+
try:
|
|
74
|
+
with httpx.Client(timeout=_config["timeout"]) as client:
|
|
75
|
+
resp = client.get(url)
|
|
76
|
+
return resp.json()
|
|
77
|
+
except Exception as e:
|
|
78
|
+
return {"status": "error", "message": str(e)}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def start(token: Optional[str] = None) -> dict:
|
|
82
|
+
"""실행 시작을 알린다."""
|
|
83
|
+
return _send(_get_token(token), "start")
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def success(
|
|
87
|
+
token: Optional[str] = None,
|
|
88
|
+
output: Optional[str] = None,
|
|
89
|
+
metrics: Optional[dict] = None,
|
|
90
|
+
) -> dict:
|
|
91
|
+
"""실행 성공을 알린다. output/metrics를 함께 전송할 수 있다."""
|
|
92
|
+
body = {}
|
|
93
|
+
if output:
|
|
94
|
+
body["output"] = output
|
|
95
|
+
if metrics:
|
|
96
|
+
body["metrics"] = metrics
|
|
97
|
+
return _send(_get_token(token), "success", body or None)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def fail(
|
|
101
|
+
token: Optional[str] = None,
|
|
102
|
+
error_message: Optional[str] = None,
|
|
103
|
+
output: Optional[str] = None,
|
|
104
|
+
) -> dict:
|
|
105
|
+
"""실행 실패를 알린다."""
|
|
106
|
+
body = {}
|
|
107
|
+
if error_message:
|
|
108
|
+
body["error_message"] = error_message
|
|
109
|
+
if output:
|
|
110
|
+
body["output"] = output
|
|
111
|
+
return _send(_get_token(token), "fail", body or None)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def tick(token: Optional[str] = None) -> dict:
|
|
115
|
+
"""하트비트를 전송한다 (start 없이 즉시 성공)."""
|
|
116
|
+
return _send(_get_token(token), "tick")
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
# ── 컨텍스트 매니저 & 데코레이터 ───────────────────────────────
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@contextmanager
|
|
123
|
+
def monitor(token: Optional[str] = None):
|
|
124
|
+
"""
|
|
125
|
+
컨텍스트 매니저 — with 블록 진입 시 start, 종료 시 success/fail 자동 전송.
|
|
126
|
+
|
|
127
|
+
사용 예:
|
|
128
|
+
with cronbark.monitor("YOUR_TOKEN"):
|
|
129
|
+
run_backup()
|
|
130
|
+
"""
|
|
131
|
+
t = _get_token(token)
|
|
132
|
+
start(t)
|
|
133
|
+
try:
|
|
134
|
+
yield
|
|
135
|
+
success(t)
|
|
136
|
+
except Exception as e:
|
|
137
|
+
# stdout/stderr 캡처
|
|
138
|
+
tb = traceback.format_exc()
|
|
139
|
+
fail(t, error_message=str(e), output=tb)
|
|
140
|
+
raise
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def job(token: Optional[str] = None) -> Callable:
|
|
144
|
+
"""
|
|
145
|
+
데코레이터 — 함수 실행을 자동으로 모니터링한다.
|
|
146
|
+
|
|
147
|
+
사용 예:
|
|
148
|
+
@cronbark.job("YOUR_TOKEN")
|
|
149
|
+
def generate_report():
|
|
150
|
+
...
|
|
151
|
+
"""
|
|
152
|
+
|
|
153
|
+
def decorator(func: Callable) -> Callable:
|
|
154
|
+
@functools.wraps(func)
|
|
155
|
+
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
156
|
+
with monitor(token):
|
|
157
|
+
return func(*args, **kwargs)
|
|
158
|
+
|
|
159
|
+
return wrapper
|
|
160
|
+
|
|
161
|
+
return decorator
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cronbark
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: CronBark Python SDK & CLI — 크론잡 모니터링을 위한 간편 연동 도구
|
|
5
|
+
Author: CronBark Team
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/wookja-0/cronbark-sdk
|
|
8
|
+
Project-URL: Repository, https://github.com/wookja-0/cronbark-sdk
|
|
9
|
+
Project-URL: Issues, https://github.com/wookja-0/cronbark-sdk/issues
|
|
10
|
+
Project-URL: Documentation, https://cronbark.com/docs
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: System Administrators
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
24
|
+
Classifier: Topic :: System :: Monitoring
|
|
25
|
+
Requires-Python: >=3.9
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
Requires-Dist: httpx>=0.25.0
|
|
29
|
+
Requires-Dist: click>=8.0.0
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
|
|
32
|
+
# CronBark Python SDK
|
|
33
|
+
|
|
34
|
+
Python SDK and CLI for [CronBark](https://cronbark.com) — monitor any cron job
|
|
35
|
+
with a single decorator.
|
|
36
|
+
|
|
37
|
+
> **Preview / Alpha.** This SDK is in preview and the CronBark SaaS service is
|
|
38
|
+
> launching soon. The public API may change in backwards-incompatible ways
|
|
39
|
+
> before the 1.0 release.
|
|
40
|
+
|
|
41
|
+
## Install
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install cronbark
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Requires **Python 3.9+**.
|
|
48
|
+
|
|
49
|
+
## Quickstart
|
|
50
|
+
|
|
51
|
+
Get an API token from your CronBark dashboard, then pick whichever style fits
|
|
52
|
+
your job best.
|
|
53
|
+
|
|
54
|
+
### 1. Context manager
|
|
55
|
+
|
|
56
|
+
Wrap any block of code — `start` is sent on entry, and `success` or `fail`
|
|
57
|
+
(with the traceback) is sent on exit.
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
import cronbark
|
|
61
|
+
|
|
62
|
+
cronbark.configure(token="YOUR_TOKEN")
|
|
63
|
+
|
|
64
|
+
with cronbark.monitor():
|
|
65
|
+
run_backup()
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### 2. Decorator
|
|
69
|
+
|
|
70
|
+
Instrument an existing function in one line.
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
import cronbark
|
|
74
|
+
|
|
75
|
+
@cronbark.job("YOUR_TOKEN")
|
|
76
|
+
def generate_report():
|
|
77
|
+
...
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 3. CLI — wrap any command
|
|
81
|
+
|
|
82
|
+
The easiest way to monitor an existing cron entry without touching its source:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
cronbark exec --token YOUR_TOKEN "python backup.py"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Use it directly in `crontab`:
|
|
89
|
+
|
|
90
|
+
```crontab
|
|
91
|
+
# Before
|
|
92
|
+
0 * * * * /usr/bin/python3 /opt/scripts/backup.py
|
|
93
|
+
|
|
94
|
+
# After
|
|
95
|
+
0 * * * * cronbark exec --token YOUR_TOKEN "/usr/bin/python3 /opt/scripts/backup.py"
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The CLI captures stdout/stderr, forwards them to CronBark, and exits with the
|
|
99
|
+
same status code as the wrapped command.
|
|
100
|
+
|
|
101
|
+
## Configuration
|
|
102
|
+
|
|
103
|
+
The SDK reads configuration from environment variables by default:
|
|
104
|
+
|
|
105
|
+
| Variable | Purpose | Default |
|
|
106
|
+
|----------|---------|---------|
|
|
107
|
+
| `CRONBARK_TOKEN` | Your job's API token | _(none — required)_ |
|
|
108
|
+
| `CRONBARK_URL` | API base URL override (self-hosted, staging, etc.) | `https://api.cronbark.com` |
|
|
109
|
+
|
|
110
|
+
You can also configure the SDK programmatically:
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
import cronbark
|
|
114
|
+
|
|
115
|
+
cronbark.configure(
|
|
116
|
+
token="YOUR_TOKEN",
|
|
117
|
+
base_url="https://api.cronbark.com", # optional
|
|
118
|
+
timeout=10, # seconds, optional
|
|
119
|
+
)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Public API
|
|
123
|
+
|
|
124
|
+
Top-level functions exported from the `cronbark` package:
|
|
125
|
+
|
|
126
|
+
| Symbol | Description |
|
|
127
|
+
|--------|-------------|
|
|
128
|
+
| `configure(token, base_url, timeout)` | Set global SDK configuration. |
|
|
129
|
+
| `monitor(token=None)` | Context manager — auto `start` / `success` / `fail`. |
|
|
130
|
+
| `job(token=None)` | Decorator wrapping a function with `monitor`. |
|
|
131
|
+
| `ping(token=None)` | One-shot `start + success`. |
|
|
132
|
+
| `start(token=None)` | Report execution start. |
|
|
133
|
+
| `success(token=None, output=None, metrics=None)` | Report success. |
|
|
134
|
+
| `fail(token=None, error_message=None, output=None)` | Report failure. |
|
|
135
|
+
| `tick(token=None)` | Heartbeat — report success without a prior `start`. |
|
|
136
|
+
|
|
137
|
+
CLI commands (`cronbark --help`):
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
cronbark exec --token TOKEN "<command>" # wrap and run a command
|
|
141
|
+
cronbark ping TOKEN # one-shot start+success
|
|
142
|
+
cronbark start TOKEN
|
|
143
|
+
cronbark success TOKEN
|
|
144
|
+
cronbark fail TOKEN --msg "..."
|
|
145
|
+
cronbark tick TOKEN
|
|
146
|
+
cronbark discover # scan local crontab
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
All HTTP calls are best-effort: network errors are swallowed and returned as
|
|
150
|
+
`{"status": "error", "message": "..."}` so an unreachable monitoring service
|
|
151
|
+
will never break your job.
|
|
152
|
+
|
|
153
|
+
## Links
|
|
154
|
+
|
|
155
|
+
- Website: [cronbark.com](https://cronbark.com)
|
|
156
|
+
- Documentation: [cronbark.com/docs](https://cronbark.com/docs) _(coming soon)_
|
|
157
|
+
- Issues: [github.com/wookja-0/cronbark-sdk/issues](https://github.com/wookja-0/cronbark-sdk/issues)
|
|
158
|
+
|
|
159
|
+
## License
|
|
160
|
+
|
|
161
|
+
MIT — see [LICENSE](./LICENSE).
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
cronbark/__init__.py
|
|
5
|
+
cronbark/cli.py
|
|
6
|
+
cronbark/client.py
|
|
7
|
+
cronbark.egg-info/PKG-INFO
|
|
8
|
+
cronbark.egg-info/SOURCES.txt
|
|
9
|
+
cronbark.egg-info/dependency_links.txt
|
|
10
|
+
cronbark.egg-info/entry_points.txt
|
|
11
|
+
cronbark.egg-info/requires.txt
|
|
12
|
+
cronbark.egg-info/top_level.txt
|
|
13
|
+
tests/test_smoke.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
cronbark
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "cronbark"
|
|
7
|
+
version = "0.0.1"
|
|
8
|
+
description = "CronBark Python SDK & CLI — 크론잡 모니터링을 위한 간편 연동 도구"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
authors = [{name = "CronBark Team"}]
|
|
13
|
+
dependencies = [
|
|
14
|
+
"httpx>=0.25.0",
|
|
15
|
+
"click>=8.0.0",
|
|
16
|
+
]
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Development Status :: 3 - Alpha",
|
|
19
|
+
"Intended Audience :: Developers",
|
|
20
|
+
"Intended Audience :: System Administrators",
|
|
21
|
+
"License :: OSI Approved :: MIT License",
|
|
22
|
+
"Operating System :: OS Independent",
|
|
23
|
+
"Programming Language :: Python",
|
|
24
|
+
"Programming Language :: Python :: 3",
|
|
25
|
+
"Programming Language :: Python :: 3.9",
|
|
26
|
+
"Programming Language :: Python :: 3.10",
|
|
27
|
+
"Programming Language :: Python :: 3.11",
|
|
28
|
+
"Programming Language :: Python :: 3.12",
|
|
29
|
+
"Programming Language :: Python :: 3.13",
|
|
30
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
31
|
+
"Topic :: System :: Monitoring",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[project.scripts]
|
|
35
|
+
cronbark = "cronbark.cli:main"
|
|
36
|
+
|
|
37
|
+
[project.urls]
|
|
38
|
+
Homepage = "https://github.com/wookja-0/cronbark-sdk"
|
|
39
|
+
Repository = "https://github.com/wookja-0/cronbark-sdk"
|
|
40
|
+
Issues = "https://github.com/wookja-0/cronbark-sdk/issues"
|
|
41
|
+
Documentation = "https://cronbark.com/docs"
|
|
42
|
+
|
|
43
|
+
[tool.setuptools.packages.find]
|
|
44
|
+
include = ["cronbark*"]
|
|
45
|
+
|
|
46
|
+
[tool.ruff]
|
|
47
|
+
# 초기 릴리스 — 느슨한 설정, 나중에 조일 수 있음
|
|
48
|
+
line-length = 100
|
|
49
|
+
target-version = "py39"
|
|
50
|
+
|
|
51
|
+
[tool.ruff.lint]
|
|
52
|
+
# pycodestyle/pyflakes 기본만, 노이즈 규칙 제외
|
|
53
|
+
select = ["E", "F", "W"]
|
|
54
|
+
ignore = [
|
|
55
|
+
"E501", # line too long (line-length 로 제어)
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
[tool.ruff.lint.per-file-ignores]
|
|
59
|
+
"__init__.py" = ["F401"] # 재수출 허용
|
|
60
|
+
"tests/*" = ["F401", "F811"]
|
|
61
|
+
|
|
62
|
+
[tool.pytest.ini_options]
|
|
63
|
+
testpaths = ["tests"]
|
cronbark-0.0.1/setup.cfg
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""스모크 테스트 — 패키지 임포트 및 버전 확인."""
|
|
2
|
+
|
|
3
|
+
import cronbark
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_import():
|
|
7
|
+
"""패키지가 정상적으로 임포트되는지 확인."""
|
|
8
|
+
assert cronbark is not None
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def test_version():
|
|
12
|
+
"""버전이 예상 값과 일치하는지 확인."""
|
|
13
|
+
assert cronbark.__version__ == "0.0.1"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_public_api():
|
|
17
|
+
"""공개 API가 모두 export 되어 있는지 확인."""
|
|
18
|
+
for name in ("configure", "monitor", "job", "ping", "start", "success", "fail", "tick"):
|
|
19
|
+
assert hasattr(cronbark, name), f"cronbark.{name} 누락"
|