adispatchd 0.0.0-stage → 0.1.0
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.
- package/LICENSE +21 -0
- package/README.md +195 -2
- package/dist/api/errors.d.ts +27 -0
- package/dist/api/errors.js +80 -0
- package/dist/api/errors.js.map +1 -0
- package/dist/api/index.d.ts +10 -0
- package/dist/api/index.js +11 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/logs.d.ts +30 -0
- package/dist/api/logs.js +88 -0
- package/dist/api/logs.js.map +1 -0
- package/dist/api/serialize.d.ts +65 -0
- package/dist/api/serialize.js +135 -0
- package/dist/api/serialize.js.map +1 -0
- package/dist/api/server.d.ts +89 -0
- package/dist/api/server.js +751 -0
- package/dist/api/server.js.map +1 -0
- package/dist/api/sse.d.ts +90 -0
- package/dist/api/sse.js +225 -0
- package/dist/api/sse.js.map +1 -0
- package/dist/app.d.ts +173 -0
- package/dist/app.js +488 -0
- package/dist/app.js.map +1 -0
- package/dist/cli/config-validate.d.ts +6 -0
- package/dist/cli/config-validate.js +39 -0
- package/dist/cli/config-validate.js.map +1 -0
- package/dist/cli/control.d.ts +31 -0
- package/dist/cli/control.js +186 -0
- package/dist/cli/control.js.map +1 -0
- package/dist/cli/http-client.d.ts +57 -0
- package/dist/cli/http-client.js +174 -0
- package/dist/cli/http-client.js.map +1 -0
- package/dist/cli/index.d.ts +14 -0
- package/dist/cli/index.js +14 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init-templates.d.ts +20 -0
- package/dist/cli/init-templates.js +275 -0
- package/dist/cli/init-templates.js.map +1 -0
- package/dist/cli/init.d.ts +16 -0
- package/dist/cli/init.js +107 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.js +25 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/program.d.ts +102 -0
- package/dist/cli/program.js +133 -0
- package/dist/cli/program.js.map +1 -0
- package/dist/cli/service.d.ts +27 -0
- package/dist/cli/service.js +252 -0
- package/dist/cli/service.js.map +1 -0
- package/dist/cli/start.d.ts +22 -0
- package/dist/cli/start.js +133 -0
- package/dist/cli/start.js.map +1 -0
- package/dist/cli/workflow-dry-run.d.ts +13 -0
- package/dist/cli/workflow-dry-run.js +144 -0
- package/dist/cli/workflow-dry-run.js.map +1 -0
- package/dist/config/field-catalog.d.ts +33 -0
- package/dist/config/field-catalog.js +172 -0
- package/dist/config/field-catalog.js.map +1 -0
- package/dist/config/filter-schema.d.ts +15 -0
- package/dist/config/filter-schema.js +110 -0
- package/dist/config/filter-schema.js.map +1 -0
- package/dist/config/index.d.ts +15 -0
- package/dist/config/index.js +16 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/issues.d.ts +27 -0
- package/dist/config/issues.js +37 -0
- package/dist/config/issues.js.map +1 -0
- package/dist/config/loader.d.ts +32 -0
- package/dist/config/loader.js +250 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/manager.d.ts +153 -0
- package/dist/config/manager.js +276 -0
- package/dist/config/manager.js.map +1 -0
- package/dist/config/model.d.ts +334 -0
- package/dist/config/model.js +2 -0
- package/dist/config/model.js.map +1 -0
- package/dist/config/normalize.d.ts +20 -0
- package/dist/config/normalize.js +200 -0
- package/dist/config/normalize.js.map +1 -0
- package/dist/config/parameters.d.ts +37 -0
- package/dist/config/parameters.js +71 -0
- package/dist/config/parameters.js.map +1 -0
- package/dist/config/public.d.ts +6 -0
- package/dist/config/public.js +18 -0
- package/dist/config/public.js.map +1 -0
- package/dist/config/schemas.d.ts +538 -0
- package/dist/config/schemas.js +321 -0
- package/dist/config/schemas.js.map +1 -0
- package/dist/config/snapshot.d.ts +38 -0
- package/dist/config/snapshot.js +57 -0
- package/dist/config/snapshot.js.map +1 -0
- package/dist/config/validate.d.ts +33 -0
- package/dist/config/validate.js +346 -0
- package/dist/config/validate.js.map +1 -0
- package/dist/core/clock.d.ts +59 -0
- package/dist/core/clock.js +133 -0
- package/dist/core/clock.js.map +1 -0
- package/dist/core/duration.d.ts +19 -0
- package/dist/core/duration.js +93 -0
- package/dist/core/duration.js.map +1 -0
- package/dist/core/errors.d.ts +62 -0
- package/dist/core/errors.js +72 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/ids.d.ts +57 -0
- package/dist/core/ids.js +88 -0
- package/dist/core/ids.js.map +1 -0
- package/dist/core/index.d.ts +13 -0
- package/dist/core/index.js +14 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/logger.d.ts +100 -0
- package/dist/core/logger.js +207 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/paths.d.ts +84 -0
- package/dist/core/paths.js +138 -0
- package/dist/core/paths.js.map +1 -0
- package/dist/core/template.d.ts +136 -0
- package/dist/core/template.js +261 -0
- package/dist/core/template.js.map +1 -0
- package/dist/core/types.d.ts +544 -0
- package/dist/core/types.js +172 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/worker-protocol.d.ts +102 -0
- package/dist/core/worker-protocol.js +75 -0
- package/dist/core/worker-protocol.js.map +1 -0
- package/dist/executors/classify.d.ts +25 -0
- package/dist/executors/classify.js +54 -0
- package/dist/executors/classify.js.map +1 -0
- package/dist/executors/dispatcher.d.ts +35 -0
- package/dist/executors/dispatcher.js +68 -0
- package/dist/executors/dispatcher.js.map +1 -0
- package/dist/executors/env.d.ts +43 -0
- package/dist/executors/env.js +66 -0
- package/dist/executors/env.js.map +1 -0
- package/dist/executors/event-parser.d.ts +41 -0
- package/dist/executors/event-parser.js +106 -0
- package/dist/executors/event-parser.js.map +1 -0
- package/dist/executors/index.d.ts +12 -0
- package/dist/executors/index.js +13 -0
- package/dist/executors/index.js.map +1 -0
- package/dist/executors/log-capture.d.ts +57 -0
- package/dist/executors/log-capture.js +155 -0
- package/dist/executors/log-capture.js.map +1 -0
- package/dist/executors/process-identity.d.ts +124 -0
- package/dist/executors/process-identity.js +283 -0
- package/dist/executors/process-identity.js.map +1 -0
- package/dist/executors/shell-executor.d.ts +35 -0
- package/dist/executors/shell-executor.js +478 -0
- package/dist/executors/shell-executor.js.map +1 -0
- package/dist/executors/stream-redactor.d.ts +40 -0
- package/dist/executors/stream-redactor.js +89 -0
- package/dist/executors/stream-redactor.js.map +1 -0
- package/dist/executors/workspace.d.ts +10 -0
- package/dist/executors/workspace.js +47 -0
- package/dist/executors/workspace.js.map +1 -0
- package/dist/filters/evaluate.d.ts +77 -0
- package/dist/filters/evaluate.js +187 -0
- package/dist/filters/evaluate.js.map +1 -0
- package/dist/filters/fields.d.ts +35 -0
- package/dist/filters/fields.js +75 -0
- package/dist/filters/fields.js.map +1 -0
- package/dist/filters/index.d.ts +11 -0
- package/dist/filters/index.js +12 -0
- package/dist/filters/index.js.map +1 -0
- package/dist/filters/iso-date.d.ts +10 -0
- package/dist/filters/iso-date.js +42 -0
- package/dist/filters/iso-date.js.map +1 -0
- package/dist/filters/predicate.d.ts +24 -0
- package/dist/filters/predicate.js +178 -0
- package/dist/filters/predicate.js.map +1 -0
- package/dist/filters/priority.d.ts +51 -0
- package/dist/filters/priority.js +91 -0
- package/dist/filters/priority.js.map +1 -0
- package/dist/filters/pushdown.d.ts +12 -0
- package/dist/filters/pushdown.js +52 -0
- package/dist/filters/pushdown.js.map +1 -0
- package/dist/filters/shell-check.d.ts +110 -0
- package/dist/filters/shell-check.js +304 -0
- package/dist/filters/shell-check.js.map +1 -0
- package/dist/identities/github-app.d.ts +55 -0
- package/dist/identities/github-app.js +160 -0
- package/dist/identities/github-app.js.map +1 -0
- package/dist/identities/index.d.ts +6 -0
- package/dist/identities/index.js +7 -0
- package/dist/identities/index.js.map +1 -0
- package/dist/identities/jwt.d.ts +24 -0
- package/dist/identities/jwt.js +56 -0
- package/dist/identities/jwt.js.map +1 -0
- package/dist/lifecycle/context.d.ts +106 -0
- package/dist/lifecycle/context.js +170 -0
- package/dist/lifecycle/context.js.map +1 -0
- package/dist/lifecycle/engine.d.ts +185 -0
- package/dist/lifecycle/engine.js +551 -0
- package/dist/lifecycle/engine.js.map +1 -0
- package/dist/lifecycle/errors.d.ts +48 -0
- package/dist/lifecycle/errors.js +71 -0
- package/dist/lifecycle/errors.js.map +1 -0
- package/dist/lifecycle/index.d.ts +9 -0
- package/dist/lifecycle/index.js +9 -0
- package/dist/lifecycle/index.js.map +1 -0
- package/dist/lifecycle/performers.d.ts +66 -0
- package/dist/lifecycle/performers.js +179 -0
- package/dist/lifecycle/performers.js.map +1 -0
- package/dist/notifications/index.d.ts +6 -0
- package/dist/notifications/index.js +7 -0
- package/dist/notifications/index.js.map +1 -0
- package/dist/notifications/service.d.ts +115 -0
- package/dist/notifications/service.js +364 -0
- package/dist/notifications/service.js.map +1 -0
- package/dist/persistence/actions.d.ts +101 -0
- package/dist/persistence/actions.js +196 -0
- package/dist/persistence/actions.js.map +1 -0
- package/dist/persistence/blocks.d.ts +85 -0
- package/dist/persistence/blocks.js +140 -0
- package/dist/persistence/blocks.js.map +1 -0
- package/dist/persistence/cron-firings.d.ts +55 -0
- package/dist/persistence/cron-firings.js +70 -0
- package/dist/persistence/cron-firings.js.map +1 -0
- package/dist/persistence/database.d.ts +60 -0
- package/dist/persistence/database.js +124 -0
- package/dist/persistence/database.js.map +1 -0
- package/dist/persistence/errors.d.ts +39 -0
- package/dist/persistence/errors.js +67 -0
- package/dist/persistence/errors.js.map +1 -0
- package/dist/persistence/events.d.ts +72 -0
- package/dist/persistence/events.js +83 -0
- package/dist/persistence/events.js.map +1 -0
- package/dist/persistence/index.d.ts +18 -0
- package/dist/persistence/index.js +19 -0
- package/dist/persistence/index.js.map +1 -0
- package/dist/persistence/instance-lock.d.ts +26 -0
- package/dist/persistence/instance-lock.js +117 -0
- package/dist/persistence/instance-lock.js.map +1 -0
- package/dist/persistence/lock-keys.d.ts +16 -0
- package/dist/persistence/lock-keys.js +41 -0
- package/dist/persistence/lock-keys.js.map +1 -0
- package/dist/persistence/notifications.d.ts +113 -0
- package/dist/persistence/notifications.js +213 -0
- package/dist/persistence/notifications.js.map +1 -0
- package/dist/persistence/persistence.d.ts +46 -0
- package/dist/persistence/persistence.js +42 -0
- package/dist/persistence/persistence.js.map +1 -0
- package/dist/persistence/retention.d.ts +46 -0
- package/dist/persistence/retention.js +68 -0
- package/dist/persistence/retention.js.map +1 -0
- package/dist/persistence/runs.d.ts +215 -0
- package/dist/persistence/runs.js +417 -0
- package/dist/persistence/runs.js.map +1 -0
- package/dist/persistence/schema.d.ts +16 -0
- package/dist/persistence/schema.js +183 -0
- package/dist/persistence/schema.js.map +1 -0
- package/dist/persistence/sqlite.d.ts +49 -0
- package/dist/persistence/sqlite.js +131 -0
- package/dist/persistence/sqlite.js.map +1 -0
- package/dist/runs/controller.d.ts +307 -0
- package/dist/runs/controller.js +1078 -0
- package/dist/runs/controller.js.map +1 -0
- package/dist/runs/errors.d.ts +20 -0
- package/dist/runs/errors.js +26 -0
- package/dist/runs/errors.js.map +1 -0
- package/dist/runs/event-bus.d.ts +106 -0
- package/dist/runs/event-bus.js +105 -0
- package/dist/runs/event-bus.js.map +1 -0
- package/dist/runs/execution.d.ts +39 -0
- package/dist/runs/execution.js +146 -0
- package/dist/runs/execution.js.map +1 -0
- package/dist/runs/index.d.ts +12 -0
- package/dist/runs/index.js +13 -0
- package/dist/runs/index.js.map +1 -0
- package/dist/runs/retention.d.ts +64 -0
- package/dist/runs/retention.js +232 -0
- package/dist/runs/retention.js.map +1 -0
- package/dist/runs/workspace.d.ts +20 -0
- package/dist/runs/workspace.js +51 -0
- package/dist/runs/workspace.js.map +1 -0
- package/dist/scheduler/checks.d.ts +41 -0
- package/dist/scheduler/checks.js +111 -0
- package/dist/scheduler/checks.js.map +1 -0
- package/dist/scheduler/cron.d.ts +32 -0
- package/dist/scheduler/cron.js +83 -0
- package/dist/scheduler/cron.js.map +1 -0
- package/dist/scheduler/dry-run.d.ts +42 -0
- package/dist/scheduler/dry-run.js +167 -0
- package/dist/scheduler/dry-run.js.map +1 -0
- package/dist/scheduler/errors.d.ts +22 -0
- package/dist/scheduler/errors.js +22 -0
- package/dist/scheduler/errors.js.map +1 -0
- package/dist/scheduler/format.d.ts +3 -0
- package/dist/scheduler/format.js +73 -0
- package/dist/scheduler/format.js.map +1 -0
- package/dist/scheduler/index.d.ts +13 -0
- package/dist/scheduler/index.js +14 -0
- package/dist/scheduler/index.js.map +1 -0
- package/dist/scheduler/planner.d.ts +88 -0
- package/dist/scheduler/planner.js +255 -0
- package/dist/scheduler/planner.js.map +1 -0
- package/dist/scheduler/scheduler.d.ts +160 -0
- package/dist/scheduler/scheduler.js +680 -0
- package/dist/scheduler/scheduler.js.map +1 -0
- package/dist/scheduler/state.d.ts +26 -0
- package/dist/scheduler/state.js +25 -0
- package/dist/scheduler/state.js.map +1 -0
- package/dist/scheduler/types.d.ts +197 -0
- package/dist/scheduler/types.js +3 -0
- package/dist/scheduler/types.js.map +1 -0
- package/dist/service/index.d.ts +6 -0
- package/dist/service/index.js +7 -0
- package/dist/service/index.js.map +1 -0
- package/dist/service/install.d.ts +93 -0
- package/dist/service/install.js +250 -0
- package/dist/service/install.js.map +1 -0
- package/dist/service/unit.d.ts +50 -0
- package/dist/service/unit.js +171 -0
- package/dist/service/unit.js.map +1 -0
- package/dist/sources/github/adapter.d.ts +99 -0
- package/dist/sources/github/adapter.js +336 -0
- package/dist/sources/github/adapter.js.map +1 -0
- package/dist/sources/github/fields.d.ts +8 -0
- package/dist/sources/github/fields.js +72 -0
- package/dist/sources/github/fields.js.map +1 -0
- package/dist/sources/github/http.d.ts +100 -0
- package/dist/sources/github/http.js +288 -0
- package/dist/sources/github/http.js.map +1 -0
- package/dist/sources/github/index.d.ts +6 -0
- package/dist/sources/github/index.js +7 -0
- package/dist/sources/github/index.js.map +1 -0
- package/dist/sources/github/pushdown.d.ts +14 -0
- package/dist/sources/github/pushdown.js +47 -0
- package/dist/sources/github/pushdown.js.map +1 -0
- package/dist/sources/github/refs.d.ts +17 -0
- package/dist/sources/github/refs.js +61 -0
- package/dist/sources/github/refs.js.map +1 -0
- package/dist/sources/index.d.ts +6 -0
- package/dist/sources/index.js +7 -0
- package/dist/sources/index.js.map +1 -0
- package/dist/ui/index.d.ts +12 -0
- package/dist/ui/index.js +28 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/public/app.css +239 -0
- package/dist/ui/public/favicon.svg +1 -0
- package/dist/ui/public/index.html +27 -0
- package/dist/ui/public/js/client/api.js +68 -0
- package/dist/ui/public/js/client/app.js +146 -0
- package/dist/ui/public/js/client/dom.js +34 -0
- package/dist/ui/public/js/client/page.js +9 -0
- package/dist/ui/public/js/client/pages/actions.js +35 -0
- package/dist/ui/public/js/client/pages/blocked.js +58 -0
- package/dist/ui/public/js/client/pages/config.js +126 -0
- package/dist/ui/public/js/client/pages/dashboard.js +175 -0
- package/dist/ui/public/js/client/pages/run.js +367 -0
- package/dist/ui/public/js/client/pages/runs.js +72 -0
- package/dist/ui/public/js/client/pages/views.js +80 -0
- package/dist/ui/public/js/client/pages/workflows.js +291 -0
- package/dist/ui/public/js/client/types.js +1 -0
- package/dist/ui/public/js/client/ui.js +152 -0
- package/dist/ui/public/js/shared/dom.js +77 -0
- package/dist/ui/public/js/shared/events.js +88 -0
- package/dist/ui/public/js/shared/format.js +213 -0
- package/dist/ui/public/js/shared/logs.js +136 -0
- package/dist/ui/public/js/shared/params.js +90 -0
- package/dist/ui/public/js/shared/route.js +36 -0
- package/dist/ui/public/js/shared/sort.js +70 -0
- package/dist/ui/public/js/shared/sse.js +193 -0
- package/dist/ui/public/js/shared/throttle.js +65 -0
- package/dist/views/index.d.ts +6 -0
- package/dist/views/index.js +7 -0
- package/dist/views/index.js.map +1 -0
- package/dist/views/service.d.ts +131 -0
- package/dist/views/service.js +264 -0
- package/dist/views/service.js.map +1 -0
- package/docs/api.md +239 -0
- package/docs/configuration-reference.md +1785 -0
- package/docs/cron-semantics.md +96 -0
- package/docs/manual-tests/github-fixture/config.yaml +30 -0
- package/docs/manual-tests/github-fixture/helpers.sh +93 -0
- package/docs/manual-tests/github-fixture/identities.yaml +18 -0
- package/docs/manual-tests/github-fixture/scripts/test-worker.sh +100 -0
- package/docs/manual-tests/github-fixture/sources.yaml +13 -0
- package/docs/manual-tests/github-fixture/views/dependencies.yaml +17 -0
- package/docs/manual-tests/github-fixture/views/needs-maintainer.yaml +17 -0
- package/docs/manual-tests/github-fixture/workflows/coder-test.yaml +76 -0
- package/docs/manual-tests/github-fixture/workflows/commenter-test.yaml +63 -0
- package/docs/manual-tests/github-fixture/workflows/reviewer-test.yaml +60 -0
- package/docs/manual-tests/github.md +1308 -0
- package/docs/manual-tests/process-supervision.md +64 -0
- package/docs/manual-tests/systemd.md +68 -0
- package/docs/operations.md +271 -0
- package/docs/worker-protocol.md +145 -0
- package/examples/config/config.yaml +35 -0
- package/examples/config/identities.yaml +18 -0
- package/examples/config/sources.yaml +13 -0
- package/examples/config/views/needs-maintainer.yaml +15 -0
- package/examples/config/workflows/coder-project-a.yaml +86 -0
- package/examples/config/workflows/deploy.yaml +15 -0
- package/examples/config/workflows/nightly-maintenance.yaml +13 -0
- package/package.json +75 -4
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Cron-Semantik (Spec §5.5, §7, §24.7)
|
|
2
|
+
|
|
3
|
+
Gilt für Workflows mit `trigger: { type: schedule, cron, timezone }` (Default-Zeitzone `UTC`). Implementiert in
|
|
4
|
+
`src/scheduler/cron.ts` (Slot-Berechnung, croner 10.0.1) und `src/scheduler/scheduler.ts` (Auslösung, Protokoll).
|
|
5
|
+
Das DST-Verhalten ist durch Tests festgenagelt (`src/scheduler/cron.test.ts`); ein croner-Update, das es ändert,
|
|
6
|
+
fällt dort auf.
|
|
7
|
+
|
|
8
|
+
## Grundregeln
|
|
9
|
+
|
|
10
|
+
- **Slot** = von croner berechneter Zeitpunkt (UTC-Instant, Sekundengenauigkeit). Syntax: croner (5 Felder, optional
|
|
11
|
+
6 mit Sekunden); Validierung in `config validate` (WI-02).
|
|
12
|
+
- **Höchstens ein automatischer Run pro Slot.** Absicherung doppelt: Primärschlüssel `cron_firings(workflow_id,
|
|
13
|
+
scheduled_for)` und Lock-Key `cron:<slot>` des Runs. Eintrag und Reservierung erfolgen in **einer** Transaktion.
|
|
14
|
+
- **Jeder behandelte Slot bekommt höchstens einen `cron_firings`-Eintrag** mit `decision`. Ausnahme Serien:
|
|
15
|
+
`skipped_disabled`, `skipped_paused`, `skipped_not_ready`, `skipped_draining` werden nur für den **ersten Slot
|
|
16
|
+
einer Serie** geschrieben (Grund endet mit „further slots are not recorded while this state lasts“); weitere Slots
|
|
17
|
+
desselben Zustands schreiben nur `lastHandled` im Speicher fort. Jede andere Entscheidung (auch ein Start) beendet
|
|
18
|
+
die Serie, danach wird ein erneuter Zustand wieder einmal protokolliert:
|
|
19
|
+
|
|
20
|
+
| decision | Bedeutung |
|
|
21
|
+
|---|---|
|
|
22
|
+
| `started` | Run reserviert und gestartet (`run_id` gesetzt) |
|
|
23
|
+
| `skipped_missed` | Slot verpasst (Downtime oder zu spät behandelt) – **wird nicht nachgeholt** |
|
|
24
|
+
| `skipped_limit` | globales oder Workflow-`max_concurrent` erreicht (aktive Runs inkl. manueller und `recovery_pending`) |
|
|
25
|
+
| `skipped_condition` / `skipped_condition_unknown` | Workflow-Condition `false` (Exit 1) / `unknown` (Exit ≥ 2, Timeout, Fehler) |
|
|
26
|
+
| `skipped_blocked` | aktiver Retry-Block im Workflow-Scope |
|
|
27
|
+
| `skipped_disabled` | Workflow deaktiviert (Config oder Runtime-Override) |
|
|
28
|
+
| `skipped_paused` / `skipped_not_ready` / `skipped_draining` | Controller pausiert / Recovery läuft noch / Drain bzw. Shutdown |
|
|
29
|
+
| `skipped_lock` | Slot-Lock bereits belegt (defensiv, sollte nicht vorkommen) |
|
|
30
|
+
| `start_failed` | Reservierung oder Start unerwartet fehlgeschlagen (`reason`) |
|
|
31
|
+
|
|
32
|
+
- **Kein Rückstau:** Ein übersprungener Slot (egal warum) wird nie nachgeholt; der nächste Run entsteht frühestens
|
|
33
|
+
zum nächsten Slot. Überlappung verschiedener Slots desselben Workflows regelt `max_concurrent` (Default 1).
|
|
34
|
+
|
|
35
|
+
## Verpasste Slots
|
|
36
|
+
|
|
37
|
+
- **Toleranz** (`cronMisfireToleranceMs`, Default 60 s): Wird ein Slot spätestens so lange nach seinem Zeitpunkt
|
|
38
|
+
behandelt (Timer-Latenz, kurzer Neustart), wird er normal entschieden. Ist er älter, gilt er als `skipped_missed`.
|
|
39
|
+
- **Nach einem Neustart** sucht der Scheduler pro Workflow ab dem jüngsten persistierten Slot (`cron_firings.latest`)
|
|
40
|
+
alle Slots bis „jetzt“: der jüngste wird – falls innerhalb der Toleranz – normal entschieden, alle älteren werden
|
|
41
|
+
`skipped_missed` mit Grund „slot passed while the orchestrator was not running“.
|
|
42
|
+
- Gibt es noch keinen persistierten Slot (erster Start, neu angelegter Workflow), beginnt die Planung bei „jetzt“ –
|
|
43
|
+
es wird nichts rückwirkend protokolliert.
|
|
44
|
+
- Wird ein Cron-Ausdruck/eine Zeitzone per Reload **geändert**, beginnt die Planung ebenfalls bei „jetzt“.
|
|
45
|
+
- Begrenzung: pro Nachhol-Prüfung werden höchstens `maxRecordedMissedSlots` (Default 100) Slots einzeln
|
|
46
|
+
protokolliert; ältere werden nur gezählt und im Grund des ältesten protokollierten Eintrags genannt
|
|
47
|
+
(„+N older missed slot(s) not recorded“). Die Suche ist auf 100 000 Slots begrenzt (danach nur noch die jüngsten).
|
|
48
|
+
- Timer werden höchstens 1 h im Voraus gestellt und danach neu berechnet; damit wirken auch Systemzeit-Sprünge
|
|
49
|
+
(NTP, Suspend) spätestens nach 1 h, und übersprungene Slots werden als `skipped_missed` protokolliert.
|
|
50
|
+
|
|
51
|
+
## Sommerzeit (DST) – beobachtetes und festgelegtes Verhalten
|
|
52
|
+
|
|
53
|
+
Beispiel `Europe/Berlin` 2026: Umstellung auf Sommerzeit 29.03. 02:00 CET → 03:00 CEST (01:00Z),
|
|
54
|
+
zurück auf Winterzeit 25.10. 03:00 CEST → 02:00 CET (01:00Z).
|
|
55
|
+
|
|
56
|
+
**Fehlende Lokalzeit (Frühjahr, Lücke 02:00–02:59):**
|
|
57
|
+
|
|
58
|
+
| Ausdruck | Verhalten am Umstellungstag |
|
|
59
|
+
|---|---|
|
|
60
|
+
| `30 2 * * *` (täglich, Zeitpunkt in der Lücke) | läuft **genau einmal**, um 03:30 CEST (= 01:30Z; um die Lückenlänge verschoben) |
|
|
61
|
+
| `0 2 * * *` | läuft einmal um 03:00 CEST (01:00Z) |
|
|
62
|
+
| `0 3 * * *` | läuft einmal um 03:00 CEST (01:00Z) – also zum selben Zeitpunkt wie `0 2` |
|
|
63
|
+
| `*/30 * * * *` | 01:30 CET, dann 03:00 CEST, 03:30 … – die nicht existierenden 02:00/02:30 entfallen ersatzlos, keine Duplikate |
|
|
64
|
+
|
|
65
|
+
Regel: Tägliche (und seltenere) Jobs mit einer Uhrzeit in der Lücke fallen **nicht** aus, sondern laufen einmal,
|
|
66
|
+
verschoben um die Lückenlänge. Sub-stündliche/stündliche Jobs verlieren die Slots in der Lücke (die verschobene Zeit
|
|
67
|
+
existiert ohnehin als regulärer Slot und läuft nur einmal).
|
|
68
|
+
|
|
69
|
+
**Doppelte Lokalzeit (Herbst, 02:00–02:59 zweimal):**
|
|
70
|
+
|
|
71
|
+
| Ausdruck | Verhalten am Umstellungstag |
|
|
72
|
+
|---|---|
|
|
73
|
+
| `30 2 * * *` | läuft **genau einmal**, beim ersten Durchlauf (02:30 CEST = 00:30Z); die zweite 02:30 (CET) läuft nicht |
|
|
74
|
+
| `0 * * * *` | 02:00 CEST (00:00Z), dann 03:00 CET (02:00Z) – die zweite 02:00 (CET, 01:00Z) entfällt |
|
|
75
|
+
| `*/30 * * * *` | 02:00, 02:30 CEST, dann 03:00 CET – Lücke von 1,5 h, die wiederholten 02:00/02:30 CET entfallen |
|
|
76
|
+
|
|
77
|
+
Regel: Jede **Wanduhrzeit** wird höchstens einmal ausgelöst – beim ersten Auftreten. Es gibt nie zwei Runs für
|
|
78
|
+
„02:30“ an einem Tag. Wer exakte Intervalle braucht, verwendet `timezone: UTC`.
|
|
79
|
+
|
|
80
|
+
**UTC** hat keine Umstellung; Slots sind unverändert.
|
|
81
|
+
|
|
82
|
+
## Zusammenspiel mit anderen Regeln
|
|
83
|
+
|
|
84
|
+
- Conditions werden pro Slot ausgewertet (Workflow-Conditions mit `cache` über den TTL-Cache, sonst neu);
|
|
85
|
+
Item-Conditions/Filter gibt es für Schedule-Workflows nicht.
|
|
86
|
+
- Retry-Blocks für Schedule-Workflows sind workflowbezogen (`itemKey = null`); ein aktiver Block führt zu
|
|
87
|
+
`skipped_blocked`, bis er entfernt wird.
|
|
88
|
+
- Manuelle Starts eines Schedule-Workflows haben eigene Lock-Keys (`manual:<runId>`), erzeugen keinen
|
|
89
|
+
`cron_firings`-Eintrag, zählen aber zur Auslastung und können dadurch einen Slot zu `skipped_limit` machen.
|
|
90
|
+
- Deaktivierte Workflows werden weiter getaktet; protokolliert wird nur der erste Slot einer
|
|
91
|
+
`skipped_disabled`-Serie (gleiches gilt für Pause/Not-ready/Drain) – keine Zeilenflut bei `* * * * *`.
|
|
92
|
+
- Folge für die Downtime-Erkennung: Startet der Orchestrator neu, während bzw. nachdem eine solche Serie lief, sind
|
|
93
|
+
die nicht protokollierten Slots nach dem letzten Eintrag nicht von Downtime unterscheidbar; sie werden (begrenzt)
|
|
94
|
+
als `skipped_missed` mit Grund „… not running (or while still skipped_disabled)“ protokolliert.
|
|
95
|
+
- Fehler bei der Behandlung eines Slots (z. B. DB-Fehler) werden geloggt; der Timer wird **immer** neu gestellt, der
|
|
96
|
+
nächste Slot wird normal entschieden (der fehlgeschlagene Slot bleibt ohne Eintrag).
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Test-Konfiguration für docs/manual-tests/github.md (manuelle Tests gegen echtes GitHub).
|
|
2
|
+
# NICHT produktiv verwenden: kurze Intervalle und Retry-Pausen, damit die Testfälle schnell beobachtbar sind.
|
|
3
|
+
# Wird von src/sources/github/manual-fixture.test.ts mit dem echten Loader validiert.
|
|
4
|
+
|
|
5
|
+
server:
|
|
6
|
+
host: 127.0.0.1
|
|
7
|
+
port: 8791 # eigener Port, kollidiert nicht mit einer produktiven Instanz (8787)
|
|
8
|
+
allowed_hosts: []
|
|
9
|
+
|
|
10
|
+
scheduler:
|
|
11
|
+
polling_interval: 20s # kurz, damit „nächster Poll“ (Szenario 3) schnell sichtbar ist
|
|
12
|
+
max_concurrent: 4 # global; Workflow-Limit von coder-test (2) greift zuerst (Szenario 1)
|
|
13
|
+
|
|
14
|
+
workspace:
|
|
15
|
+
cleanup: never # Workspaces zur Analyse behalten (liegen unter <data-dir>/workspaces)
|
|
16
|
+
|
|
17
|
+
lifecycle:
|
|
18
|
+
action_retry:
|
|
19
|
+
max_attempts: 3
|
|
20
|
+
delay: 20s # Default 30 s; kürzer für Szenario 6
|
|
21
|
+
|
|
22
|
+
retention:
|
|
23
|
+
completed_runs: 7d
|
|
24
|
+
failed_runs: 7d
|
|
25
|
+
blocked_runs: 7d
|
|
26
|
+
cleanup_interval: 24h
|
|
27
|
+
|
|
28
|
+
notifications:
|
|
29
|
+
on: [retry_block_created, lifecycle_action_failed, recovery_failed]
|
|
30
|
+
channels: [] # optional: Webhook (z. B. https://webhook.site) für Block-/Action-Fehler eintragen
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Shell-Helfer für docs/manual-tests/github.md – NICHT vom Orchestrator ausgeführt, sondern in der eigenen
|
|
2
|
+
# Shell geladen (bash oder zsh), im Repo-Root:
|
|
3
|
+
#
|
|
4
|
+
# export ADISPATCH_TEST_OWNER=<account-oder-org>
|
|
5
|
+
# source docs/manual-tests/github-fixture/helpers.sh
|
|
6
|
+
#
|
|
7
|
+
# Benötigt: gh (angemeldet als Maintainer-Mensch, Zugriff auf alle drei Test-Repos), curl, jq, openssl, npx.
|
|
8
|
+
|
|
9
|
+
: "${ADISPATCH_TEST_OWNER:?ADISPATCH_TEST_OWNER setzen (GitHub-Account oder Org der Test-Repos)}"
|
|
10
|
+
export ADISPATCH_CFG="${ADISPATCH_CFG:-$PWD/tmp/orch-cfg}"
|
|
11
|
+
export ADISPATCH_DATA="${ADISPATCH_DATA:-$PWD/tmp/orch-data}"
|
|
12
|
+
export API="${API:-http://127.0.0.1:8791/api/v1}"
|
|
13
|
+
export GH_API_VERSION="${GH_API_VERSION:-2026-03-10}" # = GITHUB_API_VERSION in src/sources/github/http.ts
|
|
14
|
+
|
|
15
|
+
# --- Orchestrator ------------------------------------------------------------------------------------------
|
|
16
|
+
orch() { npx tsx src/cli/main.ts "$@"; } # CLI aus dem Checkout
|
|
17
|
+
api() { curl -sS "$API$1"; } # GET gegen die API
|
|
18
|
+
post() { # POST mit CSRF-Header
|
|
19
|
+
local body="${2:-}"
|
|
20
|
+
[ -n "$body" ] || body='{}'
|
|
21
|
+
curl -sS -X POST -H 'X-Adispatch-Request: 1' -H 'Content-Type: application/json' -d "$body" "$API$1"
|
|
22
|
+
}
|
|
23
|
+
runs() { # runs <workflow> [limit]
|
|
24
|
+
api "/runs?workflow=$1&limit=${2:-10}" |
|
|
25
|
+
jq -r '.runs[] | [.id, .sourceItemKey, .status, .outcome, (.businessResult // "-"), .lifecycleStatus, (.finalReason // "")] | @tsv'
|
|
26
|
+
}
|
|
27
|
+
run_of() { # run_of <source> <nr> → neueste Run-ID
|
|
28
|
+
api "/runs?item=$1:$2&limit=1" | jq -r '.runs[0].id // empty'
|
|
29
|
+
}
|
|
30
|
+
diag() { # diag <workflow> → letzte Planungsdiagnose
|
|
31
|
+
api "/workflows/$1" | jq -r '.diagnosis // {} | "status=\(.status) polled=\(.polled) error=\(.error) pushdown=\(.pushdown | tojson)",
|
|
32
|
+
(.candidates // [] | .[] | [.itemKey, .decision, (.reason // "")] | @tsv)'
|
|
33
|
+
}
|
|
34
|
+
actions() { # actions <run-id> → Action-Journal
|
|
35
|
+
api "/runs/$1" | jq -r '.actions[] | [.id, .hook, .ordinal, .type, .status, "attempts=\(.attempts)", (.externalKey // "-"), (.lastError.message // "")] | @tsv'
|
|
36
|
+
}
|
|
37
|
+
events() { api "/runs/$1/events?limit=1000" | jq -c '.events[] | {type, payload}'; } # events <run-id>
|
|
38
|
+
view() { # view <view-id> → Tabelle (frisch, ohne Cache)
|
|
39
|
+
api "/views/$1/items?refresh=true" |
|
|
40
|
+
jq -r '(.columns | map(.label) | @tsv), (.rows[] | .cells | map(if .known then (.value | tostring) else "?" end) | @tsv), (.errors[] | "ERROR \(.sourceId): \(.message)")'
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
# --- GitHub (als Mensch über gh) ---------------------------------------------------------------------------
|
|
44
|
+
repo() { printf '%s/%s' "$ADISPATCH_TEST_OWNER" "$1"; } # repo orch-test-a → owner/orch-test-a
|
|
45
|
+
issue_url() { printf 'https://github.com/%s/issues/%s' "$(repo "$1")" "$2"; }
|
|
46
|
+
mkissue() { # mkissue <repo> <titel> [label ...] → Nummer
|
|
47
|
+
local r="$1" title="$2" l
|
|
48
|
+
shift 2
|
|
49
|
+
local args=()
|
|
50
|
+
for l in "$@"; do args+=(--label "$l"); done
|
|
51
|
+
gh issue create -R "$(repo "$r")" --title "$title" --body "adispatchd manual test: $title" "${args[@]}" | sed 's#.*/##'
|
|
52
|
+
}
|
|
53
|
+
blockedby() { # blockedby <repo> <nr> <blocker-repo> <blocker-nr>
|
|
54
|
+
gh issue edit "$2" -R "$(repo "$1")" --add-blocked-by "$(issue_url "$3" "$4")"
|
|
55
|
+
}
|
|
56
|
+
blockedby_api() { # wie blockedby, über REST (issue_id = Datenbank-ID!)
|
|
57
|
+
local id
|
|
58
|
+
id="$(gh api "repos/$(repo "$3")/issues/$4" --jq .id)" || return 1
|
|
59
|
+
gh api -X POST "repos/$(repo "$1")/issues/$2/dependencies/blocked_by" -H "X-GitHub-Api-Version: $GH_API_VERSION" \
|
|
60
|
+
-F issue_id="$id" --jq '"blocked_by added: #\(.number)"'
|
|
61
|
+
}
|
|
62
|
+
unblock_api() { # unblock_api <repo> <nr> <blocker-repo> <blocker-nr>
|
|
63
|
+
local id
|
|
64
|
+
id="$(gh api "repos/$(repo "$3")/issues/$4" --jq .id)" || return 1
|
|
65
|
+
gh api -X DELETE "repos/$(repo "$1")/issues/$2/dependencies/blocked_by/$id" -H "X-GitHub-Api-Version: $GH_API_VERSION" --jq '.number'
|
|
66
|
+
}
|
|
67
|
+
labels() { gh issue view "$2" -R "$(repo "$1")" --json labels --jq '[.labels[].name] | sort | join(",")'; } # labels <repo> <nr>
|
|
68
|
+
comments() { # comments <repo> <nr> → id, Autor, Typ, App-ID, letzte Zeile
|
|
69
|
+
gh api --paginate "repos/$(repo "$1")/issues/$2/comments" \
|
|
70
|
+
--jq '.[] | [.id, .user.login, .user.type, (.performed_via_github_app.id // "-" | tostring), (.body | split("\n") | last)] | @tsv'
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
# --- GitHub (als App) --------------------------------------------------------------------------------------
|
|
74
|
+
probe() { npx tsx tmp/probe.ts "$@"; } # siehe github.md, Abschnitt 2.4
|
|
75
|
+
ghraw() { # ghraw <identity> <pfad> [curl-Optionen] → Rohantwort als App
|
|
76
|
+
local identity="$1" path="$2" token
|
|
77
|
+
shift 2
|
|
78
|
+
token="$(probe token "$identity" 2>/dev/null)" || { echo "kein Token für $identity" >&2; return 1; }
|
|
79
|
+
curl -sS -H "Authorization: Bearer $token" -H 'Accept: application/vnd.github+json' \
|
|
80
|
+
-H "X-GitHub-Api-Version: $GH_API_VERSION" "$@" "https://api.github.com$path"
|
|
81
|
+
}
|
|
82
|
+
app_jwt() { # app_jwt <app-id> <pem-datei> → JWT (9 min)
|
|
83
|
+
local now h p s
|
|
84
|
+
now="$(date +%s)"
|
|
85
|
+
h="$(printf '{"alg":"RS256","typ":"JWT"}' | openssl base64 -A | tr '+/' '-_' | tr -d '=')"
|
|
86
|
+
p="$(printf '{"iat":%d,"exp":%d,"iss":"%s"}' "$((now - 60))" "$((now + 540))" "$1" | openssl base64 -A | tr '+/' '-_' | tr -d '=')"
|
|
87
|
+
s="$(printf '%s.%s' "$h" "$p" | openssl dgst -sha256 -sign "$2" -binary | openssl base64 -A | tr '+/' '-_' | tr -d '=')"
|
|
88
|
+
printf '%s.%s.%s' "$h" "$p" "$s"
|
|
89
|
+
}
|
|
90
|
+
installation_id() { # installation_id <app-id> <pem-datei> <repo>
|
|
91
|
+
curl -sS -H "Authorization: Bearer $(app_jwt "$1" "$2")" -H 'Accept: application/vnd.github+json' \
|
|
92
|
+
-H "X-GitHub-Api-Version: $GH_API_VERSION" "https://api.github.com/repos/$(repo "$3")/installation" | jq '{id, app_id, app_slug, repository_selection, permissions}'
|
|
93
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Drei GitHub Apps (eine pro Rolle). Die Zahlen sind Platzhalter und werden beim Kopieren durch die echten
|
|
2
|
+
# App-IDs und Installation-IDs ersetzt (siehe github.md, Abschnitt 2). Private Keys NUR als Env-Variablen.
|
|
3
|
+
identities:
|
|
4
|
+
maintainer:
|
|
5
|
+
type: github_app
|
|
6
|
+
app_id: 9000001
|
|
7
|
+
installation_id: 9100001
|
|
8
|
+
private_key_env: ADISPATCH_TEST_MAINTAINER_KEY
|
|
9
|
+
coder:
|
|
10
|
+
type: github_app
|
|
11
|
+
app_id: 9000002
|
|
12
|
+
installation_id: 9100002
|
|
13
|
+
private_key_env: ADISPATCH_TEST_CODER_KEY
|
|
14
|
+
reviewer:
|
|
15
|
+
type: github_app
|
|
16
|
+
app_id: 9000003
|
|
17
|
+
installation_id: 9100003
|
|
18
|
+
private_key_env: ADISPATCH_TEST_REVIEWER_KEY
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Test-Worker für docs/manual-tests/github.md. Simuliert je nach Issue-Label das Worker-Verhalten
|
|
3
|
+
# (Worker-Protokoll: docs/worker-protocol.md, Spec §8.2):
|
|
4
|
+
#
|
|
5
|
+
# (kein test:*-Label) Exit 0 + Ergebnis success → on_success
|
|
6
|
+
# test:slow vorher TEST_SLOW_SECONDS (Default 90) schlafen, alle 10 s ein progress-Event
|
|
7
|
+
# test:failure Exit 0 + Ergebnis failure (fachlich) → on_failure, kein Retry-Zähler
|
|
8
|
+
# test:crash Ergebnis success schreiben, dann Exit 1 → crashed (Datei wird ignoriert), on_crash
|
|
9
|
+
# test:invalid-result Exit 0 + ungültige Ergebnisdatei → crashed (Protokollfehler)
|
|
10
|
+
# test:token GH_TOKEN (execute.secrets) gegen die API prüfen; Exit 1, falls nicht HTTP 200
|
|
11
|
+
# test:revoke-token GH_TOKEN per DELETE /installation/token widerrufen (erzwingt Token-Erneuerung im
|
|
12
|
+
# Orchestrator: nächster Aufruf der Identity → 401 → invalidate → neues Token)
|
|
13
|
+
#
|
|
14
|
+
# Labels kombinierbar (z. B. test:slow + test:failure). Argumente: $1 = Issue-Nummer, $2 = owner/repo.
|
|
15
|
+
set -eu
|
|
16
|
+
|
|
17
|
+
number="${1:-?}"
|
|
18
|
+
repo="${2:-?}"
|
|
19
|
+
slow="${TEST_SLOW_SECONDS:-90}"
|
|
20
|
+
|
|
21
|
+
# Labels aus der Kontextdatei lesen (source.attributes.labels). node ist vorhanden, weil der Orchestrator
|
|
22
|
+
# damit läuft; Fallback grep (Labels stehen als JSON-Strings in der Datei).
|
|
23
|
+
labels=""
|
|
24
|
+
if command -v node >/dev/null 2>&1; then
|
|
25
|
+
labels="$(node -e '
|
|
26
|
+
const c = JSON.parse(require("fs").readFileSync(process.env.ADISPATCH_CONTEXT_FILE, "utf8"));
|
|
27
|
+
const l = (c.source && c.source.attributes && c.source.attributes.labels) || [];
|
|
28
|
+
process.stdout.write(l.join("\n"));
|
|
29
|
+
')"
|
|
30
|
+
else
|
|
31
|
+
labels="$(grep -o '"test:[a-z-]*"' "${ADISPATCH_CONTEXT_FILE}" | tr -d '"' || true)"
|
|
32
|
+
fi
|
|
33
|
+
has() { printf '%s\n' "${labels}" | grep -qx "$1"; }
|
|
34
|
+
|
|
35
|
+
# Ergebnis atomar schreiben (temporäre Datei im selben Verzeichnis + mv).
|
|
36
|
+
write_result() { # $1 = success|failure, $2 = message
|
|
37
|
+
tmp="${ADISPATCH_RESULT_FILE}.tmp.$$"
|
|
38
|
+
msg="$(printf '%s' "$2" | sed 's/\\/\\\\/g; s/"/\\"/g')"
|
|
39
|
+
printf '{"schema_version":1,"result":"%s","message":"%s","details":{"repo":"%s","number":"%s"}}\n' \
|
|
40
|
+
"$1" "${msg}" "${repo}" "${number}" > "${tmp}"
|
|
41
|
+
mv "${tmp}" "${ADISPATCH_RESULT_FILE}"
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
echo "test-worker: run ${ADISPATCH_RUN_ID} workflow ${ADISPATCH_WORKFLOW_ID} item ${repo}#${number}"
|
|
45
|
+
echo "test-worker: labels: $(printf '%s' "${labels}" | tr '\n' ' ')"
|
|
46
|
+
echo "test-worker: workspace ${ADISPATCH_WORKSPACE}"
|
|
47
|
+
echo '@@ADISPATCH_EVENT {"type":"status","message":"test-worker gestartet"}'
|
|
48
|
+
|
|
49
|
+
if has test:token; then
|
|
50
|
+
code="$(curl -s -o /dev/null -w '%{http_code}' \
|
|
51
|
+
-H "Authorization: Bearer ${GH_TOKEN:-missing}" \
|
|
52
|
+
-H 'Accept: application/vnd.github+json' \
|
|
53
|
+
-H 'X-GitHub-Api-Version: 2026-03-10' \
|
|
54
|
+
"https://api.github.com/repos/${repo}/issues/${number}")"
|
|
55
|
+
echo "test-worker: GET issue with GH_TOKEN → HTTP ${code}"
|
|
56
|
+
[ "${code}" = "200" ] || { echo "test-worker: GH_TOKEN rejected" >&2; exit 1; }
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
if has test:revoke-token; then
|
|
60
|
+
code="$(curl -s -o /dev/null -w '%{http_code}' -X DELETE \
|
|
61
|
+
-H "Authorization: Bearer ${GH_TOKEN:-missing}" \
|
|
62
|
+
-H 'Accept: application/vnd.github+json' \
|
|
63
|
+
-H 'X-GitHub-Api-Version: 2026-03-10' \
|
|
64
|
+
"https://api.github.com/installation/token")"
|
|
65
|
+
echo "test-worker: DELETE /installation/token → HTTP ${code} (expected 204)"
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
if has test:slow; then
|
|
69
|
+
elapsed=0
|
|
70
|
+
while [ "${elapsed}" -lt "${slow}" ]; do
|
|
71
|
+
printf '@@ADISPATCH_EVENT {"type":"progress","value":%s}\n' "$((elapsed * 100 / slow))"
|
|
72
|
+
echo "test-worker: working ${elapsed}/${slow}s"
|
|
73
|
+
sleep 10
|
|
74
|
+
elapsed=$((elapsed + 10))
|
|
75
|
+
done
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
if has test:crash; then
|
|
79
|
+
write_result success "wird ignoriert, weil Exit != 0"
|
|
80
|
+
echo "test-worker: simulating crash (exit 1)" >&2
|
|
81
|
+
exit 1
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
if has test:invalid-result; then
|
|
85
|
+
tmp="${ADISPATCH_RESULT_FILE}.tmp.$$"
|
|
86
|
+
printf '%s\n' '{"schema_version":1,"result":"maybe"}' > "${tmp}"
|
|
87
|
+
mv "${tmp}" "${ADISPATCH_RESULT_FILE}"
|
|
88
|
+
echo "test-worker: wrote invalid result file"
|
|
89
|
+
exit 0
|
|
90
|
+
fi
|
|
91
|
+
|
|
92
|
+
if has test:failure; then
|
|
93
|
+
write_result failure "Simulierter fachlicher Fehler fuer ${repo}#${number}"
|
|
94
|
+
echo '@@ADISPATCH_EVENT {"type":"question","message":"Anforderungen unklar (simuliert)"}'
|
|
95
|
+
exit 0
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
write_result success "Simulierter Erfolg fuer ${repo}#${number}"
|
|
99
|
+
echo '@@ADISPATCH_EVENT {"type":"progress","value":100}'
|
|
100
|
+
exit 0
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# Zwei Test-Repos im selben Account/Org. ADISPATCH_TEST_OWNER wird beim Kopieren ersetzt (siehe github.md, Abschnitt 2).
|
|
2
|
+
# Das dritte Repo (orch-test-hidden) ist bewusst KEINE Source und für keine App installiert.
|
|
3
|
+
sources:
|
|
4
|
+
test_a:
|
|
5
|
+
type: github
|
|
6
|
+
owner: ADISPATCH_TEST_OWNER
|
|
7
|
+
repository: orch-test-a
|
|
8
|
+
identity: maintainer # Lesen (Polling, Dependencies, Views) mit der Maintainer-App
|
|
9
|
+
test_b:
|
|
10
|
+
type: github
|
|
11
|
+
owner: ADISPATCH_TEST_OWNER
|
|
12
|
+
repository: orch-test-b
|
|
13
|
+
identity: maintainer
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# views/dependencies.yaml – Beobachtungswerkzeug für die Dependency-Fälle (github.md: T11–T14), ohne Runs zu starten.
|
|
2
|
+
# Zeigt für alle offenen Items mit Label deps-test, wie der Adapter "blocked by" auflöst.
|
|
3
|
+
id: dependencies
|
|
4
|
+
title: Dependencies (Test)
|
|
5
|
+
sources: [test_a, test_b]
|
|
6
|
+
filter:
|
|
7
|
+
all:
|
|
8
|
+
- { field: labels, operator: contains, value: deps-test }
|
|
9
|
+
fields:
|
|
10
|
+
- { field: repository, label: Repository }
|
|
11
|
+
- { field: number, label: Nr. }
|
|
12
|
+
- { field: title, label: Titel, link: url }
|
|
13
|
+
- { field: dependencies.status, label: Status }
|
|
14
|
+
- { field: dependencies.blocked_by_total, label: Blocker gesamt }
|
|
15
|
+
- { field: dependencies.blocked_by_open, label: Blocker offen }
|
|
16
|
+
- { field: dependencies.blocked, label: Blockiert }
|
|
17
|
+
sort: { field: number, direction: asc }
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# views/needs-maintainer.yaml – Szenario 10: kombiniert beide Test-Repos (github.md: T21).
|
|
2
|
+
id: needs-maintainer
|
|
3
|
+
title: Needs Maintainer (Test)
|
|
4
|
+
sources: [test_a, test_b]
|
|
5
|
+
filter:
|
|
6
|
+
all:
|
|
7
|
+
- { field: labels, operator: contains, value: needs-maintainer }
|
|
8
|
+
fields:
|
|
9
|
+
- { field: repository, label: Repository }
|
|
10
|
+
- { field: number, label: Nr. }
|
|
11
|
+
- { field: title, label: Titel, link: url }
|
|
12
|
+
- { field: is_pull_request, label: PR }
|
|
13
|
+
- { field: run.last_workflow, label: Letzter Agent }
|
|
14
|
+
- { field: run.last_status, label: Status }
|
|
15
|
+
- { field: run.last_result.message, label: Ergebnis }
|
|
16
|
+
- { field: updated_at, label: Aktualisiert }
|
|
17
|
+
sort: { field: updated_at, direction: desc }
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# workflows/coder-test.yaml – Coder-Rolle gegen orch-test-a (github.md: T3–T9, T13–T17, T20).
|
|
2
|
+
# Startet Issues/PRs mit Label ready-for-coding, deren native "blocked by"-Abhängigkeiten alle geschlossen sind.
|
|
3
|
+
# Das Verhalten des Test-Workers steuern die Labels test:* (siehe scripts/test-worker.sh).
|
|
4
|
+
id: coder-test
|
|
5
|
+
name: Coder-Test (orch-test-a)
|
|
6
|
+
enabled: true
|
|
7
|
+
priority: 50
|
|
8
|
+
max_concurrent: 2 # Szenario 1: zwei parallel, der dritte wartet (workflow_limit)
|
|
9
|
+
identity: coder # schreibende Aktionen (Labels, Kommentare) als Coder-App
|
|
10
|
+
|
|
11
|
+
trigger:
|
|
12
|
+
type: source
|
|
13
|
+
source: test_a
|
|
14
|
+
|
|
15
|
+
filter:
|
|
16
|
+
all:
|
|
17
|
+
- { field: labels, operator: contains, value: ready-for-coding } # Pushdown: ?labels=ready-for-coding
|
|
18
|
+
- { field: dependencies.blocked_by_open, operator: equals, value: 0 } # teuer, unknown → kein Start
|
|
19
|
+
|
|
20
|
+
item_priority:
|
|
21
|
+
rules:
|
|
22
|
+
- when: { field: labels, operator: contains, value: 'priority:high' }
|
|
23
|
+
value: 50
|
|
24
|
+
default: 0
|
|
25
|
+
|
|
26
|
+
execute:
|
|
27
|
+
type: shell
|
|
28
|
+
command: scripts/test-worker.sh # relativ zum config-dir
|
|
29
|
+
args: ['{item.number}', '{source.repository}']
|
|
30
|
+
env:
|
|
31
|
+
TEST_SLOW_SECONDS: '90' # Dauer bei Label test:slow
|
|
32
|
+
secrets:
|
|
33
|
+
GH_TOKEN: { identity: coder } # nur für Label test:token (Token-Prüfung im Worker)
|
|
34
|
+
timeout:
|
|
35
|
+
max_duration: 10m
|
|
36
|
+
idle_timeout: 5m
|
|
37
|
+
cancel_grace: 10s
|
|
38
|
+
|
|
39
|
+
retry:
|
|
40
|
+
max_technical_failures: 3 # Szenario 5: dritter Crash → Retry-Block
|
|
41
|
+
|
|
42
|
+
on_start:
|
|
43
|
+
- id: on_start-labels
|
|
44
|
+
type: source.update
|
|
45
|
+
labels: { add: [coding], remove: [ready-for-coding] }
|
|
46
|
+
on_success:
|
|
47
|
+
- id: on_success-labels
|
|
48
|
+
type: source.update
|
|
49
|
+
labels: { add: [ready-for-review], remove: [coding] }
|
|
50
|
+
- id: on_success-comment
|
|
51
|
+
type: source.comment
|
|
52
|
+
message: 'Coder-Test erfolgreich: {result.message}'
|
|
53
|
+
continue_on_error: true
|
|
54
|
+
on_failure: # fachliches failure (Ergebnisdatei) → Needs Maintainer, kein Retry-Zähler
|
|
55
|
+
- id: on_failure-labels
|
|
56
|
+
type: source.update
|
|
57
|
+
labels: { add: [needs-maintainer], remove: [coding] }
|
|
58
|
+
- id: on_failure-comment
|
|
59
|
+
type: source.comment
|
|
60
|
+
message: 'Needs maintainer: {result.message}'
|
|
61
|
+
on_crash: # technischer Fehler → zurück in die Warteschlange (Block stoppt nach 3×)
|
|
62
|
+
- id: on_crash-labels
|
|
63
|
+
type: source.update
|
|
64
|
+
labels: { add: [ready-for-coding], remove: [coding] }
|
|
65
|
+
on_start_error:
|
|
66
|
+
- id: on_start_error-labels
|
|
67
|
+
type: source.update
|
|
68
|
+
labels: { add: [ready-for-coding], remove: [coding] }
|
|
69
|
+
on_timeout:
|
|
70
|
+
- id: on_timeout-labels
|
|
71
|
+
type: source.update
|
|
72
|
+
labels: { add: [ready-for-coding], remove: [coding] }
|
|
73
|
+
on_cancel:
|
|
74
|
+
- id: on_cancel-labels
|
|
75
|
+
type: source.update
|
|
76
|
+
labels: { add: [ready-for-coding], remove: [coding] }
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# workflows/commenter-test.yaml – Kommentar-Actions gegen orch-test-b (github.md: T6, T7, T18, T19).
|
|
2
|
+
# on_start kommentiert als REVIEWER (Action-Identity überschreibt die Workflow-Identity),
|
|
3
|
+
# on_success als CODER mit großzügigem Retry, damit ein kurzer Netzwerkausfall überbrückt wird (Szenario 6).
|
|
4
|
+
id: commenter-test
|
|
5
|
+
name: Kommentar-Test (orch-test-b)
|
|
6
|
+
enabled: true
|
|
7
|
+
priority: 30
|
|
8
|
+
max_concurrent: 1
|
|
9
|
+
identity: coder
|
|
10
|
+
|
|
11
|
+
trigger:
|
|
12
|
+
type: source
|
|
13
|
+
source: test_b
|
|
14
|
+
|
|
15
|
+
filter:
|
|
16
|
+
all:
|
|
17
|
+
- { field: labels, operator: contains, value: ready-for-comment }
|
|
18
|
+
|
|
19
|
+
execute:
|
|
20
|
+
type: shell
|
|
21
|
+
command: scripts/test-worker.sh
|
|
22
|
+
args: ['{item.number}', '{source.repository}']
|
|
23
|
+
env:
|
|
24
|
+
TEST_SLOW_SECONDS: '60'
|
|
25
|
+
timeout:
|
|
26
|
+
max_duration: 10m
|
|
27
|
+
|
|
28
|
+
on_start:
|
|
29
|
+
- id: on_start-labels
|
|
30
|
+
type: source.update
|
|
31
|
+
labels: { add: [commenting], remove: [ready-for-comment] }
|
|
32
|
+
- id: start-comment
|
|
33
|
+
type: source.comment
|
|
34
|
+
identity: reviewer
|
|
35
|
+
message: 'Kommentar-Test gestartet (Run {run.id}, Item {item.key}).'
|
|
36
|
+
on_success:
|
|
37
|
+
- id: done-comment
|
|
38
|
+
type: source.comment
|
|
39
|
+
message: 'Kommentar-Test fertig: {result.message}'
|
|
40
|
+
retry: { max_attempts: 6, delay: 20s } # ~100 s Fenster für einen Netzwerkausfall
|
|
41
|
+
- id: on_success-labels
|
|
42
|
+
type: source.update
|
|
43
|
+
labels: { add: [commented], remove: [commenting] }
|
|
44
|
+
on_failure:
|
|
45
|
+
- id: on_failure-labels
|
|
46
|
+
type: source.update
|
|
47
|
+
labels: { add: [needs-maintainer], remove: [commenting] }
|
|
48
|
+
on_crash:
|
|
49
|
+
- id: on_crash-labels
|
|
50
|
+
type: source.update
|
|
51
|
+
labels: { add: [ready-for-comment], remove: [commenting] }
|
|
52
|
+
on_start_error:
|
|
53
|
+
- id: on_start_error-labels
|
|
54
|
+
type: source.update
|
|
55
|
+
labels: { add: [ready-for-comment], remove: [commenting] }
|
|
56
|
+
on_timeout:
|
|
57
|
+
- id: on_timeout-labels
|
|
58
|
+
type: source.update
|
|
59
|
+
labels: { add: [ready-for-comment], remove: [commenting] }
|
|
60
|
+
on_cancel:
|
|
61
|
+
- id: on_cancel-labels
|
|
62
|
+
type: source.update
|
|
63
|
+
labels: { add: [ready-for-comment], remove: [commenting] }
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# workflows/reviewer-test.yaml – Reviewer-Rolle gegen orch-test-a (github.md: T10).
|
|
2
|
+
# Nur Pull Requests mit Label ready-for-review und ohne offene Blocker. Ein Issue mit demselben Label
|
|
3
|
+
# (z. B. nach coder-test-Erfolg) wird über is_pull_request herausgefiltert.
|
|
4
|
+
id: reviewer-test
|
|
5
|
+
name: Reviewer-Test (orch-test-a, nur PRs)
|
|
6
|
+
enabled: true
|
|
7
|
+
priority: 40
|
|
8
|
+
max_concurrent: 1
|
|
9
|
+
identity: reviewer
|
|
10
|
+
|
|
11
|
+
trigger:
|
|
12
|
+
type: source
|
|
13
|
+
source: test_a
|
|
14
|
+
|
|
15
|
+
filter:
|
|
16
|
+
all:
|
|
17
|
+
- { field: labels, operator: contains, value: ready-for-review }
|
|
18
|
+
- { field: is_pull_request, operator: equals, value: true }
|
|
19
|
+
- { field: dependencies.blocked_by_open, operator: equals, value: 0 }
|
|
20
|
+
|
|
21
|
+
execute:
|
|
22
|
+
type: shell
|
|
23
|
+
command: scripts/test-worker.sh
|
|
24
|
+
args: ['{item.number}', '{source.repository}']
|
|
25
|
+
env:
|
|
26
|
+
TEST_SLOW_SECONDS: '60'
|
|
27
|
+
timeout:
|
|
28
|
+
max_duration: 10m
|
|
29
|
+
|
|
30
|
+
on_start:
|
|
31
|
+
- id: on_start-labels
|
|
32
|
+
type: source.update
|
|
33
|
+
labels: { add: [reviewing], remove: [ready-for-review] }
|
|
34
|
+
on_success:
|
|
35
|
+
- id: on_success-labels
|
|
36
|
+
type: source.update
|
|
37
|
+
labels: { add: [reviewed], remove: [reviewing] }
|
|
38
|
+
- id: on_success-comment
|
|
39
|
+
type: source.comment
|
|
40
|
+
message: 'Reviewer-Test ok: {result.message}'
|
|
41
|
+
on_failure:
|
|
42
|
+
- id: on_failure-labels
|
|
43
|
+
type: source.update
|
|
44
|
+
labels: { add: [needs-maintainer], remove: [reviewing] }
|
|
45
|
+
on_crash:
|
|
46
|
+
- id: on_crash-labels
|
|
47
|
+
type: source.update
|
|
48
|
+
labels: { add: [ready-for-review], remove: [reviewing] }
|
|
49
|
+
on_start_error:
|
|
50
|
+
- id: on_start_error-labels
|
|
51
|
+
type: source.update
|
|
52
|
+
labels: { add: [ready-for-review], remove: [reviewing] }
|
|
53
|
+
on_timeout:
|
|
54
|
+
- id: on_timeout-labels
|
|
55
|
+
type: source.update
|
|
56
|
+
labels: { add: [ready-for-review], remove: [reviewing] }
|
|
57
|
+
on_cancel:
|
|
58
|
+
- id: on_cancel-labels
|
|
59
|
+
type: source.update
|
|
60
|
+
labels: { add: [ready-for-review], remove: [reviewing] }
|