From 79c6d3586ed52603ee5a025ba2a28dbf1ea84375 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2026 20:39:26 +0000 Subject: [PATCH 1/5] Bump DeterminateSystems/nix-installer-action from 21 to 22 Bumps [DeterminateSystems/nix-installer-action](https://github.com/determinatesystems/nix-installer-action) from 21 to 22. - [Release notes](https://github.com/determinatesystems/nix-installer-action/releases) - [Commits](https://github.com/determinatesystems/nix-installer-action/compare/c5a866b6ab867e88becbed4467b93592bce69f8a...ef8a148080ab6020fd15196c2084a2eea5ff2d25) --- updated-dependencies: - dependency-name: DeterminateSystems/nix-installer-action dependency-version: '22' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 81bff6af..6f05c3c4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,7 +59,7 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: DeterminateSystems/nix-installer-action@c5a866b6ab867e88becbed4467b93592bce69f8a # v21 + - uses: DeterminateSystems/nix-installer-action@ef8a148080ab6020fd15196c2084a2eea5ff2d25 # v22 - name: Build run: nix build From 594197eae4958151a3cb75c46fa0cee5e3133c42 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2026 20:39:32 +0000 Subject: [PATCH 2/5] Bump actions/setup-go from 6.3.0 to 6.4.0 Bumps [actions/setup-go](https://github.com/actions/setup-go) from 6.3.0 to 6.4.0. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/4b73464bb391d4059bd26b0524d20df3927bd417...4a3601121dd01d1626a1e23e37211e3254c1c06c) --- updated-dependencies: - dependency-name: actions/setup-go dependency-version: 6.4.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 4 ++-- .github/workflows/release.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6f05c3c4..105cb104 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 + - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: go.mod @@ -35,7 +35,7 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 + - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: go.mod diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5e9508f8..c129fc79 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -93,7 +93,7 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 + - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: go.mod @@ -149,7 +149,7 @@ jobs: steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 + - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 with: go-version-file: go.mod From 5f92da2b83b27c40fb45493c7497d6bb46fa2d17 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Mar 2026 20:39:34 +0000 Subject: [PATCH 3/5] Bump the minor-and-patch group with 2 updates Bumps the minor-and-patch group with 2 updates: [github.com/mark3labs/mcp-go](https://github.com/mark3labs/mcp-go) and [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3). Updates `github.com/mark3labs/mcp-go` from 0.45.0 to 0.46.0 - [Release notes](https://github.com/mark3labs/mcp-go/releases) - [Commits](https://github.com/mark3labs/mcp-go/compare/v0.45.0...v0.46.0) Updates `github.com/mattn/go-sqlite3` from 1.14.37 to 1.14.38 - [Release notes](https://github.com/mattn/go-sqlite3/releases) - [Commits](https://github.com/mattn/go-sqlite3/compare/v1.14.37...v1.14.38) --- updated-dependencies: - dependency-name: github.com/mark3labs/mcp-go dependency-version: 0.46.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-and-patch - dependency-name: github.com/mattn/go-sqlite3 dependency-version: 1.14.38 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: minor-and-patch ... Signed-off-by: dependabot[bot] --- go.mod | 11 +++-------- go.sum | 23 ++++++----------------- 2 files changed, 9 insertions(+), 25 deletions(-) diff --git a/go.mod b/go.mod index 1a61d283..99258ee7 100644 --- a/go.mod +++ b/go.mod @@ -15,10 +15,10 @@ require ( github.com/google/go-cmp v0.7.0 github.com/jhillyerd/enmime v1.3.0 github.com/marcboeker/go-duckdb v1.8.5 - github.com/mark3labs/mcp-go v0.45.0 + github.com/mark3labs/mcp-go v0.46.0 github.com/mattn/go-isatty v0.0.20 github.com/mattn/go-runewidth v0.0.21 - github.com/mattn/go-sqlite3 v1.14.37 + github.com/mattn/go-sqlite3 v1.14.38 github.com/muesli/termenv v0.16.0 github.com/robfig/cron/v3 v3.0.1 github.com/spf13/cobra v1.10.2 @@ -35,8 +35,6 @@ require ( github.com/apache/arrow-go/v18 v18.1.0 // indirect github.com/atotto/clipboard v0.1.4 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect - github.com/bahlo/generic-list-go v0.2.0 // indirect - github.com/buger/jsonparser v1.1.1 // indirect github.com/catppuccin/go v0.3.0 // indirect github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect github.com/charmbracelet/colorprofile v0.4.1 // indirect @@ -53,14 +51,13 @@ require ( github.com/go-viper/mapstructure/v2 v2.3.0 // indirect github.com/goccy/go-json v0.10.5 // indirect github.com/google/flatbuffers v25.1.24+incompatible // indirect + github.com/google/jsonschema-go v0.4.2 // indirect github.com/google/uuid v1.6.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/invopop/jsonschema v0.13.0 // indirect github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 // indirect github.com/klauspost/compress v1.17.11 // indirect github.com/klauspost/cpuid/v2 v2.2.9 // indirect github.com/lucasb-eyer/go-colorful v1.3.0 // indirect - github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-localereader v0.0.1 // indirect github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect @@ -72,7 +69,6 @@ require ( github.com/spf13/cast v1.7.1 // indirect github.com/spf13/pflag v1.0.9 // indirect github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect - github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/yosida95/uritemplate/v3 v3.0.2 // indirect github.com/zeebo/xxh3 v1.0.2 // indirect @@ -81,5 +77,4 @@ require ( golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4 // indirect golang.org/x/tools v0.42.0 // indirect golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index f2ecadde..3754b6ef 100644 --- a/go.sum +++ b/go.sum @@ -16,10 +16,6 @@ github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiE github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/aymanbagabas/go-udiff v0.3.1 h1:LV+qyBQ2pqe0u42ZsUEtPiCaUoqgA9gYRDs3vj1nolY= github.com/aymanbagabas/go-udiff v0.3.1/go.mod h1:G0fsKmG+P6ylD0r6N/KgQD/nWzgfnl8ZBcNLgcbrw8E= -github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= -github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= -github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= -github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/catppuccin/go v0.3.0 h1:d+0/YicIq+hSTo5oPuRi5kOpqkVA5tAsU6dNhvRu+aY= github.com/catppuccin/go v0.3.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc= github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a h1:MISbI8sU/PSK/ztvmWKFcI7UGb5/HQT7B+i3a2myKgI= @@ -91,17 +87,16 @@ github.com/google/flatbuffers v25.1.24+incompatible h1:4wPqL3K7GzBd1CwyhSd3usxLK github.com/google/flatbuffers v25.1.24+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/jsonschema-go v0.4.2 h1:tmrUohrwoLZZS/P3x7ex0WAVknEkBZM46iALbcqoRA8= +github.com/google/jsonschema-go v0.4.2/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E= -github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0= github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 h1:iCHtR9CQyktQ5+f3dMVZfwD2KWJUgm7M0gdL9NGr8KA= github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk= github.com/jhillyerd/enmime v1.3.0 h1:LV5kzfLidiOr8qRGIpYYmUZCnhrPbcFAnAFUnWn99rw= github.com/jhillyerd/enmime v1.3.0/go.mod h1:6c6jg5HdRRV2FtvVL69LjiX1M8oE0xDX9VEhV3oy4gs= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/klauspost/asmfmt v1.3.2 h1:4Ri7ox3EwapiOjCki+hw14RyKk201CN4rzyCJRFLpK4= github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= @@ -114,12 +109,10 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag= github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/marcboeker/go-duckdb v1.8.5 h1:tkYp+TANippy0DaIOP5OEfBEwbUINqiFqgwMQ44jME0= github.com/marcboeker/go-duckdb v1.8.5/go.mod h1:6mK7+WQE4P4u5AFLvVBmhFxY5fvhymFptghgJX6B+/8= -github.com/mark3labs/mcp-go v0.45.0 h1:s0S8qR/9fWaQ3pHxz7pm1uQ0DrswoSnRIxKIjbiQtkc= -github.com/mark3labs/mcp-go v0.45.0/go.mod h1:YnJfOL382MIWDx1kMY+2zsRHU/q78dBg9aFb8W6Thdw= +github.com/mark3labs/mcp-go v0.46.0 h1:8KRibF4wcKejbLsHxCA/QBVUr5fQ9nwz/n8lGqmaALo= +github.com/mark3labs/mcp-go v0.46.0/go.mod h1:JKTC7R2LLVagkEWK7Kwu7DbmA6iIvnNAod6yrHiQMag= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= @@ -127,8 +120,8 @@ github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+Ei github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.21 h1:jJKAZiQH+2mIinzCJIaIG9Be1+0NR+5sz/lYEEjdM8w= github.com/mattn/go-runewidth v0.0.21/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= -github.com/mattn/go-sqlite3 v1.14.37 h1:3DOZp4cXis1cUIpCfXLtmlGolNLp2VEqhiB/PARNBIg= -github.com/mattn/go-sqlite3 v1.14.37/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/mattn/go-sqlite3 v1.14.38 h1:tDUzL85kMvOrvpCt8P64SbGgVFtJB11GPi2AdmITgb4= +github.com/mattn/go-sqlite3 v1.14.38/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 h1:AMFGa4R4MiIpspGNG7Z948v4n35fFGB3RR3G/ry4FWs= github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 h1:+n/aFZefKZp7spd8DFdX7uMikMLXX4oubIzJF4kv/wI= @@ -166,8 +159,6 @@ github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cma github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc= -github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4= @@ -235,7 +226,5 @@ golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6f gonum.org/v1/gonum v0.15.1 h1:FNy7N6OUZVUaWG9pTiD+jlhdQ3lMP+/LcTpJ6+a8sQ0= gonum.org/v1/gonum v0.15.1/go.mod h1:eZTZuRFrzu5pcyjN5wJhcIhnUdNijYxX1T2IcrOGY0o= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 0aea3fc3cc92b55a35b8967015ec598f7f838ea7 Mon Sep 17 00:00:00 2001 From: Wes McKinney Date: Mon, 30 Mar 2026 22:45:00 -0500 Subject: [PATCH 4/5] fix: resolve all golangci-lint v2 errcheck and staticcheck warnings golangci-lint v2 enables errcheck by default, surfacing 343 unchecked error return values and style issues across the codebase. Co-Authored-By: Claude Opus 4.6 (1M context) --- cmd/msgvault/cmd/addaccount.go | 2 +- cmd/msgvault/cmd/addimap.go | 2 +- cmd/msgvault/cmd/build_cache.go | 22 ++--- cmd/msgvault/cmd/build_cache_test.go | 88 +++++++++---------- cmd/msgvault/cmd/deletions.go | 4 +- cmd/msgvault/cmd/export_attachment.go | 8 +- cmd/msgvault/cmd/export_attachment_test.go | 12 +-- cmd/msgvault/cmd/export_attachments.go | 2 +- cmd/msgvault/cmd/export_attachments_test.go | 2 +- cmd/msgvault/cmd/export_eml.go | 2 +- cmd/msgvault/cmd/export_token.go | 22 ++--- cmd/msgvault/cmd/import_emlx.go | 46 +++++----- cmd/msgvault/cmd/import_mbox.go | 28 +++--- cmd/msgvault/cmd/initdb.go | 2 +- cmd/msgvault/cmd/list_accounts.go | 16 ++-- cmd/msgvault/cmd/list_domains.go | 2 +- cmd/msgvault/cmd/list_labels.go | 2 +- cmd/msgvault/cmd/list_senders.go | 2 +- cmd/msgvault/cmd/mcp.go | 4 +- cmd/msgvault/cmd/repair_encoding.go | 22 ++--- cmd/msgvault/cmd/search.go | 20 ++--- cmd/msgvault/cmd/serve.go | 6 +- cmd/msgvault/cmd/setup.go | 4 +- cmd/msgvault/cmd/show_message.go | 4 +- cmd/msgvault/cmd/stats.go | 2 +- cmd/msgvault/cmd/store_resolver.go | 6 +- cmd/msgvault/cmd/sync.go | 4 +- cmd/msgvault/cmd/syncfull.go | 4 +- cmd/msgvault/cmd/tui.go | 8 +- cmd/msgvault/cmd/update_account.go | 2 +- cmd/msgvault/cmd/verify.go | 6 +- internal/api/handlers.go | 10 +-- internal/api/handlers_test.go | 8 +- internal/applemail/accounts.go | 4 +- internal/applemail/accounts_test.go | 2 +- internal/config/config.go | 8 +- internal/config/config_test.go | 16 ++-- internal/export/attachments.go | 10 +-- internal/export/attachments_test.go | 10 +-- internal/export/store_attachment.go | 2 +- internal/fileutil/secure_test.go | 4 +- internal/gmail/client.go | 2 +- internal/importer/emlx_import_test.go | 2 +- internal/importer/ingest_test.go | 2 +- internal/importer/mbox_import.go | 6 +- internal/importer/mboxzip/mbox_zip.go | 14 +-- internal/mcp/handlers.go | 6 +- internal/oauth/oauth.go | 18 ++-- internal/query/duckdb.go | 16 ++-- internal/query/duckdb_test.go | 16 ++-- internal/query/shared.go | 12 +-- internal/query/sqlite.go | 10 +-- internal/query/testfixtures_test.go | 12 +-- .../query/testfixtures_validation_test.go | 4 +- internal/remote/engine.go | 12 +-- internal/remote/store.go | 10 +-- internal/remote/store_test.go | 4 +- internal/store/api.go | 16 ++-- internal/store/api_test.go | 2 +- internal/store/inspect.go | 4 +- internal/store/messages.go | 4 +- internal/store/store.go | 6 +- internal/store/sync.go | 2 +- internal/sync/testenv_test.go | 4 +- internal/testutil/archive_helpers.go | 10 +-- internal/testutil/dbtest/dbtest.go | 2 +- internal/testutil/dbtest/dbtest_test.go | 2 +- internal/testutil/email/builder_test.go | 2 +- internal/testutil/store_helpers.go | 2 +- internal/tui/actions_test.go | 4 +- internal/tui/view.go | 12 +-- internal/update/update.go | 36 ++++---- scripts/mimeshootout/main.go | 8 +- 73 files changed, 346 insertions(+), 346 deletions(-) diff --git a/cmd/msgvault/cmd/addaccount.go b/cmd/msgvault/cmd/addaccount.go index 95ab5588..ebde2eff 100644 --- a/cmd/msgvault/cmd/addaccount.go +++ b/cmd/msgvault/cmd/addaccount.go @@ -75,7 +75,7 @@ Examples: if err != nil { return fmt.Errorf("open database: %w", err) } - defer s.Close() + defer func() { _ = s.Close() }() if err := s.InitSchema(); err != nil { return fmt.Errorf("init schema: %w", err) diff --git a/cmd/msgvault/cmd/addimap.go b/cmd/msgvault/cmd/addimap.go index 910076b0..3b1d3522 100644 --- a/cmd/msgvault/cmd/addimap.go +++ b/cmd/msgvault/cmd/addimap.go @@ -139,7 +139,7 @@ Examples: if err != nil { return fmt.Errorf("open database: %w", err) } - defer s.Close() + defer func() { _ = s.Close() }() if err := s.InitSchema(); err != nil { return fmt.Errorf("init schema: %w", err) diff --git a/cmd/msgvault/cmd/build_cache.go b/cmd/msgvault/cmd/build_cache.go index 3006b85c..49f76703 100644 --- a/cmd/msgvault/cmd/build_cache.go +++ b/cmd/msgvault/cmd/build_cache.go @@ -177,7 +177,7 @@ func buildCache(dbPath, analyticsDir string, fullRebuild bool) (*buildResult, er if err != nil { return nil, fmt.Errorf("open duckdb: %w", err) } - defer db.Close() + defer func() { _ = db.Close() }() // Set up sqlite_db tables β€” either via DuckDB's sqlite extension (Linux/macOS) // or via CSV intermediate files (Windows, where sqlite_scanner is unavailable). @@ -511,7 +511,7 @@ var cacheStatsCmd = &cobra.Command{ if err != nil { return fmt.Errorf("open duckdb: %w", err) } - defer db.Close() + defer func() { _ = db.Close() }() // Query stats by joining Parquet files escapedDir := strings.ReplaceAll(analyticsDir, "'", "''") @@ -625,7 +625,7 @@ func setupSQLiteSource(duckDB *sql.DB, dbPath string) (cleanup func(), err error sqliteDB, err := sql.Open("sqlite3", dbPath+"?mode=ro") if err != nil { - os.RemoveAll(tmpDir) + _ = os.RemoveAll(tmpDir) return nil, fmt.Errorf("open sqlite for CSV export: %w", err) } @@ -650,17 +650,17 @@ func setupSQLiteSource(duckDB *sql.DB, dbPath string) (cleanup func(), err error for _, t := range tables { csvPath := filepath.Join(tmpDir, t.name+".csv") if err := exportToCSV(sqliteDB, t.query, csvPath); err != nil { - sqliteDB.Close() - os.RemoveAll(tmpDir) + _ = sqliteDB.Close() + _ = os.RemoveAll(tmpDir) return nil, fmt.Errorf("export %s to CSV: %w", t.name, err) } } - sqliteDB.Close() + _ = sqliteDB.Close() // Create sqlite_db schema with views pointing to CSV files. // This lets the existing COPY queries reference sqlite_db.tablename unchanged. if _, err := duckDB.Exec("CREATE SCHEMA sqlite_db"); err != nil { - os.RemoveAll(tmpDir) + _ = os.RemoveAll(tmpDir) return nil, fmt.Errorf("create sqlite_db schema: %w", err) } for _, t := range tables { @@ -677,12 +677,12 @@ func setupSQLiteSource(duckDB *sql.DB, dbPath string) (cleanup func(), err error t.name, escaped, csvOpts, ) if _, err := duckDB.Exec(viewSQL); err != nil { - os.RemoveAll(tmpDir) + _ = os.RemoveAll(tmpDir) return nil, fmt.Errorf("create view sqlite_db.%s: %w", t.name, err) } } - return func() { os.RemoveAll(tmpDir) }, nil + return func() { _ = os.RemoveAll(tmpDir) }, nil } // csvNullStr is written for NULL values in CSV exports so DuckDB can @@ -696,13 +696,13 @@ func exportToCSV(db *sql.DB, query string, dest string) error { if err != nil { return err } - defer rows.Close() + defer func() { _ = rows.Close() }() f, err := os.Create(dest) if err != nil { return err } - defer f.Close() + defer func() { _ = f.Close() }() w := csv.NewWriter(f) diff --git a/cmd/msgvault/cmd/build_cache_test.go b/cmd/msgvault/cmd/build_cache_test.go index a139cd8e..2369f81c 100644 --- a/cmd/msgvault/cmd/build_cache_test.go +++ b/cmd/msgvault/cmd/build_cache_test.go @@ -26,10 +26,10 @@ func setupTestSQLite(t *testing.T) (string, func()) { dbPath := filepath.Join(tmpDir, "test.db") db, err := sql.Open("sqlite3", dbPath) if err != nil { - os.RemoveAll(tmpDir) + _ = os.RemoveAll(tmpDir) t.Fatalf("open sqlite: %v", err) } - defer db.Close() + defer func() { _ = db.Close() }() // Create schema schema := ` @@ -102,7 +102,7 @@ func setupTestSQLite(t *testing.T) (string, func()) { ` if _, err := db.Exec(schema); err != nil { - os.RemoveAll(tmpDir) + _ = os.RemoveAll(tmpDir) t.Fatalf("create schema: %v", err) } @@ -179,12 +179,12 @@ func setupTestSQLite(t *testing.T) (string, func()) { ` if _, err := db.Exec(testData); err != nil { - os.RemoveAll(tmpDir) + _ = os.RemoveAll(tmpDir) t.Fatalf("insert test data: %v", err) } cleanup := func() { - os.RemoveAll(tmpDir) + _ = os.RemoveAll(tmpDir) } return tmpDir, cleanup @@ -264,7 +264,7 @@ func TestBuildCache_DataIntegrity(t *testing.T) { if err != nil { t.Fatalf("open duckdb: %v", err) } - defer db.Close() + defer func() { _ = db.Close() }() // Helper to count rows in a Parquet file countRows := func(pattern string) int64 { @@ -370,7 +370,7 @@ func TestBuildCache_IncrementalExport(t *testing.T) { INSERT INTO attachments (message_id, filename, mime_type, size) VALUES (7, 'notes.txt', 'text/plain', 500); `) - db.Close() + _ = db.Close() if err != nil { t.Fatalf("insert new messages: %v", err) } @@ -395,7 +395,7 @@ func TestBuildCache_IncrementalExport(t *testing.T) { if err != nil { t.Fatalf("open duckdb: %v", err) } - defer duckdb.Close() + defer func() { _ = duckdb.Close() }() countRows := func(pattern string) int64 { var count int64 @@ -530,7 +530,7 @@ func TestBuildCache_BackfillsMissingConversations(t *testing.T) { if err != nil { t.Fatalf("open duckdb: %v", err) } - defer duckdb.Close() + defer func() { _ = duckdb.Close() }() var count int64 q := "SELECT COUNT(*) FROM read_parquet('" + filepath.Join(conversationsDir, "*.parquet") + "')" @@ -588,7 +588,7 @@ func TestBuildCache_BackfillAfterIncrementalNoDuplicates(t *testing.T) { (7, 1, 'to', 'Alice Smith'); INSERT INTO message_labels (message_id, label_id) VALUES (6, 1), (7, 1); `) - sqliteDB.Close() + _ = sqliteDB.Close() if err != nil { t.Fatalf("insert incremental data: %v", err) } @@ -622,7 +622,7 @@ func TestBuildCache_BackfillAfterIncrementalNoDuplicates(t *testing.T) { if err != nil { t.Fatalf("open duckdb: %v", err) } - defer duckdb.Close() + defer func() { _ = duckdb.Close() }() countRows := func(pattern string) int64 { var count int64 @@ -689,7 +689,7 @@ func TestBuildCache_BackfillWithNewMessages(t *testing.T) { (6, 1, 'from', 'Alice Smith'), (6, 2, 'to', 'Bob Jones'); `) - sqliteDB.Close() + _ = sqliteDB.Close() if err != nil { t.Fatalf("insert new data: %v", err) } @@ -709,7 +709,7 @@ func TestBuildCache_BackfillWithNewMessages(t *testing.T) { if err != nil { t.Fatalf("open duckdb: %v", err) } - defer duckdb.Close() + defer func() { _ = duckdb.Close() }() var count int64 q := "SELECT COUNT(*) FROM read_parquet('" + filepath.ToSlash(filepath.Join(recipientsDir, "*.parquet")) + "')" @@ -779,7 +779,7 @@ func TestBuildCache_BackfillMissingMessages(t *testing.T) { if err != nil { t.Fatalf("open duckdb: %v", err) } - defer duckdb.Close() + defer func() { _ = duckdb.Close() }() var count int64 q := "SELECT COUNT(*) FROM read_parquet('" + filepath.ToSlash(filepath.Join(messagesDir, "**", "*.parquet")) + "', hive_partitioning=true)" @@ -843,7 +843,7 @@ func TestBuildCache_DeletedMessagesIncluded(t *testing.T) { t.Fatalf("open sqlite: %v", err) } _, err = db.Exec("UPDATE messages SET deleted_from_source_at = '2024-06-01 12:00:00' WHERE id = 3") - db.Close() + _ = db.Close() if err != nil { t.Fatalf("mark deleted: %v", err) } @@ -861,7 +861,7 @@ func TestBuildCache_DeletedMessagesIncluded(t *testing.T) { // Verify deleted_from_source_at is preserved duckdb, _ := sql.Open("duckdb", "") - defer duckdb.Close() + defer func() { _ = duckdb.Close() }() var deletedCount int64 query := "SELECT COUNT(*) FROM read_parquet('" + filepath.Join(analyticsDir, "messages", "**", "*.parquet") + "') WHERE deleted_from_source_at IS NOT NULL" @@ -891,7 +891,7 @@ func TestBuildCache_MessagesWithoutSentAt(t *testing.T) { INSERT INTO messages (id, source_id, source_message_id, subject, snippet, size_estimate) VALUES (6, 1, 'msg6', 'No Date', 'Preview', 100) `) - db.Close() + _ = db.Close() if err != nil { t.Fatalf("insert: %v", err) } @@ -925,7 +925,7 @@ func TestBuildCache_EndToEndWithQueryEngine(t *testing.T) { if err != nil { t.Fatalf("open duckdb: %v", err) } - defer db.Close() + defer func() { _ = db.Close() }() // Build the CTEs like the query engine does ctes := ` @@ -959,7 +959,7 @@ func TestBuildCache_EndToEndWithQueryEngine(t *testing.T) { _ = rows.Scan(&email, &count) senderCounts[email] = count } - rows.Close() + _ = rows.Close() if senderCounts["alice@example.com"] != 3 { t.Errorf("expected alice sent 3 messages, got %d", senderCounts["alice@example.com"]) @@ -989,7 +989,7 @@ func TestBuildCache_EndToEndWithQueryEngine(t *testing.T) { _ = rows.Scan(&name, &count) labelCounts[name] = count } - rows.Close() + _ = rows.Close() if labelCounts["INBOX"] != 5 { t.Errorf("expected INBOX has 5 messages, got %d", labelCounts["INBOX"]) @@ -1045,7 +1045,7 @@ func TestBuildCache_YearPartitioning(t *testing.T) { (6, 1, 'msg6', 'Old Message', '2020-06-15 10:00:00', 100), (7, 1, 'msg7', 'Recent Message', '2025-01-15 10:00:00', 100); `) - db.Close() + _ = db.Close() if err != nil { t.Fatalf("insert: %v", err) } @@ -1083,7 +1083,7 @@ func TestBuildCache_UTF8Handling(t *testing.T) { UPDATE messages SET subject = 'Test Γ©moji πŸŽ‰ and unicode' WHERE id = 1; UPDATE participants SET display_name = 'MΓΌller' WHERE id = 1; `) - db.Close() + _ = db.Close() if err != nil { t.Fatalf("update: %v", err) } @@ -1100,7 +1100,7 @@ func TestBuildCache_UTF8Handling(t *testing.T) { // Verify data is readable duckdb, _ := sql.Open("duckdb", "") - defer duckdb.Close() + defer func() { _ = duckdb.Close() }() var subject string query := "SELECT subject FROM read_parquet('" + filepath.Join(analyticsDir, "messages", "**", "*.parquet") + "') WHERE id = 1" @@ -1119,7 +1119,7 @@ func TestBuildCache_EmptyDatabase(t *testing.T) { if err != nil { t.Fatalf("create temp dir: %v", err) } - defer os.RemoveAll(tmpDir) + defer func() { _ = os.RemoveAll(tmpDir) }() dbPath := filepath.Join(tmpDir, "empty.db") analyticsDir := filepath.Join(tmpDir, "analytics") @@ -1136,7 +1136,7 @@ func TestBuildCache_EmptyDatabase(t *testing.T) { CREATE TABLE attachments (message_id INTEGER, size INTEGER, filename TEXT); CREATE TABLE conversations (id INTEGER PRIMARY KEY, source_conversation_id TEXT); `) - db.Close() + _ = db.Close() result, err := buildCache(dbPath, analyticsDir, false) if err != nil { @@ -1187,18 +1187,18 @@ func TestCSVFallbackPath(t *testing.T) { for _, tbl := range tables { csvPath := filepath.Join(csvDir, tbl.name+".csv") if err := exportToCSV(sqliteDB, tbl.query, csvPath); err != nil { - sqliteDB.Close() + _ = sqliteDB.Close() t.Fatalf("exportToCSV %s: %v", tbl.name, err) } } - sqliteDB.Close() + _ = sqliteDB.Close() // 2. Open DuckDB and create views (same as setupSQLiteSource) duckDB, err := sql.Open("duckdb", "") if err != nil { t.Fatalf("open duckdb: %v", err) } - defer duckDB.Close() + defer func() { _ = duckDB.Close() }() if _, err := duckDB.Exec("CREATE SCHEMA sqlite_db"); err != nil { t.Fatalf("create schema: %v", err) @@ -1318,7 +1318,7 @@ func BenchmarkBuildCache(b *testing.B) { if err != nil { b.Fatalf("create temp dir: %v", err) } - defer os.RemoveAll(tmpDir) + defer func() { _ = os.RemoveAll(tmpDir) }() dbPath := filepath.Join(tmpDir, "bench.db") analyticsDir := filepath.Join(tmpDir, "analytics") @@ -1369,12 +1369,12 @@ func BenchmarkBuildCache(b *testing.B) { _, _ = db.Exec("INSERT INTO message_labels VALUES (?, 2)", i) } } - db.Close() + _ = db.Close() b.ResetTimer() for i := 0; i < b.N; i++ { // Clear analytics dir between runs - os.RemoveAll(analyticsDir) + _ = os.RemoveAll(analyticsDir) if _, err := buildCache(dbPath, analyticsDir, true); err != nil { b.Fatalf("buildCache: %v", err) } @@ -1395,10 +1395,10 @@ func setupTestSQLiteEmpty(t *testing.T) (string, func()) { dbPath := filepath.Join(tmpDir, "test.db") db, err := sql.Open("sqlite3", dbPath) if err != nil { - os.RemoveAll(tmpDir) + _ = os.RemoveAll(tmpDir) t.Fatalf("open sqlite: %v", err) } - defer db.Close() + defer func() { _ = db.Close() }() schema := ` CREATE TABLE sources ( @@ -1462,7 +1462,7 @@ func setupTestSQLiteEmpty(t *testing.T) (string, func()) { ); ` if _, err := db.Exec(schema); err != nil { - os.RemoveAll(tmpDir) + _ = os.RemoveAll(tmpDir) t.Fatalf("create schema: %v", err) } @@ -1473,11 +1473,11 @@ func setupTestSQLiteEmpty(t *testing.T) (string, func()) { INSERT INTO labels (id, source_id, name) VALUES (1, 1, 'INBOX'); ` if _, err := db.Exec(metadata); err != nil { - os.RemoveAll(tmpDir) + _ = os.RemoveAll(tmpDir) t.Fatalf("insert metadata: %v", err) } - return tmpDir, func() { os.RemoveAll(tmpDir) } + return tmpDir, func() { _ = os.RemoveAll(tmpDir) } } // TestBuildCache_ZeroMessagesNoRepeatedRebuilds verifies that when the DB has @@ -1604,7 +1604,7 @@ func TestCacheNeedsBuild(t *testing.T) { if err != nil { t.Fatalf("open db: %v", err) } - defer db.Close() + defer func() { _ = db.Close() }() _, err = db.Exec(`INSERT INTO messages (id, source_id, source_message_id, sent_at) VALUES (10, 1, 'msg10', datetime('now'))`) if err != nil { t.Fatalf("insert message: %v", err) @@ -1623,7 +1623,7 @@ func TestCacheNeedsBuild(t *testing.T) { if err != nil { t.Fatalf("open db: %v", err) } - defer db.Close() + defer func() { _ = db.Close() }() _, err = db.Exec(`INSERT INTO messages (id, source_id, source_message_id, sent_at) VALUES (10, 1, 'msg10', datetime('now'))`) if err != nil { t.Fatalf("insert message: %v", err) @@ -1641,7 +1641,7 @@ func TestCacheNeedsBuild(t *testing.T) { if err != nil { t.Fatalf("open db: %v", err) } - defer db.Close() + defer func() { _ = db.Close() }() _, err = db.Exec(`INSERT INTO messages (id, source_id, source_message_id, sent_at) VALUES (5, 1, 'msg5', datetime('now'))`) if err != nil { t.Fatalf("insert message: %v", err) @@ -1660,7 +1660,7 @@ func TestCacheNeedsBuild(t *testing.T) { if err != nil { t.Fatalf("open db: %v", err) } - defer db.Close() + defer func() { _ = db.Close() }() _, err = db.Exec(`INSERT INTO messages (id, source_id, source_message_id, sent_at, deleted_from_source_at) VALUES (10, 1, 'msg10', datetime('now'), datetime('now'))`) if err != nil { t.Fatalf("insert message: %v", err) @@ -1688,7 +1688,7 @@ func TestCacheNeedsBuild(t *testing.T) { name: "DBOpenFailure_NeedsBuild", setup: func(t *testing.T, dbPath, analyticsDir string) { // Replace DB file with a directory so store.Open fails - os.Remove(dbPath) + _ = os.Remove(dbPath) if err := os.MkdirAll(dbPath, 0755); err != nil { t.Fatalf("MkdirAll: %v", err) } @@ -1706,7 +1706,7 @@ func TestCacheNeedsBuild(t *testing.T) { if err != nil { t.Fatalf("open db: %v", err) } - defer db.Close() + defer func() { _ = db.Close() }() _, err = db.Exec(`INSERT INTO messages (id, source_id, source_message_id, sent_at) VALUES (5, 1, 'msg5', datetime('now'))`) if err != nil { t.Fatalf("insert message: %v", err) @@ -1945,7 +1945,7 @@ func BenchmarkBuildCacheIncremental(b *testing.B) { if err != nil { b.Fatalf("create temp dir: %v", err) } - defer os.RemoveAll(tmpDir) + defer func() { _ = os.RemoveAll(tmpDir) }() dbPath := filepath.Join(tmpDir, "bench.db") analyticsDir := filepath.Join(tmpDir, "analytics") @@ -1995,7 +1995,7 @@ func BenchmarkBuildCacheIncremental(b *testing.B) { _, _ = db.Exec("INSERT INTO message_recipients VALUES (?, 2, 'to', NULL)", i) _, _ = db.Exec("INSERT INTO message_labels VALUES (?, 1)", i) } - db.Close() + _ = db.Close() b.ResetTimer() for i := 0; i < b.N; i++ { diff --git a/cmd/msgvault/cmd/deletions.go b/cmd/msgvault/cmd/deletions.go index cd5135dc..47e18302 100644 --- a/cmd/msgvault/cmd/deletions.go +++ b/cmd/msgvault/cmd/deletions.go @@ -351,7 +351,7 @@ Examples: if err != nil { return fmt.Errorf("open database: %w", err) } - defer s.Close() + defer func() { _ = s.Close() }() // Ensure schema is up to date (creates new indexes, etc.) if err := s.InitSchema(); err != nil { @@ -438,7 +438,7 @@ Examples: gmail.WithLogger(logger), gmail.WithRateLimiter(rateLimiter), ) - defer client.Close() + defer func() { _ = client.Close() }() // Create executor executor := deletion.NewExecutor(manager, s, client). diff --git a/cmd/msgvault/cmd/export_attachment.go b/cmd/msgvault/cmd/export_attachment.go index fbd38b30..a156e291 100644 --- a/cmd/msgvault/cmd/export_attachment.go +++ b/cmd/msgvault/cmd/export_attachment.go @@ -108,7 +108,7 @@ func exportAttachmentAsBase64(storagePath string) error { if err != nil { return err } - defer f.Close() + defer func() { _ = f.Close() }() encoder := base64.NewEncoder(base64.StdEncoding, os.Stdout) if _, err := io.Copy(encoder, f); err != nil { @@ -126,7 +126,7 @@ func exportAttachmentBinary(storagePath, contentHash string) error { if err != nil { return err } - defer f.Close() + defer func() { _ = f.Close() }() outputPath := exportAttachmentOutput if outputPath == "" || outputPath == "-" { @@ -142,11 +142,11 @@ func exportAttachmentBinary(storagePath, contentHash string) error { n, copyErr := io.Copy(dst, f) closeErr := dst.Close() if copyErr != nil { - os.Remove(outputPath) + _ = os.Remove(outputPath) return fmt.Errorf("write file: %w", copyErr) } if closeErr != nil { - os.Remove(outputPath) + _ = os.Remove(outputPath) return fmt.Errorf("close file: %w", closeErr) } diff --git a/cmd/msgvault/cmd/export_attachment_test.go b/cmd/msgvault/cmd/export_attachment_test.go index e6fc2e1c..8b85819e 100644 --- a/cmd/msgvault/cmd/export_attachment_test.go +++ b/cmd/msgvault/cmd/export_attachment_test.go @@ -26,15 +26,15 @@ func setupTestAttachment(t *testing.T) (string, string, []byte, func()) { subDir := filepath.Join(tmpDir, contentHash[:2]) if err := os.MkdirAll(subDir, 0755); err != nil { - os.RemoveAll(tmpDir) + _ = os.RemoveAll(tmpDir) t.Fatalf("create subdir: %v", err) } if err := os.WriteFile(filepath.Join(subDir, contentHash), data, 0600); err != nil { - os.RemoveAll(tmpDir) + _ = os.RemoveAll(tmpDir) t.Fatalf("write test file: %v", err) } - return tmpDir, contentHash, data, func() { os.RemoveAll(tmpDir) } + return tmpDir, contentHash, data, func() { _ = os.RemoveAll(tmpDir) } } func TestExportAttachment_BinaryToFile(t *testing.T) { @@ -83,7 +83,7 @@ func TestExportAttachment_JSONOutput(t *testing.T) { os.Stdout = w err := exportAttachmentAsJSON(storagePath, contentHash) - w.Close() + _ = w.Close() os.Stdout = oldStdout if err != nil { @@ -123,7 +123,7 @@ func TestExportAttachment_Base64Output(t *testing.T) { os.Stdout = w err := exportAttachmentAsBase64(storagePath) - w.Close() + _ = w.Close() os.Stdout = oldStdout if err != nil { @@ -145,7 +145,7 @@ func TestExportAttachment_MissingFile(t *testing.T) { if err != nil { t.Fatalf("create temp dir: %v", err) } - defer os.RemoveAll(tmpDir) + defer func() { _ = os.RemoveAll(tmpDir) }() hash := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" storagePath := filepath.Join(tmpDir, hash[:2], hash) diff --git a/cmd/msgvault/cmd/export_attachments.go b/cmd/msgvault/cmd/export_attachments.go index 272b4431..1482c9fc 100644 --- a/cmd/msgvault/cmd/export_attachments.go +++ b/cmd/msgvault/cmd/export_attachments.go @@ -40,7 +40,7 @@ func runExportAttachments(cmd *cobra.Command, args []string) error { if err != nil { return fmt.Errorf("open database: %w", err) } - defer s.Close() + defer func() { _ = s.Close() }() engine := query.NewSQLiteEngine(s.DB()) diff --git a/cmd/msgvault/cmd/export_attachments_test.go b/cmd/msgvault/cmd/export_attachments_test.go index 201d199b..d2201b3a 100644 --- a/cmd/msgvault/cmd/export_attachments_test.go +++ b/cmd/msgvault/cmd/export_attachments_test.go @@ -68,7 +68,7 @@ func setupExportAttachmentsTest(t *testing.T) (dataDir string, msgID int64) { createTestAttachment(t, db, attDir, 1, 1, "report.pdf", []byte("PDF content here")) createTestAttachment(t, db, attDir, 2, 1, "photo.jpg", []byte("JPEG image data")) - s.Close() + _ = s.Close() return dataDir, 1 } diff --git a/cmd/msgvault/cmd/export_eml.go b/cmd/msgvault/cmd/export_eml.go index ff1a08be..a76ae43e 100644 --- a/cmd/msgvault/cmd/export_eml.go +++ b/cmd/msgvault/cmd/export_eml.go @@ -89,7 +89,7 @@ func runExportEML(cmd *cobra.Command, messageRef, outputPath string) error { if err != nil { return fmt.Errorf("open database: %w", err) } - defer s.Close() + defer func() { _ = s.Close() }() engine := query.NewSQLiteEngine(s.DB()) diff --git a/cmd/msgvault/cmd/export_token.go b/cmd/msgvault/cmd/export_token.go index 2b60fde4..a045b28d 100644 --- a/cmd/msgvault/cmd/export_token.go +++ b/cmd/msgvault/cmd/export_token.go @@ -115,14 +115,14 @@ func (e *tokenExporter) export( baseURL := strings.TrimSuffix(remoteURL, "/") // Upload token - fmt.Fprintf(e.stdout, "Uploading token to %s...\n", remoteURL) + _, _ = fmt.Fprintf(e.stdout, "Uploading token to %s...\n", remoteURL) if parsedURL.Scheme == "http" { - fmt.Fprintf(e.stderr, "WARNING: Sending credentials over insecure HTTP connection\n") + _, _ = fmt.Fprintf(e.stderr, "WARNING: Sending credentials over insecure HTTP connection\n") } if err := e.uploadToken(baseURL, apiKey, email, tokenData); err != nil { return nil, err } - fmt.Fprintf(e.stdout, "Token uploaded successfully for %s\n", email) + _, _ = fmt.Fprintf(e.stdout, "Token uploaded successfully for %s\n", email) // Register account (best-effort) e.addAccount(baseURL, apiKey, email) @@ -151,7 +151,7 @@ func (e *tokenExporter) uploadToken( if err != nil { return fmt.Errorf("failed to connect to remote server: %w", err) } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() body, _ := io.ReadAll(resp.Body) if resp.StatusCode != http.StatusCreated { @@ -163,14 +163,14 @@ func (e *tokenExporter) uploadToken( // addAccount registers the email on the remote server. Failures are // logged as warnings since the token upload already succeeded. func (e *tokenExporter) addAccount(baseURL, apiKey, email string) { - fmt.Fprintf(e.stdout, "Adding account to remote config...\n") + _, _ = fmt.Fprintf(e.stdout, "Adding account to remote config...\n") accountURL := baseURL + "/api/v1/accounts" accountBody := fmt.Sprintf( `{"email":%q,"schedule":"0 2 * * *","enabled":true}`, email) req, err := http.NewRequest("POST", accountURL, strings.NewReader(accountBody)) if err != nil { - fmt.Fprintf(e.stderr, "Warning: Could not create account request: %v\n", err) + _, _ = fmt.Fprintf(e.stderr, "Warning: Could not create account request: %v\n", err) return } req.Header.Set("Content-Type", "application/json") @@ -178,19 +178,19 @@ func (e *tokenExporter) addAccount(baseURL, apiKey, email string) { resp, err := e.httpClient.Do(req) if err != nil { - fmt.Fprintf(e.stderr, "Warning: Could not add account to remote config: %v\n", err) + _, _ = fmt.Fprintf(e.stderr, "Warning: Could not add account to remote config: %v\n", err) return } respBody, _ := io.ReadAll(resp.Body) - resp.Body.Close() + _ = resp.Body.Close() switch resp.StatusCode { case http.StatusCreated: - fmt.Fprintf(e.stdout, "Account added to remote config\n") + _, _ = fmt.Fprintf(e.stdout, "Account added to remote config\n") case http.StatusOK: - fmt.Fprintf(e.stdout, "Account already configured on remote\n") + _, _ = fmt.Fprintf(e.stdout, "Account already configured on remote\n") default: - fmt.Fprintf(e.stderr, + _, _ = fmt.Fprintf(e.stderr, "Warning: Could not add account (HTTP %d): %s\n", resp.StatusCode, string(respBody)) } diff --git a/cmd/msgvault/cmd/import_emlx.go b/cmd/msgvault/cmd/import_emlx.go index 77b1c76a..20cd5e29 100644 --- a/cmd/msgvault/cmd/import_emlx.go +++ b/cmd/msgvault/cmd/import_emlx.go @@ -133,14 +133,14 @@ Examples: } signals++ if signals == 1 { - fmt.Fprintln( + _, _ = fmt.Fprintln( cmd.ErrOrStderr(), "\nInterrupted. Saving checkpoint...", ) cancel() continue } - fmt.Fprintln( + _, _ = fmt.Fprintln( cmd.ErrOrStderr(), "Interrupted again. Exiting immediately.", ) @@ -154,7 +154,7 @@ Examples: if err != nil { return fmt.Errorf("open database: %w", err) } - defer st.Close() + defer func() { _ = st.Close() }() if err := st.InitSchema(); err != nil { return fmt.Errorf("init schema: %w", err) @@ -256,22 +256,22 @@ func importAutoAccounts( accounts = filtered } - fmt.Fprintf(out, "Discovered %d account(s):\n", len(accounts)) + _, _ = fmt.Fprintf(out, "Discovered %d account(s):\n", len(accounts)) for _, a := range accounts { if a.Email != "" { - fmt.Fprintf(out, " - %s (%s)\n", a.Email, a.Description) + _, _ = fmt.Fprintf(out, " - %s (%s)\n", a.Email, a.Description) } else { - fmt.Fprintf(out, " - %s\n", a.Description) + _, _ = fmt.Fprintf(out, " - %s\n", a.Description) } } - fmt.Fprintln(out) + _, _ = fmt.Fprintln(out) var grandTotal importer.EmlxImportSummary var importErrors []error for _, account := range accounts { if ctx.Err() != nil { - fmt.Fprintln(out, "Import interrupted between accounts.") + _, _ = fmt.Fprintln(out, "Import interrupted between accounts.") break } @@ -279,14 +279,14 @@ func importAutoAccounts( accountDir, err := applemail.V10AccountDir(mailDir, account.GUID) if err != nil { importErrors = append(importErrors, fmt.Errorf("%s: %w", identifier, err)) - fmt.Fprintf(out, "Skipping %s: %v\n", identifier, err) + _, _ = fmt.Fprintf(out, "Skipping %s: %v\n", identifier, err) continue } if account.Email != "" { - fmt.Fprintf(out, "Importing %s (%s)...\n", account.Email, account.Description) + _, _ = fmt.Fprintf(out, "Importing %s (%s)...\n", account.Email, account.Description) } else { - fmt.Fprintf(out, "Importing %s...\n", account.Description) + _, _ = fmt.Fprintf(out, "Importing %s...\n", account.Description) } summary, err := importer.ImportEmlxDir( @@ -310,7 +310,7 @@ func importAutoAccounts( } printImportSummary(cmd, ctx, *summary) - fmt.Fprintln(out) + _, _ = fmt.Fprintln(out) // Accumulate totals. grandTotal.MailboxesTotal += summary.MailboxesTotal @@ -326,13 +326,13 @@ func importAutoAccounts( } if len(accounts) > 1 { - fmt.Fprintln(out, "=== Grand Total ===") + _, _ = fmt.Fprintln(out, "=== Grand Total ===") printImportStats(out, grandTotal) } if len(importErrors) > 0 { for _, e := range importErrors { - fmt.Fprintf(cmd.ErrOrStderr(), "Error: %v\n", e) + _, _ = fmt.Fprintf(cmd.ErrOrStderr(), "Error: %v\n", e) } return fmt.Errorf("import completed with %d account error(s)", len(importErrors)) } @@ -362,38 +362,38 @@ func printImportSummary(cmd *cobra.Command, ctx context.Context, summary importe out := cmd.OutOrStdout() if ctx.Err() != nil { - fmt.Fprintln(out, "Import interrupted. Run again to resume.") + _, _ = fmt.Fprintln(out, "Import interrupted. Run again to resume.") } else if summary.Errors > 0 { - fmt.Fprintln(out, "Import complete (with errors).") + _, _ = fmt.Fprintln(out, "Import complete (with errors).") } else { - fmt.Fprintln(out, "Import complete.") + _, _ = fmt.Fprintln(out, "Import complete.") } printImportStats(out, summary) } func printImportStats(out io.Writer, summary importer.EmlxImportSummary) { - fmt.Fprintf(out, + _, _ = fmt.Fprintf(out, " Mailboxes: %d discovered, %d imported\n", summary.MailboxesTotal, summary.MailboxesImported, ) - fmt.Fprintf(out, + _, _ = fmt.Fprintf(out, " Processed: %d messages\n", summary.MessagesProcessed, ) - fmt.Fprintf(out, + _, _ = fmt.Fprintf(out, " Added: %d messages\n", summary.MessagesAdded, ) - fmt.Fprintf(out, + _, _ = fmt.Fprintf(out, " Updated: %d messages\n", summary.MessagesUpdated, ) - fmt.Fprintf(out, + _, _ = fmt.Fprintf(out, " Skipped (dup): %d messages\n", summary.MessagesSkipped, ) - fmt.Fprintf(out, + _, _ = fmt.Fprintf(out, " Errors: %d\n", summary.Errors, ) diff --git a/cmd/msgvault/cmd/import_mbox.go b/cmd/msgvault/cmd/import_mbox.go index 2dd6cfae..1b72865e 100644 --- a/cmd/msgvault/cmd/import_mbox.go +++ b/cmd/msgvault/cmd/import_mbox.go @@ -86,11 +86,11 @@ Examples: } signals++ if signals == 1 { - fmt.Fprintln(cmd.ErrOrStderr(), "\nInterrupted. Saving checkpoint...") + _, _ = fmt.Fprintln(cmd.ErrOrStderr(), "\nInterrupted. Saving checkpoint...") cancel() continue } - fmt.Fprintln(cmd.ErrOrStderr(), "Interrupted again. Exiting immediately.") + _, _ = fmt.Fprintln(cmd.ErrOrStderr(), "Interrupted again. Exiting immediately.") os.Exit(130) } } @@ -101,7 +101,7 @@ Examples: if err != nil { return fmt.Errorf("open database: %w", err) } - defer st.Close() + defer func() { _ = st.Close() }() if err := st.InitSchema(); err != nil { return fmt.Errorf("init schema: %w", err) @@ -291,25 +291,25 @@ Examples: out := cmd.OutOrStdout() if ctx.Err() != nil { - fmt.Fprintln(out, "Import interrupted. Run again to resume.") + _, _ = fmt.Fprintln(out, "Import interrupted. Run again to resume.") } else if totalErrors > 0 { - fmt.Fprintln(out, "Import complete (with errors).") + _, _ = fmt.Fprintln(out, "Import complete (with errors).") } else { - fmt.Fprintln(out, "Import complete.") + _, _ = fmt.Fprintln(out, "Import complete.") } for _, p := range processedFiles { if p.Partial { - fmt.Fprintf(out, " Imported (partial): %s\n", p.Path) + _, _ = fmt.Fprintf(out, " Imported (partial): %s\n", p.Path) } else { - fmt.Fprintf(out, " Imported: %s\n", p.Path) + _, _ = fmt.Fprintf(out, " Imported: %s\n", p.Path) } } - fmt.Fprintf(out, " Processed: %d messages\n", totalProcessed) - fmt.Fprintf(out, " Added: %d messages\n", totalAdded) - fmt.Fprintf(out, " Updated: %d messages\n", totalUpdated) - fmt.Fprintf(out, " Skipped: %d messages\n", totalSkipped) - fmt.Fprintf(out, " Errors: %d\n", totalErrors) - fmt.Fprintf(out, " Bytes: %.2f MB\n", float64(totalBytes)/(1024*1024)) + _, _ = fmt.Fprintf(out, " Processed: %d messages\n", totalProcessed) + _, _ = fmt.Fprintf(out, " Added: %d messages\n", totalAdded) + _, _ = fmt.Fprintf(out, " Updated: %d messages\n", totalUpdated) + _, _ = fmt.Fprintf(out, " Skipped: %d messages\n", totalSkipped) + _, _ = fmt.Fprintf(out, " Errors: %d\n", totalErrors) + _, _ = fmt.Fprintf(out, " Bytes: %.2f MB\n", float64(totalBytes)/(1024*1024)) if ctx.Err() == nil && hadHardErrors { return fmt.Errorf("import completed with %d errors", totalErrors) diff --git a/cmd/msgvault/cmd/initdb.go b/cmd/msgvault/cmd/initdb.go index 9a46d74b..e33ebc75 100644 --- a/cmd/msgvault/cmd/initdb.go +++ b/cmd/msgvault/cmd/initdb.go @@ -23,7 +23,7 @@ created if they don't already exist.`, if err != nil { return fmt.Errorf("open database: %w", err) } - defer s.Close() + defer func() { _ = s.Close() }() if err := s.InitSchema(); err != nil { return fmt.Errorf("init schema: %w", err) diff --git a/cmd/msgvault/cmd/list_accounts.go b/cmd/msgvault/cmd/list_accounts.go index fdc5fb77..2eb3f424 100644 --- a/cmd/msgvault/cmd/list_accounts.go +++ b/cmd/msgvault/cmd/list_accounts.go @@ -43,7 +43,7 @@ func listRemoteAccounts() error { if err != nil { return fmt.Errorf("connect to remote: %w", err) } - defer s.Close() + defer func() { _ = s.Close() }() accounts, err := s.ListAccounts() if err != nil { @@ -69,7 +69,7 @@ func listLocalAccounts() error { if err != nil { return fmt.Errorf("open database: %w", err) } - defer s.Close() + defer func() { _ = s.Close() }() if err := s.InitSchema(); err != nil { return fmt.Errorf("init schema: %w", err) @@ -122,7 +122,7 @@ func listLocalAccounts() error { func outputAccountsTable(stats []accountStats) { w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) - fmt.Fprintln(w, "ID\tACCOUNT\tTYPE\tDISPLAY NAME\tMESSAGES\tLAST SYNC") + _, _ = fmt.Fprintln(w, "ID\tACCOUNT\tTYPE\tDISPLAY NAME\tMESSAGES\tLAST SYNC") for _, s := range stats { displayName := s.DisplayName @@ -133,10 +133,10 @@ func outputAccountsTable(stats []accountStats) { if s.LastSync != nil && !s.LastSync.IsZero() { lastSync = s.LastSync.Format("2006-01-02 15:04") } - fmt.Fprintf(w, "%d\t%s\t%s\t%s\t%s\t%s\n", s.ID, s.Email, s.Type, displayName, formatCount(s.MessageCount), lastSync) + _, _ = fmt.Fprintf(w, "%d\t%s\t%s\t%s\t%s\t%s\n", s.ID, s.Email, s.Type, displayName, formatCount(s.MessageCount), lastSync) } - w.Flush() + _ = w.Flush() } func outputAccountsJSON(stats []accountStats) error { @@ -191,7 +191,7 @@ type accountStats struct { func outputRemoteAccountsTable(accounts []remote.AccountInfo) { w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) - fmt.Fprintln(w, "EMAIL\tSCHEDULE\tENABLED\tLAST SYNC\tNEXT SYNC") + _, _ = fmt.Fprintln(w, "EMAIL\tSCHEDULE\tENABLED\tLAST SYNC\tNEXT SYNC") for _, a := range accounts { enabled := "no" @@ -210,10 +210,10 @@ func outputRemoteAccountsTable(accounts []remote.AccountInfo) { nextSync = t.Format("2006-01-02 15:04") } } - fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", a.Email, a.Schedule, enabled, lastSync, nextSync) + _, _ = fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n", a.Email, a.Schedule, enabled, lastSync, nextSync) } - w.Flush() + _ = w.Flush() } func outputRemoteAccountsJSON(accounts []remote.AccountInfo) error { diff --git a/cmd/msgvault/cmd/list_domains.go b/cmd/msgvault/cmd/list_domains.go index ae0fbf63..71207f67 100644 --- a/cmd/msgvault/cmd/list_domains.go +++ b/cmd/msgvault/cmd/list_domains.go @@ -32,7 +32,7 @@ Examples: if err != nil { return fmt.Errorf("open database: %w", err) } - defer s.Close() + defer func() { _ = s.Close() }() // Create query engine engine := query.NewSQLiteEngine(s.DB()) diff --git a/cmd/msgvault/cmd/list_labels.go b/cmd/msgvault/cmd/list_labels.go index 775949a0..0c6e54fb 100644 --- a/cmd/msgvault/cmd/list_labels.go +++ b/cmd/msgvault/cmd/list_labels.go @@ -32,7 +32,7 @@ Examples: if err != nil { return fmt.Errorf("open database: %w", err) } - defer s.Close() + defer func() { _ = s.Close() }() // Create query engine engine := query.NewSQLiteEngine(s.DB()) diff --git a/cmd/msgvault/cmd/list_senders.go b/cmd/msgvault/cmd/list_senders.go index f18a7229..24f37bbd 100644 --- a/cmd/msgvault/cmd/list_senders.go +++ b/cmd/msgvault/cmd/list_senders.go @@ -32,7 +32,7 @@ Examples: if err != nil { return fmt.Errorf("open database: %w", err) } - defer s.Close() + defer func() { _ = s.Close() }() // Create query engine engine := query.NewSQLiteEngine(s.DB()) diff --git a/cmd/msgvault/cmd/mcp.go b/cmd/msgvault/cmd/mcp.go index 1dcfdf26..fc302373 100644 --- a/cmd/msgvault/cmd/mcp.go +++ b/cmd/msgvault/cmd/mcp.go @@ -38,7 +38,7 @@ Add to Claude Desktop config: if err != nil { return fmt.Errorf("open database: %w", err) } - defer s.Close() + defer func() { _ = s.Close() }() // Ensure schema is up to date if err := s.InitSchema(); err != nil { @@ -67,7 +67,7 @@ Add to Claude Desktop config: engine = query.NewSQLiteEngine(s.DB()) } else { engine = duckEngine - defer duckEngine.Close() + defer func() { _ = duckEngine.Close() }() } } else { engine = query.NewSQLiteEngine(s.DB()) diff --git a/cmd/msgvault/cmd/repair_encoding.go b/cmd/msgvault/cmd/repair_encoding.go index 0ca655d6..57119ce3 100644 --- a/cmd/msgvault/cmd/repair_encoding.go +++ b/cmd/msgvault/cmd/repair_encoding.go @@ -42,7 +42,7 @@ charset detection issues in the MIME parser.`, if err != nil { return fmt.Errorf("open database: %w", err) } - defer s.Close() + defer func() { _ = s.Close() }() return repairEncoding(s) }, @@ -149,7 +149,7 @@ func repairMessageFields(s *store.Store, stats *repairStats) error { if err != nil { return fmt.Errorf("query messages: %w", err) } - defer rows.Close() + defer func() { _ = rows.Close() }() type messageRepair struct { id int64 @@ -380,7 +380,7 @@ func repairDisplayNames(s *store.Store, stats *repairStats) error { _ = tx.Rollback() return fmt.Errorf("prepare update: %w", err) } - defer stmt.Close() + defer func() { _ = stmt.Close() }() for _, r := range repairs { if _, err := stmt.Exec(r.newName, r.id); err != nil { @@ -419,7 +419,7 @@ func repairDisplayNames(s *store.Store, stats *repairStats) error { // Apply batch when full if len(repairs) >= batchSize { if err := applyBatch(); err != nil { - rows.Close() + _ = rows.Close() return err } } @@ -427,10 +427,10 @@ func repairDisplayNames(s *store.Store, stats *repairStats) error { } if err := rows.Err(); err != nil { - rows.Close() + _ = rows.Close() return fmt.Errorf("iterate %s: %w", table.name, err) } - rows.Close() + _ = rows.Close() // Apply remaining repairs if err := applyBatch(); err != nil { @@ -534,7 +534,7 @@ func repairOtherStrings(s *store.Store, stats *repairStats) error { _ = tx.Rollback() return fmt.Errorf("prepare update: %w", err) } - defer stmt.Close() + defer func() { _ = stmt.Close() }() for _, r := range repairs { if _, err := stmt.Exec(r.newValue, r.id); err != nil { @@ -572,7 +572,7 @@ func repairOtherStrings(s *store.Store, stats *repairStats) error { if len(repairs) >= batchSize { if err := applyBatch(); err != nil { - rows.Close() + _ = rows.Close() return err } } @@ -580,10 +580,10 @@ func repairOtherStrings(s *store.Store, stats *repairStats) error { } if err := rows.Err(); err != nil { - rows.Close() + _ = rows.Close() return fmt.Errorf("iterate %s: %w", table.name, err) } - rows.Close() + _ = rows.Close() if err := applyBatch(); err != nil { return err @@ -610,7 +610,7 @@ func tryParseMIME(rawData []byte, compression sql.NullString) *mime.Message { return nil } rawData, err = io.ReadAll(r) - r.Close() + _ = r.Close() if err != nil { return nil } diff --git a/cmd/msgvault/cmd/search.go b/cmd/msgvault/cmd/search.go index bb55b392..e3c91988 100644 --- a/cmd/msgvault/cmd/search.go +++ b/cmd/msgvault/cmd/search.go @@ -81,7 +81,7 @@ func runRemoteSearch(queryStr string) error { if err != nil { return fmt.Errorf("connect to remote: %w", err) } - defer s.Close() + defer func() { _ = s.Close() }() results, total, err := s.SearchMessages(queryStr, searchOffset, searchLimit) fmt.Fprintf(os.Stderr, "\r \r") @@ -117,7 +117,7 @@ func runLocalSearch(cmd *cobra.Command, queryStr string) error { if err != nil { return fmt.Errorf("open database: %w", err) } - defer s.Close() + defer func() { _ = s.Close() }() // Ensure schema is up to date and FTS index is populated if err := s.InitSchema(); err != nil { @@ -167,36 +167,36 @@ func runLocalSearch(cmd *cobra.Command, queryStr string) error { func outputSearchResultsTable(results []query.MessageSummary) error { w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) - fmt.Fprintln(w, "ID\tDATE\tFROM\tSUBJECT\tSIZE") - fmt.Fprintln(w, "──\t────\t────\t───────\t────") + _, _ = fmt.Fprintln(w, "ID\tDATE\tFROM\tSUBJECT\tSIZE") + _, _ = fmt.Fprintln(w, "──\t────\t────\t───────\t────") for _, msg := range results { date := msg.SentAt.Format("2006-01-02") from := truncate(msg.FromEmail, 30) subject := truncate(msg.Subject, 50) size := formatSize(msg.SizeEstimate) - fmt.Fprintf(w, "%d\t%s\t%s\t%s\t%s\n", msg.ID, date, from, subject, size) + _, _ = fmt.Fprintf(w, "%d\t%s\t%s\t%s\t%s\n", msg.ID, date, from, subject, size) } - w.Flush() + _ = w.Flush() fmt.Printf("\nShowing %d results\n", len(results)) return nil } func outputRemoteSearchResultsTable(results []store.APIMessage, total int64) error { w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) - fmt.Fprintln(w, "ID\tDATE\tFROM\tSUBJECT\tSIZE") - fmt.Fprintln(w, "──\t────\t────\t───────\t────") + _, _ = fmt.Fprintln(w, "ID\tDATE\tFROM\tSUBJECT\tSIZE") + _, _ = fmt.Fprintln(w, "──\t────\t────\t───────\t────") for _, msg := range results { date := msg.SentAt.Format("2006-01-02") from := truncate(msg.From, 30) subject := truncate(msg.Subject, 50) size := formatSize(msg.SizeEstimate) - fmt.Fprintf(w, "%d\t%s\t%s\t%s\t%s\n", msg.ID, date, from, subject, size) + _, _ = fmt.Fprintf(w, "%d\t%s\t%s\t%s\t%s\n", msg.ID, date, from, subject, size) } - w.Flush() + _ = w.Flush() fmt.Printf("\nShowing %d of %d results\n", len(results), total) return nil } diff --git a/cmd/msgvault/cmd/serve.go b/cmd/msgvault/cmd/serve.go index a9ac2bd1..c113e268 100644 --- a/cmd/msgvault/cmd/serve.go +++ b/cmd/msgvault/cmd/serve.go @@ -77,7 +77,7 @@ func runServe(cmd *cobra.Command, args []string) error { if err != nil { return fmt.Errorf("open database: %w", err) } - defer s.Close() + defer func() { _ = s.Close() }() if err := s.InitSchema(); err != nil { return fmt.Errorf("init schema: %w", err) @@ -109,7 +109,7 @@ func runServe(cmd *cobra.Command, args []string) error { } engine = query.NewSQLiteEngine(s.DB()) } - defer engine.Close() + defer func() { _ = engine.Close() }() getOAuthMgr := oauthManagerCache() @@ -302,7 +302,7 @@ func runScheduledSync(ctx context.Context, email string, s *store.Store, getOAut gmail.WithLogger(logger), gmail.WithRateLimiter(rateLimiter), ) - defer client.Close() + defer func() { _ = client.Close() }() // Set up sync options opts := sync.DefaultOptions() diff --git a/cmd/msgvault/cmd/setup.go b/cmd/msgvault/cmd/setup.go index f76f8cf8..f888dc43 100644 --- a/cmd/msgvault/cmd/setup.go +++ b/cmd/msgvault/cmd/setup.go @@ -307,13 +307,13 @@ func copyFile(src, dst string) error { if err != nil { return err } - defer srcFile.Close() + defer func() { _ = srcFile.Close() }() dstFile, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) if err != nil { return err } - defer dstFile.Close() + defer func() { _ = dstFile.Close() }() if _, err := io.Copy(dstFile, srcFile); err != nil { return err diff --git a/cmd/msgvault/cmd/show_message.go b/cmd/msgvault/cmd/show_message.go index 3edd0b34..676a8f43 100644 --- a/cmd/msgvault/cmd/show_message.go +++ b/cmd/msgvault/cmd/show_message.go @@ -56,7 +56,7 @@ func showRemoteMessage(idStr string) error { if err != nil { return fmt.Errorf("connect to remote: %w", err) } - defer s.Close() + defer func() { _ = s.Close() }() msg, err := s.GetMessage(id) if err != nil { @@ -80,7 +80,7 @@ func showLocalMessage(cmd *cobra.Command, idStr string) error { if err != nil { return fmt.Errorf("open database: %w", err) } - defer s.Close() + defer func() { _ = s.Close() }() // Create query engine engine := query.NewSQLiteEngine(s.DB()) diff --git a/cmd/msgvault/cmd/stats.go b/cmd/msgvault/cmd/stats.go index 2419d9d8..6160e39f 100644 --- a/cmd/msgvault/cmd/stats.go +++ b/cmd/msgvault/cmd/stats.go @@ -18,7 +18,7 @@ Use --local to force local database.`, if err != nil { return fmt.Errorf("open store: %w", err) } - defer s.Close() + defer func() { _ = s.Close() }() stats, err := s.GetStats() if err != nil { diff --git a/cmd/msgvault/cmd/store_resolver.go b/cmd/msgvault/cmd/store_resolver.go index a4723150..69753ec4 100644 --- a/cmd/msgvault/cmd/store_resolver.go +++ b/cmd/msgvault/cmd/store_resolver.go @@ -80,9 +80,9 @@ func openRemoteStore() (*remote.Store, error) { // Use this for commands that only work with local database. func MustBeLocal(cmdName string) error { if IsRemoteMode() && !useLocal { - return fmt.Errorf("%s requires local database\n\n"+ - "This command cannot run against a remote server.\n"+ - "Use --local flag to force local database.", cmdName) + return fmt.Errorf("%s requires local database, "+ + "this command cannot run against a remote server, "+ + "use --local flag to force local database", cmdName) } return nil } diff --git a/cmd/msgvault/cmd/sync.go b/cmd/msgvault/cmd/sync.go index a2147041..cc65cfde 100644 --- a/cmd/msgvault/cmd/sync.go +++ b/cmd/msgvault/cmd/sync.go @@ -47,7 +47,7 @@ Examples: if err != nil { return fmt.Errorf("open database: %w", err) } - defer s.Close() + defer func() { _ = s.Close() }() if err := s.InitSchema(); err != nil { return fmt.Errorf("init schema: %w", err) @@ -212,7 +212,7 @@ func runIncrementalSync(ctx context.Context, s *store.Store, getOAuthMgr func(st gmail.WithLogger(logger), gmail.WithRateLimiter(rateLimiter), ) - defer client.Close() + defer func() { _ = client.Close() }() // Set up sync options opts := sync.DefaultOptions() diff --git a/cmd/msgvault/cmd/syncfull.go b/cmd/msgvault/cmd/syncfull.go index aeae8d2b..15847e62 100644 --- a/cmd/msgvault/cmd/syncfull.go +++ b/cmd/msgvault/cmd/syncfull.go @@ -58,7 +58,7 @@ Examples: if err != nil { return fmt.Errorf("open database: %w", err) } - defer s.Close() + defer func() { _ = s.Close() }() if err := s.InitSchema(); err != nil { return fmt.Errorf("init schema: %w", err) @@ -224,7 +224,7 @@ func runFullSync(ctx context.Context, s *store.Store, getOAuthMgr func(string) ( if err != nil { return err } - defer apiClient.Close() + defer func() { _ = apiClient.Close() }() // Build query from flags (Gmail only). query := buildSyncQuery() diff --git a/cmd/msgvault/cmd/tui.go b/cmd/msgvault/cmd/tui.go index 08687a17..ecefa764 100644 --- a/cmd/msgvault/cmd/tui.go +++ b/cmd/msgvault/cmd/tui.go @@ -73,7 +73,7 @@ Remote Mode: if err != nil { return fmt.Errorf("connect to remote: %w", err) } - defer remoteEngine.Close() + defer func() { _ = remoteEngine.Close() }() engine = remoteEngine isRemote = true fmt.Printf("Connected to remote: %s\n", cfg.Remote.URL) @@ -84,7 +84,7 @@ Remote Mode: if err != nil { return fmt.Errorf("open database: %w", err) } - defer s.Close() + defer func() { _ = s.Close() }() // Ensure schema is up to date if err := s.InitSchema(); err != nil { @@ -130,7 +130,7 @@ Remote Mode: engine = query.NewSQLiteEngine(s.DB()) } else { engine = duckEngine - defer duckEngine.Close() + defer func() { _ = duckEngine.Close() }() } } else { // Use SQLite directly @@ -207,7 +207,7 @@ func cacheNeedsBuild(dbPath, analyticsDir string) cacheStaleness { Reason: "cannot verify cache status", } } - defer db.Close() + defer func() { _ = db.Close() }() var maxID int64 err = db.DB().QueryRow(` diff --git a/cmd/msgvault/cmd/update_account.go b/cmd/msgvault/cmd/update_account.go index 435dcb6c..5416f2f1 100644 --- a/cmd/msgvault/cmd/update_account.go +++ b/cmd/msgvault/cmd/update_account.go @@ -32,7 +32,7 @@ Examples: if err != nil { return fmt.Errorf("open database: %w", err) } - defer s.Close() + defer func() { _ = s.Close() }() source, err := s.GetSourceByIdentifier(email) if err != nil { diff --git a/cmd/msgvault/cmd/verify.go b/cmd/msgvault/cmd/verify.go index 2535439a..936260cf 100644 --- a/cmd/msgvault/cmd/verify.go +++ b/cmd/msgvault/cmd/verify.go @@ -47,7 +47,7 @@ Examples: if err != nil { return fmt.Errorf("open database: %w", err) } - defer s.Close() + defer func() { _ = s.Close() }() if err := s.InitSchema(); err != nil { return fmt.Errorf("init schema: %w", err) @@ -100,7 +100,7 @@ Examples: // Create Gmail client (no rate limiter needed for single call) client := gmail.NewClient(tokenSource, gmail.WithLogger(logger)) - defer client.Close() + defer func() { _ = client.Close() }() // Run SQLite integrity check first (offline, no Gmail needed) var dbCorrupt bool @@ -267,7 +267,7 @@ func runIntegrityCheck(s *store.Store) ([]string, error) { if err != nil { return nil, err } - defer rows.Close() + defer func() { _ = rows.Close() }() var errors []string for rows.Next() { diff --git a/internal/api/handlers.go b/internal/api/handlers.go index ee028809..d28afbae 100644 --- a/internal/api/handlers.go +++ b/internal/api/handlers.go @@ -468,23 +468,23 @@ func (s *Server) handleUploadToken(w http.ResponseWriter, r *http.Request) { tmpPath := tmpFile.Name() if _, err := tmpFile.Write(data); err != nil { - tmpFile.Close() - os.Remove(tmpPath) + _ = tmpFile.Close() + _ = os.Remove(tmpPath) writeError(w, http.StatusInternalServerError, "internal_error", "Failed to write token") return } if err := tmpFile.Close(); err != nil { - os.Remove(tmpPath) + _ = os.Remove(tmpPath) writeError(w, http.StatusInternalServerError, "internal_error", "Failed to close token file") return } if err := fileutil.SecureChmod(tmpPath, 0600); err != nil { - os.Remove(tmpPath) + _ = os.Remove(tmpPath) writeError(w, http.StatusInternalServerError, "internal_error", "Failed to set token permissions") return } if err := os.Rename(tmpPath, tokenPath); err != nil { - os.Remove(tmpPath) + _ = os.Remove(tmpPath) writeError(w, http.StatusInternalServerError, "internal_error", "Failed to save token") return } diff --git a/internal/api/handlers_test.go b/internal/api/handlers_test.go index 306bfcdc..537f81b8 100644 --- a/internal/api/handlers_test.go +++ b/internal/api/handlers_test.go @@ -381,7 +381,7 @@ func TestHandleUploadToken(t *testing.T) { if err != nil { t.Fatalf("failed to create temp dir: %v", err) } - defer os.RemoveAll(tmpDir) + defer func() { _ = os.RemoveAll(tmpDir) }() cfg := &config.Config{ Server: config.ServerConfig{APIPort: 8080}, @@ -468,7 +468,7 @@ func TestHandleUploadTokenInvalidJSON(t *testing.T) { if err != nil { t.Fatalf("failed to create temp dir: %v", err) } - defer os.RemoveAll(tmpDir) + defer func() { _ = os.RemoveAll(tmpDir) }() cfg := &config.Config{ Server: config.ServerConfig{APIPort: 8080}, @@ -501,7 +501,7 @@ func TestHandleUploadTokenMissingRefreshToken(t *testing.T) { if err != nil { t.Fatalf("failed to create temp dir: %v", err) } - defer os.RemoveAll(tmpDir) + defer func() { _ = os.RemoveAll(tmpDir) }() cfg := &config.Config{ Server: config.ServerConfig{APIPort: 8080}, @@ -592,7 +592,7 @@ func TestHandleAddAccount(t *testing.T) { if err != nil { t.Fatalf("failed to create temp dir: %v", err) } - defer os.RemoveAll(tmpDir) + defer func() { _ = os.RemoveAll(tmpDir) }() cfg := &config.Config{ Server: config.ServerConfig{APIPort: 8080}, diff --git a/internal/applemail/accounts.go b/internal/applemail/accounts.go index 4629d4e5..ab18d283 100644 --- a/internal/applemail/accounts.go +++ b/internal/applemail/accounts.go @@ -58,7 +58,7 @@ func ResolveAccounts(dbPath string, guids []string) (map[string]AccountInfo, err if err != nil { return nil, fmt.Errorf("open accounts db: %w", err) } - defer db.Close() + defer func() { _ = db.Close() }() // Build placeholders for IN clause. placeholders := make([]string, len(guids)) @@ -82,7 +82,7 @@ func ResolveAccounts(dbPath string, guids []string) (map[string]AccountInfo, err if err != nil { return nil, fmt.Errorf("query accounts: %w", err) } - defer rows.Close() + defer func() { _ = rows.Close() }() result := make(map[string]AccountInfo) for rows.Next() { diff --git a/internal/applemail/accounts_test.go b/internal/applemail/accounts_test.go index 6f40d351..7c8a5668 100644 --- a/internal/applemail/accounts_test.go +++ b/internal/applemail/accounts_test.go @@ -19,7 +19,7 @@ func createTestAccountsDB(t *testing.T, accounts []testAccount) string { if err != nil { t.Fatalf("create test db: %v", err) } - defer db.Close() + defer func() { _ = db.Close() }() _, err = db.Exec(` CREATE TABLE ZACCOUNT ( diff --git a/internal/config/config.go b/internal/config/config.go index 1a700a61..9c4ae4fa 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -227,8 +227,8 @@ func Load(path, homeDir string) (*Config, error) { if _, err := toml.DecodeFile(path, cfg); err != nil { if strings.Contains(err.Error(), "invalid escape") || strings.Contains(err.Error(), "hexadecimal digits after") { - return nil, fmt.Errorf("decode config: %w\n\nhint: Windows paths in TOML must use "+ - "forward slashes (C:/Games/msgvault) or single quotes ('C:\\Games\\msgvault').", err) + return nil, fmt.Errorf("decode config: %w -- hint: Windows paths in TOML must use "+ + "forward slashes (C:/Games/msgvault) or single quotes ('C:\\Games\\msgvault')", err) } return nil, fmt.Errorf("decode config: %w", err) } @@ -327,8 +327,8 @@ func (c *Config) Save() error { success := false defer func() { if !success { - tmp.Close() - os.Remove(tmpPath) + _ = tmp.Close() + _ = os.Remove(tmpPath) } }() diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 3cfdf251..fb49c9ea 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -593,7 +593,7 @@ func TestMkTempDir(t *testing.T) { if err != nil { t.Fatalf("MkTempDir failed: %v", err) } - defer os.RemoveAll(dir) + defer func() { _ = os.RemoveAll(dir) }() if _, err := os.Stat(dir); err != nil { t.Errorf("temp dir does not exist: %v", err) @@ -607,7 +607,7 @@ func TestMkTempDir(t *testing.T) { if err != nil { t.Fatalf("MkTempDir failed: %v", err) } - defer os.RemoveAll(dir) + defer func() { _ = os.RemoveAll(dir) }() if !strings.HasPrefix(dir, preferred) { t.Errorf("temp dir %q not under preferred %q", dir, preferred) @@ -620,7 +620,7 @@ func TestMkTempDir(t *testing.T) { if err != nil { t.Fatalf("MkTempDir failed: %v", err) } - defer os.RemoveAll(dir) + defer func() { _ = os.RemoveAll(dir) }() // Should have used system temp, not errored if _, err := os.Stat(dir); err != nil { @@ -633,7 +633,7 @@ func TestMkTempDir(t *testing.T) { if err != nil { t.Fatalf("MkTempDir failed: %v", err) } - defer os.RemoveAll(dir) + defer func() { _ = os.RemoveAll(dir) }() // Should have fallen back to system temp if strings.Contains(dir, "nonexistent") { @@ -657,7 +657,7 @@ func TestMkTempDir(t *testing.T) { // configurations can still write to 0500 directories). probe, probeErr := os.MkdirTemp(restrictedTmp, "probe-*") if probeErr == nil { - os.Remove(probe) + _ = os.Remove(probe) t.Skip("chmod 0500 did not restrict writes (running as root or permissive ACLs)") } @@ -670,7 +670,7 @@ func TestMkTempDir(t *testing.T) { if err != nil { t.Fatalf("MkTempDir failed: %v", err) } - defer os.RemoveAll(dir) + defer func() { _ = os.RemoveAll(dir) }() expectedBase := filepath.Join(msgvaultHome, "tmp") if !strings.HasPrefix(dir, expectedBase) { @@ -1070,8 +1070,8 @@ func TestSave_FailurePreservesExisting(t *testing.T) { // Probe whether the restriction actually works probe, probeErr := os.CreateTemp(tmpDir, "probe-*") if probeErr == nil { - probe.Close() - os.Remove(probe.Name()) + _ = probe.Close() + _ = os.Remove(probe.Name()) t.Skip("chmod 0500 did not restrict writes (running as root)") } diff --git a/internal/export/attachments.go b/internal/export/attachments.go index 79bac471..60ee374f 100644 --- a/internal/export/attachments.go +++ b/internal/export/attachments.go @@ -91,7 +91,7 @@ func Attachments(zipFilename, attachmentsDir string, attachments []query.Attachm } if stats.Count == 0 || writeError { - os.Remove(zipFilename) + _ = os.Remove(zipFilename) stats.WriteError = writeError return stats } @@ -149,7 +149,7 @@ func addAttachmentToZip(zw *zip.Writer, root string, att query.AttachmentInfo, u if err != nil { return 0, err } - defer srcFile.Close() + defer func() { _ = srcFile.Close() }() filename := resolveUniqueFilename(att.Filename, att.ContentHash, usedNames) @@ -298,7 +298,7 @@ func exportAttachmentToFile(outputDir, attachmentsDir, contentHash, filename str } return ExportedFile{}, fmt.Errorf("open source: %w", err) } - defer src.Close() + defer func() { _ = src.Close() }() destPath := filepath.Join(outputDir, filename) dst, finalPath, err := CreateExclusiveFile(destPath, 0600) @@ -309,11 +309,11 @@ func exportAttachmentToFile(outputDir, attachmentsDir, contentHash, filename str n, copyErr := io.Copy(dst, src) closeErr := dst.Close() if copyErr != nil { - os.Remove(finalPath) + _ = os.Remove(finalPath) return ExportedFile{}, fmt.Errorf("write: %w", copyErr) } if closeErr != nil { - os.Remove(finalPath) + _ = os.Remove(finalPath) return ExportedFile{}, fmt.Errorf("close: %w", closeErr) } diff --git a/internal/export/attachments_test.go b/internal/export/attachments_test.go index 3b98eade..8b3ab053 100644 --- a/internal/export/attachments_test.go +++ b/internal/export/attachments_test.go @@ -328,7 +328,7 @@ func TestCreateExclusiveFile(t *testing.T) { if err != nil { t.Fatal(err) } - f.Close() + _ = f.Close() if path != p { t.Errorf("path = %q, want %q", path, p) } @@ -351,7 +351,7 @@ func TestCreateExclusiveFile(t *testing.T) { if err != nil { t.Fatal(err) } - f.Close() + _ = f.Close() if filepath.Base(path) != "existing_1.txt" { t.Errorf("path = %q, want existing_1.txt", filepath.Base(path)) } @@ -370,7 +370,7 @@ func TestCreateExclusiveFile(t *testing.T) { if err != nil { t.Fatal(err) } - f.Close() + _ = f.Close() if filepath.Base(path) != "multi_2.txt" { t.Errorf("path = %q, want multi_2.txt", filepath.Base(path)) } @@ -386,7 +386,7 @@ func TestCreateExclusiveFile(t *testing.T) { if err != nil { t.Fatal(err) } - f.Close() + _ = f.Close() if filepath.Base(path) != "noext_1" { t.Errorf("path = %q, want noext_1", filepath.Base(path)) } @@ -557,7 +557,7 @@ func TestAttachments(t *testing.T) { if err != nil { t.Fatalf("failed to open zip: %v", err) } - defer zr.Close() + defer func() { _ = zr.Close() }() zipEntries := make(map[string]bool) for _, f := range zr.File { diff --git a/internal/export/store_attachment.go b/internal/export/store_attachment.go index 644fac10..83c2b390 100644 --- a/internal/export/store_attachment.go +++ b/internal/export/store_attachment.go @@ -190,7 +190,7 @@ func validateExistingAttachmentFile(fullPath string, expectedSize int64, expecte } time.Sleep(time.Duration(attempt+1) * 10 * time.Millisecond) } - defer f.Close() + defer func() { _ = f.Close() }() st, err := f.Stat() if err != nil { diff --git a/internal/fileutil/secure_test.go b/internal/fileutil/secure_test.go index f28b24b5..241a14c5 100644 --- a/internal/fileutil/secure_test.go +++ b/internal/fileutil/secure_test.go @@ -129,7 +129,7 @@ func TestSecureOpenFile(t *testing.T) { t.Fatalf("SecureOpenFile: %v", err) } if _, err := f.Write([]byte("data")); err != nil { - f.Close() + _ = f.Close() t.Fatalf("Write: %v", err) } if err := f.Close(); err != nil { @@ -172,7 +172,7 @@ func TestSecureOpenFile_ReadOnly(t *testing.T) { if err != nil { t.Fatalf("SecureOpenFile read-only: %v", err) } - defer f.Close() + defer func() { _ = f.Close() }() buf := make([]byte, 100) n, err := f.Read(buf) diff --git a/internal/gmail/client.go b/internal/gmail/client.go index 3559cf3c..ec9aa226 100644 --- a/internal/gmail/client.go +++ b/internal/gmail/client.go @@ -132,7 +132,7 @@ func (c *Client) request(ctx context.Context, op Operation, method, path string, } respBody, err := io.ReadAll(resp.Body) - resp.Body.Close() + _ = resp.Body.Close() if err != nil { lastErr = fmt.Errorf("read response: %w", err) continue diff --git a/internal/importer/emlx_import_test.go b/internal/importer/emlx_import_test.go index 4b5553ff..0b0a6de7 100644 --- a/internal/importer/emlx_import_test.go +++ b/internal/importer/emlx_import_test.go @@ -194,7 +194,7 @@ func TestImportEmlxDir_MultiMailboxLabels(t *testing.T) { if err != nil { t.Fatalf("query labels: %v", err) } - defer rows.Close() + defer func() { _ = rows.Close() }() for rows.Next() { var name string if err := rows.Scan(&name); err != nil { diff --git a/internal/importer/ingest_test.go b/internal/importer/ingest_test.go index b8cd495a..f8c2a014 100644 --- a/internal/importer/ingest_test.go +++ b/internal/importer/ingest_test.go @@ -102,7 +102,7 @@ func TestIngestRawMessage_SanitizesAddressFields(t *testing.T) { if err != nil { t.Fatalf("query participants: %v", err) } - defer rows.Close() + defer func() { _ = rows.Close() }() for rows.Next() { var emailAddr string diff --git a/internal/importer/mbox_import.go b/internal/importer/mbox_import.go index f09ffaae..341e0f98 100644 --- a/internal/importer/mbox_import.go +++ b/internal/importer/mbox_import.go @@ -195,7 +195,7 @@ func ImportMbox(ctx context.Context, st *store.Store, mboxPath string, opts Mbox _ = st.FailSync(syncID, err.Error()) return nil, fmt.Errorf("open mbox: %w", err) } - defer f.Close() + defer func() { _ = f.Close() }() fi, err := f.Stat() if err != nil { @@ -234,8 +234,8 @@ func ImportMbox(ctx context.Context, st *store.Store, mboxPath string, opts Mbox // If we resumed at a saved offset, the reader's logical Offset() should now be that. // However, if the offset lands in the middle of a line (corrupt checkpoint), the // reader will scan to the next separator. - var lastCheckpointOffset int64 = offset - var lastCheckpointSeq int64 = seq + var lastCheckpointOffset = offset + var lastCheckpointSeq = seq checkpointBlocked := false hardErrors := false diff --git a/internal/importer/mboxzip/mbox_zip.go b/internal/importer/mboxzip/mbox_zip.go index dea4378c..2730c5d7 100644 --- a/internal/importer/mboxzip/mbox_zip.go +++ b/internal/importer/mboxzip/mbox_zip.go @@ -115,7 +115,7 @@ func zipMboxCacheKey(zipPath string) (string, error) { if err != nil { return "", fmt.Errorf("open zip: %w", err) } - defer zr.Close() + defer func() { _ = zr.Close() }() type zipEntry struct { Name string @@ -151,9 +151,9 @@ func zipMboxCacheKey(zipPath string) (string, error) { sort.Slice(entries, func(i, j int) bool { return entries[i].Name < entries[j].Name }) h := sha256.New() - fmt.Fprintf(h, "zip:%x\n", st.Size()) + _, _ = fmt.Fprintf(h, "zip:%x\n", st.Size()) for _, e := range entries { - fmt.Fprintf(h, "%s\x00%x\x00%x\n", e.Name, e.Size, e.CRC) + _, _ = fmt.Fprintf(h, "%s\x00%x\x00%x\n", e.Name, e.Size, e.CRC) } sum := hex.EncodeToString(h.Sum(nil)) return "z" + sum[:16], nil @@ -215,7 +215,7 @@ func ExtractMboxFromZipWithLimits(zipPath, destDir string, limits ExtractLimits, if err != nil { return nil, fmt.Errorf("open zip: %w", err) } - defer zr.Close() + defer func() { _ = zr.Close() }() var outFiles []string seenNames := make(map[string]struct{}) @@ -259,7 +259,7 @@ func ExtractMboxFromZipWithLimits(zipPath, destDir string, limits ExtractLimits, w, err := fileutil.SecureOpenFile(outPath, os.O_CREATE|os.O_WRONLY|os.O_EXCL, 0600) if err != nil { - rc.Close() + _ = rc.Close() return nil, fmt.Errorf("create extracted file: %w", err) } @@ -494,7 +494,7 @@ func crc32File(path string) (uint32, error) { if err != nil { return 0, err } - defer f.Close() + defer func() { _ = f.Close() }() h := crc32.NewIEEE() if _, err := io.Copy(h, f); err != nil { @@ -508,7 +508,7 @@ func expectedMboxFilesFromZip(zipPath, destDir string, limits ExtractLimits) ([] if err != nil { return nil, fmt.Errorf("open zip: %w", err) } - defer zr.Close() + defer func() { _ = zr.Close() }() var expected []expectedMboxFile seenNames := make(map[string]struct{}) diff --git a/internal/mcp/handlers.go b/internal/mcp/handlers.go index 1491c466..2ca99e71 100644 --- a/internal/mcp/handlers.go +++ b/internal/mcp/handlers.go @@ -83,7 +83,7 @@ func (h *handlers) readAttachmentFile(contentHash string) ([]byte, error) { if err != nil { return nil, fmt.Errorf("attachment file not available: %v", err) } - defer f.Close() + defer func() { _ = f.Close() }() info, err := f.Stat() if err != nil { @@ -284,11 +284,11 @@ func (h *handlers) exportAttachment(ctx context.Context, req mcp.CallToolRequest _, writeErr := f.Write(data) closeErr := f.Close() if writeErr != nil { - os.Remove(outPath) + _ = os.Remove(outPath) return mcp.NewToolResultError(fmt.Sprintf("write failed: %v", writeErr)), nil } if closeErr != nil { - os.Remove(outPath) + _ = os.Remove(outPath) return mcp.NewToolResultError(fmt.Sprintf("write failed: %v", closeErr)), nil } diff --git a/internal/oauth/oauth.go b/internal/oauth/oauth.go index 9742f3d1..33b228ac 100644 --- a/internal/oauth/oauth.go +++ b/internal/oauth/oauth.go @@ -88,7 +88,7 @@ func (m *Manager) TokenSource(ctx context.Context, email string) (oauth2.TokenSo return nil, fmt.Errorf("refresh token: %w", err) } - if newToken.AccessToken != tf.Token.AccessToken { + if newToken.AccessToken != tf.AccessToken { // Preserve the original scopes when saving refreshed token scopes := tf.Scopes if len(scopes) == 0 { @@ -211,17 +211,17 @@ func (m *Manager) newCallbackHandler(expectedState string, codeChan chan<- strin return func(w http.ResponseWriter, r *http.Request) { if r.URL.Query().Get("state") != expectedState { errChan <- fmt.Errorf("state mismatch: possible CSRF attack") - fmt.Fprintf(w, "Error: state mismatch") + _, _ = fmt.Fprintf(w, "Error: state mismatch") return } code := r.URL.Query().Get("code") if code == "" { errChan <- fmt.Errorf("no code in callback") - fmt.Fprintf(w, "Error: no authorization code received") + _, _ = fmt.Fprintf(w, "Error: no authorization code received") return } codeChan <- code - fmt.Fprintf(w, "Authorization successful! You can close this window.") + _, _ = fmt.Fprintf(w, "Authorization successful! You can close this window.") } } @@ -517,20 +517,20 @@ func (m *Manager) saveToken(email string, token *oauth2.Token, scopes []string) tmpPath := tmpFile.Name() if _, err := tmpFile.Write(data); err != nil { - tmpFile.Close() - os.Remove(tmpPath) + _ = tmpFile.Close() + _ = os.Remove(tmpPath) return fmt.Errorf("write temp token file: %w", err) } if err := tmpFile.Close(); err != nil { - os.Remove(tmpPath) + _ = os.Remove(tmpPath) return fmt.Errorf("close temp token file: %w", err) } if err := fileutil.SecureChmod(tmpPath, 0600); err != nil { - os.Remove(tmpPath) + _ = os.Remove(tmpPath) return fmt.Errorf("chmod temp token file: %w", err) } if err := os.Rename(tmpPath, path); err != nil { - os.Remove(tmpPath) + _ = os.Remove(tmpPath) return fmt.Errorf("rename temp token file: %w", err) } return nil diff --git a/internal/query/duckdb.go b/internal/query/duckdb.go index d8465195..87af1cd7 100644 --- a/internal/query/duckdb.go +++ b/internal/query/duckdb.go @@ -90,7 +90,7 @@ func NewDuckDBEngine(analyticsDir string, sqlitePath string, sqliteDB *sql.DB, o // Use GOMAXPROCS(0) instead of NumCPU() to respect container CPU limits. threads := runtime.GOMAXPROCS(0) if _, err := db.Exec(fmt.Sprintf("SET threads = %d", threads)); err != nil { - db.Close() + _ = db.Close() return nil, fmt.Errorf("set threads: %w", err) } @@ -868,7 +868,7 @@ func (e *DuckDBEngine) executeAggregateQuery(ctx context.Context, query string, if err != nil { return nil, fmt.Errorf("aggregate query: %w", err) } - defer rows.Close() + defer func() { _ = rows.Close() }() var results []AggregateRow for rows.Next() { @@ -995,7 +995,7 @@ func (e *DuckDBEngine) ListAccounts(ctx context.Context) ([]AccountInfo, error) if err != nil { return nil, fmt.Errorf("list accounts: %w", err) } - defer rows.Close() + defer func() { _ = rows.Close() }() var accounts []AccountInfo for rows.Next() { @@ -1086,7 +1086,7 @@ func (e *DuckDBEngine) ListMessages(ctx context.Context, filter MessageFilter) ( if err != nil { return nil, fmt.Errorf("list messages: %w", err) } - defer rows.Close() + defer func() { _ = rows.Close() }() var results []MessageSummary for rows.Next() { @@ -1356,7 +1356,7 @@ func (e *DuckDBEngine) Search(ctx context.Context, q *search.Query, limit, offse if err != nil { return nil, fmt.Errorf("search messages: %w", err) } - defer rows.Close() + defer func() { _ = rows.Close() }() var results []MessageSummary for rows.Next() { @@ -1531,7 +1531,7 @@ func (e *DuckDBEngine) GetGmailIDsByFilter(ctx context.Context, filter MessageFi if err != nil { return nil, fmt.Errorf("get gmail ids: %w", err) } - defer rows.Close() + defer func() { _ = rows.Close() }() return collectGmailIDs(rows) } @@ -1649,7 +1649,7 @@ func (e *DuckDBEngine) SearchFast(ctx context.Context, q *search.Query, filter M if err != nil { return nil, fmt.Errorf("search fast: %w", err) } - defer rows.Close() + defer func() { _ = rows.Close() }() var results []MessageSummary for rows.Next() { @@ -1797,7 +1797,7 @@ func (e *DuckDBEngine) searchPageFromCache(ctx context.Context, limit, offset in if err != nil { return nil, fmt.Errorf("search fast page: %w", err) } - defer rows.Close() + defer func() { _ = rows.Close() }() // Return a copy of cached stats to prevent callers from mutating the cache var statsCopy *TotalStats diff --git a/internal/query/duckdb_test.go b/internal/query/duckdb_test.go index bc57c25b..fb2d3d88 100644 --- a/internal/query/duckdb_test.go +++ b/internal/query/duckdb_test.go @@ -35,7 +35,7 @@ func newSQLiteEngine(t *testing.T) *DuckDBEngine { if err != nil { t.Fatalf("NewDuckDBEngine: %v", err) } - t.Cleanup(func() { engine.Close() }) + t.Cleanup(func() { _ = engine.Close() }) return engine } @@ -193,7 +193,7 @@ func TestDuckDBEngine_SQLiteEngineReuse(t *testing.T) { if err != nil { t.Fatalf("NewDuckDBEngine: %v", err) } - defer engine.Close() + defer func() { _ = engine.Close() }() // Verify sqliteEngine was created if engine.sqliteEngine == nil { @@ -304,7 +304,7 @@ func TestDuckDBEngine_SQLiteEngineFTSCacheReuse(t *testing.T) { if err != nil { t.Fatalf("NewDuckDBEngine: %v", err) } - defer engine.Close() + defer func() { _ = engine.Close() }() // Capture the shared engine to verify cache state sharedEngine := engine.sqliteEngine @@ -377,7 +377,7 @@ func TestDuckDBEngine_NoSQLiteDB(t *testing.T) { if err != nil { t.Fatalf("NewDuckDBEngine: %v", err) } - defer engine.Close() + defer func() { _ = engine.Close() }() // sqliteEngine should be nil if engine.sqliteEngine != nil { @@ -452,7 +452,7 @@ func TestDuckDBEngine_DeletedMessagesIncluded(t *testing.T) { if err != nil { t.Fatalf("NewDuckDBEngine: %v", err) } - t.Cleanup(func() { engine.Close() }) + t.Cleanup(func() { _ = engine.Close() }) ctx := context.Background() @@ -490,7 +490,7 @@ func TestDuckDBEngine_SearchHideDeleted(t *testing.T) { if err != nil { t.Fatalf("NewDuckDBEngine: %v", err) } - t.Cleanup(func() { engine.Close() }) + t.Cleanup(func() { _ = engine.Close() }) ctx := context.Background() @@ -1136,7 +1136,7 @@ func TestDuckDBEngine_ThreadCount(t *testing.T) { if err != nil { t.Fatalf("NewDuckDBEngine: %v", err) } - defer engine.Close() + defer func() { _ = engine.Close() }() // Query the current thread setting var threads int @@ -1718,7 +1718,7 @@ func TestDuckDBEngine_GetGmailIDsByFilter_NoDataSource(t *testing.T) { if err != nil { t.Fatalf("NewDuckDBEngine: %v", err) } - defer engine.Close() + defer func() { _ = engine.Close() }() ctx := context.Background() _, err = engine.GetGmailIDsByFilter(ctx, MessageFilter{Sender: "test@example.com"}) diff --git a/internal/query/shared.go b/internal/query/shared.go index bfe1d157..eb149787 100644 --- a/internal/query/shared.go +++ b/internal/query/shared.go @@ -39,7 +39,7 @@ func fetchLabelsForMessageList(ctx context.Context, db *sql.DB, tablePrefix stri if err != nil { return err } - defer rows.Close() + defer func() { _ = rows.Close() }() for rows.Next() { var msgID int64 @@ -67,7 +67,7 @@ func fetchMessageLabelsDetail(ctx context.Context, db *sql.DB, tablePrefix strin if err != nil { return err } - defer rows.Close() + defer func() { _ = rows.Close() }() for rows.Next() { var name string @@ -92,7 +92,7 @@ func fetchParticipantsShared(ctx context.Context, db *sql.DB, tablePrefix string if err != nil { return err } - defer rows.Close() + defer func() { _ = rows.Close() }() for rows.Next() { var recipType, email, name string @@ -126,7 +126,7 @@ func fetchAttachmentsShared(ctx context.Context, db *sql.DB, tablePrefix string, if err != nil { return err } - defer rows.Close() + defer func() { _ = rows.Close() }() for rows.Next() { var att AttachmentInfo @@ -158,7 +158,7 @@ func extractBodyFromRawShared(ctx context.Context, db *sql.DB, tablePrefix strin if err != nil { return "", err } - defer r.Close() + defer func() { _ = r.Close() }() rawData, err = io.ReadAll(r) if err != nil { return "", err @@ -267,7 +267,7 @@ func getMessageByQueryShared(ctx context.Context, db *sql.DB, tablePrefix string // collectGmailIDs scans rows for source_message_id strings. func collectGmailIDs(rows *sql.Rows) ([]string, error) { - defer rows.Close() + defer func() { _ = rows.Close() }() var ids []string for rows.Next() { var id string diff --git a/internal/query/sqlite.go b/internal/query/sqlite.go index e0447b48..65504297 100644 --- a/internal/query/sqlite.go +++ b/internal/query/sqlite.go @@ -540,7 +540,7 @@ func (e *SQLiteEngine) executeAggregateQuery(ctx context.Context, query string, if err != nil { return nil, fmt.Errorf("aggregate query: %w", err) } - defer rows.Close() + defer func() { _ = rows.Close() }() var results []AggregateRow for rows.Next() { @@ -621,7 +621,7 @@ func (e *SQLiteEngine) ListMessages(ctx context.Context, filter MessageFilter) ( if err != nil { return nil, fmt.Errorf("list messages: %w", err) } - defer rows.Close() + defer func() { _ = rows.Close() }() var results []MessageSummary for rows.Next() { @@ -718,7 +718,7 @@ func (e *SQLiteEngine) ListAccounts(ctx context.Context) ([]AccountInfo, error) if err != nil { return nil, fmt.Errorf("list accounts: %w", err) } - defer rows.Close() + defer func() { _ = rows.Close() }() var accounts []AccountInfo for rows.Next() { @@ -988,7 +988,7 @@ func (e *SQLiteEngine) GetGmailIDsByFilter(ctx context.Context, filter MessageFi if err != nil { return nil, fmt.Errorf("get gmail ids: %w", err) } - defer rows.Close() + defer func() { _ = rows.Close() }() return collectGmailIDs(rows) } @@ -1216,7 +1216,7 @@ func (e *SQLiteEngine) executeSearchQuery(ctx context.Context, conditions []stri if err != nil { return nil, fmt.Errorf("search messages: %w", err) } - defer rows.Close() + defer func() { _ = rows.Close() }() var results []MessageSummary for rows.Next() { diff --git a/internal/query/testfixtures_test.go b/internal/query/testfixtures_test.go index b501af13..c022fdfc 100644 --- a/internal/query/testfixtures_test.go +++ b/internal/query/testfixtures_test.go @@ -431,7 +431,7 @@ func (b *TestDataBuilder) BuildEngine() *DuckDBEngine { if err != nil { b.t.Fatalf("NewDuckDBEngine: %v", err) } - b.t.Cleanup(func() { engine.Close() }) + b.t.Cleanup(func() { _ = engine.Close() }) return engine } @@ -495,14 +495,14 @@ func (b *parquetBuilder) build() (string, func()) { db, err := sql.Open("duckdb", "") if err != nil { - os.RemoveAll(tmpDir) + _ = os.RemoveAll(tmpDir) b.t.Fatalf("open duckdb: %v", err) } - defer db.Close() + defer func() { _ = db.Close() }() b.writeParquetFiles(db, tmpDir) - return tmpDir, func() { os.RemoveAll(tmpDir) } + return tmpDir, func() { _ = os.RemoveAll(tmpDir) } } // createTempDirs creates the temp directory and all required subdirectories. @@ -517,7 +517,7 @@ func (b *parquetBuilder) createTempDirs() string { for _, tbl := range b.tables { dir := filepath.Join(tmpDir, tbl.subdir) if err := os.MkdirAll(dir, 0755); err != nil { - os.RemoveAll(tmpDir) + _ = os.RemoveAll(tmpDir) b.t.Fatalf("create dir %s: %v", dir, err) } } @@ -573,7 +573,7 @@ func createEngineFromBuilder(t testing.TB, pb *parquetBuilder) *DuckDBEngine { if err != nil { t.Fatalf("NewDuckDBEngine: %v", err) } - t.Cleanup(func() { engine.Close() }) + t.Cleanup(func() { _ = engine.Close() }) return engine } diff --git a/internal/query/testfixtures_validation_test.go b/internal/query/testfixtures_validation_test.go index 934873c9..bac13e58 100644 --- a/internal/query/testfixtures_validation_test.go +++ b/internal/query/testfixtures_validation_test.go @@ -84,7 +84,7 @@ func TestAddMessage_UsesFirstSource(t *testing.T) { // Also verify through the engine that the data is correctly built engine := b.BuildEngine() - defer engine.Close() + defer func() { _ = engine.Close() }() stats, err := engine.GetTotalStats(context.Background(), StatsOptions{}) if err != nil { @@ -121,7 +121,7 @@ func TestBuild_EmptyAuxiliaryTables(t *testing.T) { }) engine := b.BuildEngine() - defer engine.Close() + defer func() { _ = engine.Close() }() // Should be able to query without errors. stats, err := engine.GetTotalStats(context.Background(), StatsOptions{}) diff --git a/internal/remote/engine.go b/internal/remote/engine.go index bb9bad06..b1b06925 100644 --- a/internal/remote/engine.go +++ b/internal/remote/engine.go @@ -397,7 +397,7 @@ func (e *Engine) Aggregate(ctx context.Context, groupBy query.ViewType, opts que if err != nil { return nil, err } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode != 200 { return nil, handleErrorResponse(resp) @@ -434,7 +434,7 @@ func (e *Engine) SubAggregate(ctx context.Context, filter query.MessageFilter, g if err != nil { return nil, err } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode != 200 { return nil, handleErrorResponse(resp) @@ -457,7 +457,7 @@ func (e *Engine) ListMessages(ctx context.Context, filter query.MessageFilter) ( if err != nil { return nil, err } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode != 200 { return nil, handleErrorResponse(resp) @@ -557,7 +557,7 @@ func (e *Engine) Search(ctx context.Context, q *search.Query, limit, offset int) if err != nil { return nil, err } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode != 200 { return nil, handleErrorResponse(resp) @@ -607,7 +607,7 @@ func (e *Engine) SearchFastWithStats(ctx context.Context, q *search.Query, query if err != nil { return nil, err } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode != 200 { return nil, handleErrorResponse(resp) @@ -670,7 +670,7 @@ func (e *Engine) GetTotalStats(ctx context.Context, opts query.StatsOptions) (*q if err != nil { return nil, err } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode != 200 { return nil, handleErrorResponse(resp) diff --git a/internal/remote/store.go b/internal/remote/store.go index a817df4b..c35296ae 100644 --- a/internal/remote/store.go +++ b/internal/remote/store.go @@ -137,7 +137,7 @@ func (s *Store) GetStats() (*store.Stats, error) { if err != nil { return nil, err } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode != http.StatusOK { return nil, handleErrorResponse(resp) @@ -244,7 +244,7 @@ func (s *Store) ListMessages(offset, limit int) ([]store.APIMessage, int64, erro if err != nil { return nil, 0, err } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode != http.StatusOK { return nil, 0, handleErrorResponse(resp) @@ -270,7 +270,7 @@ func (s *Store) GetMessage(id int64) (*store.APIMessage, error) { if err != nil { return nil, err } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode == http.StatusNotFound { return nil, nil @@ -325,7 +325,7 @@ func (s *Store) SearchMessages(query string, offset, limit int) ([]store.APIMess if err != nil { return nil, 0, err } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode != http.StatusOK { return nil, 0, handleErrorResponse(resp) @@ -366,7 +366,7 @@ func (s *Store) ListAccounts() ([]AccountInfo, error) { if err != nil { return nil, err } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode != http.StatusOK { return nil, handleErrorResponse(resp) diff --git a/internal/remote/store_test.go b/internal/remote/store_test.go index 6081fb79..5cdc3485 100644 --- a/internal/remote/store_test.go +++ b/internal/remote/store_test.go @@ -134,7 +134,7 @@ func TestDoRequest_SetsAuthHeader(t *testing.T) { if err != nil { t.Fatalf("doRequest error = %v", err) } - resp.Body.Close() + _ = resp.Body.Close() } func TestDoRequest_OmitsAuthHeaderWhenEmpty(t *testing.T) { @@ -151,7 +151,7 @@ func TestDoRequest_OmitsAuthHeaderWhenEmpty(t *testing.T) { if err != nil { t.Fatalf("doRequest error = %v", err) } - resp.Body.Close() + _ = resp.Body.Close() } func TestHandleErrorResponse_JSONBody(t *testing.T) { diff --git a/internal/store/api.go b/internal/store/api.go index e5ead2a7..02a5f2ee 100644 --- a/internal/store/api.go +++ b/internal/store/api.go @@ -64,7 +64,7 @@ func (s *Store) ListMessages(offset, limit int) ([]APIMessage, int64, error) { if err != nil { return nil, 0, err } - defer rows.Close() + defer func() { _ = rows.Close() }() // Use scanMessageRows for robust date parsing messages, ids, err := scanMessageRows(rows) @@ -161,7 +161,7 @@ func (s *Store) GetMessage(id int64) (*APIMessage, error) { // Get attachments attRows, err := s.db.Query("SELECT filename, mime_type, size FROM attachments WHERE message_id = ?", id) if err == nil { - defer attRows.Close() + defer func() { _ = attRows.Close() }() for attRows.Next() { var att APIAttachment if err := attRows.Scan(&att.Filename, &att.MimeType, &att.Size); err == nil { @@ -202,7 +202,7 @@ func (s *Store) SearchMessages(query string, offset, limit int) ([]APIMessage, i // FTS5 might not be available, fall back to LIKE search return s.searchMessagesLike(query, offset, limit) } - defer rows.Close() + defer func() { _ = rows.Close() }() messages, ids, err := scanMessageRows(rows) if err != nil { @@ -279,7 +279,7 @@ func (s *Store) searchMessagesLike(query string, offset, limit int) ([]APIMessag if err != nil { return nil, 0, err } - defer rows.Close() + defer func() { _ = rows.Close() }() messages, ids, err := scanMessageRows(rows) if err != nil { @@ -389,7 +389,7 @@ func (s *Store) batchGetRecipients(messageIDs []int64, recipientType string) (ma if err != nil { return nil, fmt.Errorf("batch get recipients: %w", err) } - defer rows.Close() + defer func() { _ = rows.Close() }() result := make(map[int64][]string, len(messageIDs)) for rows.Next() { @@ -432,7 +432,7 @@ func (s *Store) batchGetLabels(messageIDs []int64) (map[int64][]string, error) { if err != nil { return nil, fmt.Errorf("batch get labels: %w", err) } - defer rows.Close() + defer func() { _ = rows.Close() }() result := make(map[int64][]string, len(messageIDs)) for rows.Next() { @@ -462,7 +462,7 @@ func (s *Store) getRecipients(messageID int64, recipientType string) ([]string, if err != nil { return nil, fmt.Errorf("get recipients: %w", err) } - defer rows.Close() + defer func() { _ = rows.Close() }() var recipients []string for rows.Next() { @@ -491,7 +491,7 @@ func (s *Store) getLabels(messageID int64) ([]string, error) { if err != nil { return nil, fmt.Errorf("get labels: %w", err) } - defer rows.Close() + defer func() { _ = rows.Close() }() var labels []string for rows.Next() { diff --git a/internal/store/api_test.go b/internal/store/api_test.go index bb31e53c..d35663e4 100644 --- a/internal/store/api_test.go +++ b/internal/store/api_test.go @@ -44,7 +44,7 @@ func openTestStore(t *testing.T) *Store { if err := st.InitSchema(); err != nil { t.Fatalf("InitSchema: %v", err) } - t.Cleanup(func() { st.Close() }) + t.Cleanup(func() { _ = st.Close() }) return st } diff --git a/internal/store/inspect.go b/internal/store/inspect.go index 8dc64487..7f06181d 100644 --- a/internal/store/inspect.go +++ b/internal/store/inspect.go @@ -79,7 +79,7 @@ func (s *Store) InspectMessage(sourceMessageID string) (*MessageInspection, erro if err != nil { return nil, err } - defer rows.Close() + defer func() { _ = rows.Close() }() for rows.Next() { var recipType string var count int @@ -103,7 +103,7 @@ func (s *Store) InspectMessage(sourceMessageID string) (*MessageInspection, erro if err != nil { return nil, err } - defer rows.Close() + defer func() { _ = rows.Close() }() for rows.Next() { var recipType, email, displayName string if err := rows.Scan(&recipType, &email, &displayName); err != nil { diff --git a/internal/store/messages.go b/internal/store/messages.go index eda58fd1..1543ffd6 100644 --- a/internal/store/messages.go +++ b/internal/store/messages.go @@ -301,7 +301,7 @@ func (s *Store) GetMessageRaw(messageID int64) ([]byte, error) { if err != nil { return nil, fmt.Errorf("zlib reader: %w", err) } - defer r.Close() + defer func() { _ = r.Close() }() return io.ReadAll(r) } @@ -728,7 +728,7 @@ func (s *Store) GetRandomMessageIDs(sourceID int64, limit int) ([]int64, error) if err != nil { return nil, err } - defer rows.Close() + defer func() { _ = rows.Close() }() var ids []int64 for rows.Next() { diff --git a/internal/store/store.go b/internal/store/store.go index 483913d2..7e71f3e7 100644 --- a/internal/store/store.go +++ b/internal/store/store.go @@ -63,7 +63,7 @@ func Open(dbPath string) (*Store, error) { // Test connection if err := db.Ping(); err != nil { - db.Close() + _ = db.Close() return nil, fmt.Errorf("ping database: %w", err) } @@ -154,11 +154,11 @@ func queryInChunks[T any](db *sql.DB, ids []T, prefixArgs []interface{}, queryTe for rows.Next() { if err := fn(rows); err != nil { - rows.Close() + _ = rows.Close() return err } } - rows.Close() + _ = rows.Close() if err := rows.Err(); err != nil { return err } diff --git a/internal/store/sync.go b/internal/store/sync.go index d63ec568..b5f29d88 100644 --- a/internal/store/sync.go +++ b/internal/store/sync.go @@ -344,7 +344,7 @@ func (s *Store) ListSources(sourceType string) ([]*Source, error) { if err != nil { return nil, fmt.Errorf("list sources: %w", err) } - defer rows.Close() + defer func() { _ = rows.Close() }() var sources []*Source for rows.Next() { diff --git a/internal/sync/testenv_test.go b/internal/sync/testenv_test.go index 4aff1456..a61a0d70 100644 --- a/internal/sync/testenv_test.go +++ b/internal/sync/testenv_test.go @@ -34,14 +34,14 @@ func newTestEnv(t *testing.T, opt ...*Options) *TestEnv { if err != nil { t.Fatalf("create temp dir: %v", err) } - t.Cleanup(func() { os.RemoveAll(tmpDir) }) + t.Cleanup(func() { _ = os.RemoveAll(tmpDir) }) dbPath := filepath.Join(tmpDir, "test.db") st, err := store.Open(dbPath) if err != nil { t.Fatalf("open store: %v", err) } - t.Cleanup(func() { st.Close() }) + t.Cleanup(func() { _ = st.Close() }) if err := st.InitSchema(); err != nil { t.Fatalf("init schema: %v", err) diff --git a/internal/testutil/archive_helpers.go b/internal/testutil/archive_helpers.go index aee55f76..9c8ad3a2 100644 --- a/internal/testutil/archive_helpers.go +++ b/internal/testutil/archive_helpers.go @@ -26,12 +26,12 @@ func CreateTarGz(t *testing.T, path string, entries []ArchiveEntry) { if err != nil { t.Fatal(err) } - defer f.Close() + defer func() { _ = f.Close() }() gzw := gzip.NewWriter(f) - defer gzw.Close() + defer func() { _ = gzw.Close() }() tw := tar.NewWriter(gzw) - defer tw.Close() + defer func() { _ = tw.Close() }() for _, e := range entries { mode := e.Mode @@ -63,7 +63,7 @@ func CreateZip(t *testing.T, path string, entries []ArchiveEntry) { if err != nil { t.Fatal(err) } - defer f.Close() + defer func() { _ = f.Close() }() w := zip.NewWriter(f) for _, e := range entries { @@ -92,7 +92,7 @@ func CreateTempZip(t *testing.T, entries map[string]string) string { if err != nil { t.Fatalf("create zip file: %v", err) } - defer f.Close() + defer func() { _ = f.Close() }() w := zip.NewWriter(f) keys := make([]string, 0, len(entries)) diff --git a/internal/testutil/dbtest/dbtest.go b/internal/testutil/dbtest/dbtest.go index 3cf7af25..8b37db28 100644 --- a/internal/testutil/dbtest/dbtest.go +++ b/internal/testutil/dbtest/dbtest.go @@ -37,7 +37,7 @@ func NewTestDB(t testing.TB, schemaPath string) *TestDB { if err != nil { t.Fatalf("open db: %v", err) } - t.Cleanup(func() { db.Close() }) + t.Cleanup(func() { _ = db.Close() }) schema, err := os.ReadFile(schemaPath) if err != nil { diff --git a/internal/testutil/dbtest/dbtest_test.go b/internal/testutil/dbtest/dbtest_test.go index 8c0d7bbf..ba06a6c8 100644 --- a/internal/testutil/dbtest/dbtest_test.go +++ b/internal/testutil/dbtest/dbtest_test.go @@ -82,7 +82,7 @@ func TestAddMessage_DBErrorFailsTest(t *testing.T) { } // Close the DB to force a non-ErrNoRows error on the source_id lookup. - tdb.DB.Close() + _ = tdb.DB.Close() func() { defer func() { _ = recover() }() diff --git a/internal/testutil/email/builder_test.go b/internal/testutil/email/builder_test.go index ab832c0a..958abfe3 100644 --- a/internal/testutil/email/builder_test.go +++ b/internal/testutil/email/builder_test.go @@ -69,7 +69,7 @@ func TestHeaderOrder(t *testing.T) { if i1 < 0 || i2 < 0 || i3 < 0 { t.Fatalf("missing headers in output:\n%s", got) } - if !(i1 < i2 && i2 < i3) { + if i1 >= i2 || i2 >= i3 { t.Errorf("headers not in insertion order: positions %d, %d, %d", i1, i2, i3) } } diff --git a/internal/testutil/store_helpers.go b/internal/testutil/store_helpers.go index 7721bbb7..79d8f70b 100644 --- a/internal/testutil/store_helpers.go +++ b/internal/testutil/store_helpers.go @@ -20,7 +20,7 @@ func NewTestStore(t *testing.T) *store.Store { // Register close on cleanup t.Cleanup(func() { - st.Close() + _ = st.Close() }) // Initialize schema diff --git a/internal/tui/actions_test.go b/internal/tui/actions_test.go index 35b3fe3a..672eef39 100644 --- a/internal/tui/actions_test.go +++ b/internal/tui/actions_test.go @@ -334,7 +334,7 @@ func TestExportAttachments_PartialSuccess(t *testing.T) { // Clean up the zip file that gets created in current directory. // TODO: ExportAttachments should write to a configurable output directory. - t.Cleanup(func() { os.Remove("Test_1.zip") }) + t.Cleanup(func() { _ = os.Remove("Test_1.zip") }) // Create a valid attachment file (must be valid 64-char hex SHA-256 hash) validHash := "abc123def456abc123def456abc123def456abc123def456abc123def456abc1" @@ -386,7 +386,7 @@ func TestExportAttachments_FullSuccess(t *testing.T) { // Clean up the zip file that gets created in current directory. // TODO: ExportAttachments should write to a configurable output directory. - t.Cleanup(func() { os.Remove("Test_1.zip") }) + t.Cleanup(func() { _ = os.Remove("Test_1.zip") }) // Create a valid attachment file (must be valid 64-char hex SHA-256 hash) validHash := "abc123def456abc123def456abc123def456abc123def456abc123def456abc1" diff --git a/internal/tui/view.go b/internal/tui/view.go index 48df6208..eae7f8af 100644 --- a/internal/tui/view.go +++ b/internal/tui/view.go @@ -1197,7 +1197,7 @@ func (m Model) renderDeleteConfirmModal() string { var sb strings.Builder sb.WriteString(modalTitleStyle.Render("Confirm Deletion")) sb.WriteString("\n\n") - sb.WriteString(fmt.Sprintf("Stage %d messages for deletion?\n\n", len(m.pendingManifest.GmailIDs))) + _, _ = fmt.Fprintf(&sb, "Stage %d messages for deletion?\n\n", len(m.pendingManifest.GmailIDs)) sb.WriteString("This creates a deletion batch. Messages will NOT be\n") sb.WriteString("deleted until you run 'msgvault delete-staged'.\n\n") if m.pendingManifest.Filters.Account == "" { @@ -1231,14 +1231,14 @@ func (m Model) renderAccountSelectorModal() string { if m.modalCursor == 0 { indicator = "●" } - sb.WriteString(fmt.Sprintf(" %s All Accounts\n", indicator)) + _, _ = fmt.Fprintf(&sb, " %s All Accounts\n", indicator) // Individual accounts for i, acc := range m.accounts { indicator = "β—‹" if m.modalCursor == i+1 { indicator = "●" } - sb.WriteString(fmt.Sprintf(" %s %s\n", indicator, acc.Identifier)) + _, _ = fmt.Fprintf(&sb, " %s %s\n", indicator, acc.Identifier) } sb.WriteString("\n[↑/↓] Navigate [Enter] Select [Esc] Cancel") return sb.String() @@ -1268,7 +1268,7 @@ func (m Model) renderFilterModal() string { if opt.checked { checkbox = "[x]" } - sb.WriteString(fmt.Sprintf("%s%s %s\n", cursor, checkbox, opt.label)) + _, _ = fmt.Fprintf(&sb, "%s%s %s\n", cursor, checkbox, opt.label) } sb.WriteString("\n[↑/↓] Navigate [Space/x] Toggle [Enter/Esc] Apply") @@ -1321,7 +1321,7 @@ func (m Model) renderExportAttachmentsModal() string { if m.exportSelection[i] { checkbox = "β˜‘" } - sb.WriteString(fmt.Sprintf("%s %s %s (%s)\n", cursor, checkbox, att.Filename, formatBytes(att.Size))) + _, _ = fmt.Fprintf(&sb, "%s %s %s (%s)\n", cursor, checkbox, att.Filename, formatBytes(att.Size)) } // Count selected selectedCount := 0 @@ -1330,7 +1330,7 @@ func (m Model) renderExportAttachmentsModal() string { selectedCount++ } } - sb.WriteString(fmt.Sprintf("\n%d of %d selected\n", selectedCount, len(m.messageDetail.Attachments))) + _, _ = fmt.Fprintf(&sb, "\n%d of %d selected\n", selectedCount, len(m.messageDetail.Attachments)) sb.WriteString("\n[↑/↓] Navigate [Space] Toggle [a] All [n] None\n") sb.WriteString("[Enter] Export [Esc] Cancel") return sb.String() diff --git a/internal/update/update.go b/internal/update/update.go index 1cf21abd..bef2323e 100644 --- a/internal/update/update.go +++ b/internal/update/update.go @@ -139,7 +139,7 @@ func PerformUpdate(info *UpdateInfo, progressFn func(downloaded, total int64)) e if err != nil { return err } - defer os.RemoveAll(tempDir) + defer func() { _ = os.RemoveAll(tempDir) }() archivePath := filepath.Join(tempDir, info.AssetName) downloadChecksum, err := downloadFile(info.DownloadURL, archivePath, info.Size, progressFn) @@ -161,7 +161,7 @@ func hashFile(path string) (string, error) { if err != nil { return "", err } - defer f.Close() + defer func() { _ = f.Close() }() h := sha256.New() if _, err := io.Copy(h, f); err != nil { @@ -198,7 +198,7 @@ func installFromArchiveTo(archivePath, expectedChecksum, dstPath string, precomp if err != nil { return err } - defer os.RemoveAll(extractDir) + defer func() { _ = os.RemoveAll(extractDir) }() if strings.HasSuffix(archivePath, ".zip") { if err := extractZip(archivePath, extractDir); err != nil { @@ -263,7 +263,7 @@ func installBinaryTo(srcPath, dstPath string) error { // Remove stale backup from a previous update. On Windows this may fail // if the previous binary is still running; that's fine β€” it will be // cleaned up on the next successful update. - os.Remove(backupPath) + _ = os.Remove(backupPath) // Backup existing binary if it exists (rename works even on Windows // for the currently running executable). @@ -287,7 +287,7 @@ func installBinaryTo(srcPath, dstPath string) error { // Clean up backup. On Windows this will fail for the running executable // (silently ignored); the stale .old file is removed at the top of the // next update. - os.Remove(backupPath) + _ = os.Remove(backupPath) return nil } @@ -309,7 +309,7 @@ func fetchLatestRelease() (*Release, error) { if err != nil { return nil, err } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("GitHub API returned %s", resp.Status) @@ -328,7 +328,7 @@ func downloadFile(url, dest string, totalSize int64, progressFn func(downloaded, if err != nil { return "", err } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode != http.StatusOK { return "", fmt.Errorf("download failed: %s", resp.Status) @@ -338,7 +338,7 @@ func downloadFile(url, dest string, totalSize int64, progressFn func(downloaded, if err != nil { return "", err } - defer out.Close() + defer func() { _ = out.Close() }() hasher := sha256.New() writer := io.MultiWriter(out, hasher) @@ -382,13 +382,13 @@ func extractTarGz(archivePath, destDir string) error { if err != nil { return err } - defer file.Close() + defer func() { _ = file.Close() }() gzr, err := gzip.NewReader(file) if err != nil { return err } - defer gzr.Close() + defer func() { _ = gzr.Close() }() tr := tar.NewReader(gzr) for { @@ -424,10 +424,10 @@ func extractTarGz(archivePath, destDir string) error { return err } if _, err := io.Copy(outFile, tr); err != nil { - outFile.Close() + _ = outFile.Close() return err } - outFile.Close() + _ = outFile.Close() if err := os.Chmod(target, os.FileMode(header.Mode)); err != nil { return err } @@ -484,7 +484,7 @@ func extractZip(archivePath, destDir string) error { if err != nil { return err } - defer r.Close() + defer func() { _ = r.Close() }() for _, f := range r.File { target, err := sanitizeTarPath(absDestDir, f.Name) @@ -510,13 +510,13 @@ func extractZip(archivePath, destDir string) error { outFile, err := os.Create(target) if err != nil { - rc.Close() + _ = rc.Close() return err } _, copyErr := io.Copy(outFile, rc) closeErr := outFile.Close() - rc.Close() + _ = rc.Close() if copyErr != nil { return copyErr } @@ -533,13 +533,13 @@ func copyFile(src, dst string) error { if err != nil { return err } - defer in.Close() + defer func() { _ = in.Close() }() out, err := os.Create(dst) if err != nil { return err } - defer out.Close() + defer func() { _ = out.Close() }() if _, err := io.Copy(out, in); err != nil { return err @@ -554,7 +554,7 @@ func fetchChecksumFromFile(url, assetName string) (string, error) { if err != nil { return "", err } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() if resp.StatusCode != http.StatusOK { return "", fmt.Errorf("failed to fetch checksums: %s", resp.Status) diff --git a/scripts/mimeshootout/main.go b/scripts/mimeshootout/main.go index bd9409a0..4d8e444e 100644 --- a/scripts/mimeshootout/main.go +++ b/scripts/mimeshootout/main.go @@ -97,7 +97,7 @@ func main() { logger.Error("failed to open database", "error", err, "path", dbPath) os.Exit(1) } - defer db.Close() + defer func() { _ = db.Close() }() // Get messages with raw MIME data messages, err := loadMessages(db, limit) @@ -306,7 +306,7 @@ func loadMessages(db *sql.DB, limit int) ([]PythonMessage, error) { if err != nil { return nil, fmt.Errorf("query messages: %w", err) } - defer rows.Close() + defer func() { _ = rows.Close() }() var messages []PythonMessage for rows.Next() { @@ -340,7 +340,7 @@ func loadParticipants(db *sql.DB, messageID int64, recipientType string) ([]Addr if err != nil { return nil, err } - defer rows.Close() + defer func() { _ = rows.Close() }() var addresses []Address for rows.Next() { @@ -372,7 +372,7 @@ func loadRawMime(db *sql.DB, messageID int64) ([]byte, error) { if err != nil { return nil, fmt.Errorf("zlib reader: %w", err) } - defer r.Close() + defer func() { _ = r.Close() }() return io.ReadAll(r) } From ef08fe27d3b2cf42ec9bf7be1c4a21152c613d74 Mon Sep 17 00:00:00 2001 From: Wes McKinney Date: Mon, 30 Mar 2026 22:45:13 -0500 Subject: [PATCH 5/5] fix: update nix vendorHash for dependency changes Co-Authored-By: Claude Opus 4.6 (1M context) --- flake.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flake.nix b/flake.nix index becb27da..16ca3e60 100644 --- a/flake.nix +++ b/flake.nix @@ -29,7 +29,7 @@ pname = "msgvault"; version = "0.11.0"; src = ./.; - vendorHash = "sha256-o7yjPy1pDkD6Ia1H/4Ny/GYqfwv4Vbsd86bQJY6IiVo="; + vendorHash = "sha256-gvK36/Vd2eN7Fy315Y/OpbZwPLIXnRj+7C3YLfTa5a0="; proxyVendor = true; subPackages = [ "cmd/msgvault" ]; tags = [ "fts5" ];