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
2 changes: 1 addition & 1 deletion .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# `cargo coverage`: the tests under cargo-llvm-cov, failing below these thresholds. CI runs it on
# every OS; raise the thresholds to just under the lowest OS's numbers when coverage goes up. Only
# Linux; raise the thresholds to just under its numbers when coverage goes up. Only
# the code that ships is measured: test modules live in files of their own (`tests.rs`, and the
# TUI's `props.rs`, `screens.rs` and `testing.rs`), left out with `test_util.rs` and the test
# plugin (`bin/test-plugin/`, which the CLI's debug builds include too) by the regex, which takes
Expand Down
6 changes: 4 additions & 2 deletions .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,15 @@ ast-grep's rules (`sgconfig.yml`, in `tools/ast-grep/rules/` with their cases in

`cargo remove` also deletes the root `Cargo.toml`'s `[profile.dev.package.*]` entries, with their comments, for every crate no member depends on directly: its clean-up after a removal ([rust-lang/cargo#11194](https://github.com/rust-lang/cargo/pull/11194), since Cargo 1.67) only keeps the entries for direct dependencies. `tools/policy.sh` fails while one is missing and its crate is still in `Cargo.lock`, so put it back with `git checkout -p Cargo.toml`. A new entry goes in `dev_optimised` in `tools/policy/cargo.rego` too.

CI (`ci.yml`) runs a tool's checks only when its files (or the shared ones) change, and ends with a "CI passed" check that reports either way, the check `main` requires before a merge. On a draft pull request, nothing runs until you mark it ready for review, so open a pull request as a draft while it isn't next to merge (a stacked one, say). "CI passed" fails on a draft, since none of its checks ran. Marking it ready starts a new run, and if one doesn't show up, re-run the workflow. The slow suites run weekly in the `*-scheduled.yml` workflows, and you can start them on any branch from the Actions tab. A weekly run that fails opens an issue labelled `scheduled-failure`, and the next weekly run that passes closes it.
CI (`ci.yml`) runs a tool's checks only when its files (or the shared ones) change, and ends with a "CI passed" check that reports either way, the check `main` requires before a merge. On a draft pull request, nothing runs until you mark it ready for review, so open a pull request as a draft while it isn't next to merge (a stacked one, say). "CI passed" fails on a draft, since none of its checks ran. Marking it ready starts a new run, and if one doesn't show up, re-run the workflow.

Pull requests run on Linux. locallibrary's macOS and Windows checks run in the merge queue: once a pull request is approved and passing, add it to the queue, which runs them on it together with the pull requests ahead of it and merges it when they pass. To run them on a pull request first, add the `all-os` label and re-run CI. The slow suites run weekly in the `*-scheduled.yml` workflows, and you can start them on any branch from the Actions tab. A weekly run that fails opens an issue labelled `scheduled-failure`, and the next weekly run that passes closes it.

On a pull request, each tool's workflow keeps one comment up to date with its coverage: the Rust workspace's, with the Hegel property tests' alone beside it, and the website's and the extension's against `main`, with each changed file's. The website's and the extension's comments also show how the change moves what they ship against `main`: the website's client JavaScript and CSS, and the extension's zip. A pull request from a fork can't be commented on, so it finds the same in the run's summary. The website's Lighthouse scores are in the run's summary too, when the change touches what its pages are built from.

Pull requests also get a dependency review (`dependency-review.yml`). It fails when a dependency the pull request adds or updates (dev tooling aside) has a known advisory or a licence `deny.toml` doesn't allow (fonts may also be OFL-1.1), and then comments on the pull request with what it found.

CodeQL scans the workflows, the TypeScript and the Rust on every pull request (`codeql.yml`), and `main` won't take a pull request that adds a high or critical security alert, or an error. The Rust tests are left out (`.github/codeql/codeql-config.yml`), so a new `#[cfg(test)]` module needs one of the names listed there, which `ast-grep scan` checks.
CodeQL scans the workflows, the TypeScript and the Rust on every pull request and merge queue group (`codeql.yml`), and `main` won't take a pull request that adds a high or critical security alert, or an error. The Rust tests are left out (`.github/codeql/codeql-config.yml`), so a new `#[cfg(test)]` module needs one of the names listed there, which `ast-grep scan` checks.

## Pull requests

Expand Down
10 changes: 6 additions & 4 deletions .github/NEW_PROJECT.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,17 @@ Logging: the website logs with [pino](https://getpino.io) (`apps/web/src/lib/ser

### CI

Pull requests and pushes to `main` run one workflow, `ci.yml`, in three parts:
Pull requests, merge queue groups and pushes to `main` run one workflow, `ci.yml`, in three parts:

1. a `changes` job that runs [dorny/paths-filter](https://github.com/dorny/paths-filter) over each tool's files, including the shared ones it depends on (the lockfile, `.node-version`, the lint and format configs, its workflow), with one output per tool. On a push it diffs against the last commit the workflow passed on (`.github/actions/last-passed`), so a run that a newer push cancelled, or one that failed, leaves its files to the next run. A draft pull request runs nothing until it's marked ready for review;
1. a `changes` job that runs [dorny/paths-filter](https://github.com/dorny/paths-filter) over each tool's files, including the shared ones it depends on (the lockfile, `.node-version`, the lint and format configs, its workflow), with one output per tool. On a push it diffs against the last commit the workflow passed on (`.github/actions/last-passed`), so a run that a newer push cancelled, or one that failed, leaves its files to the next run, and a merge queue group against the commit it goes on top of. A draft pull request runs nothing until it's marked ready for review;
2. a job per tool that calls its workflow, `<tool>-ci.yml`, when the tool's output is true. Copy `localtoolbox-ci.yml`, the smallest: it triggers only on `workflow_call`, has no concurrency group (a call gets the caller's, and would cancel it), and its jobs do the work;
3. a `passed` job named "CI passed", with `if: always()`, that fails if any call failed or was cancelled, was skipped while its output was true, or the pull request was a draft. A call's result covers every job in it, so a job skipped because one it needs didn't finish fails its tool's call.

Jobs that only report have `continue-on-error: true`, so they never fail their call or block a merge: the coverage `report` jobs that comment on the pull request, and locallibrary-ci's `semver`. `ci.yml`'s concurrency group cancels the run a new push supersedes, on `main` too.

The "Protect Main" ruleset requires "CI passed", which reports either way, so a tool's checks never block a pull request that doesn't touch it. The ruleset is `.github/rulesets/protect-main.json`: an admin applies it with `tools/ruleset.sh apply` once the pull request that changes it merges, and the Repo checks workflow fails on `main` while GitHub's copy differs. A pull request that renames or drops a required check is the exception: GitHub won't merge it while the old name is required, so the admin applies the file from its branch first, and open pull requests then merge `main` to report the new name. It requires CI passed and code scanning, which is `codeql.yml`'s CodeQL analysis of the workflows, the TypeScript and the Rust, leaving out the Rust tests (`.github/codeql/codeql-config.yml`).
Pull requests and pushes to `main` run on Linux. The plan's 5 macOS runners can't keep up with every push, so locallibrary's macOS and Windows job (`other-os`) runs once for each merge queue group, which runs nothing else, or on a pull request with the `all-os` label. `locallibrary-cache.yml` runs it nightly on `main`, which is where its caches are saved.

The "Protect Main" ruleset requires "CI passed", which reports either way, so a tool's checks never block a pull request that doesn't touch it. The ruleset is `.github/rulesets/protect-main.json`: an admin applies it with `tools/ruleset.sh apply` once the pull request that changes it merges, and the Repo checks workflow fails on `main` while GitHub's copy differs. A pull request that renames or drops a required check is the exception: GitHub won't merge it while the old name is required, so the admin applies the file from its branch first, and open pull requests then merge `main` to report the new name. It merges through a merge queue, which builds at most 2 groups of up to 5 pull requests at once, and requires CI passed and code scanning, which is `codeql.yml`'s CodeQL analysis of the workflows, the TypeScript and the Rust, leaving out the Rust tests (`.github/codeql/codeql-config.yml`).

Changes reach `main` only through a pull request, merged as a merge commit. Each one needs an approval from a code owner (`.github/CODEOWNERS`) given after its last push, and a new push dismisses earlier approvals, so an agent's pull request waits for a person to approve it.

Expand Down Expand Up @@ -123,7 +125,7 @@ Then do the steps every app shares, below.

1. Create `apps/<tool>/` and add it to `members` in the root `Cargo.toml`. Inherit `edition`, `rust-version`, `license` and `repository` from the workspace, but give it a `version` of its own: the workspace's moves with locallibrary's releases. Add `[lints] workspace = true`. Take shared dependencies from `[workspace.dependencies]` (`cargo add --package <name>`).
2. Put unit tests in each module's own `tests.rs` (`#[cfg(test)] mod tests;`), so coverage leaves them out, and integration tests in one binary, `tests/it/`.
3. `locallibrary-ci.yml` already checks the whole workspace (fmt, clippy with `-D warnings`, the tests on macOS, Linux and Windows, `cargo coverage` and its thresholds, the MSRV build on 1.91, the docs and `cargo deny`), but only when its paths change. Add `apps/<tool>/**` to the `locallibrary` filter in `ci.yml`. The new app's code counts toward the shared coverage thresholds and has to build on the MSRV. Anything only the app needs goes in a `<tool>-ci.yml` that `ci.yml` calls, as for a TypeScript app.
3. `locallibrary-ci.yml` already checks the whole workspace (fmt, clippy with `-D warnings`, the tests on Linux, and on macOS and Windows in the merge queue, `cargo coverage` and its thresholds, the MSRV build on 1.91, the docs and `cargo deny`), but only when its paths change. Add `apps/<tool>/**` to the `locallibrary` filter in `ci.yml`. The new app's code counts toward the shared coverage thresholds and has to build on the MSRV. Anything only the app needs goes in a `<tool>-ci.yml` that `ci.yml` calls, as for a TypeScript app.
4. `locallibrary-scheduled.yml` runs cargo-mutants weekly over every workspace package but locallibrary's library and CLI, so the app is in it. If its tests are too slow to run once per mutant, leave it out there as those two are.
5. Set `publish = false` unless it's meant for crates.io: clippy's `cargo_common_metadata` wants full metadata on anything publishable, and dist (which releases on `locallibrary` tags) leaves unpublished packages out. When it gets a release of its own, set `[package.metadata.dist] dist = true`, as `apps/locallibrary/Cargo.toml` does.

Expand Down
9 changes: 8 additions & 1 deletion .github/actions/last-passed/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ description: >-
failed run never checked whenever the next push doesn't touch the same files; diffed against the
last commit that passed, those files count until a run passes. Empty, with a warning, when none
of the workflow's last 100 runs is one, which leaves paths-filter on the commit before the push.
Needs `actions: read`.
For a merge queue group, the commit it goes on top of, so the group checks what its pull requests
change. Needs `actions: read`.

outputs:
sha:
Expand All @@ -22,7 +23,13 @@ runs:
GH_REPO: ${{ github.repository }}
WORKFLOW_REF: ${{ github.workflow_ref }}
BRANCH: ${{ github.ref_name }}
GROUP_BASE: ${{ github.event.merge_group.base_sha }}
run: |
if [ -n "$GROUP_BASE" ]; then
echo "sha=$GROUP_BASE" >> "$GITHUB_OUTPUT"
echo "Changes since $GROUP_BASE, the commit the group goes on top of"
exit 0
fi
# owner/repo/.github/workflows/<file>@<ref> to <file>.
workflow=${WORKFLOW_REF%@*}
workflow=${workflow##*/}
Expand Down
6 changes: 6 additions & 0 deletions .github/labels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,12 @@
color: "000000"
description: Pull requests that update GitHub Actions code

# Read by .github/workflows/ci.yml: a pull request with it runs locallibrary's macOS and Windows
# jobs too, which otherwise wait for the merge queue. Re-run CI after adding it.
- name: all-os
color: "5319e7"
description: Run the macOS and Windows checks on this pull request

# Opened and closed by .github/workflows/scheduled-issue.yml, which finds its open issue by it.
- name: scheduled-failure
color: "b60205"
Expand Down
12 changes: 12 additions & 0 deletions .github/rulesets/protect-main.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,18 @@
"required_reviewers": []
}
},
{
"type": "merge_queue",
"parameters": {
"check_response_timeout_minutes": 120,
"grouping_strategy": "ALLGREEN",
"max_entries_to_build": 2,
"max_entries_to_merge": 5,
"merge_method": "MERGE",
"min_entries_to_merge": 1,
"min_entries_to_merge_wait_minutes": 5
}
},
{
"type": "code_scanning",
"parameters": {
Expand Down
Loading
Loading