Skip to content

ci: consolidate workflow templates into lint and php - #148

Draft
turegjorup wants to merge 5 commits into
developfrom
feature/consolidate-workflow-templates
Draft

ci: consolidate workflow templates into lint and php#148
turegjorup wants to merge 5 commits into
developfrom
feature/consolidate-workflow-templates

Conversation

@turegjorup

@turegjorupturegjorup commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Draft. Groups the workflow templates by what a check needs instead of by which tool runs it, so a commit pays for two runner jobs rather than up to eleven.

Measured on itk-dev for August 2026: the eight private repos billed 1,828 Actions minutes, of which 1,399 were referenceregister alone. Actual compute was 1,167 minutes — 700 of the billed minutes were the per-job round-up, because 953 of 1,287 jobs finished in under 60 seconds. This PR targets that, not runtime.

Changes

  • Newgithub/workflows/<type>/lint.yaml — replaces changelog.yaml, markdown.yaml, yaml.yaml, <type>/styles.yaml and <type>/javascript.yaml. Nothing in it needs the project's dependencies, so it runs markdownlint and prettier only.
  • Absorbedcomposer.yaml and twig.yaml into github/workflows/<type>/php.yamlcomposer install runs once and validate, normalize, audit, coding standards and Twig all reuse it.
  • Removed the seven superseded templates and their symlinks across all ten project templates.
  • task/scripts/github-actions-link — a workflow can now map to several configuration folders (php.yamlphp + twig, lint.yamlmarkdown + yaml). The generic folder is linked before the project type specific one, so the specific one wins where both exist.
  • docs/ regenerated, and the naming-convention prose updated (it referenced markdown.yaml, which no longer exists).

Estimated saving

Top ten repositories by Actions minutes in August 2026, taken from the org usage dashboard, with the model applied to each. Minutes are metered per job including GitHub's whole-minute round-up. Only private repositories are billed — public-repo minutes on standard runners are free, so they are runner time saved, not money.

RepositoryNowAfterSaved
referenceregisterprivate1,3991,114285
deltag.aarhus.dkpublic1,4821,287195
openid-connect-bundlepublic803585218
economicspublic47037397
selvbetjening.aarhuskommune.dkpublic27120071
iotlab-data-proxyprivate258105153
itk-projectspublic663234
os2looppublic58517
promptfoo-python-utilityprivate49418
itk-workspaceprivate472324
Top ten4,9033,8111,092

Of that 1,092, 470 minutes are billable (the four private repos) and 622 are free runner time.

Across all fourteen repositories that ran template workflows in August: 5,008 → 3,858, saving 1,150 minutes — 528 of them billable, 26% of the 2,000-minute quota. At a more conservative 15 s per eliminated job the total is 1,063.

Method and exclusions

  • Measured from the runs and jobs APIs, summing each job's duration rounded up to a whole minute. This tracks the usage dashboard closely — openid-connect-bundle 803 vs 803, selvbetjening 271 vs 272, itk-projects 66 vs 67, os2loop 58 vs 56 — except deltag.aarhus.dk, which measures 1,482 against the dashboard's 1,377.
  • Savings are modelled by merging the jobs of the grouped workflows per commit and charging 22 s of setup for each job removed, measured from the step timings (1.3 s job setup, 1.4 s checkout, 0.4 s network, plus ~19 s of docker compose warmup).
  • Only the eight templates this PR changes are counted: changelog, markdown, yaml, styles, javascript, composer, php, twig. Repo-local workflows such as referenceregister's code-analysis.yaml, docs.yaml and user-manual.yaml are left out.
  • openid-connect-bundle/php.yaml is excluded. It is not the template but a nine-job PHP-version matrix (unit tests across 8.3/8.4/8.5 × prefer-lowest/prefer-stable, mutation tests, PHPStan, coding standards). Consolidating that would defeat the matrix. Its 474 minutes stay as they are. Its composer.yamlis counted, and is worth a look on its own: four jobs, two of which are a prefer-lowest/prefer-stable matrix that no step ever reads.
  • A repo whose copy of a template has more jobs than the template is treated the same way and left alone. Across these fourteen repos only openid-connect-bundle/php.yaml triggers that.

Relation to the open PRs

Open questions

  1. Naming. The convention says a workflow is named for what it is concerned with, not how. php.yaml covering Composer and Twig is defensible; lint.yaml is more of a how. Better name welcome.
  2. Required status checks.Markdown, YAML, Composer, Twig etc. disappear and Lint / PHP appear. Every repo with branch protection needs its required checks updated in step with the template update, or merges will block on checks that can never report.
  3. Pre-existing violations surface at once. The old paths: filters meant styles.yaml only ran when CSS changed. lint.yaml runs on every pull request, so long-standing violations in untouched files appear immediately. Worth running each linter across the affected repos once before rolling this out.
  4. Wall-clock goes up. Four parallel PHP jobs at ~46 s become one serial job at ~80 s. Fewer minutes billed, slower feedback.

Test plan

Not yet run against a real project — that is what keeps this a draft.

Done so far:

  • task github-actions:link regenerates every symlink; each project template resolves to lint.yaml, php.yaml (+ site.yaml for Drupal) plus the right config files — .markdownlint.jsonc, .markdownlintignore, .prettierrc.yaml, .phpcs.xml.dist / .php-cs-fixer.dist.php, .twig-cs-fixer.dist.php
  • task github-actions:template-headers:update leaves the headers unchanged
  • task github-actions:documentation:update regenerates cleanly and task lint:markdown passes
  • task lint:yaml passes

Still to do:

  • run both workflows on a real Drupal, Drupal module and Symfony project
  • confirm --no-deps is safe for every check in all three project types
  • confirm the vendor cache actually hits, and that a restored vendor does not upset composer validate --strict

A runner job is billed a whole minute however little it does, and pays for
its own checkout and image pull. Eleven templates meant up to eleven jobs
per commit, most of them finishing in well under a minute.
Group the checks by what they need rather than by tool:
- <type>/lint.yaml replaces changelog, markdown, yaml, styles and
javascript. Nothing here needs the project's dependencies.
- <type>/php.yaml absorbs composer and twig, so composer install runs once
and every check reuses the result.
Every check keeps running when an earlier one fails, so a pull request
still reports all problems at once rather than stopping at the first.
Use --no-deps throughout: none of these checks touch the database or cache
services, so there is no reason to start or pull them. Cache the vendor
directory keyed on composer.lock and docker-compose.yml to avoid
re-downloading dependencies on every run.
github-actions-link now maps a workflow to several configuration folders,
since a consolidated workflow needs the configuration of every topic it
covers. Linking the generic folder before the project type specific one
also means the specific one wins where both exist.
@turegjorupturegjorup self-assigned this Aug 21, 2026
The install step had no guard, so a failing composer validate skipped it
and left the checks that need vendor to fail for the wrong reason. Guard
it too, and skip the vendor-dependent checks when the install itself
fails so they report as skipped rather than failed.
composer audit --locked and composer validate --strict read the lock and
manifest, so they keep running regardless.
Sign up for freeto 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.

1 participant

@turegjorup