Skip to content
Merged
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
127 changes: 127 additions & 0 deletions .github/renovate.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended",
":semanticCommits",
"group:monorepos",
"group:recommended",
":maintainLockFilesWeekly"
],
"timezone": "Europe/Berlin",
"schedule": ["before 6am on monday"],
"labels": ["dependencies"],
"prConcurrentLimit": 10,
"prHourlyLimit": 4,
"dependencyDashboard": true,
"configMigration": true,

"major": {
"dependencyDashboardApproval": true
},

"vulnerabilityAlerts": {
"enabled": true,
"labels": ["dependencies", "security"],
"schedule": ["at any time"]
},
"osvVulnerabilityAlerts": true,

"customManagers": [
{
"customType": "regex",
"description": "Rust channel in rust-toolchain.toml",
"managerFilePatterns": ["/^rust-toolchain\\.toml$/"],
"matchStrings": ["channel\\s*=\\s*\"(?<currentValue>\\d+\\.\\d+(\\.\\d+)?)\""],
"depNameTemplate": "rust",
"packageNameTemplate": "rust-lang/rust",
"datasourceTemplate": "github-releases"
},
{
"customType": "regex",
"description": "node-version input of actions/setup-node",
"managerFilePatterns": ["/^\\.github/workflows/[^/]+\\.ya?ml$/"],
"matchStrings": ["node-version:\\s*(?<currentValue>\\d+(\\.\\d+)*)"],
"depNameTemplate": "node",
"datasourceTemplate": "node-version",
"versioningTemplate": "node"
},
{
"customType": "regex",
"description": "version input of pnpm/action-setup",
"managerFilePatterns": ["/^\\.github/workflows/[^/]+\\.ya?ml$/"],
"matchStrings": [
"uses:\\s*pnpm/action-setup@[^\\n]*\\n\\s*with:\\n\\s*version:\\s*(?<currentValue>\\d+(\\.\\d+)*)"
],
"depNameTemplate": "pnpm",
"datasourceTemplate": "npm",
"versioningTemplate": "npm"
},
{
"customType": "regex",
"description": "ZIG_VERSION build arg in the Dockerfile",
"managerFilePatterns": ["/^Dockerfile$/"],
"matchStrings": ["ARG ZIG_VERSION=(?<currentValue>\\d+\\.\\d+\\.\\d+)"],
"depNameTemplate": "zig",
"packageNameTemplate": "ziglang/zig",
"datasourceTemplate": "github-releases"
}
],

"packageRules": [
{
"description": "CI gates every PR (nextest, vitest, playwright, clippy, cargo-deny), so anything below a major merges itself once green.",
"matchUpdateTypes": ["patch", "minor", "pin", "digest"],
"automerge": true,
"automergeType": "pr",
"platformAutomerge": true
},
{
"description": "Refreshing the lockfiles is how transitive advisories reach us — a direct dependency bump cannot pull a patched sub-dependency on its own.",
"matchUpdateTypes": ["lockFileMaintenance"],
"automerge": true,
"automergeType": "pr",
"platformAutomerge": true
},
{
"description": "The Rust version is pinned in three places (rust-toolchain.toml, the dtolnay/rust-toolchain action, and the Dockerfile's rust image) and they must move together. Never automerged: a new toolchain relints and reformats the workspace, which is a deliberate change.",
"groupName": "rust toolchain",
"matchDepNames": ["rust", "dtolnay/rust-toolchain"],
"automerge": false,
"dependencyDashboardApproval": true
},
{
"description": "pnpm is pinned in web/package.json, e2e/package.json and the pnpm/action-setup input. web/pnpm-workspace.yaml documents that these are one atomic change — pnpm 9 hard-fails on the workspace file this repo now ships.",
"groupName": "pnpm",
"matchDepNames": ["pnpm"],
"automerge": false,
"dependencyDashboardApproval": true
},
{
"description": "Node is pinned in the Dockerfile's web-builder stage and in actions/setup-node; keep the build and CI on the same major.",
"groupName": "node",
"matchDepNames": ["node"],
"automerge": false,
"dependencyDashboardApproval": true
},
{
"description": "web/pnpm-workspace.yaml pins these EXACTLY, to deduplicate the ProseMirror core that @tiptap/pm and y-prosemirror both depend on. They are not ordinary patches: taking prosemirror-view 1.42 changed how a remote replace-the-document update applies and broke history restore, which only CI caught. Surfaced on the dashboard so they stay visible, never opened or merged automatically.",
"matchPackageNames": [
"prosemirror-model",
"prosemirror-state",
"prosemirror-view"
],
"automerge": false,
"dependencyDashboardApproval": true
},
{
"description": "Security floors in web/pnpm-workspace.yaml overrides. Raising them is welcome, but they exist because a parent hard-pins a vulnerable version, so a bump here needs a human to confirm the parent still resolves.",
"matchFileNames": ["web/pnpm-workspace.yaml"],
"automerge": false
},
{
"description": "Playwright ships its browser binaries per version; a bump changes what the e2e suite actually runs against.",
"matchPackageNames": ["@playwright/test"],
"automerge": false
}
]
}
35 changes: 35 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,41 @@ This regenerates both `crates/knot-markdown/src/schema.rs` and `web/src/features
- `build:` — build system (Dockerfile, Makefile, Cargo.toml)
- `ci:` — GitHub Actions

## Dependencies

[Renovate](https://docs.renovatebot.com/) (`.github/renovate.json`) keeps the
Cargo workspace, both pnpm projects, the GitHub Actions, and the Dockerfile base
images current. Patch and minor updates merge themselves once CI is green;
everything else waits for a human.

Majors do not open a PR on their own — they are listed on the **Dependency
Dashboard** issue, and ticking a box there tells Renovate to raise that one.
That keeps a major migration (tiptap 2 → 3, tailwind 3 → 4) a deliberate choice
rather than eight red PRs.

Weekly `lockFileMaintenance` runs `cargo update` / `pnpm update` against the
lockfiles. This is the only route by which a patched *transitive* dependency
reaches us — bumping a direct dependency cannot pull one in on its own — so it
is how most RUSTSEC and GHSA advisories actually get closed here.

Three versions are pinned in more than one file and must move together.
Renovate groups each into a single PR; do the same by hand:

| what | pinned in |
|---|---|
| Rust | `rust-toolchain.toml`, `dtolnay/rust-toolchain@…` in `ci.yml`, `rust:…-alpine` in `Dockerfile` |
| pnpm | `packageManager` in `web/` and `e2e/package.json`, `pnpm/action-setup` `version:` |
| Node | `node:…-alpine` in `Dockerfile`, `node-version:` in `ci.yml` |

Left out of automation on purpose: the exact `prosemirror-*` pins in
`web/pnpm-workspace.yaml` (they deduplicate the editor core — see the comment
there for what breaks), the security floors in the same file's `overrides`, and
`@playwright/test`, which ships the browser the e2e suite runs against.

`cargo deny check` gates every PR. When an advisory has no reachable fix, add it
to `deny.toml` with the dependency path and the upstream event that would let us
drop it again.

## Pull requests

Before opening a PR:
Expand Down