Repository navigation
fork: delete four outside-started upstream workflows and keep them deleted - #14
scuttletoby wants to merge 2 commits into
Conversation
…leted The fork's sweep disables upstream workflows only once GitHub lists them, so it cannot stop the first run of one an outside event starts. Owner decision 2026-11-12: delete mobile-eas-preview-fork.yml (pull_request_target), desktop-macos-preview.yml, thread-transfer-report.yml and mobile-fingerprint-label.yml (workflow_run on "CI", a name an outside PR can take). .github/fork-removed-workflows.txt lists them. fork-follow.sh reads it from the series head (missing or malformed: the plan stops), resolves a rebase conflict on listed files only by deleting them, deletes again any listed file upstream brings back, refuses a same-base series that still has one, and refuses a new upstream workflow that outside events can start (or whose triggers fork-workflow-triggers.cjs cannot read) unless the list names it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
lens: security S1 · reviewer: opus high (t3lens-14-s1) · read at: cf7f3b8 covered: the full diff of 919556a..cf7f3b8 and every function it calls in fork-follow.sh (fail, on_error, workflow_paths, new_workflows, refuse_upstream_fork_named, schema_check, latest_release, the advance/stage path); fork-workflow-triggers.cjs line by line; the fork-follow.yml plan job (checkout, env, token scope); fork-workflows.sh's unregistered/REQUIRE_NEW_OFF gate; the new tests in fork-follow.test.sh and fork-contracts.test.py. I formed my view before reading the PR body. Live state (read only): GitHub lists 19 workflows in the fork. The 4 deleted files are not among them. Of the files on main that can be started from outside, the listed ones (desktop-macos-preview-publish, pr-limits, pr-size, pr-vouch, relay-preview-cleanup) are all disabled_manually. 8 upstream files on main were never listed: issue-labels, mobile-android-apk, mobile-showcase-screenshots, publish-aur, release-desktop, release, stale-needs-more-info, windows-tests. Today none of them has an outside trigger. has_wiki=true, has_issues=false, has_discussions=false. checks:
finding: minor .github/scripts/fork-workflow-triggers.cjs:49 the reader takes the first column-0 blockers: none. Safe to merge as it stands. Finding 1 is cheap to close in this PR, and finding 2 can be a follow-up. |
|
lens: security S2 · reviewer: opus high (t3lens-14-s2) · read at: cf7f3b8 covered: the full diff (fork-removed-workflows.txt, fork-follow.sh, fork-workflow-triggers.cjs, fork-contracts.test.py, fork-follow.test.sh, fork-follow.yml) and what it calls or feeds: fail, on_error, workflow_paths, new_workflows, refuse_upstream_fork_named, schema_check, latest_release, the plan/push/sweep steps in fork-follow.yml, and fork-workflows.sh (NEW_WORKFLOWS, REQUIRE_NEW_OFF). I also listed the fork's registered workflows (gh api, read only) and checked every branch in the fork for the deleted files. checks:
finding: minor .github/scripts/fork-workflow-triggers.cjs:29 The parser fails open (exit 0, prints |
…tests - fork-workflow-triggers.cjs exports readTriggers and gets fork-workflow-triggers.test.cjs, like the other fork-*.cjs scripts. The test requires it, so knip no longer reports it as an unused file. - The reader refuses: a line break other than \n or \r\n (a lone \r, U+0085, U+2028, U+2029); an unindented line that is not a plain `key:` (`on :`, escaped or quoted keys, `? on`, directives, a root list, a BOM); a quote left open at the end of a line; a bracket list whose later lines are not indented more than its first, or that never closes. Block scalar text is skipped. Indent counts ASCII spaces only. - Outside events add pull_request_review, pull_request_review_comment, gollum, check_run, check_suite, status and deployment_status. - fork-follow.test.sh: plan cases for a CRLF list, the plain-text pull_request_target backstop, the new events and a decoy on: in a multi-line string. - fork-schema-check.test.cjs: drop an unused parameter that vp check reports as an error.
What
The fork's sweep (
fork-workflows.sh) turns off upstream workflows only after GitHub lists them. That means it cannot stop the first run of a workflow that an outside event starts. On 2026-11-12 the owner decided to delete four such upstream workflows from the fork:.github/workflows/mobile-eas-preview-fork.ymlpull_request_target: it runs main's copy with a write token.github/workflows/desktop-macos-preview.yml.github/workflows/thread-transfer-report.ymlworkflow_runon a workflow named "CI", and an outside PR can add a workflow with that name.github/workflows/mobile-fingerprint-label.ymlworkflow_run"CI" patternHow they stay deleted
.github/fork-removed-workflows.txtis the list, kept in config rather than in a hand-kept patch.fork-follow.shreads it from the series head..github/workflows/<name>.yml(or is afork-*file), stops the plan red and leaves the series as it is.fork: delete upstream workflows listed in …). This happens when the fork's deleting patch was dropped as empty because upstream deleted the file itself, and upstream later re-adds it.The events that count, from GitHub's full event list:
pull_request_target,pull_request_review,pull_request_review_comment(anyone can review a PR from afork/*branch, and that branch's copy then runs with the repository's token),issues,issue_comment,discussion,discussion_comment,watch,fork,gollum(the fork's wiki is on).workflow_run(an outside PR can add a workflow with the watched name), andcheck_run,check_suite,statusanddeployment_status(apps post these on an outside PR's commits).pull_requestfrom another fork gets a read-only token and no secrets. The other events need write access or more.The triggers are read by the new dependency-free
fork-workflow-triggers.cjs. It reads only the top-levelon:, soissues: writeunderpermissionsor a job namedissuesdoes not count.Any
pull_request_targetorworkflow_runword anywhere in the file also counts.The reader reads plain YAML only. Anything else counts as unreadable, so the plan stops:
\nor\r\n(a lone\r, U+0085, U+2028, U+2029), or a tab in the indent;key:(or"on":),---or.... This refuseson :, escaped or quoted keys,? on, directives, a root-level list and a BOM;needs:), or one that never closes;on:twice, several YAML documents, noon:;on:: a flow mapping, a list over lines, an anchor, alias or tag, a block scalar,<<, uneven indent, or a name that is not an event name.So a decoy
on:cannot hide in a multi-line string or list. Over all 30 workflow files on fork main and all 26 on upstream main, the reader's output matches PyYAML'son:keys.fork-contracts.test.pygets a newremovedsection. It checks that the list exists, that every line is an upstream workflow path that is not in the tree, and that the four files above are on it. Each of these has a broken copy that must fail.What happens when this runs again, stops half-way, or meets existing, old or empty state?
git rebase --abort), so the series is left as it is. Later steps (candidate push, release, branch move) are unchanged and already handle re-runs.fork-*path, a path with a space, and../).SERIES_BRANCH(fork-follow.yml), so it runs main's currentfork-follow.sh. Before this merges that script has no list logic, hits a modify/delete conflict on these files and stops red, as today. After it merges, the run uses this logic. Either way it does not ship them.Tests (red on fork main, green here)
The tests never reach GitHub:
fork-follow.test.shruns underfork-test-gh.sh(noGH_TOKEN/GITHUB_TOKEN,.invalidGH_HOST, stubgh), and the plan uses only local git remotes.bash .github/scripts/fork-follow.test.sh: all 131 checks pass here. Run against fork main's scripts (FORK_FOLLOW_DIR=<main's .github/scripts>), 24 fail: every new plan check except the three that cover unchanged behaviour (an empty list goes on, a push-only new workflow goes on, a rename is refused, which main also does through its plain conflict).node --test .github/scripts/fork-workflow-triggers.test.cjs: 13 tests of the reader. They cover every shape it reads and every refusal, including upstream's own forms (CRLF, block scalars with quotes in them, aneeds:list over lines).fork-workflow-triggers.test.cjs(1 test each): the odd line break check, the tab check, the plain-key:rule, the open-quote rule, the bracket-indent rule, the never-closed bracket, the aligned closing line,on:twice, several documents, the unknownon:forms,<<, uneven indent, emptyon:, the event-name check, a nested list underon:, quotes only at a token start, ASCII-only indent, the block-scalar skip, the block's key and dash indent, the block's end, and exit 2 on error.fork-follow.test.sh: the CR strip on list lines (1 fails), the plain-textpull_request_target|workflow_runbackstop (1), and the review and gollum events (3).key:rule. The reader test covers the quote rule on its own.python3 .github/scripts/fork-contracts.test.py: all pass here. Run on fork main's tree with this test file, 3 fail (the list is missing, the four files are not on it, and the "listed file still in the tree" mutant).node .github/scripts/fork-ci-tests.cjs(every listed fork test:fork-release.test.sh,fork-secret-scan.test.sh,fork-release-workflow.test.py, thefork-*.test.cjstests),node .github/scripts/fork-tests-drift.cjs, actionlint 1.7.12 (the pinned, checksummed release) onfork-*.yml,vp fmt --checkandvp linton.github, and knip's file check. Without the new test file knip reportsfork-workflow-triggers.cjsas unused, as CI did; with it, it does not.Lint fixes
fork-workflow-triggers.cjsnow exportsreadTriggers, andfork-workflow-triggers.test.cjsrequires it. Every otherfork-*.cjsscript is kept in use this way, through knip's.github/**/*.test.cjsentry. The test is listed in.github/fork-ci-tests.txt.vp check: knip failed first, sovp checknever ran on this PR.fork-schema-check.test.cjs(from PR fork: outside PRs run nothing, one checkout without actions/checkout, contract tests #12, which merged without a Fork tests run) has an unusedcmdparameter thatvp lintreports as an error. It is dropped here.Checks on this head
vp lintfix touchesfork-schema-check.test.cjs, and the check refuses any change to its own files ("Only the owner may do that: the check stays red, and the owner merges past it"). Reverting the fix would make this check green but leavevp checkred in Lint, so the fix stays.fork-tests.ymlis disabled in the fork (since 17:33Z), so no run started for the push. Once it is enabled, a new push or a close and reopen of this PR starts it.Not done
desktop-macos-preview-publish.yml(workflow_runon "Desktop macOS Preview" pluspull_request_target),pr-limits.yml,pr-size.yml,pr-vouch.yml(alsoissue_comment),relay-preview-cleanup.yml. They stay as they are (already listed by GitHub and disabled by the sweep). The general check covers only files that are new on a release. Adding any of them to the list is a one-line change if wanted..github/fork-ci-tests.txthas a stale comment, "Upstream runs this one from thread-transfer-report.yml". That file belongs to PR fork: Fork tests on the repository's own fleet runners, chosen per run (CI_RUNNERS) #13, so it is left alone. The test it describes still runs fromfork-tests.yml..github/fork-ci-tests.txtis also changed in PR fork: Fork tests on the repository's own fleet runners, chosen per run (CI_RUNNERS) #13. This PR adds one line afterfork-tests-drift.test.cjs, and fork: Fork tests on the repository's own fleet runners, chosen per run (CI_RUNNERS) #13 adds one afterfork-contracts.test.py. The hunks are apart, so either order merges cleanly.Follow-ups (the fork has Issues off, so they are listed here)
issue-labels,mobile-android-apk,mobile-showcase-screenshots,publish-aur,release,release-desktop,stale-needs-more-info,windows-tests. If a release edits one of them to add an outside event, the plan passes it. Only the laterREQUIRE_NEW_OFFgate infork-workflows.shstops the branch move, and its advice is to move main by hand if the files look harmless. Fix: also check changed files and refuse when the new version has an outside event the old one lacked (or check every added or changed file GitHub does not list).fork/*branches still carrymobile-eas-preview-fork.yml(for examplefork/ci,fork/auto-update,fork/follow-upstream,fork/release-workflow,fork/fleet-runners).pull_request_targetruns the base branch's copy, so an outside PR aimed at one of those branches still starts it, and the sweep cannot disable it. Today it fires only onlabeled, with a jobifon the label, which needs triage rights. Fix: delete the stale branches, or restrict PR base branches.