Sync automation-template.yml to consumer repos with pull requests
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 5/5
- Thời gian dự kiến
- Hơn một tuần
- Mức phù hợp với người mới
- 55/100
Hướng nghiên cứu
Trước tiên, hãy xác minh phạm vi của ứng dụng learningequality-bot và cơ chế bảo vệ nhánh mặc định cho tám consumer. Đọc automation-template.yml, automation-registry.yml, docs/automation.md và cấu trúc workflow Dependabot hiện có; sau đó theo dõi cách workflow cần so sánh và cập nhật các bản sao của consumer. Được coi là hoàn tất khi có các pull request idempotent, đánh giá của con người, báo cáo về các repository bị thiếu hoặc không thể ghi và các xung đột toolchain, tài liệu, cùng các kiểm tra thủ công được liệt kê.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Overview
Consumer repos hold a copied automation-template.yml. When the generated template changes, every
consumer must copy it again, and nothing today detects that or does it. A stale copy fails silently.
Add a workflow in this repo that opens a pull request in each consumer repo whose copy no longer
matches the template, in the same shape dependabot uses. The workflow proposes the change. A person
in that repo reviews and merges it.
Complexity: Medium
Target branch: main
Context
#88 gives every consumer one file to copy, .github/workflows/automation.yml, generated from
automation-registry.yml. Most registry changes reach consumers on their own, because that file only
says uses: learningequality/.github/.github/workflows/automation.yml@main. Toggling an automation,
changing a dispatch if:, or pointing one at a different leaf all propagate with no consumer action.
Four things leave a consumer's copy out of date:
- A new event or activity type enters the
on:union. - The permissions union widens.
- The secret list changes.
- The consumer's own tooling rewrites the copy.
The fourth is not theoretical. It happened twice during #88, before any migration. yamlfmt v0.16.0
stripped a blank line from the header, and the template lacked the inline
# zizmor: ignore[dangerous-triggers] that the callers it replaces carry. Both were fixed in #88.
Neither was visible from this repo, because this repo pins an older yamlfmt, excludes the generated
files from it, and runs no zizmor.
All four fail quietly. The consumer keeps running its old on: block, so a new automation simply
never fires there. There is no error and no log line.
All four also end in the same state: the consumer's file differs from automation-template.yml. One
mechanism resolves all of them, rather than engineering each cause away separately.
The Change
Add a workflow in this repo that compares each consumer's .github/workflows/automation.yml against
automation-template.yml on main, and opens a pull request replacing it wherever the two differ.
Follow the dependabot shape. The pull request is the whole artifact. There is no accompanying issue,
because the change is a byte copy of one file with nothing to author and nothing to decide, and a
second artifact per repo per change is noise.
The work must be done by the workflow itself, not delegated to an agent. The task is deterministic,
and routing it through an agent means the fix lands only if that agent picks it up, which reintroduces
the silent drift this workflow exists to end.
A core maintainer approves and merges every change. This is a constraint on how the workflow is
written:
- It opens pull requests on a branch, and never commits to a default branch.
- It never merges, never enables auto-merge, and never approves its own pull request.
- Each consumer repo's existing review rules gate the change, unchanged.
- A pull request left unmerged stays unmerged. The workflow updates it and never routes around it.
There is no bot pre-review. The copy either matches the template or it does not, and the workflow
writes those bytes by construction, so there is no content question a reviewer could answer
differently. What the core maintainer decides is whether to accept the file landing in their repo.
It must be idempotent. A repo already in sync gets nothing. A repo with a sync pull request already
open gets that pull request updated, not a second one.
Detection then falls out of remediation. No open pull request means no drift.
The app's scope. The workflows authenticate as learning-equality-bot[bot], the app behind
LE_BOT_APP_ID. That is a different identity from rtibblesbot, the user account that opened the
migration pull requests for #88, so the migration was no evidence of the app's scope. The app needs
contents: write and pull-requests: write, and holds both.
Installation is separate from permissions and is granted per repo. morango had the secrets and the
permissions and still failed, because the app was not installed there. A repo the app is not
installed on is invisible to discovery, so installation is what enrols it.
Note that contents: write is also enough to push directly to a default branch. Default-branch
protection on each consumer is what makes the human-in-the-loop constraint enforceable rather than
merely intended, so confirm that alongside the app scope.
Classify drift against the template's own history. Compare the time automation-template.yml
last changed here with the time the repo's last sync pull request closed. A template change after
that is ordinary drift. Without one, the difference came from the consumer, and which side acted
decides the outcome:
- A merged pull request means the consumer's own tooling rewrote the copy. Report it as a toolchain
conflict, because the template is not stable under that toolchain, and reopening would loop
forever. Report on the first occurrence rather than waiting for a second, which would only spend a
maintainer's attention to confirm what is already known. - A pull request closed unmerged means a maintainer declined it. Leave that repo alone until the
template moves again, so the workflow stops asking without forgetting. This state does not fail
the run, so a repo can sit drifted while the workflow stays green.
An unknown template date resolves to ordinary drift. A needless pull request costs one review, where
a wrong toolchain conflict stops syncing the repo entirely.
Build the pull request body from the consumer's own template. A repo with its own pull request
template, or a check on the description, needs more than generic text. Read that repo's template and
use it, so its shape stays current without anything stored here. A repo with no template gets a short
explanation instead.
Answer the template's fields rather than copying them. A template ships each field with instructions
for a human author, and a body full of "Choose from: yes / no" reads as an unanswered form. Name the
change in Description, put none in Products impact, and leave a dash everywhere else. That also
satisfies a repo checking that the description is no longer its placeholder, which is what
kolibri-design-system does with check-description.
Find the consumers rather than listing them. Walk the org's repos, skip archived ones and forks,
and keep every repo whose .github/workflows/automation.yml calls this repo's automation.yml.
This repo's own reusable workflow sits at the same path and does not call it, so that marker
excludes it without a special case.
Target each repo's default branch. That is not a free choice: GitHub evaluates workflow triggers
only from the default branch, so any other value is wrong.
A list would need editing whenever a repo onboards, and would carry a base branch per repo that can
drift from reality. Discovery removes both. It also means a repo the app is not installed on never
appears, so installation is what enrols a repo.
Out of Scope
- Generating the
on:block with every activity type for the events in use. It would stop new
activity types from changing the template, but consumers would then run the workflow on more events
with every job skipping.pull_request_target: synchronizealone fires on every push to every pull
request. Sync pull requests make this unnecessary, and theon:block stays minimal. - Replacing the computed permissions union with a fixed ceiling, for the same reason.
- Auto-merging the sync pull requests.
- Bumping this repo's yamlfmt pin from v0.14.0 to v0.16.0. It would catch the formatting class of
problem locally and matchle-utils, but it also reformats.github/dependabot.ymland
automation-registry.yml.
Acceptance Criteria
Prerequisites
-
learning-equality-bot[bot]is confirmed to holdcontents: writeandpull-requests: write
on all eight consumer repos, and the confirmation is recorded on this issue. Confirmed by
@akolson on 2026-09-23, see the comment below. - Default-branch protection is confirmed on all eight, so a direct push cannot bypass review.
Human in the loop
- The workflow opens pull requests on a branch and never commits to a default branch.
- It never merges a pull request, never enables auto-merge, and never approves one.
- Each consumer's existing review rules gate the change, unchanged.
- The pull request body says what changed and that the file is generated, not hand-edited.
Behaviour
- The workflow runs on a schedule, on
workflow_dispatch, and whenautomation-template.yml
changes onmain. - It opens a pull request in each consumer repo whose
.github/workflows/automation.ymldiffers
from the template, replacing that file and nothing else. - No accompanying issue is opened.
- A repo already in sync gets no pull request.
- A repo with an open sync pull request gets it updated, never duplicated.
- The consumers are discovered from the org rather than listed, by the marker that a copy calls
this repo'sautomation.yml. - Each pull request targets the repo's own default branch.
- Archived repos and forks are skipped.
kolibri-app,kolibri-installer-androidand
kolibri-image-pihold inert copies, because Actions do not run on archived repos. - A repo with no
automation.ymlis not a consumer, and produces no result at all. - A failed repo listing stops the run loudly rather than syncing a partial set.
- Drift after a merged sync pull request, with no template change since, is reported as a
toolchain conflict on the first occurrence, and no pull request is opened. - Drift after a sync pull request closed unmerged is reported as declined, and no pull request
is opened until the template changes again. - An unreadable template history resolves to ordinary drift rather than stopping a repo.
- A repo the workflow cannot write to is reported, not skipped silently.
- The pull request body comes from the consumer's own pull request template when it has one,
with its fields answered, and from a short explanation when it does not. -
kolibri-design-systemreceives a body itscheck-descriptionjob accepts, with nothing
about that repo stored here. - Two overlapping runs cannot both open the same pull request.
- The state classification and the write payloads are covered by tests with an injected client,
including that the write targets the sync branch and never a default branch. -
docs/automation.mdsays the sync workflow exists, what it opens, who merges it, and what
each state that needs a core maintainer means.
Testing
- Run the workflow manually while every consumer is in sync. Confirm that it finds the expected
repos and opens nothing. - Change
automation-registry.ymlso the template changes, merge it, and confirm that the workflow
opens one pull request per consumer, and merges none of them. - Run it again before merging those. Confirm that the existing pull requests are updated and no
duplicates appear. - Leave a sync pull request unmerged across two runs. Confirm that the consumer's file is unchanged
on its default branch. - Merge one consumer's pull request by hand, run again, and confirm that repo is reported in sync.
- Revert a merged sync pull request's change in a consumer repo, then run once. Confirm that the
run reports a toolchain conflict and opens nothing. - Close a sync pull request without merging it, then run again. Confirm that the repo is reported
as declined and no pull request reopens. - Point the workflow at a repo the app cannot write to. Confirm that it reports the failure rather
than passing.
References
- #88 introduces the template, the registry and the generator.
- #86 is the consolidation tracking issue, including the decision against
secrets: inherit. docs/automation.mddescribes the entry point and the cases that need a re-copy.- The two toolchain rewrites found during #88: yamlfmt stripping the header blank line, and the
missing inline zizmor ignore.
AI usage
I used Claude Code while reviewing #88, where the two toolchain rewrites and this propagation gap
surfaced, and to draft this issue from that work. I verified the failure modes against the consumer
repos, chose the dependabot shape over detection alone and over routing the work through an agent,
set the human-in-the-loop constraint, and edited the wording.
- Ngôn ngữ chính
- JavaScript
- Star
- 1
- Fork
- 7
- Merge trung bình
- 1 ngày 12 giờ
- Pull request đã merge (30 ngày)
- 2
Hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của learningequality/.github
-
github_actions
learningequality/.github#83 · 1 người được giao ·
-
learningequality/.github#67 · 1 bình luận · 1 người được giao ·
Tất cả issue của learningequality/.github
Issue tương tự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 70/100
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
mksglu/context-mode#1200 ·
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
neondatabase/website#5944 ·
-
module: core
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
bigbluebutton/bigbluebutton#25849 ·
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
jaegertracing/jaeger-ui#4506 ·