5.4 KiB
5.4 KiB
REPORT — 2026-08-29 17:00 — Crush: mopac-imap-go (Redmine 501)
- task: new repo
ukrrs/mopac-imap-go— stdlib-only Go IMAP client (CLI + library): RFC 2177 IDLE watch, fetch+parse (headers, text body, attachments to spool), mark seen, SMTP passthrough send. EnvIMAP_HOST/IMAP_USER/IMAP_PASS. Fake-server tests. Feeds the fleet's email-driven intake. - result: DONE — 12 atomic conventional commits pushed to
origin/main(https://git.knownelement.com/ukrrs/mopac-imap-go, private, UKRRS org), HEAD66a9135, branch in sync, tests green, smoke PASS. NO Redmine writes performed.
1. What shipped
Repo created via tea (UKRRS/mopac-imap-go, private). Sibling-layout
to mopac-redmine-go at ~/projects/meta/MOPAC/mopac-imap-go:
Makefile → dev.sh → digest-pinned golang:1.26-bookworm builder
(golang@sha256:e8c859f5…) — every build/vet/test ran in Docker; the
host never ran a Go toolchain.
First commit ships full AGPLv3 LICENSE (661 lines, verbatim from the
sibling corpus). Module: git.knownelement.com/ukrrs/mopac-imap-go,
go 1.26, zero third-party dependencies.
Library packages
imap/— IMAP4rev1 client: implicit-TLS or plain dial with greeting validation; quoted-string LOGIN (password only ever inside the command, never in errors); SELECT parsing EXISTS/RECENT/UNSEEN/UIDVALIDITY; FETCH(UID FLAGS BODY.PEEK[])with a literal-inlining wire reader (payloads containing{N}\r\n- looking bytes parse correctly — pinned by test); STORE+FLAGS.SILENT (\Seen); RFC 2177 IDLE with typed events (exists/recent/expunge/tick/other), tick-driven re-idle, and a Stop that consumes the tagged DONE completion so the stream stays clean (a subtle readLoop/select race was found by-race-adjacent flake hunting and fixed: after stop, readLoop keeps reading until the tagged line). Sentinel errors: unreachable/protocol/auth/mailbox/ timeout, one-line messages.mail/— RFC 5322 + MIME parse: RFC 2047 header decoding, QP/base64 transfer decoding, recursive multipart (mixed/alternative nesting), attachments via Content-Disposition filename or Content-Type name. Non-UTF-8 charsets pass through raw (no x/text in this repo).Spool()writesSPOOL_DIR/<sha256-12>/containingraw.eml,meta.json(seq, ids, decoded headers, attachment index) andattachments/<filename>— content-hashed dirs make re-spooling idempotent across seq renumbering; hostile filenames reduced to basename (escape attempt pinned by test); 0700 dirs / 0600 files.mailer/— SMTP passthrough: raw bytes on the wire unmodified; STARTTLS when offered, AUTH PLAIN when credentials set; sentinels unreachable/auth/rejected.
CLI (mimap) + config
internal/config:IMAP_HOST/IMAP_USER/IMAP_PASSrequired (env wins over an optional 0600KEY=VALUEenv file, looser-than-0600 refused before read); defaults 993/TLS/INBOX,SPOOL_DIR=spool, SMTP set optional withSMTP_FROMfalling back to the IMAP user. Errors name keys, never values.internal/cli:watch(backlog fetch → IDLE; per message: spool → JSON line on stdout → mark\Seen, so a crash re-delivers idempotently instead of dropping; IDLE stopped before any FETCH; ticks re-SELECT so a missed EXISTS self-heals; SIGINT/SIGTERM → clean LOGOUT, exit 0),check(mailbox state as JSON),send(--torepeatable,--file PATH|-→ SMTP passthrough). Exit codes 0/1/2 (ok/usage/protocol) matching the mred convention.
2. Verification
- TDD throughout: each feature commit landed test-first against
protocol-correct fakes (
internal/fakeimapself-test pins every wire exchange incl. literal FETCH bodies and IDLE push;internal/fakesmtppins AUTH/530/dot-unstuffing). ./dev.sh check(build+vet+test) green;go test -race -count=3 ./...green after fixing two real concurrency findings (DONE- completion race in idle.go; unsynchronized test buffer); imap package hammered-count=10../dev.sh smoke: builds the realbin/mimapin the pinned builder and drives it across process boundaries against the fakes —checkreports backlog,sendrelays,watchproduces backlog + live-IDLE-push JSON lines, 2 spool dirs, 2×\Seen, exit 0 on SIGINT. Output:smoke: PASS.
3. Commits (all on origin/main)
66a9135 docs: README with CLI semantics, spool/intake contract, layout
019be0d test: end-to-end smoke of the real binary against fakes
ef2aec2 feat(cli): mimap watch/check/send over the library
d0a38b5 feat(config): env + 0600 env-file connection settings
4f5a43a feat(mailer): SMTP passthrough send with fake-server tests
2865718 feat(mail): spool messages and attachments to disk
dd1a462 feat(mail): parse RFC 5322 messages with MIME walk
444f297 feat(imap): RFC 2177 IDLE with clean Stop and re-idle
9d4ae78 feat(imap): fetch raw messages and mark seen
435fd81 feat(imap): client core — connect, login, select, logout
8552c9a test: in-process fake IMAP4rev1 server for client tests
f6332bf chore: bootstrap module (AGPLv3, docker dev harness)
4. Notes / limitations
- Charset conversion beyond UTF-8/ASCII is raw passthrough (stdlib only); documented in package doc and README.
- Fake IMAP is one-mailbox (INBOX), no EXPUNGE push; the client still handles EXPUNGE events defensively via re-SELECT.
- No Redmine API calls were made from this task, per instructions.