~ knotless Search/
↑↓ move↵ open⇧↵ all results

grain.social/grain

photography-focused photo sharing platform

grain.social
1 collaborator
0
2

The import is its own crate, and grain's crates no longer name hatk

Merged
Merged chadtmiller.com asked to merge into feature/rust from grain.social/grain:rust/no-hatk

What changes

The import is its own crate, crates/grain-import. The binary is grain-import <old grain.db> [--into <index>] [--strict] and behaves as grain-serve import did: the same summary, exit codes, GRAIN_INDEX fallback and checks. Its library is the old index/import.rs, its unit tests the old import/tests.rs, and the binary's test moves from grain-serve's tests/main.rs to crates/grain-import/tests/main.rs. It depends on grain-serve as a library, which now exposes index::migrate (a neutral name; nothing else needed widening). grain-serve has no import subcommand. The crate doc says it is a one-shot cutover tool, deleted once the production import has run, and the only code that knows the old database's layout.

Migration V5 creates notification_seen and notification_pref and fills neither. It no longer moves lastSeenNotifications and notificationPrefs out of preference. The import writes both tables itself, so that conversion lives only in grain-import. No index has been deployed, so V5 is changed in place rather than given a later step, and the migrations keep their numbers. The test of that move (notifications_preferences_move_to_their_own_tables) is removed. V4 and V6 fill search and mentions from grain's own tables, and V1 to V3 only create tables, so no other migration reads the old server's data.

Comments describe grain on its own terms. In crates/ outside grain-import and in rust.yml, comments that cited the old server's files or behaviour now say what the code does and why. Useful facts stay: the Porter/unaccented tokenizer, the toISOString timestamp format and why it sorts, the stop-word list's source, the lexicon validator's spec choices. Nothing under lexicons/social/ mentioned it.

rust.yml has a new first step, "the old server is named only in grain-import". It fails if git grep -il finds the name in crates, harness or rust.yml outside crates/grain-import. The pattern is written h[a]tk so the workflow file does not match itself. The step catches git grep's status, since the runner stops a step at the first failing command: 1 (no match) passes, 0 fails with the lines found, and anything else fails as a search that could not run, so a broken checkout cannot pass silently.

Why

The rule for the new stack is that it has no hatk in it. Keeping the cutover in one deletable crate means deleting that crate after the production import leaves nothing behind, and the check stops the name coming back.

How it was checked

Locally: cargo fmt --check and cargo clippy --workspace --all-targets --locked -D warnings. The new step was run by hand under sh -e, as the runner runs it, four ways: on a clean tree (passes), with the name added to crates/grain (fails and lists the line), with it added in crates/grain-import (passes), and outside a repository (fails). Its first version failed on the runner on a clean tree, because git grep's exit 1 ended the step; the fourth commit fixes that. Tests and the coverage gate are left to CI. harness/cov.mjs builds --workspace, so the new crate's tests and coverage are counted without any change.

Review

Four commits, best read one at a time. The first is a pure move that git detects as renames, with only paths changed. The second is the V5 change. The third rewrites the comments and adds the check. The fourth makes the check pass on a clean tree under the runner's shell.

🤖 Generated with Claude Code

Approved

This moves the import into crates/grain-import, removes the import subcommand from grain-serve, and makes migrate public. It also empties V5 of its data move, rewrites the old-server comments, and adds the h[a]tk grep step to rust.yml.

I found nothing that must change.

  • The import already writes notification_seen and notification_pref itself (crates/grain-import/src/lib.rs:350). Its tests cover them, so dropping the V5 data move loses no behaviour.
  • Editing V5 in place is fine as long as no index has been deployed, as the description says.
  • The binary, its integration test and the workspace and lockfile entries are consistent with the move.

One thing to check when CI runs: the grep step relies on case $? right after found=$(git grep …). If the runner runs steps under sh -e, a no-match exit of 1 would end the step with a failure before the case runs. You ran it by hand on a clean tree and it passed, so this only matters if the runner differs. The checks were still queued when I looked, so I haven't seen the step pass there.

Approved

The new commit fixes the concern from my last round. The grep step in rust.yml now runs git grep … || rc=$?, so a no-match exit of 1 can't end the step under sh -e. The case then maps 1 to pass, 0 to fail with the lines shown, and anything else to a failed search. The pattern is still bracketed as h[a]tk, so the workflow file doesn't match itself. The git config safe.directory line also guards against git refusing to search a checkout owned by another user.

My earlier approval of the crate move and the V5 change stands, and nothing in this commit touches them. The checks on cc3e40f were all still queued when I looked, so I haven't seen the step pass on the runner. Confirm that when CI finishes.

Merged as cc3e40f
Part of a stack of 2
  1. The import is its own crate, and grain's crates no longer name hatk Landed as cc3e40f
  2. main