cronstable 1.2.11__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.
- cronstable-1.2.11/.dockerignore +27 -0
- cronstable-1.2.11/.editorconfig +15 -0
- cronstable-1.2.11/.gitattributes +6 -0
- cronstable-1.2.11/.github/CODEOWNERS +3 -0
- cronstable-1.2.11/.github/ISSUE_TEMPLATE.md +15 -0
- cronstable-1.2.11/.github/dependabot.yml +59 -0
- cronstable-1.2.11/.github/workflows/build.yml +517 -0
- cronstable-1.2.11/.github/workflows/docker.yml +260 -0
- cronstable-1.2.11/.github/workflows/release.yml +842 -0
- cronstable-1.2.11/.github/workflows/tox.yml +103 -0
- cronstable-1.2.11/.gitignore +70 -0
- cronstable-1.2.11/.pre-commit-config.yaml +17 -0
- cronstable-1.2.11/CONTRIBUTING.md +156 -0
- cronstable-1.2.11/Dockerfile +102 -0
- cronstable-1.2.11/HISTORY.md +1891 -0
- cronstable-1.2.11/LICENSE +21 -0
- cronstable-1.2.11/MANIFEST.in +8 -0
- cronstable-1.2.11/PKG-INFO +1957 -0
- cronstable-1.2.11/README.md +1898 -0
- cronstable-1.2.11/codecov.yml +23 -0
- cronstable-1.2.11/cronstable/__init__.py +0 -0
- cronstable-1.2.11/cronstable/__main__.py +278 -0
- cronstable-1.2.11/cronstable/_json.py +127 -0
- cronstable-1.2.11/cronstable/backends/__init__.py +55 -0
- cronstable-1.2.11/cronstable/backends/etcd.py +1113 -0
- cronstable-1.2.11/cronstable/backends/filesystem.py +882 -0
- cronstable-1.2.11/cronstable/backends/kubernetes.py +1577 -0
- cronstable-1.2.11/cronstable/cluster.py +3402 -0
- cronstable-1.2.11/cronstable/config.py +3136 -0
- cronstable-1.2.11/cronstable/cron.py +7709 -0
- cronstable-1.2.11/cronstable/cronexpr.py +512 -0
- cronstable-1.2.11/cronstable/crontabs.py +277 -0
- cronstable-1.2.11/cronstable/dag.py +1201 -0
- cronstable-1.2.11/cronstable/dagrun.py +1773 -0
- cronstable-1.2.11/cronstable/fingerprint.py +284 -0
- cronstable-1.2.11/cronstable/job.py +1022 -0
- cronstable-1.2.11/cronstable/jobapi.py +838 -0
- cronstable-1.2.11/cronstable/jobcli.py +805 -0
- cronstable-1.2.11/cronstable/jobstate.py +422 -0
- cronstable-1.2.11/cronstable/leadership.py +600 -0
- cronstable-1.2.11/cronstable/platform.py +416 -0
- cronstable-1.2.11/cronstable/prometheus.py +1122 -0
- cronstable-1.2.11/cronstable/redact.py +235 -0
- cronstable-1.2.11/cronstable/resources.py +989 -0
- cronstable-1.2.11/cronstable/state.py +2847 -0
- cronstable-1.2.11/cronstable/state_admin.py +666 -0
- cronstable-1.2.11/cronstable/statsd.py +82 -0
- cronstable-1.2.11/cronstable/version.py +24 -0
- cronstable-1.2.11/cronstable/web/__init__.py +3 -0
- cronstable-1.2.11/cronstable/web/index.html +7107 -0
- cronstable-1.2.11/cronstable.egg-info/PKG-INFO +1957 -0
- cronstable-1.2.11/cronstable.egg-info/SOURCES.txt +262 -0
- cronstable-1.2.11/cronstable.egg-info/dependency_links.txt +1 -0
- cronstable-1.2.11/cronstable.egg-info/entry_points.txt +2 -0
- cronstable-1.2.11/cronstable.egg-info/requires.txt +29 -0
- cronstable-1.2.11/cronstable.egg-info/top_level.txt +1 -0
- cronstable-1.2.11/docker/Dockerfile.alpine +85 -0
- cronstable-1.2.11/docker/Dockerfile.amazonlinux +80 -0
- cronstable-1.2.11/docker/Dockerfile.distroless +83 -0
- cronstable-1.2.11/docker/Dockerfile.fedora +86 -0
- cronstable-1.2.11/docker/Dockerfile.opensuse +93 -0
- cronstable-1.2.11/docker/Dockerfile.rhel +88 -0
- cronstable-1.2.11/docker/Dockerfile.ubuntu +95 -0
- cronstable-1.2.11/docker-compose-acme.yml +104 -0
- cronstable-1.2.11/docker-compose-cluster-large.yml +95 -0
- cronstable-1.2.11/docker-compose-cluster.yml +138 -0
- cronstable-1.2.11/docker-compose-grand-tour.yml +174 -0
- cronstable-1.2.11/docker-compose-pulse-cluster.yml +98 -0
- cronstable-1.2.11/docker-compose-pulse.yml +23 -0
- cronstable-1.2.11/docker-compose-zen.yml +26 -0
- cronstable-1.2.11/docker-compose.yml +20 -0
- cronstable-1.2.11/docs/img/dashboard-boot.png +0 -0
- cronstable-1.2.11/docs/img/dashboard-cluster.png +0 -0
- cronstable-1.2.11/docs/img/dashboard-dag-approval.png +0 -0
- cronstable-1.2.11/docs/img/dashboard-dag-graph.png +0 -0
- cronstable-1.2.11/docs/img/dashboard-fleet.png +0 -0
- cronstable-1.2.11/docs/img/dashboard-heatmap.png +0 -0
- cronstable-1.2.11/docs/img/dashboard-history.png +0 -0
- cronstable-1.2.11/docs/img/dashboard-incident-timeline.png +0 -0
- cronstable-1.2.11/docs/img/dashboard-logs.png +0 -0
- cronstable-1.2.11/docs/img/dashboard-multitail.png +0 -0
- cronstable-1.2.11/docs/img/dashboard-overview.png +0 -0
- cronstable-1.2.11/docs/img/dashboard-palette.png +0 -0
- cronstable-1.2.11/docs/img/dashboard-schedule.png +0 -0
- cronstable-1.2.11/docs/img/dashboard-settings.png +0 -0
- cronstable-1.2.11/docs/img/dashboard-shortcuts.png +0 -0
- cronstable-1.2.11/docs/img/dashboard-state.png +0 -0
- cronstable-1.2.11/docs/img/dashboard-theme-amber.png +0 -0
- cronstable-1.2.11/docs/img/dashboard-theme-carolina-light.png +0 -0
- cronstable-1.2.11/docs/img/dashboard-theme-green.png +0 -0
- cronstable-1.2.11/docs/img/dashboard-theme-modern.png +0 -0
- cronstable-1.2.11/docs/img/dashboard-wallboard.png +0 -0
- cronstable-1.2.11/docs/img/logo-spin-light.gif +0 -0
- cronstable-1.2.11/docs/img/logo-spin.gif +0 -0
- cronstable-1.2.11/docs/img/social-preview.png +0 -0
- cronstable-1.2.11/docs/screenshots/README.md +78 -0
- cronstable-1.2.11/docs/screenshots/capture_dashboard.py +528 -0
- cronstable-1.2.11/docs/screenshots/capture_logo_gif.py +135 -0
- cronstable-1.2.11/docs/screenshots/capture_logs_closeup.py +67 -0
- cronstable-1.2.11/docs/screenshots/capture_social_card.py +55 -0
- cronstable-1.2.11/docs/screenshots/logs-demo.yaml +56 -0
- cronstable-1.2.11/docs/screenshots/shots/dashboard-boot.png +0 -0
- cronstable-1.2.11/docs/screenshots/shots/dashboard-cluster.png +0 -0
- cronstable-1.2.11/docs/screenshots/shots/dashboard-dag-approval.png +0 -0
- cronstable-1.2.11/docs/screenshots/shots/dashboard-dag-graph.png +0 -0
- cronstable-1.2.11/docs/screenshots/shots/dashboard-fleet.png +0 -0
- cronstable-1.2.11/docs/screenshots/shots/dashboard-heatmap.png +0 -0
- cronstable-1.2.11/docs/screenshots/shots/dashboard-history.png +0 -0
- cronstable-1.2.11/docs/screenshots/shots/dashboard-incident-timeline.png +0 -0
- cronstable-1.2.11/docs/screenshots/shots/dashboard-logs.png +0 -0
- cronstable-1.2.11/docs/screenshots/shots/dashboard-multitail.png +0 -0
- cronstable-1.2.11/docs/screenshots/shots/dashboard-overview.png +0 -0
- cronstable-1.2.11/docs/screenshots/shots/dashboard-palette.png +0 -0
- cronstable-1.2.11/docs/screenshots/shots/dashboard-schedule.png +0 -0
- cronstable-1.2.11/docs/screenshots/shots/dashboard-settings.png +0 -0
- cronstable-1.2.11/docs/screenshots/shots/dashboard-shortcuts.png +0 -0
- cronstable-1.2.11/docs/screenshots/shots/dashboard-state.png +0 -0
- cronstable-1.2.11/docs/screenshots/shots/dashboard-theme-amber.png +0 -0
- cronstable-1.2.11/docs/screenshots/shots/dashboard-theme-carolina-light.png +0 -0
- cronstable-1.2.11/docs/screenshots/shots/dashboard-theme-green.png +0 -0
- cronstable-1.2.11/docs/screenshots/shots/dashboard-theme-modern.png +0 -0
- cronstable-1.2.11/docs/screenshots/shots/dashboard-wallboard.png +0 -0
- cronstable-1.2.11/docs/screenshots/shots/logo-spin-light.gif +0 -0
- cronstable-1.2.11/docs/screenshots/shots/logo-spin.gif +0 -0
- cronstable-1.2.11/docs/screenshots/shots/social-preview.png +0 -0
- cronstable-1.2.11/docs/screenshots/social-card.html +89 -0
- cronstable-1.2.11/example/acme-platform/README.md +84 -0
- cronstable-1.2.11/example/acme-platform/acme.env +6 -0
- cronstable-1.2.11/example/acme-platform/gen-certs.sh +61 -0
- cronstable-1.2.11/example/acme-platform/jobs.yaml +450 -0
- cronstable-1.2.11/example/acme-platform/node-entrypoint.sh +67 -0
- cronstable-1.2.11/example/adhoc.cronstable.d/_inc.yaml +66 -0
- cronstable-1.2.11/example/adhoc.cronstable.d/test-tz.yaml +16 -0
- cronstable-1.2.11/example/adhoc.cronstable.d/test-utc.yaml +16 -0
- cronstable-1.2.11/example/adhoc.cronstable.d/test.yaml +64 -0
- cronstable-1.2.11/example/cluster/README +29 -0
- cronstable-1.2.11/example/cluster/gen-certs.sh +77 -0
- cronstable-1.2.11/example/cluster/jobs.yaml +68 -0
- cronstable-1.2.11/example/cluster/node-a.yaml +28 -0
- cronstable-1.2.11/example/cluster/node-b.yaml +18 -0
- cronstable-1.2.11/example/cluster/node-c.yaml +18 -0
- cronstable-1.2.11/example/cluster-large/README +63 -0
- cronstable-1.2.11/example/cluster-large/gen-certs.sh +64 -0
- cronstable-1.2.11/example/cluster-large/jobs.yaml +139 -0
- cronstable-1.2.11/example/cluster-large/node-entrypoint.sh +72 -0
- cronstable-1.2.11/example/crontab/README +44 -0
- cronstable-1.2.11/example/crontab/dashboard.yaml +18 -0
- cronstable-1.2.11/example/crontab/legacy.crontab +19 -0
- cronstable-1.2.11/example/dag/cronstable.yaml +95 -0
- cronstable-1.2.11/example/dag-cluster/README +84 -0
- cronstable-1.2.11/example/dag-cluster/cronstable.yaml +107 -0
- cronstable-1.2.11/example/dag-cluster/docker-compose.yml +91 -0
- cronstable-1.2.11/example/demo/cronstable.yaml +100 -0
- cronstable-1.2.11/example/docker/Dockerfile +7 -0
- cronstable-1.2.11/example/docker/README +28 -0
- cronstable-1.2.11/example/docker/cronstable.yaml +10 -0
- cronstable-1.2.11/example/docker/k8s-deploy.yaml +29 -0
- cronstable-1.2.11/example/etcd/README +64 -0
- cronstable-1.2.11/example/etcd/cronstable.yaml +47 -0
- cronstable-1.2.11/example/etcd/docker-compose.yml +69 -0
- cronstable-1.2.11/example/grand-tour/README.md +270 -0
- cronstable-1.2.11/example/grand-tour/_defaults.yaml +69 -0
- cronstable-1.2.11/example/grand-tour/gen-certs.sh +58 -0
- cronstable-1.2.11/example/grand-tour/legacy.crontab +20 -0
- cronstable-1.2.11/example/grand-tour/node-entrypoint.sh +132 -0
- cronstable-1.2.11/example/grand-tour/platform.env +7 -0
- cronstable-1.2.11/example/grand-tour/platform.yaml +1178 -0
- cronstable-1.2.11/example/grand-tour/secrets/signing.key +1 -0
- cronstable-1.2.11/example/job-state/cronstable.yaml +97 -0
- cronstable-1.2.11/example/kubernetes/README +63 -0
- cronstable-1.2.11/example/kubernetes/cronstable.yaml +50 -0
- cronstable-1.2.11/example/kubernetes/deployment.yaml +109 -0
- cronstable-1.2.11/example/pulse-cluster/README.md +89 -0
- cronstable-1.2.11/example/pulse-cluster/gen-certs.sh +68 -0
- cronstable-1.2.11/example/pulse-cluster/jobs.yaml +133 -0
- cronstable-1.2.11/example/pulse-cluster/node-a.yaml +23 -0
- cronstable-1.2.11/example/pulse-cluster/node-b.yaml +16 -0
- cronstable-1.2.11/example/pulse-cluster/node-c.yaml +20 -0
- cronstable-1.2.11/example/pulse-monitor/README.md +96 -0
- cronstable-1.2.11/example/pulse-monitor/cronstable.yaml +138 -0
- cronstable-1.2.11/example/zen-demo/cronstable.yaml +74 -0
- cronstable-1.2.11/packaging/homebrew/cronstable.rb.tmpl +45 -0
- cronstable-1.2.11/packaging/homebrew/render-formula.sh +40 -0
- cronstable-1.2.11/pyinstaller/Dockerfile +43 -0
- cronstable-1.2.11/pyinstaller/Makefile +9 -0
- cronstable-1.2.11/pyinstaller/cronstable +3 -0
- cronstable-1.2.11/pyinstaller/cronstable.spec +85 -0
- cronstable-1.2.11/pyinstaller/entitlements.plist +30 -0
- cronstable-1.2.11/pyinstaller/install_orjson.sh +53 -0
- cronstable-1.2.11/pyinstaller/verify_orjson.py +42 -0
- cronstable-1.2.11/pyinstaller/verify_uvloop.py +32 -0
- cronstable-1.2.11/pyproject.toml +206 -0
- cronstable-1.2.11/requirements_dev.txt +14 -0
- cronstable-1.2.11/setup.cfg +4 -0
- cronstable-1.2.11/tests/__init__.py +0 -0
- cronstable-1.2.11/tests/_commands.py +108 -0
- cronstable-1.2.11/tests/data/cron_golden.json +8796 -0
- cronstable-1.2.11/tests/fixtures/.testenv +7 -0
- cronstable-1.2.11/tests/fixtures/.testenv-invalid +1 -0
- cronstable-1.2.11/tests/gen_cron_golden.py +398 -0
- cronstable-1.2.11/tests/test_backend_etcd.py +957 -0
- cronstable-1.2.11/tests/test_backend_filesystem.py +764 -0
- cronstable-1.2.11/tests/test_backend_kubernetes.py +1200 -0
- cronstable-1.2.11/tests/test_cluster.py +5433 -0
- cronstable-1.2.11/tests/test_config.py +1086 -0
- cronstable-1.2.11/tests/test_config_backends.py +1104 -0
- cronstable-1.2.11/tests/test_cron.py +4568 -0
- cronstable-1.2.11/tests/test_cron_state_hardening.py +937 -0
- cronstable-1.2.11/tests/test_cronexpr.py +163 -0
- cronstable-1.2.11/tests/test_crontabs.py +325 -0
- cronstable-1.2.11/tests/test_fingerprint.py +759 -0
- cronstable-1.2.11/tests/test_include_parent.yaml +10 -0
- cronstable-1.2.11/tests/test_included_child.yaml +12 -0
- cronstable-1.2.11/tests/test_job.py +1585 -0
- cronstable-1.2.11/tests/test_json_portability.py +77 -0
- cronstable-1.2.11/tests/test_leadership.py +486 -0
- cronstable-1.2.11/tests/test_main.py +74 -0
- cronstable-1.2.11/tests/test_platform.py +153 -0
- cronstable-1.2.11/tests/test_prometheus.py +1344 -0
- cronstable-1.2.11/tests/test_redact.py +236 -0
- cronstable-1.2.11/tests/test_resources.py +743 -0
- cronstable-1.2.11/tests/test_state.py +1438 -0
- cronstable-1.2.11/tests/test_state_admin.py +824 -0
- cronstable-1.2.11/tests/test_state_dag.py +1480 -0
- cronstable-1.2.11/tests/test_state_dag_run.py +1343 -0
- cronstable-1.2.11/tests/test_state_fleet_ha.py +1048 -0
- cronstable-1.2.11/tests/test_state_hardening.py +899 -0
- cronstable-1.2.11/tests/test_state_job_api.py +811 -0
- cronstable-1.2.11/tests/test_state_job_cli.py +476 -0
- cronstable-1.2.11/tests/test_state_job_primitives.py +656 -0
- cronstable-1.2.11/tests/test_state_lifecycle_hardening.py +345 -0
- cronstable-1.2.11/tests/test_state_scheduler_durability.py +1286 -0
- cronstable-1.2.11/tests/test_ui_endpoints.py +431 -0
- cronstable-1.2.11/tests/testbrokenconfig.yaml +3 -0
- cronstable-1.2.11/tests/testconfig.yaml +15 -0
- cronstable-1.2.11/tox.ini +57 -0
- cronstable-1.2.11/wiki/Architecture-and-Internals.md +797 -0
- cronstable-1.2.11/wiki/CLI-Reference.md +594 -0
- cronstable-1.2.11/wiki/Classic-Crontabs.md +219 -0
- cronstable-1.2.11/wiki/Clustering-and-Leader-Election.md +1480 -0
- cronstable-1.2.11/wiki/Commands-and-Environment.md +304 -0
- cronstable-1.2.11/wiki/Concurrency-and-Timeouts.md +437 -0
- cronstable-1.2.11/wiki/Configuration-Reference.md +676 -0
- cronstable-1.2.11/wiki/Contributing-and-Releasing.md +190 -0
- cronstable-1.2.11/wiki/Durable-State.md +1029 -0
- cronstable-1.2.11/wiki/Failure-Detection-and-Retries.md +206 -0
- cronstable-1.2.11/wiki/HTTP-API.md +1191 -0
- cronstable-1.2.11/wiki/Home.md +43 -0
- cronstable-1.2.11/wiki/Includes-and-Defaults.md +268 -0
- cronstable-1.2.11/wiki/Installation.md +290 -0
- cronstable-1.2.11/wiki/Logging-Configuration.md +163 -0
- cronstable-1.2.11/wiki/Metrics-with-Prometheus.md +197 -0
- cronstable-1.2.11/wiki/Metrics-with-Statsd.md +115 -0
- cronstable-1.2.11/wiki/Migration-from-yacron.md +215 -0
- cronstable-1.2.11/wiki/Orchestration-and-DAGs.md +265 -0
- cronstable-1.2.11/wiki/Output-Capturing.md +206 -0
- cronstable-1.2.11/wiki/Production-Deployment.md +411 -0
- cronstable-1.2.11/wiki/Reporting.md +459 -0
- cronstable-1.2.11/wiki/Running-on-Windows.md +330 -0
- cronstable-1.2.11/wiki/Schedules-and-Timezones.md +212 -0
- cronstable-1.2.11/wiki/Troubleshooting.md +608 -0
- cronstable-1.2.11/wiki/Web-Dashboard.md +488 -0
- cronstable-1.2.11/wiki/_Footer.md +3 -0
- cronstable-1.2.11/wiki/_Sidebar.md +32 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Keep the build context small. NOTE: .git MUST be included — setuptools_scm
|
|
2
|
+
# derives the version from git tags during `pip install .`. README.md and
|
|
3
|
+
# LICENSE must also stay: pyproject.toml references them at build time.
|
|
4
|
+
.venv
|
|
5
|
+
.venv314
|
|
6
|
+
.tox
|
|
7
|
+
.mypy_cache
|
|
8
|
+
.ruff_cache
|
|
9
|
+
.pytest_cache
|
|
10
|
+
dist
|
|
11
|
+
build
|
|
12
|
+
*.egg-info
|
|
13
|
+
__pycache__
|
|
14
|
+
*.pyc
|
|
15
|
+
|
|
16
|
+
# Not needed to build the package; excluded to shrink the build context.
|
|
17
|
+
.claude
|
|
18
|
+
.github
|
|
19
|
+
docker
|
|
20
|
+
example
|
|
21
|
+
pyinstaller
|
|
22
|
+
scripts
|
|
23
|
+
target
|
|
24
|
+
tests
|
|
25
|
+
.editorconfig
|
|
26
|
+
.gitignore
|
|
27
|
+
.pre-commit-config.yaml
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# The example shell scripts (example/*/gen-certs.sh, node-entrypoint.sh) are
|
|
2
|
+
# bind-mounted from the working tree and executed by `sh` inside Linux
|
|
3
|
+
# containers, so they must check out with LF line endings everywhere. Without
|
|
4
|
+
# this, a default Windows clone (core.autocrlf=true) converts them to CRLF and
|
|
5
|
+
# every cluster demo dies at its first line ("set: illegal option -").
|
|
6
|
+
*.sh text eol=lf
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
* cronstable version:
|
|
2
|
+
* Python version:
|
|
3
|
+
* Operating System:
|
|
4
|
+
|
|
5
|
+
### Description
|
|
6
|
+
|
|
7
|
+
Describe what you were trying to get done.
|
|
8
|
+
Tell us what happened, what went wrong, and what you expected to happen.
|
|
9
|
+
|
|
10
|
+
### What I Did
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
Paste the command(s) you ran and the output.
|
|
14
|
+
If there was a crash, please include the traceback here.
|
|
15
|
+
```
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Dependabot keeps the CI/CD supply chain current: it opens PRs to bump the
|
|
2
|
+
# pinned versions as new releases land, so the exact pins in .github/workflows
|
|
3
|
+
# and the Dockerfiles don't silently rot back into known-vulnerable versions.
|
|
4
|
+
#
|
|
5
|
+
# Two mechanisms, one file:
|
|
6
|
+
# - VERSION updates (configured below) run on the weekly schedule and bump a
|
|
7
|
+
# dependency simply because a newer release exists.
|
|
8
|
+
# - SECURITY updates -- the ones that matter most here, where Dependabot raises
|
|
9
|
+
# a PR the moment a dependency has a published advisory, regardless of
|
|
10
|
+
# schedule -- are switched on in the repo UI, not here: Settings -> Code
|
|
11
|
+
# security -> enable "Dependabot alerts" AND "Dependabot security updates".
|
|
12
|
+
# They reuse the ecosystems declared below to know which manifests to patch,
|
|
13
|
+
# so keep this file in sync with what the pipeline actually uses.
|
|
14
|
+
#
|
|
15
|
+
# Updates are grouped per ecosystem so a quiet week brings at most one PR per
|
|
16
|
+
# ecosystem to review, not one per dependency.
|
|
17
|
+
version: 2
|
|
18
|
+
updates:
|
|
19
|
+
# The GitHub Actions pinned in .github/workflows/*.yml. They are exact-pinned
|
|
20
|
+
# (e.g. actions/checkout@v7.0.0), which is precisely what lets Dependabot see
|
|
21
|
+
# each one as outdated and open a reviewable bump -- a moving major tag (@v7)
|
|
22
|
+
# would hide minor/patch drift behind a mutable ref instead.
|
|
23
|
+
- package-ecosystem: github-actions
|
|
24
|
+
directory: "/" # scans .github/workflows/ (and any composite actions)
|
|
25
|
+
schedule:
|
|
26
|
+
interval: weekly
|
|
27
|
+
open-pull-requests-limit: 10
|
|
28
|
+
groups:
|
|
29
|
+
github-actions:
|
|
30
|
+
patterns: ["*"]
|
|
31
|
+
|
|
32
|
+
# Python dependencies: the pyproject.toml optional-dependency groups (incl. the
|
|
33
|
+
# dev extra) and requirements_dev.txt. Catches CVEs in the aiohttp/ruamel stack
|
|
34
|
+
# and the dev toolchain (tox, ruff, mypy, pytest, uv-via-tox-uv).
|
|
35
|
+
- package-ecosystem: pip
|
|
36
|
+
directory: "/"
|
|
37
|
+
schedule:
|
|
38
|
+
interval: weekly
|
|
39
|
+
open-pull-requests-limit: 10
|
|
40
|
+
groups:
|
|
41
|
+
python:
|
|
42
|
+
patterns: ["*"]
|
|
43
|
+
|
|
44
|
+
# Base images in the Dockerfiles: the published multi-distro image matrix
|
|
45
|
+
# (/Dockerfile is the default Debian image; docker/Dockerfile.* are the
|
|
46
|
+
# variants) plus the local PyInstaller build image (pyinstaller/Dockerfile,
|
|
47
|
+
# which also carries the uv COPY --from pin). Base-image bumps can change a
|
|
48
|
+
# distro's default Python, so review these rather than automerging.
|
|
49
|
+
- package-ecosystem: docker
|
|
50
|
+
directories:
|
|
51
|
+
- "/"
|
|
52
|
+
- "/docker"
|
|
53
|
+
- "/pyinstaller"
|
|
54
|
+
schedule:
|
|
55
|
+
interval: weekly
|
|
56
|
+
open-pull-requests-limit: 10
|
|
57
|
+
groups:
|
|
58
|
+
docker:
|
|
59
|
+
patterns: ["*"]
|
|
@@ -0,0 +1,517 @@
|
|
|
1
|
+
name: build
|
|
2
|
+
|
|
3
|
+
# Per-commit build verification. Runs EVERY build that release.yml performs --
|
|
4
|
+
# the wheel + sdist and the self-contained PyInstaller binaries for Linux
|
|
5
|
+
# (amd64, arm64, i686, armv7, ppc64le, s390x and riscv64 in both glibc and
|
|
6
|
+
# musl/Alpine flavors, plus armv6 in musl only), macOS (arm64 + amd64) and
|
|
7
|
+
# Windows (amd64 + arm64) -- on
|
|
8
|
+
# every push, but WITHOUT publishing,
|
|
9
|
+
# tagging,
|
|
10
|
+
# signing, or pushing anything. A broken build (or a broken bundle the
|
|
11
|
+
# `--version` smoke test
|
|
12
|
+
# catches) fails the commit early instead of only surfacing at release time.
|
|
13
|
+
#
|
|
14
|
+
# This is the package/binary analog of docker.yml's per-commit build-only job:
|
|
15
|
+
# - no PyPI publish, no git tag, no GitHub Release, no image push;
|
|
16
|
+
# - the macOS binaries are built and smoke-tested but NOT signed/notarized --
|
|
17
|
+
# signing needs secrets and is a release-time concern (see release.yml's
|
|
18
|
+
# binaries-macos "Sign and notarize" step);
|
|
19
|
+
# - binaries build at the natural setuptools_scm version derived from git
|
|
20
|
+
# (e.g. 1.0.9.devN+g<sha>) rather than a pinned release version.
|
|
21
|
+
# The real, version-pinned, signed, *published* builds still live in
|
|
22
|
+
# release.yml; this workflow only proves every commit can still build them.
|
|
23
|
+
#
|
|
24
|
+
# The build steps below are kept deliberately identical to their release.yml
|
|
25
|
+
# counterparts (same Python, same pyinstaller==6.21.0 pin, same `--version`
|
|
26
|
+
# smoke test, same matrices) so a change there and a drift here are easy to
|
|
27
|
+
# spot -- only the publish/sign/version-pin differences and the macOS 26
|
|
28
|
+
# coverage are intentional.
|
|
29
|
+
#
|
|
30
|
+
# armv6 note: it is musl-only -- Debian/glibc has no arm32v6 image (it ships
|
|
31
|
+
# arm32v5), so armv6 appears only in binaries-musl (no binaries-glibc-extra
|
|
32
|
+
# entry) and the published container image does not cover it (the Debian-based
|
|
33
|
+
# Dockerfile has no arm32v6 base). riscv64 and armv6 have no precompiled
|
|
34
|
+
# PyInstaller bootloader, so pip compiles it from the sdist via waf under QEMU.
|
|
35
|
+
|
|
36
|
+
on:
|
|
37
|
+
push:
|
|
38
|
+
branches: ["**"] # build on every commit, on every branch
|
|
39
|
+
workflow_dispatch:
|
|
40
|
+
|
|
41
|
+
concurrency:
|
|
42
|
+
# One in-flight build per ref; a newer commit supersedes an older build.
|
|
43
|
+
group: build-${{ github.workflow }}-${{ github.ref }}
|
|
44
|
+
cancel-in-progress: true
|
|
45
|
+
|
|
46
|
+
# Build-only: nothing in this workflow writes anywhere (no PyPI/OIDC, no
|
|
47
|
+
# contents:write, no packages:write). Read is all any job needs.
|
|
48
|
+
permissions:
|
|
49
|
+
contents: read
|
|
50
|
+
|
|
51
|
+
# Network hardening for a transient PyPI/network blip. The runner-native jobs
|
|
52
|
+
# (dist, native binaries, macOS, Windows, version) install with uv, which reads
|
|
53
|
+
# UV_HTTP_TIMEOUT for its per-request socket timeout (and retries internally);
|
|
54
|
+
# UV_PYTHON_DOWNLOADS=never keeps uv on the setup-python interpreter rather than
|
|
55
|
+
# fetching a managed one, so the build Python stays exactly the one CI pinned.
|
|
56
|
+
# The emulated foreign-arch binary jobs (binaries-musl, binaries-glibc-extra)
|
|
57
|
+
# stay on pip inside their `docker run` containers -- uv's official image is
|
|
58
|
+
# amd64/arm64 only and its musl ppc64le/s390x wheels don't exist, so pip is the
|
|
59
|
+
# arch-portable choice there -- and read PIP_RETRIES/PIP_TIMEOUT, forwarded into
|
|
60
|
+
# `docker run` with `-e`.
|
|
61
|
+
env:
|
|
62
|
+
PIP_RETRIES: "5"
|
|
63
|
+
PIP_TIMEOUT: "60"
|
|
64
|
+
UV_HTTP_TIMEOUT: "60"
|
|
65
|
+
UV_PYTHON_DOWNLOADS: "never"
|
|
66
|
+
|
|
67
|
+
jobs:
|
|
68
|
+
# Compute the version once on the host (which has git + full history) and feed
|
|
69
|
+
# it to every build via SETUPTOOLS_SCM_PRETEND_VERSION -- mirroring release.yml,
|
|
70
|
+
# which pins all its builds to one number. Here the number is the natural
|
|
71
|
+
# setuptools_scm dev version, not a bumped release version. Pinning it also
|
|
72
|
+
# lets the musl build run inside an Alpine container with no git installed
|
|
73
|
+
# (the container can't run `git describe`, so it relies on the pretend value).
|
|
74
|
+
version:
|
|
75
|
+
runs-on: ubuntu-latest
|
|
76
|
+
outputs:
|
|
77
|
+
new: ${{ steps.ver.outputs.new }}
|
|
78
|
+
steps:
|
|
79
|
+
- uses: actions/checkout@v7.0.0
|
|
80
|
+
with:
|
|
81
|
+
fetch-depth: 0 # full history + tags so setuptools_scm resolves a version
|
|
82
|
+
- uses: actions/setup-python@v6.3.0
|
|
83
|
+
with:
|
|
84
|
+
python-version: '3.14'
|
|
85
|
+
- uses: astral-sh/setup-uv@v8.3.0
|
|
86
|
+
with:
|
|
87
|
+
version: "0.11.26"
|
|
88
|
+
- name: Compute build version
|
|
89
|
+
id: ver
|
|
90
|
+
run: |
|
|
91
|
+
set -euo pipefail
|
|
92
|
+
# Resolve setuptools_scm into a throwaway env and read the version off
|
|
93
|
+
# git -- no venv to create, no install to leave behind.
|
|
94
|
+
new="$(uv run --no-project --with setuptools-scm python -m setuptools_scm)"
|
|
95
|
+
echo "new=$new" >> "$GITHUB_OUTPUT"
|
|
96
|
+
echo "::notice::Build-only at version $new (no release)."
|
|
97
|
+
|
|
98
|
+
# Wheel + sdist, built and `twine check`ed exactly as release.yml does for
|
|
99
|
+
# PyPI -- but never uploaded to PyPI. The artifact is kept only for download.
|
|
100
|
+
dist:
|
|
101
|
+
needs: version
|
|
102
|
+
runs-on: ubuntu-latest
|
|
103
|
+
steps:
|
|
104
|
+
- uses: actions/checkout@v7.0.0
|
|
105
|
+
with:
|
|
106
|
+
fetch-depth: 0 # full history so setuptools_scm has a base (version is pinned below)
|
|
107
|
+
- uses: actions/setup-python@v6.3.0
|
|
108
|
+
with:
|
|
109
|
+
python-version: '3.14'
|
|
110
|
+
- uses: astral-sh/setup-uv@v8.3.0
|
|
111
|
+
with:
|
|
112
|
+
version: "0.11.26"
|
|
113
|
+
- name: Build distributions
|
|
114
|
+
env:
|
|
115
|
+
SETUPTOOLS_SCM_PRETEND_VERSION: ${{ needs.version.outputs.new }}
|
|
116
|
+
run: |
|
|
117
|
+
set -euo pipefail
|
|
118
|
+
uv build # wheel + sdist into dist/
|
|
119
|
+
uvx twine check dist/*.whl dist/*.tar.gz # twine, run ephemerally
|
|
120
|
+
- uses: actions/upload-artifact@v7.0.1
|
|
121
|
+
with:
|
|
122
|
+
name: dist
|
|
123
|
+
path: |
|
|
124
|
+
dist/*.whl
|
|
125
|
+
dist/*.tar.gz
|
|
126
|
+
if-no-files-found: error
|
|
127
|
+
retention-days: 7
|
|
128
|
+
|
|
129
|
+
# glibc PyInstaller binaries, one per arch on a native runner. Mirrors
|
|
130
|
+
# release.yml's `binaries` job (same pyinstaller pin, same `--version` smoke
|
|
131
|
+
# test); the only difference is the version is the dev version, not a release.
|
|
132
|
+
binaries:
|
|
133
|
+
needs: version
|
|
134
|
+
strategy:
|
|
135
|
+
fail-fast: false
|
|
136
|
+
matrix:
|
|
137
|
+
include:
|
|
138
|
+
- arch: amd64
|
|
139
|
+
runner: ubuntu-24.04
|
|
140
|
+
- arch: arm64
|
|
141
|
+
runner: ubuntu-24.04-arm
|
|
142
|
+
runs-on: ${{ matrix.runner }}
|
|
143
|
+
steps:
|
|
144
|
+
- uses: actions/checkout@v7.0.0
|
|
145
|
+
with:
|
|
146
|
+
fetch-depth: 0 # full history so setuptools_scm has a base (version is pinned below)
|
|
147
|
+
- uses: actions/setup-python@v6.3.0
|
|
148
|
+
with:
|
|
149
|
+
python-version: '3.14'
|
|
150
|
+
- uses: astral-sh/setup-uv@v8.3.0
|
|
151
|
+
with:
|
|
152
|
+
version: "0.11.26"
|
|
153
|
+
- name: Build binary
|
|
154
|
+
env:
|
|
155
|
+
SETUPTOOLS_SCM_PRETEND_VERSION: ${{ needs.version.outputs.new }}
|
|
156
|
+
run: |
|
|
157
|
+
set -euo pipefail
|
|
158
|
+
# Runner-native, all-wheels arch: install into a throwaway uv venv and
|
|
159
|
+
# freeze from it with `uv run` (uv discovers the setup-python 3.14).
|
|
160
|
+
uv venv --python 3.14
|
|
161
|
+
uv pip install pyinstaller==6.21.0 # pin matches pyinstaller/Dockerfile
|
|
162
|
+
uv pip install . # bakes the pinned version into cronstable/version.py
|
|
163
|
+
# Bundle uvloop (the `speedups` extra) so the binary runs the faster
|
|
164
|
+
# event loop. Install a wheel where one exists, else build it from
|
|
165
|
+
# sdist (ship uvloop as broadly as it compiles); a failed install
|
|
166
|
+
# falls back to asyncio. verify_uvloop.py then proves the result
|
|
167
|
+
# actually runs a loop (a source build can miscompile under emulation)
|
|
168
|
+
# and uninstalls it if not, so a broken uvloop is never frozen in and
|
|
169
|
+
# the binary cleanly uses asyncio.
|
|
170
|
+
uv pip install "uvloop>=0.19" || echo "uvloop unavailable; using asyncio"
|
|
171
|
+
uv run python pyinstaller/verify_uvloop.py || uv pip uninstall uvloop || true
|
|
172
|
+
# Bundle orjson (the `speedups` extra) to accelerate the durable-state
|
|
173
|
+
# and cluster JSON paths; amd64/arm64 and macOS ship wheels (no Rust
|
|
174
|
+
# needed). The shared script round-trip-verifies it and drops a broken
|
|
175
|
+
# build, so the binary falls back to stdlib json (cronstable/_json).
|
|
176
|
+
PIP="uv pip" PIPUNINST="uv pip uninstall" PY="uv run python" \
|
|
177
|
+
sh pyinstaller/install_orjson.sh
|
|
178
|
+
uv run pyinstaller pyinstaller/cronstable.spec
|
|
179
|
+
dist/cronstable --version # smoke test: fails the job if the bundle is broken
|
|
180
|
+
mv dist/cronstable "dist/cronstable-linux-${{ matrix.arch }}"
|
|
181
|
+
- uses: actions/upload-artifact@v7.0.1
|
|
182
|
+
with:
|
|
183
|
+
name: cronstable-linux-${{ matrix.arch }}
|
|
184
|
+
path: dist/cronstable-linux-${{ matrix.arch }}
|
|
185
|
+
if-no-files-found: error
|
|
186
|
+
retention-days: 7
|
|
187
|
+
|
|
188
|
+
# musl/Alpine PyInstaller binaries, built inside an Alpine container (see
|
|
189
|
+
# release.yml's binaries-musl for why it's `docker run`, not a job container).
|
|
190
|
+
# Mirrors release.yml's binaries-musl; the pinned version is what lets the
|
|
191
|
+
# no-git Alpine image resolve a setuptools_scm version. Covers all eight Linux
|
|
192
|
+
# musl arches: amd64/arm64 build natively on a matching runner; i686 (linux/386)
|
|
193
|
+
# runs natively on the amd64 runner (x86-64 executes 32-bit x86 directly);
|
|
194
|
+
# armv7, armv6, ppc64le, s390x and riscv64 have no native execution on the
|
|
195
|
+
# runners, so they run under QEMU emulation. musllinux wheels exist for the
|
|
196
|
+
# whole stack on armv7/ppc64le/s390x, but NOT for the i686 aiohttp family
|
|
197
|
+
# (aiohttp/yarl/frozenlist/propcache), nothing at all for armv6 (no musllinux
|
|
198
|
+
# armv6 wheels exist), or multidict/frozenlist/ruamel.yaml.clib on riscv64 --
|
|
199
|
+
# build-base compiles those from sdist.
|
|
200
|
+
binaries-musl:
|
|
201
|
+
needs: version
|
|
202
|
+
strategy:
|
|
203
|
+
fail-fast: false
|
|
204
|
+
matrix:
|
|
205
|
+
include:
|
|
206
|
+
- arch: amd64
|
|
207
|
+
runner: ubuntu-24.04
|
|
208
|
+
platform: linux/amd64
|
|
209
|
+
- arch: arm64
|
|
210
|
+
runner: ubuntu-24.04-arm
|
|
211
|
+
platform: linux/arm64
|
|
212
|
+
- arch: i686
|
|
213
|
+
runner: ubuntu-24.04
|
|
214
|
+
platform: linux/386
|
|
215
|
+
- arch: armv7
|
|
216
|
+
runner: ubuntu-24.04
|
|
217
|
+
platform: linux/arm/v7
|
|
218
|
+
qemu: true # no native 32-bit ARM on the runners -> emulate
|
|
219
|
+
- arch: ppc64le
|
|
220
|
+
runner: ubuntu-24.04
|
|
221
|
+
platform: linux/ppc64le
|
|
222
|
+
qemu: true # no native ppc64le on the runners -> emulate
|
|
223
|
+
- arch: s390x
|
|
224
|
+
runner: ubuntu-24.04
|
|
225
|
+
platform: linux/s390x
|
|
226
|
+
qemu: true # no native s390x on the runners -> emulate
|
|
227
|
+
- arch: riscv64
|
|
228
|
+
runner: ubuntu-24.04
|
|
229
|
+
platform: linux/riscv64
|
|
230
|
+
qemu: true # no native riscv64 on the runners -> emulate
|
|
231
|
+
# armv6 is musl-only: Debian/glibc ships no arm32v6 image (it ships
|
|
232
|
+
# arm32v5), so there is no binaries-glibc-extra counterpart. Entire
|
|
233
|
+
# C-ext stack + PyInstaller bootloader compile from sdist (no musllinux
|
|
234
|
+
# armv6 wheels) -> slowest leg.
|
|
235
|
+
- arch: armv6
|
|
236
|
+
runner: ubuntu-24.04
|
|
237
|
+
platform: linux/arm/v6
|
|
238
|
+
qemu: true # no native armv6 on the runners -> emulate
|
|
239
|
+
runs-on: ${{ matrix.runner }}
|
|
240
|
+
steps:
|
|
241
|
+
- uses: actions/checkout@v7.0.0
|
|
242
|
+
with:
|
|
243
|
+
fetch-depth: 0 # full history so setuptools_scm has a base (version is pinned below)
|
|
244
|
+
# Register binfmt/QEMU so `docker run --platform` can execute a foreign-arch
|
|
245
|
+
# container (armv7, armv6, ppc64le, s390x, riscv64). amd64/arm64/i686 run
|
|
246
|
+
# natively and skip this.
|
|
247
|
+
- if: ${{ matrix.qemu }}
|
|
248
|
+
uses: docker/setup-qemu-action@v4.2.0
|
|
249
|
+
- name: Build binary (Alpine/musl)
|
|
250
|
+
env:
|
|
251
|
+
SETUPTOOLS_SCM_PRETEND_VERSION: ${{ needs.version.outputs.new }}
|
|
252
|
+
run: |
|
|
253
|
+
set -euo pipefail
|
|
254
|
+
docker run --rm --platform ${{ matrix.platform }} \
|
|
255
|
+
-e SETUPTOOLS_SCM_PRETEND_VERSION \
|
|
256
|
+
-e PIP_RETRIES -e PIP_TIMEOUT \
|
|
257
|
+
-v "$PWD:/src" -w /src \
|
|
258
|
+
python:3.14-alpine sh -euc '
|
|
259
|
+
# build-base brings the C toolchain + strip (the spec sets
|
|
260
|
+
# strip=True); libffi/zlib headers cover any dep that lacks a
|
|
261
|
+
# musllinux wheel and falls back to compiling from sdist (notably
|
|
262
|
+
# the i686 aiohttp stack, which ships no musllinux_i686 wheels).
|
|
263
|
+
apk add --no-cache build-base libffi-dev zlib-dev
|
|
264
|
+
pip install --upgrade pip
|
|
265
|
+
pip install pyinstaller==6.21.0 # pin matches the glibc job (supports 3.14)
|
|
266
|
+
pip install . # bakes the pinned version into cronstable/version.py
|
|
267
|
+
# Bundle uvloop (the `speedups` extra): a musllinux wheel where one
|
|
268
|
+
# exists (amd64/arm64), else a source build (ship it as broadly as
|
|
269
|
+
# it compiles -- build-base above supplies the toolchain).
|
|
270
|
+
# verify_uvloop.py proves the result runs a loop (a source build can
|
|
271
|
+
# miscompile under QEMU) and uninstalls it otherwise, so a broken
|
|
272
|
+
# uvloop is never frozen in; the binary then cleanly falls back to
|
|
273
|
+
# asyncio.
|
|
274
|
+
pip install "uvloop>=0.19" || echo "uvloop unavailable; using asyncio"
|
|
275
|
+
python pyinstaller/verify_uvloop.py || pip uninstall -y uvloop || true
|
|
276
|
+
# Bundle orjson (the `speedups` extra): a musllinux wheel on amd64/
|
|
277
|
+
# arm64, else a Rust source build. RUST_SETUP installs a CURRENT
|
|
278
|
+
# Rust via rustup (distro rustc is older than the orjson MSRV) only
|
|
279
|
+
# when the wheel is missing; the shared script round-trip-verifies
|
|
280
|
+
# it and drops a broken QEMU build -> stdlib json fallback.
|
|
281
|
+
RUST_SETUP="apk add --no-cache curl && curl --proto =https --tlsv1.2 -sSf https://sh.rustup.rs | env CARGO_HOME=/opt/cargo RUSTUP_HOME=/opt/rustup sh -s -- -y --default-toolchain stable --profile minimal --no-modify-path" sh pyinstaller/install_orjson.sh
|
|
282
|
+
pyinstaller pyinstaller/cronstable.spec
|
|
283
|
+
dist/cronstable --version # smoke test: a musl binary on a musl host
|
|
284
|
+
mv dist/cronstable dist/cronstable-linux-${{ matrix.arch }}-musl
|
|
285
|
+
'
|
|
286
|
+
- uses: actions/upload-artifact@v7.0.1
|
|
287
|
+
with:
|
|
288
|
+
name: cronstable-linux-${{ matrix.arch }}-musl
|
|
289
|
+
path: dist/cronstable-linux-${{ matrix.arch }}-musl
|
|
290
|
+
if-no-files-found: error
|
|
291
|
+
retention-days: 7
|
|
292
|
+
|
|
293
|
+
# glibc PyInstaller binaries for the arches with no native GitHub runner
|
|
294
|
+
# (i686, armv7, ppc64le, s390x, riscv64). The native `binaries` job covers only
|
|
295
|
+
# amd64 and arm64 (the runners GitHub hosts) and PyInstaller is not a
|
|
296
|
+
# cross-compiler, so -- like binaries-musl -- these build inside a container via
|
|
297
|
+
# `docker run --platform`, here on python:3.14-slim (Debian glibc). i686
|
|
298
|
+
# (linux/386) runs natively on the amd64 runner; armv7, ppc64le, s390x and
|
|
299
|
+
# riscv64 run under QEMU. glibc wheel coverage varies: ppc64le/s390x have full
|
|
300
|
+
# manylinux wheels, while the i686 aiohttp stack, propcache-on-armv7, and
|
|
301
|
+
# multidict/frozenlist/ruamel.yaml.clib on riscv64 ship no wheel and compile
|
|
302
|
+
# from sdist -- so build-essential is installed either way. Assets carry no
|
|
303
|
+
# -musl suffix and sit beside the 64-bit glibc binaries.
|
|
304
|
+
binaries-glibc-extra:
|
|
305
|
+
needs: version
|
|
306
|
+
strategy:
|
|
307
|
+
fail-fast: false
|
|
308
|
+
matrix:
|
|
309
|
+
include:
|
|
310
|
+
- arch: i686
|
|
311
|
+
runner: ubuntu-24.04
|
|
312
|
+
platform: linux/386
|
|
313
|
+
- arch: armv7
|
|
314
|
+
runner: ubuntu-24.04
|
|
315
|
+
platform: linux/arm/v7
|
|
316
|
+
qemu: true
|
|
317
|
+
- arch: ppc64le
|
|
318
|
+
runner: ubuntu-24.04
|
|
319
|
+
platform: linux/ppc64le
|
|
320
|
+
qemu: true
|
|
321
|
+
- arch: s390x
|
|
322
|
+
runner: ubuntu-24.04
|
|
323
|
+
platform: linux/s390x
|
|
324
|
+
qemu: true
|
|
325
|
+
- arch: riscv64
|
|
326
|
+
runner: ubuntu-24.04
|
|
327
|
+
platform: linux/riscv64
|
|
328
|
+
qemu: true
|
|
329
|
+
runs-on: ${{ matrix.runner }}
|
|
330
|
+
steps:
|
|
331
|
+
- uses: actions/checkout@v7.0.0
|
|
332
|
+
with:
|
|
333
|
+
fetch-depth: 0 # full history so setuptools_scm has a base (version is pinned below)
|
|
334
|
+
- if: ${{ matrix.qemu }}
|
|
335
|
+
uses: docker/setup-qemu-action@v4.2.0
|
|
336
|
+
- name: Build binary (Debian/glibc container)
|
|
337
|
+
env:
|
|
338
|
+
SETUPTOOLS_SCM_PRETEND_VERSION: ${{ needs.version.outputs.new }}
|
|
339
|
+
run: |
|
|
340
|
+
set -euo pipefail
|
|
341
|
+
docker run --rm --platform ${{ matrix.platform }} \
|
|
342
|
+
-e SETUPTOOLS_SCM_PRETEND_VERSION \
|
|
343
|
+
-e PIP_RETRIES -e PIP_TIMEOUT \
|
|
344
|
+
-v "$PWD:/src" -w /src \
|
|
345
|
+
python:3.14-slim sh -euc '
|
|
346
|
+
# build-essential brings gcc + strip (the spec sets strip=True);
|
|
347
|
+
# libffi/zlib headers cover the C-extension deps that ship no glibc
|
|
348
|
+
# wheel and compile from sdist (the i686 aiohttp stack; propcache on
|
|
349
|
+
# armv7). ppc64le/s390x have full manylinux wheels, so the toolchain
|
|
350
|
+
# goes unused there.
|
|
351
|
+
apt-get update
|
|
352
|
+
apt-get install -y --no-install-recommends build-essential libffi-dev zlib1g-dev
|
|
353
|
+
pip install --upgrade pip
|
|
354
|
+
pip install pyinstaller==6.21.0 # pin matches the other binary jobs
|
|
355
|
+
pip install . # bakes the pinned version into cronstable/version.py
|
|
356
|
+
# Bundle uvloop (the `speedups` extra): a manylinux wheel where one
|
|
357
|
+
# exists (amd64/arm64), else a source build (ship it as broadly as
|
|
358
|
+
# it compiles -- build-essential above supplies the toolchain).
|
|
359
|
+
# verify_uvloop.py proves the result runs a loop (a source build can
|
|
360
|
+
# miscompile under QEMU) and uninstalls it otherwise, so a broken
|
|
361
|
+
# uvloop is never frozen in; the binary then cleanly falls back to
|
|
362
|
+
# asyncio.
|
|
363
|
+
pip install "uvloop>=0.19" || echo "uvloop unavailable; using asyncio"
|
|
364
|
+
python pyinstaller/verify_uvloop.py || pip uninstall -y uvloop || true
|
|
365
|
+
# Bundle orjson (the `speedups` extra): a manylinux wheel on amd64/
|
|
366
|
+
# arm64, else a Rust source build. RUST_SETUP installs a CURRENT
|
|
367
|
+
# Rust via rustup (distro rustc is older than the orjson MSRV) only
|
|
368
|
+
# when the wheel is missing; the shared script round-trip-verifies
|
|
369
|
+
# it and drops a broken QEMU build -> stdlib json fallback.
|
|
370
|
+
RUST_SETUP="apt-get -o Acquire::Retries=5 update && apt-get -o Acquire::Retries=5 install -y --no-install-recommends curl ca-certificates && curl --proto =https --tlsv1.2 -sSf https://sh.rustup.rs | env CARGO_HOME=/opt/cargo RUSTUP_HOME=/opt/rustup sh -s -- -y --default-toolchain stable --profile minimal --no-modify-path" sh pyinstaller/install_orjson.sh
|
|
371
|
+
pyinstaller pyinstaller/cronstable.spec
|
|
372
|
+
dist/cronstable --version # smoke test: native (i686) or emulated (armv7/ppc64le/s390x)
|
|
373
|
+
mv dist/cronstable dist/cronstable-linux-${{ matrix.arch }}
|
|
374
|
+
'
|
|
375
|
+
- uses: actions/upload-artifact@v7.0.1
|
|
376
|
+
with:
|
|
377
|
+
name: cronstable-linux-${{ matrix.arch }}
|
|
378
|
+
path: dist/cronstable-linux-${{ matrix.arch }}
|
|
379
|
+
if-no-files-found: error
|
|
380
|
+
retention-days: 7
|
|
381
|
+
|
|
382
|
+
# macOS PyInstaller binaries. Per-commit we build on BOTH macOS 15 and macOS
|
|
383
|
+
# 26 (Tahoe), each on a native runner, arm64 + Intel -- four jobs -- so OS-
|
|
384
|
+
# version-specific build breakage surfaces early. (release.yml still ships
|
|
385
|
+
# only the macOS 15 pair; this extra macOS 26 coverage is CI-only.) Mirrors
|
|
386
|
+
# release.yml's binaries-macos -- including the arch assertion that guards
|
|
387
|
+
# against Rosetta silently building the wrong arch -- but deliberately OMITS
|
|
388
|
+
# the "Sign and notarize" step (that needs secrets and only matters for a
|
|
389
|
+
# published release). The matrix carries an `os` (15/26) discriminator so the
|
|
390
|
+
# four binaries/artifacts get unique names instead of colliding on arch.
|
|
391
|
+
binaries-macos:
|
|
392
|
+
needs: version
|
|
393
|
+
strategy:
|
|
394
|
+
fail-fast: false
|
|
395
|
+
matrix:
|
|
396
|
+
include:
|
|
397
|
+
- arch: arm64
|
|
398
|
+
runner: macos-15 # macOS 15, Apple Silicon
|
|
399
|
+
os: '15'
|
|
400
|
+
- arch: amd64
|
|
401
|
+
runner: macos-15-intel # macOS 15, Intel
|
|
402
|
+
os: '15'
|
|
403
|
+
- arch: arm64
|
|
404
|
+
runner: macos-26 # macOS 26 (Tahoe), Apple Silicon
|
|
405
|
+
os: '26'
|
|
406
|
+
- arch: amd64
|
|
407
|
+
runner: macos-26-intel # macOS 26 (Tahoe), Intel
|
|
408
|
+
os: '26'
|
|
409
|
+
runs-on: ${{ matrix.runner }}
|
|
410
|
+
steps:
|
|
411
|
+
- uses: actions/checkout@v7.0.0
|
|
412
|
+
with:
|
|
413
|
+
fetch-depth: 0 # full history so setuptools_scm has a base (version is pinned below)
|
|
414
|
+
- uses: actions/setup-python@v6.3.0
|
|
415
|
+
with:
|
|
416
|
+
python-version: '3.14' # set explicitly; don't rely on the runner's preinstalled patch level
|
|
417
|
+
- uses: astral-sh/setup-uv@v8.3.0
|
|
418
|
+
with:
|
|
419
|
+
version: "0.11.26"
|
|
420
|
+
- name: Build binary
|
|
421
|
+
env:
|
|
422
|
+
SETUPTOOLS_SCM_PRETEND_VERSION: ${{ needs.version.outputs.new }}
|
|
423
|
+
run: |
|
|
424
|
+
set -euo pipefail
|
|
425
|
+
# Runner-native, all-wheels arch: install into a throwaway uv venv and
|
|
426
|
+
# freeze from it with `uv run` (uv discovers the setup-python 3.14).
|
|
427
|
+
uv venv --python 3.14
|
|
428
|
+
uv pip install pyinstaller==6.21.0 # pin matches the Linux jobs
|
|
429
|
+
uv pip install . # bakes the pinned version into cronstable/version.py
|
|
430
|
+
# Bundle uvloop (the `speedups` extra) for the faster event loop; macOS
|
|
431
|
+
# ships wheels for both arches. verify_uvloop.py confirms it imports and
|
|
432
|
+
# runs a loop, else uninstalls it so the binary falls back to asyncio
|
|
433
|
+
# (cronstable/__main__._new_event_loop).
|
|
434
|
+
uv pip install "uvloop>=0.19" || echo "uvloop unavailable; using asyncio"
|
|
435
|
+
uv run python pyinstaller/verify_uvloop.py || uv pip uninstall uvloop || true
|
|
436
|
+
# Bundle orjson (the `speedups` extra) to accelerate the durable-state
|
|
437
|
+
# and cluster JSON paths; amd64/arm64 and macOS ship wheels (no Rust
|
|
438
|
+
# needed). The shared script round-trip-verifies it and drops a broken
|
|
439
|
+
# build, so the binary falls back to stdlib json (cronstable/_json).
|
|
440
|
+
PIP="uv pip" PIPUNINST="uv pip uninstall" PY="uv run python" \
|
|
441
|
+
sh pyinstaller/install_orjson.sh
|
|
442
|
+
uv run pyinstaller pyinstaller/cronstable.spec
|
|
443
|
+
dist/cronstable --version # smoke test: fails the job if the bundle is broken
|
|
444
|
+
# Assert the binary is the intended native arch: Rosetta would happily
|
|
445
|
+
# run an x86_64 build on the arm64 runner and let the smoke test pass,
|
|
446
|
+
# silently producing a mislabelled asset. `file` reports x86_64/arm64.
|
|
447
|
+
case "${{ matrix.arch }}" in
|
|
448
|
+
amd64) want=x86_64 ;;
|
|
449
|
+
arm64) want=arm64 ;;
|
|
450
|
+
esac
|
|
451
|
+
file dist/cronstable | grep -q "$want"
|
|
452
|
+
mv dist/cronstable "dist/cronstable-macos${{ matrix.os }}-${{ matrix.arch }}"
|
|
453
|
+
- uses: actions/upload-artifact@v7.0.1
|
|
454
|
+
with:
|
|
455
|
+
name: cronstable-macos${{ matrix.os }}-${{ matrix.arch }}
|
|
456
|
+
path: dist/cronstable-macos${{ matrix.os }}-${{ matrix.arch }}
|
|
457
|
+
if-no-files-found: error
|
|
458
|
+
retention-days: 7
|
|
459
|
+
|
|
460
|
+
# Windows PyInstaller binaries: amd64 on the hosted runner, arm64 on the
|
|
461
|
+
# windows-11-arm runner. Mirrors release.yml's binaries-windows. Any C-
|
|
462
|
+
# extension dep without a win_arm64 wheel (parts of the aiohttp stack --
|
|
463
|
+
# multidict/yarl/frozenlist/propcache -- and ruamel.yaml.clib) compiles from
|
|
464
|
+
# sdist using the runner's Visual Studio ARM64 toolchain, the same way the
|
|
465
|
+
# QEMU Linux legs compile from sdist for riscv64/armv6. Same pyinstaller pin
|
|
466
|
+
# and `--version` smoke test as the other binary jobs; the bundle is a single
|
|
467
|
+
# self-contained cronstable-windows-<arch>.exe. Steps use bash (git bash ships on
|
|
468
|
+
# the Windows runners) so they read identically to the Linux/macOS jobs.
|
|
469
|
+
binaries-windows:
|
|
470
|
+
needs: version
|
|
471
|
+
strategy:
|
|
472
|
+
fail-fast: false
|
|
473
|
+
matrix:
|
|
474
|
+
include:
|
|
475
|
+
- arch: amd64
|
|
476
|
+
runner: windows-latest
|
|
477
|
+
- arch: arm64
|
|
478
|
+
runner: windows-11-arm
|
|
479
|
+
runs-on: ${{ matrix.runner }}
|
|
480
|
+
defaults:
|
|
481
|
+
run:
|
|
482
|
+
shell: bash
|
|
483
|
+
steps:
|
|
484
|
+
- uses: actions/checkout@v7.0.0
|
|
485
|
+
with:
|
|
486
|
+
fetch-depth: 0 # full history so setuptools_scm has a base (version is pinned below)
|
|
487
|
+
- uses: actions/setup-python@v6.3.0
|
|
488
|
+
with:
|
|
489
|
+
python-version: '3.14'
|
|
490
|
+
- uses: astral-sh/setup-uv@v8.3.0
|
|
491
|
+
with:
|
|
492
|
+
version: "0.11.26"
|
|
493
|
+
- name: Build binary
|
|
494
|
+
env:
|
|
495
|
+
SETUPTOOLS_SCM_PRETEND_VERSION: ${{ needs.version.outputs.new }}
|
|
496
|
+
run: |
|
|
497
|
+
set -euo pipefail
|
|
498
|
+
# Runner-native (uv ships a win_arm64 build too); install into a
|
|
499
|
+
# throwaway uv venv and freeze from it with `uv run`.
|
|
500
|
+
uv venv --python 3.14
|
|
501
|
+
uv pip install pyinstaller==6.21.0 # pin matches the Linux/macOS jobs
|
|
502
|
+
uv pip install . # bakes the pinned version into cronstable/version.py
|
|
503
|
+
# Bundle orjson (the `speedups` extra) for the durable-state / cluster
|
|
504
|
+
# JSON paths; Windows amd64 ships a wheel (arm64 falls back if none).
|
|
505
|
+
# The shared script round-trip-verifies it and drops a broken build,
|
|
506
|
+
# so the binary falls back to stdlib json (cronstable/_json).
|
|
507
|
+
PIP="uv pip" PIPUNINST="uv pip uninstall" PY="uv run python" \
|
|
508
|
+
sh pyinstaller/install_orjson.sh
|
|
509
|
+
uv run pyinstaller pyinstaller/cronstable.spec
|
|
510
|
+
./dist/cronstable.exe --version # smoke test: fails the job if the bundle is broken
|
|
511
|
+
mv dist/cronstable.exe "dist/cronstable-windows-${{ matrix.arch }}.exe"
|
|
512
|
+
- uses: actions/upload-artifact@v7.0.1
|
|
513
|
+
with:
|
|
514
|
+
name: cronstable-windows-${{ matrix.arch }}
|
|
515
|
+
path: dist/cronstable-windows-${{ matrix.arch }}.exe
|
|
516
|
+
if-no-files-found: error
|
|
517
|
+
retention-days: 7
|