Skip to content

ci: bound all 16 unbounded workflow jobs on main with timeout-minutes - #423

Merged
rubenvdlinde merged 1 commit into
mainfrom
hotfix/bound-main-job-timeouts
Aug 3, 2026
Merged

ci: bound all 16 unbounded workflow jobs on main with timeout-minutes#423
rubenvdlinde merged 1 commit into
mainfrom
hotfix/bound-main-job-timeouts

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

What

Adds timeout-minutes to all 16 jobs on the main branch. Every one of them was unbounded, so a hang burned a runner for GitHub's 6-hour default.

Why main specifically

development now calls the shared reusable workflows in ConductionNL/.github, which are bounded at source. main still carries the older inline layout, and those files are live:

  • release-workflow.yaml has recent runs on main.
  • The PR-triggered jobs (php-checks matrix, frontend-quality, lint-check, and both check-branch duplicates) fire on every PR into main — i.e. on every release.

Bounds

file job timeout
pull-request-lint-check.yaml lint-check 15
pull-request-from-branch-check.yaml check-branch 10
pr-check.yaml check-branch 10
push-development-to-beta.yaml create-pr 20
code-quality.yml php-checks 30
code-quality.yml frontend-quality 20
documentation.yml deploy 20
sync-beta.yaml sync-to-beta-release 20
sync-dev.yaml sync-to-development-release 20
release-stable.yaml update-changelog 15
release-stable.yaml release-management 45
beta-release.yaml release-management 45
unstable-release.yaml release-management 45
release-workflow.yaml release-management 45
release-beta.yaml release-management 45
release-unstable.yaml release-management 45

Derived from measured fleet-wide durations (successful + failed executions, skipped excluded) and deliberately loose — a timeout that fires under normal contention is worse than no timeout, because it converts a slow run into a phantom defect.

One deviation from the rollout table: php-checks is 30, not 20

A single job-level bound covers all six matrix legs, so it must clear the slowest leg. Lint/PHPCS/PHPMD/Psalm/PHPStan run ~4 min, but the PHPUnit leg has been observed at 18.9 min while succeeding. A 20 bound would leave ~6% headroom over a known-good run.

Notes

  • No job here is a uses: reusable-workflow caller, so timeout-minutes is valid on all 16 (a uses: job that carries it makes the whole file startup_failure). Nothing was skipped.
  • l10n.yml does not exist on main — it is a development-only file. The main job count is therefore 16 without it.
  • pr-check.yaml and pull-request-from-branch-check.yaml are byte-identical duplicate Branch Protection workflows; both were bounded. Deduplicating them is out of scope here.

Verification

  • Every touched file re-parsed with yaml.safe_load; job set asserted unchanged vs origin/main and each job asserted to carry its exact expected value — 16/16.
  • Negative control: the same probe run against the unedited origin/main blobs reports None for all 16, so the probe genuinely distinguishes bounded from unbounded.
  • Diff is additions-only (41 insertions, 0 deletions) and touches .github/workflows/ only. No step, trigger, or job was modified.

The main branch carries a different, older set of workflow files than
development (which now calls the shared reusable workflows in
ConductionNL/.github, bounded at source). None of main's 16 jobs
declared timeout-minutes, so a hung job burned a runner for GitHub's
6-hour default.

These are not dead files: release-workflow.yaml has recent runs on
main, and the PR-triggered jobs (php-checks matrix, frontend-quality,
lint-check, both check-branch duplicates) fire on every PR into main,
i.e. on every release.

Bounds are derived from measured fleet-wide durations (successful and
failed executions, skipped excluded) and are deliberately loose -- a
timeout that fires under normal contention turns a slow run into a
phantom defect.

php-checks is set to 30 rather than 20: one job-level bound covers all
six matrix legs, and while lint/PHPCS/PHPMD/Psalm/PHPStan run ~4 min,
the PHPUnit leg has been observed at 18.9 min while succeeding.

No job here calls a reusable workflow, so timeout-minutes is valid on
all 16. Additions only; no step, trigger, or job was modified.
@rubenvdlinde

Copy link
Copy Markdown
Contributor Author

Red checks are pre-existing — proven on the base SHA

7 checks are red. None are caused by this PR: the diff is additions-only and touches .github/workflows/ exclusively — zero PHP, JS, or CSS. Notably, no job timed out (lint-check 1m19s vs its new 15-min bound; Frontend Quality 1m23s vs 20), so the added bounds are not implicated.

Control: cloned origin/main at the PR's own base SHA a1d7bee51c62fc5876d29e2513abe2ca2a2ad42e (confirmed equal to baseRefOid), unmodified, and ran the identical commands in clean containers. Every failure reproduces:

check CI (run 30817342013 / 30817341789) base-SHA control, unmodified
PHPCS exit 2 exit 2 — 442 ERROR lines
PHPMD exit 2 exit 2
Psalm exit 2 exit 2 — 94 errors
PHPStan exit 1 — Found 2234 errors exit 1 — Found 2234 errors (identical count)
PHPUnit exit 2 — Class "OC_App" not found exit 2 — Class "OC_App" not found (identical)
lint-check fail npm run lint exit 1 — 5 errors, 7 warnings
Frontend Quality fail npm run stylelint exit 2 — 2 errors

Two of these are structural rather than incidental debt:

  • PHPUnit can never pass in this workflow. phpunit.xml bootstraps tests/bootstrap.php, which requires a Nextcloud runtime (OC_App), but the legacy inline code-quality.yml on main installs only Composer deps — no Nextcloud. The repo's own composer test:all script wraps the same command in || echo 'Tests require Nextcloud environment, skipping...'; the workflow calls the binary directly, so it fails hard.
  • PHPStan's 2234 errors are almost entirely unknown class OCP\... — same root cause: the Nextcloud sources it type-checks against are not on the include path.

development has since moved to the shared reusable workflows in ConductionNL/.github, which do install Nextcloud; this legacy inline layout on main did not. Fixing that is a separate change — out of scope for a timeout bounding PR, and deliberately not attempted here so the diff stays additions-only.

Merging via admin on that basis.

@rubenvdlinde
rubenvdlinde merged commit 753c0b2 into main Aug 3, 2026
7 of 14 checks passed
@rubenvdlinde
rubenvdlinde deleted the hotfix/bound-main-job-timeouts branch August 3, 2026 13:31
@rubenvdlinde
rubenvdlinde restored the hotfix/bound-main-job-timeouts branch August 8, 2026 16:20
@rubenvdlinde
rubenvdlinde deleted the hotfix/bound-main-job-timeouts branch August 14, 2026 09:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants