hiiro 0.1.308.pre.6 → 0.1.308

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (5) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/bin/h-db +98 -0
  4. data/lib/hiiro/version.rb +1 -1
  5. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c2e93dab3f776838c51b113721effb0ac86a0cbc20dd9a61742004d5a5335a6e
4
- data.tar.gz: a1ae67e29ac11aec8f3e476dbd708cf7ec3d5b85967d78a1565c3df193af4a1d
3
+ metadata.gz: 293e176ea2c898019b001c0db7277c079980b304b43199dc270d31d91a869cca
4
+ data.tar.gz: c3b6118daf238da019e7f739b2b602d6eab1f6b643c18d3dfb6f502335887e4b
5
5
  SHA512:
6
- metadata.gz: bea26329f94d13d1806fb90603b9c23c7f41dd42eb1e954ba8d6c6a4bd2f8e9ed88e80770314e4b3a6ec0223115f9a82ae633e547e4252ba2b84626ff3dee6dd
7
- data.tar.gz: 36c1711d34c509e4070aed248c030601a87801a02e8a15aa2ad806ed475d1e2f8792b30903c35ea3a513ed479db5ec903ca95a1bf099285840ae5453bc64fa15
6
+ metadata.gz: d5878d6857a592da9b566b93eb8df31918196584e58c1f8ee4b637bbf281fbf0c8e72fd207d9751754eadadfbde8124d35c2b1f178bce9426d3809bfffe7494b
7
+ data.tar.gz: 6217a52654077df7be803b923de5a87fb58efb3e0dce4ec51601e4cd040e98a2f911e70755ced173e5b3764de291acece7792259eb97ee1a19b76a3f04e4839a
data/CHANGELOG.md CHANGED
@@ -1,6 +1,14 @@
1
1
  ```markdown
2
2
  # Changelog
3
3
 
4
+ ## [0.1.308] - 2026-03-31
5
+
6
+ ### Added
7
+ - `h db cleanup` subcommand to preview and prune duplicate rows from SQLite tables
8
+
9
+ ### Fixed
10
+ - Prevent duplicate pinned_prs during import with `insert_conflict` and per-row rescue
11
+
4
12
  ## [0.1.308.pre.6] - 2026-03-31
5
13
 
6
14
  ### Fixed
data/bin/h-db CHANGED
@@ -171,6 +171,104 @@ Hiiro.run(*ARGV) do
171
171
  Hiiro::DB.remigrate!(only: tables)
172
172
  end
173
173
 
174
+ add_subcmd(:cleanup) do
175
+ require 'fileutils'
176
+ db = Hiiro::DB.connection
177
+ timestamp = Time.now.strftime('%Y%m%d%H%M%S')
178
+ sql_path = File.expand_path("~/notes/files/hiiro-cleanup-#{timestamp}.sql")
179
+ FileUtils.mkdir_p(File.dirname(sql_path))
180
+
181
+ # Natural unique keys per table — used to detect duplicate groups
182
+ dedup_keys = {
183
+ prs: %w[number],
184
+ branches: %w[name],
185
+ tags: %w[name taggable_type taggable_id],
186
+ links: %w[url],
187
+ tasks: %w[name],
188
+ apps: %w[name],
189
+ projects: %w[name],
190
+ assignments: %w[worktree],
191
+ pins: %w[command key],
192
+ pane_homes: %w[name],
193
+ check_runs: %w[pr_number name],
194
+ reminders: %w[text],
195
+ }
196
+
197
+ lines = []
198
+ lines << "-- Hiiro DB duplicate cleanup — #{Time.now.iso8601}"
199
+ lines << "-- Review each statement. Remove lines you do NOT want to run, then:"
200
+ lines << "--"
201
+ lines << "-- sqlite3 #{Hiiro::DB::DB_FILE} < #{sql_path}"
202
+ lines << "--"
203
+ lines << "-- Or run individual statements with:"
204
+ lines << "--"
205
+ lines << "-- h db q \"DELETE FROM ...\""
206
+ lines << "--"
207
+ lines << ""
208
+
209
+ found_any = false
210
+
211
+ dedup_keys.each do |table, keys|
212
+ next unless db.table_exists?(table)
213
+ schema_cols = db.schema(table).map { |col, _| col.to_s }
214
+ next unless (keys - schema_cols).empty?
215
+ next unless schema_cols.include?('id')
216
+
217
+ key_cols_sql = keys.map { |k| "\"#{k}\"" }.join(', ')
218
+ dupes = db.fetch(<<~SQL).all
219
+ SELECT #{key_cols_sql}, COUNT(*) AS cnt
220
+ FROM #{table}
221
+ GROUP BY #{key_cols_sql}
222
+ HAVING cnt > 1
223
+ SQL
224
+ next if dupes.empty?
225
+
226
+ found_any = true
227
+ lines << "-- ===== #{table}: #{dupes.length} duplicate group#{'s' unless dupes.length == 1} ====="
228
+ lines << ""
229
+
230
+ dupes.each do |row|
231
+ # Build a Sequel dataset filtered to this duplicate group
232
+ ds = db[table]
233
+ keys.each do |k|
234
+ val = row[k.to_sym]
235
+ ds = val.nil? ? ds.where(Sequel.lit("\"#{k}\" IS NULL")) : ds.where(k.to_sym => val)
236
+ end
237
+
238
+ all_ids = ds.select(:id).map(:id).sort
239
+ keep_id = all_ids.max
240
+ drop_ids = all_ids - [keep_id]
241
+
242
+ key_desc = keys.map { |k| "#{k}=#{row[k.to_sym].inspect}" }.join(', ')
243
+ lines << "-- #{table}: #{key_desc}"
244
+ lines << "-- keeping id=#{keep_id}, dropping id=#{drop_ids.join(', ')}"
245
+ lines << "DELETE FROM #{table} WHERE id IN (#{drop_ids.join(', ')});"
246
+ lines << ""
247
+ end
248
+ end
249
+
250
+ if found_any
251
+ File.write(sql_path, lines.join("\n"))
252
+
253
+ # Update ~/notes/files/INDEX
254
+ index_path = File.expand_path('~/notes/files/INDEX')
255
+ File.open(index_path, 'a') { |f| f.puts sql_path }
256
+
257
+ edit_files(sql_path)
258
+
259
+ puts
260
+ puts "SQL file: #{sql_path}"
261
+ puts
262
+ puts "To apply after editing:"
263
+ puts " sqlite3 #{Hiiro::DB::DB_FILE} < #{sql_path}"
264
+ puts
265
+ puts "Or run individual statements with:"
266
+ puts " h db q \"DELETE FROM ...\""
267
+ else
268
+ puts "No duplicates found across tracked tables."
269
+ end
270
+ end
271
+
174
272
  add_subcmd(:restore) do
175
273
  config_dir = File.expand_path('~/.config/hiiro')
176
274
  archives = Dir.glob(File.join(config_dir, 'sqlite-migration.*.tar.gz')).sort
data/lib/hiiro/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  class Hiiro
2
- VERSION = "0.1.308.pre.6"
2
+ VERSION = "0.1.308"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hiiro
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.308.pre.6
4
+ version: 0.1.308
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joshua Toyota