zabbix-crontroller 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.
@@ -0,0 +1,218 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[codz]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py.cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ # Pipfile.lock
96
+
97
+ # UV
98
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ # uv.lock
102
+
103
+ # poetry
104
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
105
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
106
+ # commonly ignored for libraries.
107
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
108
+ # poetry.lock
109
+ # poetry.toml
110
+
111
+ # pdm
112
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
113
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
114
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
115
+ # pdm.lock
116
+ # pdm.toml
117
+ .pdm-python
118
+ .pdm-build/
119
+
120
+ # pixi
121
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
122
+ # pixi.lock
123
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
124
+ # in the .venv directory. It is recommended not to include this directory in version control.
125
+ .pixi
126
+
127
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
128
+ __pypackages__/
129
+
130
+ # Celery stuff
131
+ celerybeat-schedule
132
+ celerybeat.pid
133
+
134
+ # Redis
135
+ *.rdb
136
+ *.aof
137
+ *.pid
138
+
139
+ # RabbitMQ
140
+ mnesia/
141
+ rabbitmq/
142
+ rabbitmq-data/
143
+
144
+ # ActiveMQ
145
+ activemq-data/
146
+
147
+ # SageMath parsed files
148
+ *.sage.py
149
+
150
+ # Environments
151
+ .env
152
+ .envrc
153
+ .venv
154
+ env/
155
+ venv/
156
+ ENV/
157
+ env.bak/
158
+ venv.bak/
159
+
160
+ # Spyder project settings
161
+ .spyderproject
162
+ .spyproject
163
+
164
+ # Rope project settings
165
+ .ropeproject
166
+
167
+ # mkdocs documentation
168
+ /site
169
+
170
+ # mypy
171
+ .mypy_cache/
172
+ .dmypy.json
173
+ dmypy.json
174
+
175
+ # Pyre type checker
176
+ .pyre/
177
+
178
+ # pytype static type analyzer
179
+ .pytype/
180
+
181
+ # Cython debug symbols
182
+ cython_debug/
183
+
184
+ # PyCharm
185
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
186
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
187
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
188
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
189
+ # .idea/
190
+
191
+ # Abstra
192
+ # Abstra is an AI-powered process automation framework.
193
+ # Ignore directories containing user credentials, local state, and settings.
194
+ # Learn more at https://abstra.io/docs
195
+ .abstra/
196
+
197
+ # Visual Studio Code
198
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
199
+ # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
200
+ # and can be added to the global gitignore or merged into this file. However, if you prefer,
201
+ # you could uncomment the following to ignore the entire vscode folder
202
+ # .vscode/
203
+ # Temporary file for partial code execution
204
+ tempCodeRunnerFile.py
205
+
206
+ # Ruff stuff:
207
+ .ruff_cache/
208
+
209
+ # PyPI configuration file
210
+ .pypirc
211
+
212
+ # Marimo
213
+ marimo/_static/
214
+ marimo/_lsp/
215
+ __marimo__/
216
+
217
+ # Streamlit
218
+ .streamlit/secrets.toml
@@ -0,0 +1,151 @@
1
+ # Contributing
2
+
3
+ Use Python 3.14 or later and uv. The deployment target is Linux with cron
4
+ installed; other operating systems are not supported deployment targets.
5
+
6
+ ## Development setup
7
+
8
+ Install the package and run all tests:
9
+
10
+ ```sh
11
+ uv sync
12
+ uv run python -m unittest discover -s tests -v
13
+ ```
14
+
15
+ The tests use Python's standard library. They cover the public parser API,
16
+ documentation and repository examples, importing the package from outside the
17
+ repository, and actual wrapper subprocesses. Runtime tests exercise byte capture,
18
+ signals, concurrent runs, retention and injected storage/forwarding failures.
19
+ All tests must pass before a change is accepted. Linux is the deployment target;
20
+ passing tests on another POSIX platform does not establish cron compatibility.
21
+
22
+ Use `uv run zabbix-crontroller --help` to exercise the installed entry point. The
23
+ wrapper is also available as `uv run python -m zabbix_crontroller.app`. Test run
24
+ evidence belongs in temporary directories, with `ZC_STATE_DIR` set explicitly.
25
+ See [runtime behaviour](docs/RUNTIME.md) for the evidence format and a Linux cron
26
+ integration procedure. Do not install a test crontab over a user's real jobs.
27
+
28
+ ## Automated Linux checks
29
+
30
+ The CI workflow runs on branch pushes and pull requests that change a `.py` file
31
+ at any depth, `.python-version`, `LICENSE`, `pyproject.toml` or `uv.lock`. Every
32
+ matching run executes the full suite, including `LICENSE`-only changes.
33
+ Documentation-only and workflow-only changes do not trigger CI, as required by
34
+ [AGENTS.md](AGENTS.md). A separate tag-push workflow always runs the same checks,
35
+ regardless of changed paths, before building release packages.
36
+
37
+ The shared [Linux workflow](.github/workflows/linux-tests.yml) runs Black and
38
+ tests these distribution families on GitHub's Ubuntu x86-64 Docker runners:
39
+
40
+ | Distribution | Container base | Cron package |
41
+ | --- | --- | --- |
42
+ | Ubuntu 24.04 LTS | `ubuntu:24.04` | `cron` |
43
+ | Debian 13 | `debian:13-slim` | `cron` |
44
+ | Fedora 44 | `fedora:44` | `cronie` |
45
+ | Rocky Linux 9 | `rockylinux:9` | `cronie` |
46
+ | openSUSE Leap 16.0 | `opensuse/leap:16.0` | `cronie` |
47
+ | Alpine 3.24 | `alpine:3.24` | `cronie` |
48
+
49
+ uv installs the latest patch of the Python version in `.python-version`, including
50
+ the musl build on Alpine. Alpine uses Cronie explicitly; these checks do not
51
+ establish compatibility with BusyBox crond. Each container runs every unittest
52
+ as root and `zc-test`, then starts the native cron daemon and waits for actual
53
+ minute-boundary dispatch. Three monitored jobs per account plus an unwrapped
54
+ control check exit status, stdout/stderr capture and forwarding, `%` handling,
55
+ stdin, environment, working directory, umask, shell selection, correlation IDs
56
+ and evidence permissions. Both executable installation locations and both
57
+ candidate-file and `crontab -l` validation are exercised.
58
+
59
+ The checkout is mounted read-only and copied into the disposable container. The
60
+ test installation is editable and uses a placeholder version; CI does not create
61
+ release distributions. The harness refuses to replace existing crontabs. The
62
+ Alpine image removes its stock maintenance crontab during image preparation.
63
+ Containers do not run systemd, so service-manager integration and host security
64
+ policies such as SELinux remain separate deployment checks.
65
+
66
+ To reproduce one matrix entry locally, run these commands from the repository
67
+ root with Docker available. Change `BASE_IMAGE` to another value from the table:
68
+
69
+ ```sh
70
+ docker build --pull --build-arg BASE_IMAGE=debian:13-slim \
71
+ --build-arg PYTHON_VERSION="$(cat .python-version)" \
72
+ --tag zc-linux-tests tests/linux
73
+ docker run --name zc-linux-tests --init \
74
+ --mount "type=bind,source=$PWD,target=/source,readonly" zc-linux-tests
75
+ # Collect evidence even if the preceding test command failed.
76
+ mkdir -p /tmp/zc-linux-reports
77
+ docker cp zc-linux-tests:/reports/. /tmp/zc-linux-reports/
78
+ docker logs zc-linux-tests > /tmp/zc-linux-reports/container.log 2>&1
79
+ docker rm -f zc-linux-tests
80
+ ```
81
+
82
+ The workflow uploads `linux-DISTRIBUTION` diagnostic artefacts for 14 days, even
83
+ on failure. They contain the full unittest logs, cron configuration and daemon
84
+ log, captured run evidence and `result.json` with operating system, architecture,
85
+ Python and cron versions. Local Docker uses the host's architecture by default;
86
+ use `--platform linux/amd64` for both build and run to reproduce GitHub's target
87
+ on an ARM host. The cron check normally completes within a minute after the
88
+ suite; it fails after 100 seconds without the expected run evidence.
89
+
90
+ ## Code and documentation
91
+
92
+ Follow PEP 8 and PEP 257, use Python docstrings, and keep implementations simple.
93
+ Prefer standard-library solutions where suitable. Write documentation, docstrings
94
+ and commit messages in British English.
95
+
96
+ Format Python changes with Black and check formatting before submitting:
97
+
98
+ ```sh
99
+ uvx black src tests
100
+ uvx black --check src tests
101
+ ```
102
+
103
+ Follow the frozen [Syntax Specification](docs/SYNTAX_SPECIFICATION.md) and keep
104
+ the [Syntax User Guide](README.md#syntax), examples and tests consistent with it.
105
+ Changes to published syntax require a new syntax version; published versions
106
+ must remain supported. The required wrapper invocation was added to version `1.0`
107
+ before its first publication. Keep the parser importable independently of the
108
+ application; filesystem checks belong to `zc validate`, not `parse_crontab`.
109
+
110
+ ## Commits and releases
111
+
112
+ Use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/#specification).
113
+ The `master` branch is not guaranteed to be stable. Follow
114
+ [Semantic Versioning](https://semver.org) for package releases.
115
+
116
+ Build and publish release packages through GitHub workflows triggered by a pushed
117
+ tag. Those workflows build both source distributions and wheels, then publish
118
+ them to PyPI and GitHub Releases. Commits alone must not trigger release builds
119
+ or publishing. See [AGENTS.md](AGENTS.md) for repository workflow rules.
120
+
121
+ The [release workflow](.github/workflows/release.yml) accepts `vMAJOR.MINOR.PATCH`
122
+ tags, for example `v1.0.0`. Pre-release tags use `-alpha.N`, `-beta.N` or `-rc.N`,
123
+ for example `v1.0.0-rc.1`, which becomes Python package version `1.0.0rc1`.
124
+ Other tag formats fail before testing or building. Choose the version according
125
+ to SemVer and never move an already published release tag.
126
+
127
+ After the Linux matrix and Black pass, `uv build --sdist --wheel` builds both
128
+ distributions. Twine checks the metadata, and both package versions must match
129
+ the tag. The same uploaded artefacts are then published to PyPI and attached to
130
+ a GitHub Release with generated notes. Pre-releases are marked accordingly.
131
+ PyPI publishing uses short-lived OIDC credentials through
132
+ [Trusted Publishing](https://docs.pypi.org/trusted-publishers/), without an API
133
+ token secret. The PyPI job alone has `id-token: write`; the GitHub Release job
134
+ alone has `contents: write`. Publishing is never triggered by a branch or pull
135
+ request run.
136
+
137
+ Before the first release, a maintainer must:
138
+
139
+ 1. Create the GitHub environment `pypi` and restrict its deployment policy to
140
+ release tags. Add reviewers if the project requires release approval.
141
+ 2. Register a PyPI Trusted Publisher (or a pending publisher for a new project)
142
+ with owner `theriverman`, repository `zabbix-crontroller`, workflow filename
143
+ `release.yml`, and environment `pypi`.
144
+ 3. Allow GitHub Actions in the repository and protect release tags with a
145
+ repository ruleset appropriate for the maintainers.
146
+
147
+ Retry failed publishing jobs from the existing workflow run so they reuse the
148
+ validated distributions. uv checks PyPI for identical existing files before
149
+ uploading; a conflicting file fails. The GitHub job can finish attaching the
150
+ same artefacts to an existing release. Do not run release builds or publish
151
+ packages locally to work around a failed workflow.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kristof Daja
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.
@@ -0,0 +1,235 @@
1
+ Metadata-Version: 2.5
2
+ Name: zabbix-crontroller
3
+ Version: 0.0.1
4
+ Summary: Monitor cron jobs in Zabbix with crontab as the single source of truth.
5
+ Project-URL: Homepage, https://github.com/theriverman/zabbix-crontroller
6
+ Project-URL: Repository, https://github.com/theriverman/zabbix-crontroller
7
+ Author-email: Kristof Daja <22156894+theriverman@users.noreply.github.com>
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 5 - Production/Stable
11
+ Classifier: Environment :: No Input/Output (Daemon)
12
+ Classifier: Intended Audience :: System Administrators
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Natural Language :: English
15
+ Classifier: Operating System :: POSIX :: Linux
16
+ Classifier: Programming Language :: Python
17
+ Classifier: Programming Language :: Python :: 3.14
18
+ Classifier: Topic :: System :: Monitoring
19
+ Classifier: Topic :: System :: Systems Administration
20
+ Classifier: Topic :: Text Processing
21
+ Classifier: Topic :: Utilities
22
+ Requires-Python: >=3.14
23
+ Description-Content-Type: text/markdown
24
+
25
+ # Zabbix-cRontroller
26
+ Monitor cron jobs in Zabbix with crontab as the single source of truth
27
+
28
+ # Introduction
29
+ Keep job schedules and metadata in the user's crontab. The `zc` execution wrapper
30
+ records each run's exit status, duration, stdout and stderr in private per-user
31
+ storage. It forwards output to cron or the entry's existing redirections.
32
+
33
+ This implementation provides local run evidence. Zabbix integration and direct
34
+ file, syslog and journal collection are not implemented yet.
35
+
36
+ # Installation & Integration
37
+
38
+ Use Python 3.14 and uv on Linux with cron installed. From a checkout, install for
39
+ the current user and create the short-name symlink:
40
+
41
+ ```sh
42
+ UV_TOOL_BIN_DIR="$HOME/.local/bin" uv tool install --python 3.14 .
43
+ ln -s zabbix-crontroller "$HOME/.local/bin/zc"
44
+ ```
45
+
46
+ For a system installation with sudo:
47
+
48
+ ```sh
49
+ sudo env UV_TOOL_DIR=/opt/zabbix-crontroller/tools \
50
+ UV_PYTHON_INSTALL_DIR=/opt/zabbix-crontroller/python \
51
+ UV_TOOL_BIN_DIR=/usr/local/bin uv tool install --python 3.14 .
52
+ sudo ln -s zabbix-crontroller /usr/local/bin/zc
53
+ ```
54
+
55
+ These are local installations, not release builds. Release packages are produced
56
+ only by tagged GitHub workflows. The executable is `zabbix-crontroller`; `zc`
57
+ must be a symlink to it. Use its absolute path in cron, or explicitly set cron's
58
+ `PATH` to include its directory. A non-root example is
59
+ `PATH=/home/batch/.local/bin:/usr/bin:/bin`; cron does not expand `$HOME` in PATH.
60
+ For system installations, the uv tool environment and interpreter must be readable
61
+ and executable by the job owners; the shared `/opt` locations avoid placing them
62
+ inside root's private home. Validate from each job owner's account.
63
+
64
+ Validate a candidate file, or the current user's installed crontab:
65
+
66
+ ```sh
67
+ zc validate example.crontab
68
+ zc validate
69
+ ```
70
+
71
+ Validation checks the complete syntax and then wrapper and shell availability as
72
+ the invoking user. It never executes a job. Success returns `0`; invalid input,
73
+ acquisition failures and unavailable executables return `1`. Missing crontabs are
74
+ failures, not successful empty configurations.
75
+
76
+ # Syntax
77
+
78
+ Keep schedules and commands in your normal user crontab. The
79
+ [Syntax Specification](docs/SYNTAX_SPECIFICATION.md) defines the frozen `1.0`
80
+ notation supported by the parser.
81
+
82
+ 1. Declare `# zc:version = "1.0"` exactly once, before `# zc:start`.
83
+ 2. Enclose all monitored jobs in one section, from `# zc:start` to `# zc:end`.
84
+ 3. Start each job declaration with `job`: a stable, unique identifier using
85
+ lowercase ASCII letters, digits, underscores or hyphens, beginning with a letter.
86
+ 4. Add a required `name` and, if useful, an optional `description`. These can
87
+ contain Unicode; the description may appear before or after the name.
88
+ 5. Put exactly one cron entry immediately after its metadata. It completes the
89
+ job declaration. `# zc:end` closes the entire section, not an individual job.
90
+ 6. Begin the command with `zc run JOB -- 'COMMAND'`, repeating the exact `job`
91
+ identifier. Use one literally quoted shell command. Both executable names and
92
+ absolute paths ending in `zc` or `zabbix-crontroller` are accepted.
93
+
94
+ For example, a description is not required:
95
+
96
+ ```cron
97
+ # zc:version = "1.0"
98
+ PATH=/usr/local/bin:/usr/bin:/bin
99
+ # zc:start
100
+ # zc:job = "backup-postgres"
101
+ # zc:name = "Orders database backup"
102
+ 30 1 * * * zc run backup-postgres -- '/opt/company/maintenance/bin/backup-postgres --database orders'
103
+ # zc:end
104
+ ```
105
+
106
+ Write one property per line using a quoted TOML string. Keep each metadata header
107
+ and its cron entry together. Blank lines, ordinary comments and environment
108
+ assignments may appear between completed jobs. Delimiters may have trailing
109
+ `#` comments, but take no value. Changing a name or command does not require
110
+ changing the job identifier.
111
+
112
+ The wrapper executes the quoted command using cron's `SHELL`, or `/bin/sh` when
113
+ unset. Put pipelines, command chains and substitutions inside single quotes so
114
+ they execute within the monitored shell. Keep existing output redirections
115
+ outside the quotes if they should receive forwarded output while capture stays
116
+ separate. Use `'"'"'` to include an apostrophe in a single-quoted command.
117
+ Cron still requires escaping literal percentages as `\%`, even inside quotes.
118
+ See [runtime behaviour and evidence](docs/RUNTIME.md) for limits and failure
119
+ handling.
120
+
121
+ Every cron entry inside the section needs its own metadata. An additional entry
122
+ without metadata is an error, including after the final job. Unannotated entries
123
+ are allowed before and after the section. Job metadata outside the section is
124
+ an error. An empty section is valid, but both delimiters remain required.
125
+
126
+ Commenting out only a job's cron entry is an error: its metadata will not attach
127
+ to a later entry. To stop discovering a job while keeping it running, remove its
128
+ metadata header **and move its cron entry outside the monitored section**.
129
+ Preserve the relevant environment settings when moving it; section boundaries
130
+ do not reset the cron environment. Removing only the header inside the section
131
+ is an error. Remove the entire declaration to remove both monitoring and execution.
132
+ Malformed configurations prevent publication of the discovery snapshot.
133
+
134
+ ## Syntax example
135
+
136
+ The [example crontab](example.crontab) contains five monitored jobs and a separate
137
+ unmonitored entry:
138
+
139
+ ```cron
140
+ # Production batch jobs — svc_batch@lon-batch-01
141
+ # Owner: Platform Operations
142
+ # Host timezone: Europe/London. Change requests must reference an approved ticket.
143
+
144
+ # zc:version = "1.0"
145
+
146
+ SHELL=/bin/bash
147
+ PATH=/usr/local/bin:/usr/bin:/bin
148
+ MAILTO=platform-operations@example.com
149
+ HOME=/var/lib/batch
150
+
151
+ # zc:start
152
+
153
+ # zc:job = "supplier-sync"
154
+ # zc:name = "Supplier stock synchronisation"
155
+ # zc:description = "Collect supplier stock feeds during business hours, Monday–Friday."
156
+ 12 7-19 * * 1-5 zc run supplier-sync -- '/usr/bin/flock -n /var/lib/batch/locks/supplier-sync.lock /opt/company/integrations/bin/supplier-sync --config /etc/company/supplier-sync.toml' >> /var/log/batch/supplier-sync.log 2>&1
157
+
158
+ # zc:job = "backup-postgres"
159
+ # zc:name = "Orders database backup"
160
+ # zc:description = "Nightly database backup; credentials supplied via .pgpass."
161
+ 30 1 * * * zc run backup-postgres -- '/opt/company/maintenance/bin/backup-postgres --database orders --retention-days 14' >> /var/log/batch/backup-postgres.log 2>&1
162
+
163
+ # zc:job = "reconcile-payments"
164
+ # zc:name = "Reconcile payments"
165
+ # zc:description = "Generate the previous business day's reconciliation report before Finance arrives."
166
+ 15 6 * * 1-5 zc run reconcile-payments -- '/opt/company/finance/bin/reconcile-payments --period previous-business-day' >> /var/log/batch/reconcile-payments.log 2>&1
167
+
168
+ # zc:job = "staging-cleanup"
169
+ # zc:name = "Staging cleanup"
170
+ # zc:description = "Remove expired integration staging files during the Sunday maintenance window."
171
+ 45 3 * * 0 zc run staging-cleanup -- '/usr/bin/find /var/lib/batch/staging -xdev -type f -mtime +30 -delete' >> /var/log/batch/staging-cleanup.log 2>&1
172
+
173
+ # zc:job = "sap-export"
174
+ # zc:name = "SAP export"
175
+ # zc:description = "Forward pending warehouse transactions to SAP; prevent overlapping runs."
176
+ */5 * * * * zc run sap-export -- '/usr/bin/flock -n /var/lib/batch/locks/sap-export.lock /opt/company/integrations/bin/sap-export --env production' >> /var/log/batch/sap-export.log 2>&1
177
+
178
+ # zc:end
179
+
180
+ # This separate cron entry runs outside monitoring.
181
+ 45 3 * * 0 /usr/bin/find /var/lib/batch/staging -xdev -type f -mtime +30 -delete >> /var/log/batch/staging-cleanup.log 2>&1
182
+ ```
183
+
184
+ # Library usage
185
+
186
+ Import the parser independently of the application. The caller supplies the
187
+ crontab text; the parser does not run `crontab -l`, execute jobs, read files or
188
+ contact Zabbix.
189
+
190
+ ```python
191
+ from pathlib import Path
192
+
193
+ from zabbix_crontroller.parser import parse_crontab
194
+
195
+ text = Path("example.crontab").read_text(encoding="utf-8")
196
+ crontab = parse_crontab(text)
197
+
198
+ for job in crontab.jobs:
199
+ print(job.job_id, job.name, job.schedule, job.command)
200
+ ```
201
+
202
+ `parse_crontab(text: str) -> Crontab` returns the declared `version` and a tuple of
203
+ `CronJob` records in source order. Jobs contain the identifier, name, optional
204
+ description, schedule, complete command, `wrapper_executable`, environment
205
+ snapshot and source line numbers. The wrapper field contains its decoded name
206
+ or absolute path; parsing does not check whether that executable is installed.
207
+ Records and their environment mappings are read-only. Missing or blank
208
+ descriptions become `None`. An explicitly empty monitored section returns an
209
+ empty `jobs` tuple.
210
+
211
+ The parser extracts five-field schedules and `@` nicknames. Schedule field
212
+ values, nickname availability, calendar ranges, time zones and daemon-specific
213
+ extensions are **not validated**. This is a metadata parser, not a replacement
214
+ for the daemon's crontab validator. Input is interpreted as a user crontab, with
215
+ no seconds or username column. Commands retain their internal and trailing
216
+ whitespace, quoting and cron percent sequences without evaluation. Only the
217
+ literal wrapper invocation is validated; the inner shell programme remains
218
+ opaque. Unmonitored commands retain their previous parsing behaviour.
219
+
220
+ Each environment snapshot contains only preceding explicit assignments, in file
221
+ order, with outer quotes removed. It does not add process environment variables
222
+ or cron defaults, perform substitutions, or model daemon-specific overrides.
223
+
224
+ Invalid input raises `ParseError`, with `ConfigurationError` for version errors
225
+ and `CrontabSyntaxError` for other parsing errors. Exceptions expose `message`,
226
+ `line_number`, `job_id`, `declaration_line_number` and `previous_line_number`;
227
+ inapplicable locations are `None`. Source lines are one-based. The exception text
228
+ includes the relevant locations. Parsing returns a complete result or raises an
229
+ error; it never returns partial discovery data. Non-string input raises
230
+ `TypeError`.
231
+
232
+ # Contributing
233
+
234
+ See [CONTRIBUTE.md](CONTRIBUTE.md) for development setup, tests and contribution
235
+ guidelines.