Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 30 additions & 6 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@ updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
day: monday
# Monthly, not weekly. Every proposed bump costs a full pipeline and a review, and weekly produced
# more of both than the changes justified — build tooling that moves a patch version does not need
# attention four times a month. SECURITY updates are unaffected: they arrive on their own schedule
# regardless of this interval, which is the point of separating them.
interval: monthly
open-pull-requests-limit: 5
commit-message:
prefix: build # Conventional Commits — the commit-msg hook and the changelog both depend on it
Expand All @@ -25,15 +28,23 @@ updates:
security:
applies-to: security-updates
patterns: ['*']
ignore:
# A major of an action can change its inputs or its runtime, and every one here is pinned by commit
# SHA — so the diff is opaque by design and the changelog is the only way to know what moved.
- dependency-name: '*'
update-types: [version-update:semver-major]

# Build tooling only: vitest/jsdom for the frontend tests, commitlint for the commit gate, and the
# Agent SDK as protocol reference. None of it ships (see SECURITY.md), which is exactly why the
# updates are grouped — they are maintenance, not security releases, and one PR a week is enough.
- package-ecosystem: npm
directory: /
schedule:
interval: weekly
day: monday
# Monthly, not weekly. Every proposed bump costs a full pipeline and a review, and weekly produced
# more of both than the changes justified — build tooling that moves a patch version does not need
# attention four times a month. SECURITY updates are unaffected: they arrive on their own schedule
# regardless of this interval, which is the point of separating them.
interval: monthly
open-pull-requests-limit: 3
commit-message:
prefix: build
Expand All @@ -52,6 +63,11 @@ updates:
applies-to: security-updates
patterns: ['*']
ignore:
# Majors are proposed by a human, not by a bot. They are where a bump actually breaks something, they
# need reading the changelog and running the suite, and a PR that sits open for weeks re-running CI on
# every merge into develop is worse than no PR. Patch and minor keep arriving grouped.
- dependency-name: '*'
update-types: [version-update:semver-major]
# The SDK baseline is not Dependabot's to move. `checkDrift` bumps it as part of a *reconciled*
# protocol review — taking the new version without reading the surface diff is how a protocol gap
# gets silently blessed.
Expand All @@ -60,8 +76,11 @@ updates:
- package-ecosystem: gradle
directory: /
schedule:
interval: weekly
day: monday
# Monthly, not weekly. Every proposed bump costs a full pipeline and a review, and weekly produced
# more of both than the changes justified — build tooling that moves a patch version does not need
# attention four times a month. SECURITY updates are unaffected: they arrive on their own schedule
# regardless of this interval, which is the point of separating them.
interval: monthly
open-pull-requests-limit: 3
commit-message:
prefix: build
Expand All @@ -78,6 +97,11 @@ updates:
applies-to: security-updates
patterns: ['*']
ignore:
# Majors by hand — and this ecosystem is the cautionary tale: the IntelliJ Platform Gradle Plugin
# 2.16 -> 2.18 bump passed CI and hung the headless suite locally, forever, inside the platform's own
# test fixture. A bot cannot make that call and CI would not have caught it either.
- dependency-name: '*'
update-types: [version-update:semver-major]
# kotlinx-serialization is provided by the IntelliJ Platform at runtime; the declared version has
# to match what the targeted IDEs ship, not the newest release. Bumping it blindly is a
# NoSuchMethodError on a user's IDE, not an upgrade.
Expand Down
37 changes: 24 additions & 13 deletions .github/rulesets/develop.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,28 @@
"enforcement": "active",
"conditions": {
"ref_name": {
"include": ["refs/heads/develop"],
"include": [
"refs/heads/develop"
],
"exclude": []
}
},
"bypass_actors": [],
"rules": [
{ "type": "deletion" },
{ "type": "non_fast_forward" },
{ "type": "required_signatures" },
{
"type": "deletion"
},
{
"type": "non_fast_forward"
},
{
"type": "required_signatures"
},
{
"type": "pull_request",
"parameters": {
"_comment": [
"0, not 1 — see the long note in main.json. GitHub does not let an author approve their own",
"0, not 1 \u2014 see the long note in main.json. GitHub does not let an author approve their own",
"pull request, so on a single-maintainer repository requiring an approval makes the branch",
"unmergeable rather than well-guarded. Raise it to 1 when a second maintainer exists."
],
Expand All @@ -26,21 +34,24 @@
"require_code_owner_review": false,
"require_last_push_approval": false,
"required_review_thread_resolution": false,
"allowed_merge_methods": ["squash", "merge"]
"allowed_merge_methods": [
"squash",
"merge"
]
}
},
{
"type": "required_status_checks",
"parameters": {
"strict_required_status_checks_policy": true,
"strict_required_status_checks_policy": false,
"do_not_enforce_on_create": false,
"required_status_checks": [
{ "context": "JVM tests" },
{ "context": "Static analysis" },
{ "context": "Frontend tests" },
{ "context": "Dependency audit" },
{ "context": "Plugin verifier" },
{ "context": "Build plugin" }
{
"context": "JVM tests"
},
{
"context": "Frontend tests"
}
]
}
}
Expand Down
56 changes: 41 additions & 15 deletions .github/rulesets/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,28 @@
"enforcement": "active",
"conditions": {
"ref_name": {
"include": ["refs/heads/main"],
"include": [
"refs/heads/main"
],
"exclude": []
}
},
"bypass_actors": [],
"rules": [
{ "type": "deletion" },
{ "type": "non_fast_forward" },
{ "type": "required_signatures" },
{
"type": "deletion"
},
{
"type": "non_fast_forward"
},
{
"type": "required_signatures"
},
{
"type": "pull_request",
"parameters": {
"_comment": [
"required_approving_review_count is 0 ON PURPOSE, and it is not a weakened gate — it is the",
"required_approving_review_count is 0 ON PURPOSE, and it is not a weakened gate \u2014 it is the",
"only value that is not a deadlock. GitHub does not let an author approve their own pull",
"request, so on a single-maintainer repository 'require 1 approval' with no bypass actors means",
"NOTHING can ever be merged: no direct push, no approval available, no way around it.",
Expand All @@ -27,7 +35,7 @@
"talked out of. A human approval is a real control when there IS a second human; requiring one",
"that cannot exist is theatre that locks the door from the inside.",
"",
"RAISE THIS TO 1 the moment a second maintainer has write access — and re-enable",
"RAISE THIS TO 1 the moment a second maintainer has write access \u2014 and re-enable",
"require_code_owner_review and require_last_push_approval at the same time, both of which are",
"off for the same reason."
],
Expand All @@ -36,7 +44,9 @@
"require_code_owner_review": false,
"require_last_push_approval": false,
"required_review_thread_resolution": true,
"allowed_merge_methods": ["merge"]
"allowed_merge_methods": [
"merge"
]
}
},
{
Expand All @@ -45,14 +55,30 @@
"strict_required_status_checks_policy": true,
"do_not_enforce_on_create": false,
"required_status_checks": [
{ "context": "JVM tests" },
{ "context": "Static analysis" },
{ "context": "Frontend tests" },
{ "context": "Dependency audit" },
{ "context": "Plugin verifier" },
{ "context": "Build plugin" },
{ "context": "CodeQL (java-kotlin)" },
{ "context": "CodeQL (javascript-typescript)" }
{
"context": "JVM tests"
},
{
"context": "Static analysis"
},
{
"context": "Frontend tests"
},
{
"context": "Dependency audit"
},
{
"context": "CodeQL (java-kotlin)"
},
{
"context": "CodeQL (javascript-typescript)"
},
{
"context": "Plugin verifier"
},
{
"context": "Build plugin"
}
]
}
}
Expand Down
47 changes: 42 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,16 @@ jobs:
- name: Set up Gradle
uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0
with:
# Only the trunk writes the shared cache. A PR from a fork must never be able to poison what
# the next build on develop reads back.
cache-read-only: ${{ github.ref != 'refs/heads/develop' && github.ref != 'refs/heads/main' }}
# Read-only ONLY for pull requests from forks. Every other branch writes its own cache, which is
# what stops a second push from re-downloading 1.25 GB of IDEs it already had.
#
# This is safe without our help, and the previous blanket read-only was more conservative than the
# platform requires. GitHub scopes caches per branch: "Workflow runs cannot restore caches created
# for child branches or sibling branches", and a cache created on a pull request is written to the
# merge ref, so it "can only be restored by re-runs of the pull request". A topic branch therefore
# cannot reach — let alone overwrite — what develop reads back. Forks stay read-only anyway: there
# is no reason to let untrusted code populate anything this repository will later restore.
cache-read-only: ${{ github.event.pull_request.head.repo.fork == true }}

# Coverage is verified HERE, in the same job and the same Gradle invocation as the tests.
# `koverVerify` depends on `:test`, so running it in the separate `Static analysis` job re-ran the whole
Expand Down Expand Up @@ -98,6 +105,14 @@ jobs:
name: Static analysis
runs-on: ubuntu-latest
timeout-minutes: 20
# Same door as the verifier: pull requests into main, and the protected branches themselves.
# A branch iterating towards develop runs only the two test suites; formatting, lint and coverage are
# settled before anything is promoted. The cost is real and worth naming — a formatting or detekt
# failure now lands ON develop and is fixed by a follow-up commit, instead of being caught in the PR.
if: >-
(github.event_name == 'pull_request' && github.base_ref == 'main') ||
(github.event_name == 'push' &&
(github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/main'))
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
Expand All @@ -110,7 +125,7 @@ jobs:

- uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' && github.ref != 'refs/heads/main' }}
cache-read-only: ${{ github.event.pull_request.head.repo.fork == true }}

- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
Expand Down Expand Up @@ -193,6 +208,13 @@ jobs:
name: Dependency audit
runs-on: ubuntu-latest
timeout-minutes: 10
# Same door. NB this is the check that judges exactly what a Dependabot pull request changes, so it no
# longer runs on the PR that proposes the bump — only once that bump is on develop, and again before it
# can reach main. Nothing ships un-audited; the finding simply arrives one merge later.
if: >-
(github.event_name == 'pull_request' && github.base_ref == 'main') ||
(github.event_name == 'push' &&
(github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/main'))
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
Expand All @@ -219,6 +241,21 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 60
needs: [test, frontend-test]
# The expensive one: ~10 minutes and 1.25 GB of IDE downloads. It runs where the answer is load-bearing —
# on every pull request, and on the protected branches — and NOT on each push to a topic branch, where it
# was re-verifying a commit nobody was about to merge. The gate is unchanged: it is still a required check
# on develop and main, and a PR cannot merge without it. What is lost is early detection mid-branch, which
# is a real cost and the reason it ran everywhere until now.
# Where the exhaustive check belongs: the develop -> main door, plus the protected branches themselves.
#
# NOT on topic branches and NOT on pull requests into develop — those iterate constantly and this job is
# ~10 minutes and 1.25 GB of IDE downloads. It DOES run on any pull request targeting main, because that
# is the merge that publishes, and it is the only gate that catches a BINARY incompatibility across the
# 251 -> 262 range (compiling against 252 proves nothing about 262 — see the 4.4.1 /login regression).
if: >-
(github.event_name == 'pull_request' && github.base_ref == 'main') ||
(github.event_name == 'push' &&
(github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/main'))
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
Expand All @@ -231,7 +268,7 @@ jobs:

- uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0
with:
cache-read-only: ${{ github.ref != 'refs/heads/develop' && github.ref != 'refs/heads/main' }}
cache-read-only: ${{ github.event.pull_request.head.repo.fork == true }}

# The runner ships with a few GB of preinstalled toolchains we will never use, and the verifier
# needs room for multiple extracted IDEs. Reclaiming it is cheaper than debugging a disk-full run.
Expand Down
Loading