diff --git a/Gemfile b/Gemfile index 1c49850..7b040f2 100644 --- a/Gemfile +++ b/Gemfile @@ -4,7 +4,8 @@ source "https://rubygems.org" gemspec -#gem "ecosystems-bibliothecary", git: "https://github.com/ecosyste-ms/bibliothecary.git", require: "bibliothecary" +gem "ecosystems-bibliothecary", git: "https://github.com/ecosyste-ms/bibliothecary.git", require: "bibliothecary" +# gem "ecosystems-bibliothecary", path: "/Users/andrew/code/ecosystems/bibliothecary", require: "bibliothecary" gem "ostruct" gem "irb" diff --git a/Gemfile.lock b/Gemfile.lock index e06143b..555e4ef 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,59 +1,38 @@ +GIT + remote: https://github.com/ecosyste-ms/bibliothecary.git + revision: 827bda17dc8866d3f712d40942760f4d52e23b63 + specs: + ecosystems-bibliothecary (15.0.1) + bundler + csv + json (~> 2.8) + ox (>= 2.8.1) + racc + tomlrb (~> 2.0) + PATH remote: . specs: git-pkgs (0.5.0) - activerecord (>= 7.0) ecosystems-bibliothecary (~> 15.0) rugged (~> 1.0) + sequel (>= 5.0) sqlite3 (>= 2.0) GEM remote: https://rubygems.org/ specs: - activemodel (8.1.1) - activesupport (= 8.1.1) - activerecord (8.1.1) - activemodel (= 8.1.1) - activesupport (= 8.1.1) - timeout (>= 0.4.0) - activesupport (8.1.1) - base64 - bigdecimal - concurrent-ruby (~> 1.0, >= 1.3.1) - connection_pool (>= 2.2.5) - drb - i18n (>= 1.6, < 2) - json - logger (>= 1.4.2) - minitest (>= 5.1) - securerandom (>= 0.3) - tzinfo (~> 2.0, >= 2.0.5) - uri (>= 0.13.1) - base64 (0.3.0) benchmark (0.5.0) bigdecimal (4.0.1) - concurrent-ruby (1.3.6) - connection_pool (3.0.2) csv (3.3.5) date (3.5.1) - drb (2.2.3) - ecosystems-bibliothecary (15.0.1) - bundler - csv - json (~> 2.8) - ox (>= 2.8.1) - racc - tomlrb (~> 2.0) erb (6.0.1) - i18n (1.14.8) - concurrent-ruby (~> 1.0) io-console (0.8.2) irb (1.16.0) pp (>= 0.6.0) rdoc (>= 4.0.0) reline (>= 0.4.2) json (2.18.0) - logger (1.7.0) minitest (6.0.1) prism (~> 1.5) ostruct (0.6.3) @@ -75,7 +54,8 @@ GEM reline (0.6.3) io-console (~> 0.5) rugged (1.9.0) - securerandom (0.4.1) + sequel (5.100.0) + bigdecimal sqlite3 (2.9.0-aarch64-linux-gnu) sqlite3 (2.9.0-aarch64-linux-musl) sqlite3 (2.9.0-arm-linux-gnu) @@ -87,12 +67,8 @@ GEM sqlite3 (2.9.0-x86_64-linux-gnu) sqlite3 (2.9.0-x86_64-linux-musl) stringio (3.2.0) - timeout (0.6.0) tomlrb (2.0.4) tsort (0.2.0) - tzinfo (2.0.6) - concurrent-ruby (~> 1.0) - uri (1.1.1) PLATFORMS aarch64-linux-gnu @@ -108,6 +84,7 @@ PLATFORMS DEPENDENCIES benchmark + ecosystems-bibliothecary! git-pkgs! irb minitest @@ -115,25 +92,16 @@ DEPENDENCIES rake CHECKSUMS - activemodel (8.1.1) sha256=8b7e2496b9e333ced06248c16a43217b950192c98e0fe3aa117eee21501c6fbd - activerecord (8.1.1) sha256=e32c3a03e364fd803498eb4150c21bedc995aa83bc27122a94d480ab1dcb3d17 - activesupport (8.1.1) sha256=5e92534e8d0c8b8b5e6b16789c69dbea65c1d7b752269f71a39422e9546cea67 - base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b benchmark (0.5.0) sha256=465df122341aedcb81a2a24b4d3bd19b6c67c1530713fd533f3ff034e419236c bigdecimal (4.0.1) sha256=8b07d3d065a9f921c80ceaea7c9d4ae596697295b584c296fe599dd0ad01c4a7 - concurrent-ruby (1.3.6) sha256=6b56837e1e7e5292f9864f34b69c5a2cbc75c0cf5338f1ce9903d10fa762d5ab - connection_pool (3.0.2) sha256=33fff5ba71a12d2aa26cb72b1db8bba2a1a01823559fb01d29eb74c286e62e0a csv (3.3.5) sha256=6e5134ac3383ef728b7f02725d9872934f523cb40b961479f69cf3afa6c8e73f date (3.5.1) sha256=750d06384d7b9c15d562c76291407d89e368dda4d4fff957eb94962d325a0dc0 - drb (2.2.3) sha256=0b00d6fdb50995fe4a45dea13663493c841112e4068656854646f418fda13373 - ecosystems-bibliothecary (15.0.1) sha256=e557381cbe1a56931cb68c8a685a33dd9156a7b331a583874b4ac05f94df6f1c + ecosystems-bibliothecary (15.0.1) erb (6.0.1) sha256=28ecdd99c5472aebd5674d6061e3c6b0a45c049578b071e5a52c2a7f13c197e5 git-pkgs (0.5.0) - i18n (1.14.8) sha256=285778639134865c5e0f6269e0b818256017e8cde89993fdfcbfb64d088824a5 io-console (0.8.2) sha256=d6e3ae7a7cc7574f4b8893b4fca2162e57a825b223a177b7afa236c5ef9814cc irb (1.16.0) sha256=2abe56c9ac947cdcb2f150572904ba798c1e93c890c256f8429981a7675b0806 json (2.18.0) sha256=b10506aee4183f5cf49e0efc48073d7b75843ce3782c68dbeb763351c08fd505 - logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203 minitest (6.0.1) sha256=7854c74f48e2e975969062833adc4013f249a4b212f5e7b9d5c040bf838d54bb ostruct (0.6.3) sha256=95a2ed4a4bd1d190784e666b47b2d3f078e4a9efda2fccf18f84ddc6538ed912 ox (2.14.23) sha256=4a9aedb4d6c78c5ebac1d7287dc7cc6808e14a8831d7adb727438f6a1b461b66 @@ -146,7 +114,7 @@ CHECKSUMS rdoc (7.0.3) sha256=dfe3d0981d19b7bba71d9dbaeb57c9f4e3a7a4103162148a559c4fc687ea81f9 reline (0.6.3) sha256=1198b04973565b36ec0f11542ab3f5cfeeec34823f4e54cebde90968092b1835 rugged (1.9.0) sha256=7faaa912c5888d6e348d20fa31209b6409f1574346b1b80e309dbc7e8d63efac - securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1 + sequel (5.100.0) sha256=cb0329b62287a01db68eead46759c14497a3fae01b174e2c41da108a9e9b4a12 sqlite3 (2.9.0-aarch64-linux-gnu) sha256=cfe1e0216f46d7483839719bf827129151e6c680317b99d7b8fc1597a3e13473 sqlite3 (2.9.0-aarch64-linux-musl) sha256=56a35cb2d70779afc2ac191baf2c2148242285ecfed72f9b021218c5c4917913 sqlite3 (2.9.0-arm-linux-gnu) sha256=a19a21504b0d7c8c825fbbf37b358ae316b6bd0d0134c619874060b2eef05435 @@ -158,11 +126,8 @@ CHECKSUMS sqlite3 (2.9.0-x86_64-linux-gnu) sha256=72fff9bd750070ba3af695511ba5f0e0a2d8a9206f84869640b3e99dfaf3d5a5 sqlite3 (2.9.0-x86_64-linux-musl) sha256=ef716ba7a66d7deb1ccc402ac3a6d7343da17fac862793b7f0be3d2917253c90 stringio (3.2.0) sha256=c37cb2e58b4ffbd33fe5cd948c05934af997b36e0b6ca6fdf43afa234cf222e1 - timeout (0.6.0) sha256=6d722ad619f96ee383a0c557ec6eb8c4ecb08af3af62098a0be5057bf00de1af tomlrb (2.0.4) sha256=262f77947ac3ac9b3366a0a5940ecd238300c553e2e14f22009e2afcd2181b99 tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f - tzinfo (2.0.6) sha256=8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b - uri (1.1.1) sha256=379fa58d27ffb1387eaada68c749d1426738bd0f654d812fcc07e7568f5c57c6 BUNDLED WITH 4.0.1 diff --git a/benchmark/bulk.rb b/benchmark/bulk.rb index 115b54f..55a0606 100644 --- a/benchmark/bulk.rb +++ b/benchmark/bulk.rb @@ -8,10 +8,10 @@ repo_path = ARGV[0] || "/Users/andrew/code/octobox" sample_size = (ARGV[1] || 500).to_i -# In-memory with WAL mode equivalent (journal_mode=memory for in-memory DB) +# In-memory with optimized settings Git::Pkgs::Database.connect_memory -ActiveRecord::Base.connection.execute("PRAGMA synchronous = OFF") -ActiveRecord::Base.connection.execute("PRAGMA journal_mode = MEMORY") +Git::Pkgs::Database.db.run("PRAGMA synchronous = OFF") +Git::Pkgs::Database.db.run("PRAGMA journal_mode = MEMORY") repo = Git::Pkgs::Repository.new(repo_path) analyzer = Git::Pkgs::Analyzer.new(repo) @@ -111,11 +111,11 @@ # Bulk insert insert_time = Benchmark.realtime do # Insert commits - Git::Pkgs::Models::Commit.insert_all(all_commits) if all_commits.any? + Git::Pkgs::Models::Commit.multi_insert(all_commits) if all_commits.any? # Build SHA -> ID map - commit_ids = Git::Pkgs::Models::Commit.where(sha: all_commits.map { |c| c[:sha] }).pluck(:sha, :id).to_h - manifest_ids = Git::Pkgs::Models::Manifest.pluck(:path, :id).to_h + commit_ids = Git::Pkgs::Models::Commit.where(sha: all_commits.map { |c| c[:sha] }).select_map([:sha, :id]).to_h + manifest_ids = Git::Pkgs::Models::Manifest.select_map([:path, :id]).to_h # Insert branch_commits with resolved IDs branch_commit_records = all_branch_commits.map do |bc| @@ -125,7 +125,7 @@ position: bc[:commit_position] } end - Git::Pkgs::Models::BranchCommit.insert_all(branch_commit_records) if branch_commit_records.any? + Git::Pkgs::Models::BranchCommit.multi_insert(branch_commit_records) if branch_commit_records.any? # Insert changes with resolved IDs change_records = all_changes.map do |c| @@ -142,7 +142,7 @@ updated_at: c[:updated_at] } end - Git::Pkgs::Models::DependencyChange.insert_all(change_records) if change_records.any? + Git::Pkgs::Models::DependencyChange.multi_insert(change_records) if change_records.any? # Insert snapshots with resolved IDs snapshot_records = all_snapshots.map do |s| @@ -157,7 +157,7 @@ updated_at: s[:updated_at] } end - Git::Pkgs::Models::DependencySnapshot.insert_all(snapshot_records) if snapshot_records.any? + Git::Pkgs::Models::DependencySnapshot.multi_insert(snapshot_records) if snapshot_records.any? end puts "Insert time: #{insert_time.round(3)}s" diff --git a/benchmark/db.rb b/benchmark/db.rb index 30171be..8a0f88a 100644 --- a/benchmark/db.rb +++ b/benchmark/db.rb @@ -52,11 +52,10 @@ counts[:commits] += 1 timings[:branch_commit_create] += Benchmark.realtime do - Git::Pkgs::Models::BranchCommit.find_or_create_by( - branch: branch, - commit: commit, - position: position - ) + Git::Pkgs::Models::BranchCommit.find_or_create( + branch_id: branch.id, + commit_id: commit.id + ) { |bc| bc.position = position } end counts[:branch_commits] += 1 @@ -77,7 +76,7 @@ end timings[:change_create] += Benchmark.realtime do - Git::Pkgs::Models::DependencyChange.create!( + Git::Pkgs::Models::DependencyChange.create( commit: commit, manifest: manifest, name: change[:name], @@ -95,10 +94,10 @@ snapshot.each do |(manifest_path, name), dep_info| timings[:snapshot_create] += Benchmark.realtime do - manifest = Git::Pkgs::Models::Manifest.find_by(path: manifest_path) - Git::Pkgs::Models::DependencySnapshot.find_or_create_by( - commit: commit, - manifest: manifest, + manifest = Git::Pkgs::Models::Manifest.first(path: manifest_path) + Git::Pkgs::Models::DependencySnapshot.find_or_create( + commit_id: commit.id, + manifest_id: manifest.id, name: name ) do |s| s.ecosystem = dep_info[:ecosystem] diff --git a/git-pkgs.gemspec b/git-pkgs.gemspec index 15c34b4..2248d95 100644 --- a/git-pkgs.gemspec +++ b/git-pkgs.gemspec @@ -32,7 +32,7 @@ Gem::Specification.new do |spec| spec.require_paths = ["lib"] spec.add_dependency "rugged", "~> 1.0" - spec.add_dependency "activerecord", ">= 7.0" + spec.add_dependency "sequel", ">= 5.0" spec.add_dependency "sqlite3", ">= 2.0" spec.add_dependency "ecosystems-bibliothecary", "~> 15.0" end diff --git a/lib/git/pkgs/commands/blame.rb b/lib/git/pkgs/commands/blame.rb index e35bd48..e51f6f7 100644 --- a/lib/git/pkgs/commands/blame.rb +++ b/lib/git/pkgs/commands/blame.rb @@ -19,12 +19,12 @@ def run # Get current dependencies at the last analyzed commit branch_name = @options[:branch] || repo.default_branch - branch = Models::Branch.find_by(name: branch_name) + branch = Models::Branch.first(name: branch_name) error "No analysis found for branch '#{branch_name}'. Run 'git pkgs init' first." unless branch&.last_analyzed_sha - current_commit = Models::Commit.find_by(sha: branch.last_analyzed_sha) - snapshots = current_commit&.dependency_snapshots&.includes(:manifest) || [] + current_commit = Models::Commit.first(sha: branch.last_analyzed_sha) + snapshots = current_commit&.dependency_snapshots&.eager(:manifest) || [] if @options[:ecosystem] snapshots = snapshots.where(ecosystem: @options[:ecosystem]) @@ -41,7 +41,7 @@ def run names = snapshots.map(&:name).uniq all_added_changes = Models::DependencyChange - .includes(:commit) + .eager(:commit) .added .where(manifest_id: manifest_ids, name: names) .to_a diff --git a/lib/git/pkgs/commands/branch.rb b/lib/git/pkgs/commands/branch.rb index 697ebe0..d1d9500 100644 --- a/lib/git/pkgs/commands/branch.rb +++ b/lib/git/pkgs/commands/branch.rb @@ -50,7 +50,7 @@ def add_branch error "Branch '#{branch_name}' not found. Check 'git branch -a' for available branches." unless repo.branch_exists?(branch_name) - existing = Models::Branch.find_by(name: branch_name) + existing = Models::Branch.first(name: branch_name) if existing info "Branch '#{branch_name}' already tracked (#{existing.commits.count} commits)" info "Use 'git pkgs update' to refresh" @@ -59,7 +59,7 @@ def add_branch Database.optimize_for_bulk_writes - branch = Models::Branch.create!(name: branch_name) + branch = Models::Branch.create(name: branch_name) analyzer = Analyzer.new(repo) info "Analyzing branch: #{branch_name}" @@ -100,7 +100,7 @@ def list_branches puts "Tracked branches:" branches.each do |branch| commit_count = branch.commits.count - dep_commits = branch.commits.where(has_dependency_changes: true).count + dep_commits = branch.commits_dataset.where(has_dependency_changes: true).count last_sha = branch.last_analyzed_sha&.slice(0, 7) || "none" puts " #{branch.name}: #{commit_count} commits (#{dep_commits} with deps), last: #{last_sha}" end @@ -115,13 +115,13 @@ def remove_branch Database.connect(repo.git_dir) - branch = Models::Branch.find_by(name: branch_name) + branch = Models::Branch.first(name: branch_name) error "Branch '#{branch_name}' not tracked. Run 'git pkgs branch list' to see tracked branches." unless branch # Only delete branch_commits, keep shared commits count = branch.branch_commits.count - branch.branch_commits.delete_all - branch.destroy + branch.branch_commits_dataset.delete + branch.delete info "Removed branch '#{branch_name}' (#{count} branch-commit links)" end @@ -143,28 +143,27 @@ def bulk_process_commits(commits, branch, analyzer, total, repo) flush = lambda do return if pending_commits.empty? - ActiveRecord::Base.transaction do - # Use upsert for commits since they may already exist from other branches + Database.db.transaction do + # Use insert with on_conflict for commits since they may already exist from other branches if pending_commits.any? - Models::Commit.upsert_all( - pending_commits, - unique_by: :sha - ) + Models::Commit.dataset + .insert_conflict(target: :sha, update: { has_dependency_changes: Sequel[:excluded][:has_dependency_changes] }) + .multi_insert(pending_commits) end commit_ids = Models::Commit .where(sha: pending_commits.map { |c| c[:sha] }) - .pluck(:sha, :id).to_h + .select_hash(:sha, :id) if pending_branch_commits.any? branch_commit_records = pending_branch_commits.map do |bc| { branch_id: bc[:branch_id], commit_id: commit_ids[bc[:sha]], position: bc[:position] } end - Models::BranchCommit.insert_all(branch_commit_records) + Models::BranchCommit.dataset.multi_insert(branch_commit_records) end if pending_changes.any? - manifest_ids = Models::Manifest.pluck(:path, :id).to_h + manifest_ids = Models::Manifest.select_hash(:path, :id) change_records = pending_changes.map do |c| { commit_id: commit_ids[c[:sha]], @@ -179,11 +178,11 @@ def bulk_process_commits(commits, branch, analyzer, total, repo) updated_at: now } end - Models::DependencyChange.insert_all(change_records) + Models::DependencyChange.dataset.multi_insert(change_records) end if pending_snapshots.any? - manifest_ids ||= Models::Manifest.pluck(:path, :id).to_h + manifest_ids ||= Models::Manifest.select_hash(:path, :id) snapshot_records = pending_snapshots.map do |s| { commit_id: commit_ids[s[:sha]], @@ -196,7 +195,7 @@ def bulk_process_commits(commits, branch, analyzer, total, repo) updated_at: now } end - Models::DependencySnapshot.insert_all(snapshot_records) + Models::DependencySnapshot.dataset.multi_insert(snapshot_records) end end diff --git a/lib/git/pkgs/commands/diff.rb b/lib/git/pkgs/commands/diff.rb index 3eebc51..2971e40 100644 --- a/lib/git/pkgs/commands/diff.rb +++ b/lib/git/pkgs/commands/diff.rb @@ -38,22 +38,24 @@ def run # Get all changes between the two commits changes = Models::DependencyChange - .includes(:commit, :manifest) - .joins(:commit) - .where("commits.committed_at > ? AND commits.committed_at <= ?", - from_commit.committed_at, to_commit.committed_at) - .order("commits.committed_at ASC") + .eager(:commit, :manifest) + .join(:commits, id: :commit_id) + .where { Sequel[:commits][:committed_at] > from_commit.committed_at } + .where { Sequel[:commits][:committed_at] <= to_commit.committed_at } + .order(Sequel[:commits][:committed_at]) if @options[:ecosystem] - changes = changes.where(ecosystem: @options[:ecosystem]) + changes = changes.where(Sequel[:dependency_changes][:ecosystem] => @options[:ecosystem]) end - if changes.empty? + changes_list = changes.all + + if changes_list.empty? empty_result "No dependency changes between #{from_commit.short_sha} and #{to_commit.short_sha}" return end - paginate { output_text(from_commit, to_commit, changes) } + paginate { output_text(from_commit, to_commit, changes_list) } end def output_text(from_commit, to_commit, changes) diff --git a/lib/git/pkgs/commands/history.rb b/lib/git/pkgs/commands/history.rb index cf99eb3..16b01af 100644 --- a/lib/git/pkgs/commands/history.rb +++ b/lib/git/pkgs/commands/history.rb @@ -20,8 +20,8 @@ def run Database.connect(repo.git_dir) changes = Models::DependencyChange - .includes(:commit, :manifest) - .order("commits.committed_at ASC") + .eager_graph(:commit, :manifest) + .order(Sequel[:commit][:committed_at]) changes = changes.for_package(package_name) if package_name @@ -31,32 +31,34 @@ def run if @options[:author] author = @options[:author] - changes = changes.joins(:commit).where( - "commits.author_name LIKE ? OR commits.author_email LIKE ?", - "%#{author}%", "%#{author}%" + changes = changes.where( + Sequel.like(Sequel[:commit][:author_name], "%#{author}%") | + Sequel.like(Sequel[:commit][:author_email], "%#{author}%") ) end if @options[:since] since_time = parse_time(@options[:since]) - changes = changes.joins(:commit).where("commits.committed_at >= ?", since_time) + changes = changes.where { Sequel[:commit][:committed_at] >= since_time } end if @options[:until] until_time = parse_time(@options[:until]) - changes = changes.joins(:commit).where("commits.committed_at <= ?", until_time) + changes = changes.where { Sequel[:commit][:committed_at] <= until_time } end - if changes.empty? + changes_list = changes.all + + if changes_list.empty? msg = package_name ? "No history found for '#{package_name}'" : "No dependency changes found" empty_result msg return end if @options[:format] == "json" - output_json(changes) + output_json(changes_list) else - paginate { output_text(changes, package_name) } + paginate { output_text(changes_list, package_name) } end end diff --git a/lib/git/pkgs/commands/info.rb b/lib/git/pkgs/commands/info.rb index a476246..c314b27 100644 --- a/lib/git/pkgs/commands/info.rb +++ b/lib/git/pkgs/commands/info.rb @@ -67,7 +67,7 @@ def run puts "-" * 40 total_dep_commits = Models::Commit.where(has_dependency_changes: true).count snapshot_commits = Models::Commit - .joins(:dependency_snapshots) + .join(:dependency_snapshots, commit_id: :id) .distinct .count puts " Commits with dependency changes: #{total_dep_commits}" diff --git a/lib/git/pkgs/commands/init.rb b/lib/git/pkgs/commands/init.rb index 7f3f677..870068b 100644 --- a/lib/git/pkgs/commands/init.rb +++ b/lib/git/pkgs/commands/init.rb @@ -90,22 +90,22 @@ def bulk_process_commits(commits, branch, analyzer, total) flush = lambda do return if pending_commits.empty? - ActiveRecord::Base.transaction do - Models::Commit.insert_all(pending_commits) if pending_commits.any? + Database.db.transaction do + Models::Commit.dataset.multi_insert(pending_commits) if pending_commits.any? commit_ids = Models::Commit .where(sha: pending_commits.map { |c| c[:sha] }) - .pluck(:sha, :id).to_h + .select_hash(:sha, :id) if pending_branch_commits.any? branch_commit_records = pending_branch_commits.map do |bc| { branch_id: bc[:branch_id], commit_id: commit_ids[bc[:sha]], position: bc[:position] } end - Models::BranchCommit.insert_all(branch_commit_records) + Models::BranchCommit.dataset.multi_insert(branch_commit_records) end if pending_changes.any? - manifest_ids = Models::Manifest.pluck(:path, :id).to_h + manifest_ids = Models::Manifest.select_hash(:path, :id) change_records = pending_changes.map do |c| { commit_id: commit_ids[c[:sha]], @@ -120,11 +120,11 @@ def bulk_process_commits(commits, branch, analyzer, total) updated_at: now } end - Models::DependencyChange.insert_all(change_records) + Models::DependencyChange.dataset.multi_insert(change_records) end if pending_snapshots.any? - manifest_ids ||= Models::Manifest.pluck(:path, :id).to_h + manifest_ids ||= Models::Manifest.select_hash(:path, :id) snapshot_records = pending_snapshots.map do |s| { commit_id: commit_ids[s[:sha]], @@ -137,7 +137,7 @@ def bulk_process_commits(commits, branch, analyzer, total) updated_at: now } end - Models::DependencySnapshot.insert_all(snapshot_records) + Models::DependencySnapshot.dataset.multi_insert(snapshot_records) end end diff --git a/lib/git/pkgs/commands/list.rb b/lib/git/pkgs/commands/list.rb index a78ba06..26d10db 100644 --- a/lib/git/pkgs/commands/list.rb +++ b/lib/git/pkgs/commands/list.rb @@ -18,7 +18,7 @@ def run Database.connect(repo.git_dir) commit_sha = @options[:commit] || repo.head_sha - target_commit = Models::Commit.find_by(sha: commit_sha) + target_commit = Models::Commit.first(sha: commit_sha) error "Commit #{commit_sha[0, 7]} not in database. Run 'git pkgs update' to index new commits." unless target_commit @@ -61,21 +61,21 @@ def output_text(deps) def compute_dependencies_at_commit(target_commit, repo) branch_name = @options[:branch] || repo.default_branch - branch = Models::Branch.find_by(name: branch_name) + branch = Models::Branch.first(name: branch_name) return [] unless branch # Find the nearest snapshot commit before or at target - snapshot_commit = branch.commits - .joins(:dependency_snapshots) - .where("commits.committed_at <= ?", target_commit.committed_at) - .order(committed_at: :desc) + snapshot_commit = branch.commits_dataset + .join(:dependency_snapshots, commit_id: :id) + .where { Sequel[:commits][:committed_at] <= target_commit.committed_at } + .order(Sequel.desc(Sequel[:commits][:committed_at])) .distinct .first # Build initial state from snapshot deps = {} if snapshot_commit - snapshot_commit.dependency_snapshots.includes(:manifest).each do |s| + snapshot_commit.dependency_snapshots.each do |s| key = [s.manifest.path, s.name] deps[key] = { manifest_path: s.manifest.path, @@ -89,13 +89,15 @@ def compute_dependencies_at_commit(target_commit, repo) # Replay changes from snapshot to target if snapshot_commit && snapshot_commit.id != target_commit.id + commit_ids = branch.commits_dataset.select_map(:id) changes = Models::DependencyChange - .joins(:commit) - .where(commits: { id: branch.commit_ids }) - .where("commits.committed_at > ? AND commits.committed_at <= ?", - snapshot_commit.committed_at, target_commit.committed_at) - .order("commits.committed_at ASC") - .includes(:manifest) + .join(:commits, id: :commit_id) + .where(Sequel[:commits][:id] => commit_ids) + .where { Sequel[:commits][:committed_at] > snapshot_commit.committed_at } + .where { Sequel[:commits][:committed_at] <= target_commit.committed_at } + .order(Sequel[:commits][:committed_at]) + .eager(:manifest) + .all changes.each do |change| key = [change.manifest.path, change.name] diff --git a/lib/git/pkgs/commands/log.rb b/lib/git/pkgs/commands/log.rb index 311a391..65013dc 100644 --- a/lib/git/pkgs/commands/log.rb +++ b/lib/git/pkgs/commands/log.rb @@ -18,16 +18,22 @@ def run Database.connect(repo.git_dir) commits = Models::Commit - .includes(:dependency_changes) + .eager(:dependency_changes) .where(has_dependency_changes: true) - .order(committed_at: :desc) + .order(Sequel.desc(:committed_at), Sequel.desc(:id)) + + if @options[:author] + author = @options[:author] + commits = commits.where( + Sequel.like(:author_name, "%#{author}%") | + Sequel.like(:author_email, "%#{author}%") + ) + end - commits = commits.where("author_name LIKE ? OR author_email LIKE ?", - "%#{@options[:author]}%", "%#{@options[:author]}%") if @options[:author] - commits = commits.where("committed_at >= ?", parse_time(@options[:since])) if @options[:since] - commits = commits.where("committed_at <= ?", parse_time(@options[:until])) if @options[:until] + commits = commits.where { committed_at >= parse_time(@options[:since]) } if @options[:since] + commits = commits.where { committed_at <= parse_time(@options[:until]) } if @options[:until] - commits = commits.limit(@options[:limit] || 20) + commits = commits.limit(@options[:limit] || 20).all if commits.empty? empty_result "No commits with dependency changes found" diff --git a/lib/git/pkgs/commands/schema.rb b/lib/git/pkgs/commands/schema.rb index d34b64d..e00002e 100644 --- a/lib/git/pkgs/commands/schema.rb +++ b/lib/git/pkgs/commands/schema.rb @@ -33,32 +33,29 @@ def run end def fetch_schema - conn = ActiveRecord::Base.connection + db = Database.db tables = {} - conn.tables.sort.each do |table_name| - next if table_name == "ar_internal_metadata" - next if table_name == "schema_migrations" - - columns = conn.columns(table_name).map do |col| + db.tables.sort.each do |table_name| + columns = db.schema(table_name).map do |name, col_info| { - name: col.name, - type: col.type, - sql_type: col.sql_type, - null: col.null, - default: col.default + name: name.to_s, + type: col_info[:type], + sql_type: col_info[:db_type], + null: col_info[:allow_null], + default: col_info[:default] } end - indexes = conn.indexes(table_name).map do |idx| + indexes = db.indexes(table_name).map do |name, idx_info| { - name: idx.name, - columns: idx.columns, - unique: idx.unique + name: name.to_s, + columns: idx_info[:columns].map(&:to_s), + unique: idx_info[:unique] } end - tables[table_name] = { columns: columns, indexes: indexes } + tables[table_name.to_s] = { columns: columns, indexes: indexes } end tables @@ -72,7 +69,7 @@ def output_text(tables) info[:columns].each do |col| nullable = col[:null] ? "NULL" : "NOT NULL" default = col[:default] ? " DEFAULT #{col[:default]}" : "" - puts " #{col[:name].ljust(25)} #{col[:sql_type].ljust(15)} #{nullable}#{default}" + puts " #{col[:name].ljust(25)} #{col[:sql_type].to_s.ljust(15)} #{nullable}#{default}" end if info[:indexes].any? @@ -89,16 +86,16 @@ def output_text(tables) end def output_sql(tables) - conn = ActiveRecord::Base.connection + db = Database.db tables.each do |table_name, info| - sql = conn.execute("SELECT sql FROM sqlite_master WHERE type='table' AND name='#{table_name}'").first - puts sql["sql"] + ";" if sql + sql = db.fetch("SELECT sql FROM sqlite_master WHERE type='table' AND name=?", table_name).first + puts sql[:sql] + ";" if sql puts info[:indexes].each do |idx| - idx_sql = conn.execute("SELECT sql FROM sqlite_master WHERE type='index' AND name='#{idx[:name]}'").first - puts idx_sql["sql"] + ";" if idx_sql && idx_sql["sql"] + idx_sql = db.fetch("SELECT sql FROM sqlite_master WHERE type='index' AND name=?", idx[:name]).first + puts idx_sql[:sql] + ";" if idx_sql && idx_sql[:sql] end puts if info[:indexes].any? diff --git a/lib/git/pkgs/commands/search.rb b/lib/git/pkgs/commands/search.rb index 93ddbc2..31f0841 100644 --- a/lib/git/pkgs/commands/search.rb +++ b/lib/git/pkgs/commands/search.rb @@ -23,19 +23,19 @@ def run # Search for dependencies matching the pattern query = Models::DependencyChange - .joins(:manifest) - .where("dependency_changes.name LIKE ?", "%#{pattern}%") + .join(:manifests, id: :manifest_id) + .where(Sequel.like(Sequel[:dependency_changes][:name], "%#{pattern}%")) if @options[:ecosystem] - query = query.where(ecosystem: @options[:ecosystem]) + query = query.where(Sequel[:dependency_changes][:ecosystem] => @options[:ecosystem]) end if @options[:direct] - query = query.where(manifests: { kind: "manifest" }) + query = query.where(Sequel[:manifests][:kind] => "manifest") end # Get unique dependency names - matches = query.distinct.pluck(:name, :ecosystem) + matches = query.distinct.select_map([Sequel[:dependency_changes][:name], Sequel[:dependency_changes][:ecosystem]]) if matches.empty? empty_result "No dependencies found matching '#{pattern}'" @@ -70,12 +70,14 @@ def output_json(matches, pattern) results = matches.map do |name, platform| changes = Models::DependencyChange .where(name: name, ecosystem: platform) - .includes(:commit) - .order("commits.committed_at ASC") + .eager(:commit) + .association_join(:commit) + .order(Sequel[:commits][:committed_at]) + .all first = changes.first last = changes.last - current = changes.where(change_type: %w[added modified]).last + current = changes.select { |c| %w[added modified].include?(c.change_type) }.last { name: name, @@ -94,8 +96,10 @@ def output_json(matches, pattern) def dependency_summary(name, platform) changes = Models::DependencyChange .where(name: name, ecosystem: platform) - .includes(:commit) - .order("commits.committed_at ASC") + .eager(:commit) + .association_join(:commit) + .order(Sequel[:commits][:committed_at]) + .all first = changes.first last = changes.last @@ -106,7 +110,7 @@ def dependency_summary(name, platform) if last.change_type == "removed" parts << "removed #{last.commit.committed_at.strftime('%Y-%m-%d')}" else - current = changes.where(change_type: %w[added modified]).last + current = changes.select { |c| %w[added modified].include?(c.change_type) }.last parts << "current: #{current&.requirement || 'unknown'}" end diff --git a/lib/git/pkgs/commands/show.rb b/lib/git/pkgs/commands/show.rb index 4c0f930..ec858e8 100644 --- a/lib/git/pkgs/commands/show.rb +++ b/lib/git/pkgs/commands/show.rb @@ -26,13 +26,15 @@ def run error "Commit '#{sha[0..7]}' not in database. Run 'git pkgs update' to index new commits." unless commit changes = Models::DependencyChange - .includes(:commit, :manifest) + .eager(:commit, :manifest) .where(commit_id: commit.id) if @options[:ecosystem] changes = changes.where(ecosystem: @options[:ecosystem]) end + changes = changes.all + if changes.empty? empty_result "No dependency changes in #{commit.short_sha}" return diff --git a/lib/git/pkgs/commands/stale.rb b/lib/git/pkgs/commands/stale.rb index de25fc6..951fdec 100644 --- a/lib/git/pkgs/commands/stale.rb +++ b/lib/git/pkgs/commands/stale.rb @@ -18,12 +18,12 @@ def run Database.connect(repo.git_dir) branch_name = @options[:branch] || repo.default_branch - branch = Models::Branch.find_by(name: branch_name) + branch = Models::Branch.first(name: branch_name) error "No analysis found for branch '#{branch_name}'. Run 'git pkgs init' first." unless branch&.last_analyzed_sha - current_commit = Models::Commit.find_by(sha: branch.last_analyzed_sha) - snapshots = current_commit&.dependency_snapshots&.includes(:manifest) || [] + current_commit = Models::Commit.first(sha: branch.last_analyzed_sha) + snapshots = current_commit&.dependency_snapshots&.eager(:manifest) || [] if @options[:ecosystem] snapshots = snapshots.where(ecosystem: @options[:ecosystem]) @@ -40,7 +40,7 @@ def run names = snapshots.map(&:name).uniq all_changes = Models::DependencyChange - .includes(:commit) + .eager(:commit) .where(manifest_id: manifest_ids, name: names) .to_a diff --git a/lib/git/pkgs/commands/stats.rb b/lib/git/pkgs/commands/stats.rb index 87d5a43..82b5307 100644 --- a/lib/git/pkgs/commands/stats.rb +++ b/lib/git/pkgs/commands/stats.rb @@ -18,7 +18,7 @@ def run Database.connect(repo.git_dir) branch_name = @options[:branch] || repo.default_branch - branch = Models::Branch.find_by(name: branch_name) + branch = Models::Branch.first(name: branch_name) if @options[:by_author] output_by_author @@ -39,9 +39,9 @@ def collect_stats(branch, branch_name) since_time = @options[:since] ? parse_time(@options[:since]) : nil until_time = @options[:until] ? parse_time(@options[:until]) : nil - commits = branch&.commits || Models::Commit.none - commits = commits.where("committed_at >= ?", since_time) if since_time - commits = commits.where("committed_at <= ?", until_time) if until_time + commits = branch ? branch.commits_dataset : Models::Commit.where(false) + commits = commits.where { committed_at >= since_time } if since_time + commits = commits.where { committed_at <= until_time } if until_time data = { branch: branch_name, @@ -57,59 +57,59 @@ def collect_stats(branch, branch_name) } if branch&.last_analyzed_sha - current_commit = Models::Commit.find_by(sha: branch.last_analyzed_sha) - snapshots = current_commit&.dependency_snapshots || [] + current_commit = Models::Commit.first(sha: branch.last_analyzed_sha) + snapshots = current_commit ? current_commit.dependency_snapshots_dataset : Models::DependencySnapshot.where(false) snapshots = snapshots.where(ecosystem: ecosystem) if ecosystem data[:current_dependencies] = { total: snapshots.count, - by_platform: snapshots.group(:ecosystem).count, - by_type: snapshots.group(:dependency_type).count + by_platform: snapshots.group_and_count(:ecosystem).as_hash(:ecosystem, :count), + by_type: snapshots.group_and_count(:dependency_type).as_hash(:dependency_type, :count) } end - changes = Models::DependencyChange.joins(:commit) - changes = changes.where(ecosystem: ecosystem) if ecosystem - changes = changes.where("commits.committed_at >= ?", since_time) if since_time - changes = changes.where("commits.committed_at <= ?", until_time) if until_time + changes = Models::DependencyChange.join(:commits, id: :commit_id) + changes = changes.where(Sequel[:dependency_changes][:ecosystem] => ecosystem) if ecosystem + changes = changes.where { Sequel[:commits][:committed_at] >= since_time } if since_time + changes = changes.where { Sequel[:commits][:committed_at] <= until_time } if until_time data[:changes] = { total: changes.count, - by_type: changes.group(:change_type).count + by_type: changes.group_and_count(Sequel[:dependency_changes][:change_type]).as_hash(:change_type, :count) } most_changed = changes - .group(:name, :ecosystem) - .order("count_all DESC") + .group(Sequel[:dependency_changes][:name], Sequel[:dependency_changes][:ecosystem]) + .order(Sequel.desc(:count)) .limit(10) - .count + .select_append { count.function.*.as(:count) } + .select_map([:name, :ecosystem, :count]) - data[:most_changed] = most_changed.map do |(name, eco), count| + data[:most_changed] = most_changed.map do |name, eco, count| { name: name, ecosystem: eco, changes: count } end - manifests = Models::Manifest.all + manifests = Models::Manifest.dataset manifests = manifests.where(ecosystem: ecosystem) if ecosystem - manifest_ids = manifests.pluck(:id) + manifest_ids = manifests.select_map(:id) change_counts_query = Models::DependencyChange - .joins(:commit) - .where(manifest_id: manifest_ids) - change_counts_query = change_counts_query.where("commits.committed_at >= ?", since_time) if since_time - change_counts_query = change_counts_query.where("commits.committed_at <= ?", until_time) if until_time - change_counts = change_counts_query.group(:manifest_id).count + .join(:commits, id: :commit_id) + .where(Sequel[:dependency_changes][:manifest_id] => manifest_ids) + change_counts_query = change_counts_query.where { Sequel[:commits][:committed_at] >= since_time } if since_time + change_counts_query = change_counts_query.where { Sequel[:commits][:committed_at] <= until_time } if until_time + change_counts = change_counts_query.group_and_count(Sequel[:dependency_changes][:manifest_id]).as_hash(:manifest_id, :count) - data[:manifests] = manifests.map do |manifest| + data[:manifests] = manifests.all.map do |manifest| { path: manifest.path, ecosystem: manifest.ecosystem, changes: change_counts[manifest.id] || 0 } end top_authors = changes - .where(change_type: "added") - .joins(:commit) - .group("commits.author_name") - .order("count_all DESC") + .where(Sequel[:dependency_changes][:change_type] => "added") + .group_and_count(Sequel[:commits][:author_name]) + .order(Sequel.desc(:count)) .limit(5) - .count + .as_hash(:author_name, :count) data[:top_authors] = top_authors.map { |name, count| { name: name, added: count } } @@ -185,18 +185,18 @@ def output_by_author until_time = @options[:until] ? parse_time(@options[:until]) : nil changes = Models::DependencyChange - .joins(:commit) - .where(change_type: "added") + .join(:commits, id: :commit_id) + .where(Sequel[:dependency_changes][:change_type] => "added") - changes = changes.where(ecosystem: @options[:ecosystem]) if @options[:ecosystem] - changes = changes.where("commits.committed_at >= ?", since_time) if since_time - changes = changes.where("commits.committed_at <= ?", until_time) if until_time + changes = changes.where(Sequel[:dependency_changes][:ecosystem] => @options[:ecosystem]) if @options[:ecosystem] + changes = changes.where { Sequel[:commits][:committed_at] >= since_time } if since_time + changes = changes.where { Sequel[:commits][:committed_at] <= until_time } if until_time counts = changes - .group("commits.author_name") - .order("count_all DESC") + .group_and_count(Sequel[:commits][:author_name]) + .order(Sequel.desc(:count)) .limit(@options[:limit] || 20) - .count + .as_hash(:author_name, :count) if counts.empty? empty_result "No dependency additions found" diff --git a/lib/git/pkgs/commands/tree.rb b/lib/git/pkgs/commands/tree.rb index 6b20da8..1579124 100644 --- a/lib/git/pkgs/commands/tree.rb +++ b/lib/git/pkgs/commands/tree.rb @@ -24,21 +24,23 @@ def run error "No dependency data for commit #{commit_sha[0, 7]}. Run 'git pkgs update' to index new commits." unless commit # Get current snapshots - snapshots = commit.dependency_snapshots.includes(:manifest) + snapshots = commit.dependency_snapshots_dataset.eager(:manifest) if @options[:ecosystem] snapshots = snapshots.where(ecosystem: @options[:ecosystem]) end - if snapshots.empty? + snapshots_list = snapshots.all + + if snapshots_list.empty? empty_result "No dependencies found" return end # Group by manifest and build tree - grouped = snapshots.group_by { |s| s.manifest } + grouped = snapshots_list.group_by { |s| s.manifest } - paginate { output_text(grouped, snapshots) } + paginate { output_text(grouped, snapshots_list) } end def output_text(grouped, snapshots) @@ -73,19 +75,20 @@ def print_dependency(dep, indent) end def find_commit_with_snapshot(sha, repo) - commit = Models::Commit.find_by(sha: sha) || - Models::Commit.where("sha LIKE ?", "#{sha}%").first + commit = Models::Commit.first(sha: sha) || + Models::Commit.where(Sequel.like(:sha, "#{sha}%")).first return commit if commit&.dependency_snapshots&.any? # Find most recent commit with a snapshot branch_name = @options[:branch] || repo.default_branch - branch = Models::Branch.find_by(name: branch_name) + branch = Models::Branch.first(name: branch_name) return nil unless branch - branch.commits - .joins(:dependency_snapshots) - .where("commits.committed_at <= ?", commit&.committed_at || Time.now) - .order(committed_at: :desc) + target_time = commit&.committed_at || Time.now + branch.commits_dataset + .join(:dependency_snapshots, commit_id: :id) + .where { Sequel[:commits][:committed_at] <= target_time } + .order(Sequel.desc(Sequel[:commits][:committed_at])) .distinct .first end diff --git a/lib/git/pkgs/commands/update.rb b/lib/git/pkgs/commands/update.rb index a58fd1a..e178845 100644 --- a/lib/git/pkgs/commands/update.rb +++ b/lib/git/pkgs/commands/update.rb @@ -18,7 +18,7 @@ def run Database.connect(repo.git_dir) branch_name = @options[:branch] || repo.default_branch - branch = Models::Branch.find_by(name: branch_name) + branch = Models::Branch.first(name: branch_name) error "Branch '#{branch_name}' not in database. Run 'git pkgs init --branch=#{branch_name}' first." unless branch @@ -33,11 +33,11 @@ def run analyzer = Analyzer.new(repo) # Get current snapshot from last analyzed commit - last_commit = Models::Commit.find_by(sha: since_sha) + last_commit = Models::Commit.first(sha: since_sha) snapshot = {} if last_commit - last_commit.dependency_snapshots.includes(:manifest).each do |s| + last_commit.dependency_snapshots.each do |s| key = [s.manifest.path, s.name] snapshot[key] = { ecosystem: s.ecosystem, @@ -54,12 +54,12 @@ def run processed = 0 dependency_commits = 0 - last_position = Models::BranchCommit.where(branch: branch).maximum(:position) || 0 + last_position = Models::BranchCommit.where(branch: branch).max(:position) || 0 info "Updating branch: #{branch_name}" info "Found #{total} new commits" - ActiveRecord::Base.transaction do + Database.db.transaction do commits.each do |rugged_commit| processed += 1 print "\rProcessing commit #{processed}/#{total}..." unless Git::Pkgs.quiet @@ -67,11 +67,12 @@ def run result = analyzer.analyze_commit(rugged_commit, snapshot) commit = Models::Commit.find_or_create_from_rugged(rugged_commit) - Models::BranchCommit.find_or_create_by( + Models::BranchCommit.find_or_create( branch: branch, - commit: commit, - position: last_position + processed - ) + commit: commit + ) do |bc| + bc.position = last_position + processed + end if result && result[:changes].any? dependency_commits += 1 @@ -84,7 +85,7 @@ def run kind: change[:kind] ) - Models::DependencyChange.create!( + Models::DependencyChange.create( commit: commit, manifest: manifest, name: change[:name], @@ -99,8 +100,8 @@ def run snapshot = result[:snapshot] snapshot.each do |(manifest_path, name), dep_info| - manifest = Models::Manifest.find_by(path: manifest_path) - Models::DependencySnapshot.find_or_create_by( + manifest = Models::Manifest.first(path: manifest_path) + Models::DependencySnapshot.find_or_create( commit: commit, manifest: manifest, name: name diff --git a/lib/git/pkgs/commands/where.rb b/lib/git/pkgs/commands/where.rb index 572f5bd..5f6eda0 100644 --- a/lib/git/pkgs/commands/where.rb +++ b/lib/git/pkgs/commands/where.rb @@ -22,7 +22,7 @@ def run Database.connect(repo.git_dir) workdir = File.dirname(repo.git_dir) - branch = Models::Branch.find_by(name: @options[:branch] || repo.default_branch) + branch = Models::Branch.first(name: @options[:branch] || repo.default_branch) unless branch error "Branch not found. Run 'git pkgs init' first." @@ -31,7 +31,10 @@ def run snapshots = Models::DependencySnapshot.current_for_branch(branch) snapshots = snapshots.where(ecosystem: @options[:ecosystem]) if @options[:ecosystem] - manifest_paths = snapshots.for_package(name).joins(:manifest).pluck("manifests.path").uniq + manifest_paths = snapshots.for_package(name) + .join(:manifests, id: :manifest_id) + .select_map(Sequel[:manifests][:path]) + .uniq if manifest_paths.empty? empty_result "Package '#{name}' not found in current dependencies" diff --git a/lib/git/pkgs/commands/why.rb b/lib/git/pkgs/commands/why.rb index 1ba22d7..bdc80a6 100644 --- a/lib/git/pkgs/commands/why.rb +++ b/lib/git/pkgs/commands/why.rb @@ -23,7 +23,7 @@ def run # Find the first time this package was added added_change = Models::DependencyChange - .includes(:commit, :manifest) + .eager(:commit, :manifest) .for_package(package_name) .added .order("commits.committed_at ASC") diff --git a/lib/git/pkgs/database.rb b/lib/git/pkgs/database.rb index 6762f25..ad49728 100644 --- a/lib/git/pkgs/database.rb +++ b/lib/git/pkgs/database.rb @@ -1,7 +1,15 @@ # frozen_string_literal: true -require "active_record" -require "sqlite3" +require "sequel" + +# Set up a placeholder DB so models can be defined before connection +DB = Sequel.sqlite +Sequel::Model.plugin :timestamps, update_on_create: true +Sequel::Model.plugin :update_or_create +Sequel::Model.require_valid_table = false +Sequel::Model.unrestrict_primary_key +# Allow mass assignment of any column (similar to AR default behavior) +Sequel::Model.strict_param_setting = false module Git module Pkgs @@ -9,6 +17,10 @@ class Database DB_FILE = "pkgs.sqlite3" SCHEMA_VERSION = 1 + class << self + attr_accessor :db + end + def self.path(git_dir = nil) return Git::Pkgs.db_path if Git::Pkgs.db_path @@ -23,7 +35,6 @@ def self.find_git_dir raise NotInGitRepoError, "GIT_DIR '#{Git::Pkgs.git_dir}' does not exist" end - # Start from work_tree if set, otherwise current directory dir = Git::Pkgs.work_tree || Dir.pwd loop do git_dir = File.join(dir, ".git") @@ -37,20 +48,54 @@ def self.find_git_dir end def self.connect(git_dir = nil, check_version: true) + disconnect db_path = path(git_dir) - ActiveRecord::Base.establish_connection( - adapter: "sqlite3", - database: db_path - ) + @db = Sequel.sqlite(db_path) + Sequel::Model.db = @db + refresh_models check_version! if check_version + @db end def self.connect_memory - ActiveRecord::Base.establish_connection( - adapter: "sqlite3", - database: ":memory:" - ) + disconnect + @db = Sequel.sqlite + Sequel::Model.db = @db + refresh_models create_schema + @db + end + + def self.disconnect + return unless @db + + Sequel::DATABASES.delete(@db) + @db.disconnect rescue nil + @db = nil + end + + def self.refresh_models + # Force models to use the new database connection + [ + Git::Pkgs::Models::Branch, + Git::Pkgs::Models::BranchCommit, + Git::Pkgs::Models::Commit, + Git::Pkgs::Models::Manifest, + Git::Pkgs::Models::DependencyChange, + Git::Pkgs::Models::DependencySnapshot + ].each do |model| + model.dataset = @db[model.table_name] + # Clear cached association datasets and loaders that may reference old db + model.association_reflections.each_value do |reflection| + if reflection[:cache] + reflection[:cache].delete(:_dataset) + reflection[:cache].delete(:associated_eager_dataset) + reflection[:cache].delete(:placeholder_eager_loader) + end + end + rescue Sequel::Error + # Table may not exist yet + end end def self.exists?(git_dir = nil) @@ -58,111 +103,110 @@ def self.exists?(git_dir = nil) end def self.create_schema(with_indexes: true) - ActiveRecord::Schema.verbose = false - ActiveRecord::Schema.define do - create_table :schema_info, if_not_exists: true do |t| - t.integer :version, null: false - end + @db.create_table?(:schema_info) do + Integer :version, null: false + end - create_table :branches, if_not_exists: true do |t| - t.string :name, null: false - t.string :last_analyzed_sha - t.timestamps - end - add_index :branches, :name, unique: true, if_not_exists: true - - create_table :commits, if_not_exists: true do |t| - t.string :sha, null: false - t.text :message - t.string :author_name - t.string :author_email - t.datetime :committed_at - t.boolean :has_dependency_changes, default: false - t.timestamps - end - add_index :commits, :sha, unique: true, if_not_exists: true + @db.create_table?(:branches) do + primary_key :id + String :name, null: false + String :last_analyzed_sha + DateTime :created_at + DateTime :updated_at + index :name, unique: true + end - create_table :branch_commits, if_not_exists: true do |t| - t.references :branch, foreign_key: true - t.references :commit, foreign_key: true - t.integer :position - end - add_index :branch_commits, [:branch_id, :commit_id], unique: true, if_not_exists: true + @db.create_table?(:commits) do + primary_key :id + String :sha, null: false + String :message, text: true + String :author_name + String :author_email + DateTime :committed_at + TrueClass :has_dependency_changes, default: false + DateTime :created_at + DateTime :updated_at + index :sha, unique: true + end - create_table :manifests, if_not_exists: true do |t| - t.string :path, null: false - t.string :ecosystem - t.string :kind - t.timestamps - end - add_index :manifests, :path, if_not_exists: true - - create_table :dependency_changes, if_not_exists: true do |t| - t.references :commit, foreign_key: true - t.references :manifest, foreign_key: true - t.string :name, null: false - t.string :ecosystem - t.string :change_type, null: false - t.string :requirement - t.string :previous_requirement - t.string :dependency_type - t.timestamps - end + @db.create_table?(:branch_commits) do + primary_key :id + foreign_key :branch_id, :branches + foreign_key :commit_id, :commits + Integer :position + index [:branch_id, :commit_id], unique: true + end - create_table :dependency_snapshots, if_not_exists: true do |t| - t.references :commit, foreign_key: true - t.references :manifest, foreign_key: true - t.string :name, null: false - t.string :ecosystem - t.string :requirement - t.string :dependency_type - t.timestamps - end + @db.create_table?(:manifests) do + primary_key :id + String :path, null: false + String :ecosystem + String :kind + DateTime :created_at + DateTime :updated_at + index :path + end + + @db.create_table?(:dependency_changes) do + primary_key :id + foreign_key :commit_id, :commits + foreign_key :manifest_id, :manifests + String :name, null: false + String :ecosystem + String :change_type, null: false + String :requirement + String :previous_requirement + String :dependency_type + DateTime :created_at + DateTime :updated_at + end + + @db.create_table?(:dependency_snapshots) do + primary_key :id + foreign_key :commit_id, :commits + foreign_key :manifest_id, :manifests + String :name, null: false + String :ecosystem + String :requirement + String :dependency_type + DateTime :created_at + DateTime :updated_at end set_version create_bulk_indexes if with_indexes + refresh_models end def self.create_bulk_indexes - conn = ActiveRecord::Base.connection - - # dependency_changes indexes - conn.add_index :dependency_changes, :name, if_not_exists: true - conn.add_index :dependency_changes, :ecosystem, if_not_exists: true - conn.add_index :dependency_changes, [:commit_id, :name], if_not_exists: true + @db.alter_table(:dependency_changes) do + add_index :name, if_not_exists: true + add_index :ecosystem, if_not_exists: true + add_index [:commit_id, :name], if_not_exists: true + end - # dependency_snapshots indexes - conn.add_index :dependency_snapshots, [:commit_id, :manifest_id, :name], - unique: true, name: "idx_snapshots_unique", if_not_exists: true - conn.add_index :dependency_snapshots, :name, if_not_exists: true - conn.add_index :dependency_snapshots, :ecosystem, if_not_exists: true + @db.alter_table(:dependency_snapshots) do + add_index [:commit_id, :manifest_id, :name], unique: true, name: "idx_snapshots_unique", if_not_exists: true + add_index :name, if_not_exists: true + add_index :ecosystem, if_not_exists: true + end end def self.stored_version - conn = ActiveRecord::Base.connection - return nil unless conn.table_exists?(:schema_info) + return nil unless @db.table_exists?(:schema_info) - result = conn.select_value("SELECT version FROM schema_info LIMIT 1") - result&.to_i + @db[:schema_info].get(:version) end def self.set_version(version = SCHEMA_VERSION) - conn = ActiveRecord::Base.connection - conn.execute("DELETE FROM schema_info") - conn.execute("INSERT INTO schema_info (version) VALUES (#{version})") + @db[:schema_info].delete + @db[:schema_info].insert(version: version) end def self.needs_upgrade? - conn = ActiveRecord::Base.connection - - # No tables at all = fresh database, no upgrade needed - return false unless conn.table_exists?(:commits) - - # Has commits table but no schema_info = old database, needs upgrade - return true unless conn.table_exists?(:schema_info) + return false unless @db.table_exists?(:commits) + return true unless @db.table_exists?(:schema_info) - # Check version stored = stored_version || 0 stored < SCHEMA_VERSION end @@ -177,19 +221,18 @@ def self.check_version! end def self.optimize_for_bulk_writes - conn = ActiveRecord::Base.connection - conn.execute("PRAGMA synchronous = OFF") - conn.execute("PRAGMA journal_mode = WAL") - conn.execute("PRAGMA cache_size = -64000") # 64MB cache + @db.run("PRAGMA synchronous = OFF") + @db.run("PRAGMA journal_mode = WAL") + @db.run("PRAGMA cache_size = -64000") end def self.optimize_for_reads - conn = ActiveRecord::Base.connection - conn.execute("PRAGMA synchronous = NORMAL") + @db.run("PRAGMA synchronous = NORMAL") end def self.drop(git_dir = nil) - ActiveRecord::Base.connection.close if ActiveRecord::Base.connected? + @db&.disconnect + @db = nil File.delete(path(git_dir)) if exists?(git_dir) end end diff --git a/lib/git/pkgs/models/branch.rb b/lib/git/pkgs/models/branch.rb index df7aaae..adb5243 100644 --- a/lib/git/pkgs/models/branch.rb +++ b/lib/git/pkgs/models/branch.rb @@ -3,14 +3,12 @@ module Git module Pkgs module Models - class Branch < ActiveRecord::Base - has_many :branch_commits, dependent: :destroy - has_many :commits, through: :branch_commits - - validates :name, presence: true, uniqueness: true + class Branch < Sequel::Model + one_to_many :branch_commits + many_to_many :commits, join_table: :branch_commits def self.find_or_create(name) - find_or_create_by(name: name) + first(name: name) || create(name: name) end end end diff --git a/lib/git/pkgs/models/branch_commit.rb b/lib/git/pkgs/models/branch_commit.rb index 148a9af..ef6cab8 100644 --- a/lib/git/pkgs/models/branch_commit.rb +++ b/lib/git/pkgs/models/branch_commit.rb @@ -3,11 +3,9 @@ module Git module Pkgs module Models - class BranchCommit < ActiveRecord::Base - belongs_to :branch - belongs_to :commit - - validates :branch_id, uniqueness: { scope: :commit_id } + class BranchCommit < Sequel::Model + many_to_one :branch + many_to_one :commit end end end diff --git a/lib/git/pkgs/models/commit.rb b/lib/git/pkgs/models/commit.rb index 227f247..d0a5c90 100644 --- a/lib/git/pkgs/models/commit.rb +++ b/lib/git/pkgs/models/commit.rb @@ -3,16 +3,17 @@ module Git module Pkgs module Models - class Commit < ActiveRecord::Base - has_many :branch_commits, dependent: :destroy - has_many :branches, through: :branch_commits - has_many :dependency_changes, dependent: :destroy - has_many :dependency_snapshots, dependent: :destroy + class Commit < Sequel::Model + unrestrict_primary_key + plugin :validation_helpers - validates :sha, presence: true, uniqueness: true + one_to_many :branch_commits + one_to_many :dependency_changes + one_to_many :dependency_snapshots + many_to_many :branches, join_table: :branch_commits def self.find_or_create_from_rugged(rugged_commit) - find_or_create_by(sha: rugged_commit.oid) do |commit| + find_or_create(sha: rugged_commit.oid) do |commit| commit.message = rugged_commit.message&.strip commit.author_name = rugged_commit.author[:name] commit.author_email = rugged_commit.author[:email] @@ -21,13 +22,13 @@ def self.find_or_create_from_rugged(rugged_commit) end def self.find_or_create_from_repo(repo, sha) - commit = find_by(sha: sha) || where("sha LIKE ?", "#{sha}%").first + commit = first(sha: sha) || where(Sequel.like(:sha, "#{sha}%")).first return commit if commit rugged_commit = repo.lookup(sha) return nil unless rugged_commit - create!( + create( sha: rugged_commit.oid, message: rugged_commit.message, author_name: rugged_commit.author[:name], diff --git a/lib/git/pkgs/models/dependency_change.rb b/lib/git/pkgs/models/dependency_change.rb index 5f45881..43a7a91 100644 --- a/lib/git/pkgs/models/dependency_change.rb +++ b/lib/git/pkgs/models/dependency_change.rb @@ -3,18 +3,31 @@ module Git module Pkgs module Models - class DependencyChange < ActiveRecord::Base - belongs_to :commit - belongs_to :manifest + class DependencyChange < Sequel::Model + many_to_one :commit + many_to_one :manifest - validates :name, presence: true - validates :change_type, presence: true, inclusion: { in: %w[added modified removed] } + dataset_module do + def added + where(change_type: "added") + end - scope :added, -> { where(change_type: "added") } - scope :modified, -> { where(change_type: "modified") } - scope :removed, -> { where(change_type: "removed") } - scope :for_package, ->(name) { where(name: name) } - scope :for_platform, ->(platform) { where(ecosystem: platform) } + def modified + where(change_type: "modified") + end + + def removed + where(change_type: "removed") + end + + def for_package(name) + where(name: name) + end + + def for_platform(platform) + where(ecosystem: platform) + end + end end end end diff --git a/lib/git/pkgs/models/dependency_snapshot.rb b/lib/git/pkgs/models/dependency_snapshot.rb index 071c0c5..ef57759 100644 --- a/lib/git/pkgs/models/dependency_snapshot.rb +++ b/lib/git/pkgs/models/dependency_snapshot.rb @@ -3,21 +3,29 @@ module Git module Pkgs module Models - class DependencySnapshot < ActiveRecord::Base - belongs_to :commit - belongs_to :manifest + class DependencySnapshot < Sequel::Model + many_to_one :commit + many_to_one :manifest - validates :name, presence: true + dataset_module do + def for_package(name) + where(name: name) + end - scope :for_package, ->(name) { where(name: name) } - scope :for_platform, ->(platform) { where(ecosystem: platform) } - scope :at_commit, ->(commit) { where(commit: commit) } + def for_platform(platform) + where(ecosystem: platform) + end + + def at_commit(commit) + where(commit: commit) + end + end def self.current_for_branch(branch) - return none unless branch.last_analyzed_sha + return dataset.where(false) unless branch.last_analyzed_sha - commit = Commit.find_by(sha: branch.last_analyzed_sha) - return none unless commit + commit = Commit.first(sha: branch.last_analyzed_sha) + return dataset.where(false) unless commit where(commit: commit) end diff --git a/lib/git/pkgs/models/manifest.rb b/lib/git/pkgs/models/manifest.rb index cf2c939..3ce5aee 100644 --- a/lib/git/pkgs/models/manifest.rb +++ b/lib/git/pkgs/models/manifest.rb @@ -3,17 +3,15 @@ module Git module Pkgs module Models - class Manifest < ActiveRecord::Base - has_many :dependency_changes, dependent: :destroy - has_many :dependency_snapshots, dependent: :destroy + class Manifest < Sequel::Model + one_to_many :dependency_changes + one_to_many :dependency_snapshots - validates :path, presence: true + def self.find_or_create(path:, ecosystem: nil, kind: nil) + existing = first(path: path) + return existing if existing - def self.find_or_create(path:, ecosystem:, kind:) - find_or_create_by(path: path) do |m| - m.ecosystem = ecosystem - m.kind = kind - end + create(path: path, ecosystem: ecosystem, kind: kind) end end end diff --git a/test/git/pkgs/test_cli.rb b/test/git/pkgs/test_cli.rb index 69eefdd..194237e 100644 --- a/test/git/pkgs/test_cli.rb +++ b/test/git/pkgs/test_cli.rb @@ -2,6 +2,7 @@ require "test_helper" require "stringio" +require "securerandom" class Git::Pkgs::TestCLI < Minitest::Test include TestHelpers @@ -110,7 +111,7 @@ def test_find_or_create_from_repo_finds_existing_commit sha = repo.head_sha # Create commit in database first - Git::Pkgs::Models::Commit.create!( + Git::Pkgs::Models::Commit.create( sha: sha, message: "Test", author_name: "Test", @@ -129,14 +130,14 @@ def test_find_or_create_from_repo_creates_missing_commit sha = repo.head_sha # Commit doesn't exist in database yet - assert_nil Git::Pkgs::Models::Commit.find_by(sha: sha) + assert_nil Git::Pkgs::Models::Commit.first(sha: sha) result = Git::Pkgs::Models::Commit.find_or_create_from_repo(repo, sha) assert result assert_equal sha, result.sha # Verify it was persisted - assert Git::Pkgs::Models::Commit.find_by(sha: sha) + assert Git::Pkgs::Models::Commit.first(sha: sha) end def test_find_or_create_from_repo_returns_nil_for_invalid_sha @@ -148,24 +149,15 @@ def test_find_or_create_from_repo_returns_nil_for_invalid_sha end end -class Git::Pkgs::TestShowCommand < Minitest::Test - include TestHelpers - +class Git::Pkgs::TestShowCommand < Git::Pkgs::DatabaseTest def setup - create_test_repo + super add_file("Gemfile", "source 'https://rubygems.org'\ngem 'rails'") commit("Add rails") @first_sha = get_head_sha add_file("Gemfile", "source 'https://rubygems.org'\ngem 'rails'\ngem 'puma'") commit("Add puma") @second_sha = get_head_sha - @git_dir = File.join(@test_dir, ".git") - Git::Pkgs::Database.connect(@git_dir) - Git::Pkgs::Database.create_schema - end - - def teardown - cleanup_test_repo end def test_show_displays_changes_for_commit @@ -246,7 +238,7 @@ def get_head_sha end def create_commit_with_changes(sha, changes) - commit = Git::Pkgs::Models::Commit.create!( + commit = Git::Pkgs::Models::Commit.create( sha: sha, message: "Test commit", author_name: "Test User", @@ -255,14 +247,14 @@ def create_commit_with_changes(sha, changes) has_dependency_changes: true ) - manifest = Git::Pkgs::Models::Manifest.create!( + manifest = Git::Pkgs::Models::Manifest.create( path: "Gemfile", ecosystem: "rubygems", kind: "manifest" ) changes.each do |change| - Git::Pkgs::Models::DependencyChange.create!( + Git::Pkgs::Models::DependencyChange.create( commit: commit, manifest: manifest, name: change[:name], @@ -398,7 +390,7 @@ def test_history_filters_by_date_range def create_commit_with_author(name, email, changes) sha = SecureRandom.hex(20) - commit = Git::Pkgs::Models::Commit.create!( + commit = Git::Pkgs::Models::Commit.create( sha: sha, message: "Test commit", author_name: name, @@ -407,14 +399,14 @@ def create_commit_with_author(name, email, changes) has_dependency_changes: true ) - manifest = Git::Pkgs::Models::Manifest.find_or_create_by!( + manifest = Git::Pkgs::Models::Manifest.find_or_create( path: "Gemfile", ecosystem: "rubygems", kind: "manifest" ) changes.each do |change| - Git::Pkgs::Models::DependencyChange.create!( + Git::Pkgs::Models::DependencyChange.create( commit: commit, manifest: manifest, name: change[:name], @@ -430,7 +422,7 @@ def create_commit_with_author(name, email, changes) def create_commit_at(time, changes) sha = SecureRandom.hex(20) - commit = Git::Pkgs::Models::Commit.create!( + commit = Git::Pkgs::Models::Commit.create( sha: sha, message: "Test commit", author_name: "Test User", @@ -439,14 +431,14 @@ def create_commit_at(time, changes) has_dependency_changes: true ) - manifest = Git::Pkgs::Models::Manifest.find_or_create_by!( + manifest = Git::Pkgs::Models::Manifest.find_or_create( path: "Gemfile", ecosystem: "rubygems", kind: "manifest" ) changes.each do |change| - Git::Pkgs::Models::DependencyChange.create!( + Git::Pkgs::Models::DependencyChange.create( commit: commit, manifest: manifest, name: change[:name], @@ -607,7 +599,7 @@ def test_stats_by_author_respects_until_filter def create_commit_with_author(name, email, changes, committed_at: Time.now) sha = SecureRandom.hex(20) - commit = Git::Pkgs::Models::Commit.create!( + commit = Git::Pkgs::Models::Commit.create( sha: sha, message: "Test commit", author_name: name, @@ -616,14 +608,14 @@ def create_commit_with_author(name, email, changes, committed_at: Time.now) has_dependency_changes: true ) - manifest = Git::Pkgs::Models::Manifest.find_or_create_by!( + manifest = Git::Pkgs::Models::Manifest.find_or_create( path: "Gemfile", ecosystem: "rubygems", kind: "manifest" ) changes.each do |change| - Git::Pkgs::Models::DependencyChange.create!( + Git::Pkgs::Models::DependencyChange.create( commit: commit, manifest: manifest, name: change[:name], @@ -647,20 +639,11 @@ def capture_stdout end end -class Git::Pkgs::TestLogCommand < Minitest::Test - include TestHelpers - +class Git::Pkgs::TestLogCommand < Git::Pkgs::DatabaseTest def setup - create_test_repo + super add_file("Gemfile", "source 'https://rubygems.org'\ngem 'rails'") commit("Add rails") - @git_dir = File.join(@test_dir, ".git") - Git::Pkgs::Database.connect(@git_dir) - Git::Pkgs::Database.create_schema - end - - def teardown - cleanup_test_repo end def test_log_shows_commits_with_changes @@ -737,7 +720,7 @@ def test_log_json_format def create_commit_with_changes(message, changes, author_name: "Test User", author_email: "test@example.com") sha = SecureRandom.hex(20) - commit = Git::Pkgs::Models::Commit.create!( + commit = Git::Pkgs::Models::Commit.create( sha: sha, message: message, author_name: author_name, @@ -746,14 +729,14 @@ def create_commit_with_changes(message, changes, author_name: "Test User", autho has_dependency_changes: true ) - manifest = Git::Pkgs::Models::Manifest.find_or_create_by!( + manifest = Git::Pkgs::Models::Manifest.find_or_create( path: "Gemfile", ecosystem: "rubygems", kind: "manifest" ) changes.each do |change| - Git::Pkgs::Models::DependencyChange.create!( + Git::Pkgs::Models::DependencyChange.create( commit: commit, manifest: manifest, name: change[:name], @@ -766,15 +749,6 @@ def create_commit_with_changes(message, changes, author_name: "Test User", autho commit end - - def capture_stdout - original = $stdout - $stdout = StringIO.new - yield - $stdout.string - ensure - $stdout = original - end end class Git::Pkgs::TestInfoCommand < Minitest::Test @@ -796,7 +770,7 @@ def teardown def test_info_with_zero_snapshots_does_not_crash # Create commits with dependency changes but no snapshots sha = SecureRandom.hex(20) - Git::Pkgs::Models::Commit.create!( + Git::Pkgs::Models::Commit.create( sha: sha, message: "Test commit", author_name: "Test User", @@ -820,7 +794,7 @@ def test_info_with_zero_snapshots_does_not_crash def test_info_with_snapshots_shows_ratio sha = SecureRandom.hex(20) - commit = Git::Pkgs::Models::Commit.create!( + commit = Git::Pkgs::Models::Commit.create( sha: sha, message: "Test commit", author_name: "Test User", @@ -829,13 +803,13 @@ def test_info_with_snapshots_shows_ratio has_dependency_changes: true ) - manifest = Git::Pkgs::Models::Manifest.create!( + manifest = Git::Pkgs::Models::Manifest.create( path: "Gemfile", ecosystem: "rubygems", kind: "manifest" ) - Git::Pkgs::Models::DependencySnapshot.create!( + Git::Pkgs::Models::DependencySnapshot.create( commit: commit, manifest: manifest, name: "rails", @@ -878,17 +852,17 @@ def setup # Create branch and snapshot repo = Git::Pkgs::Repository.new(@test_dir) - Git::Pkgs::Models::Branch.create!(name: repo.default_branch, last_analyzed_sha: repo.head_sha) + Git::Pkgs::Models::Branch.create(name: repo.default_branch, last_analyzed_sha: repo.head_sha) rugged_commit = repo.lookup(repo.head_sha) commit_record = Git::Pkgs::Models::Commit.find_or_create_from_rugged(rugged_commit) - manifest = Git::Pkgs::Models::Manifest.create!( + manifest = Git::Pkgs::Models::Manifest.create( path: "Gemfile", ecosystem: "rubygems", kind: "manifest" ) - Git::Pkgs::Models::DependencySnapshot.create!( + Git::Pkgs::Models::DependencySnapshot.create( commit: commit_record, manifest: manifest, name: "rails", @@ -896,7 +870,7 @@ def setup requirement: "~> 7.0" ) - Git::Pkgs::Models::DependencySnapshot.create!( + Git::Pkgs::Models::DependencySnapshot.create( commit: commit_record, manifest: manifest, name: "puma", diff --git a/test/git/pkgs/test_database.rb b/test/git/pkgs/test_database.rb index a98ef45..a7a710f 100644 --- a/test/git/pkgs/test_database.rb +++ b/test/git/pkgs/test_database.rb @@ -35,12 +35,13 @@ def test_create_schema_creates_tables Git::Pkgs::Database.connect(@git_dir) Git::Pkgs::Database.create_schema - assert ActiveRecord::Base.connection.table_exists?(:branches) - assert ActiveRecord::Base.connection.table_exists?(:commits) - assert ActiveRecord::Base.connection.table_exists?(:branch_commits) - assert ActiveRecord::Base.connection.table_exists?(:manifests) - assert ActiveRecord::Base.connection.table_exists?(:dependency_changes) - assert ActiveRecord::Base.connection.table_exists?(:dependency_snapshots) + db = Git::Pkgs::Database.db + assert db.table_exists?(:branches) + assert db.table_exists?(:commits) + assert db.table_exists?(:branch_commits) + assert db.table_exists?(:manifests) + assert db.table_exists?(:dependency_changes) + assert db.table_exists?(:dependency_snapshots) end def test_drop_removes_database diff --git a/test/git/pkgs/test_models.rb b/test/git/pkgs/test_models.rb index 1f9f087..b421be4 100644 --- a/test/git/pkgs/test_models.rb +++ b/test/git/pkgs/test_models.rb @@ -71,7 +71,7 @@ def test_dependency_change_scopes kind: "manifest" ) - Git::Pkgs::Models::DependencyChange.create!( + Git::Pkgs::Models::DependencyChange.create( commit: commit, manifest: manifest, name: "rails", @@ -80,7 +80,7 @@ def test_dependency_change_scopes requirement: "~> 7.0" ) - Git::Pkgs::Models::DependencyChange.create!( + Git::Pkgs::Models::DependencyChange.create( commit: commit, manifest: manifest, name: "puma", @@ -102,7 +102,7 @@ def test_branch_commit_associations branch = Git::Pkgs::Models::Branch.find_or_create("main") commit = Git::Pkgs::Models::Commit.find_or_create_from_rugged(rugged_commit) - Git::Pkgs::Models::BranchCommit.create!( + Git::Pkgs::Models::BranchCommit.create( branch: branch, commit: commit, position: 1 diff --git a/test/test_helper.rb b/test/test_helper.rb index 7c5baee..99e7773 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -13,8 +13,6 @@ require "fileutils" require "tmpdir" -ActiveRecord::Migration.verbose = false - module TestHelpers def create_test_repo @test_dir = Dir.mktmpdir("git-pkgs-test") @@ -27,6 +25,7 @@ def create_test_repo end def cleanup_test_repo + Git::Pkgs::Database.disconnect FileUtils.rm_rf(@test_dir) if @test_dir && File.exist?(@test_dir) end @@ -64,4 +63,29 @@ def sample_package_json(deps = {}) "dependencies" => deps }) end + + def capture_stdout + original = $stdout + $stdout = StringIO.new + yield + $stdout.string + ensure + $stdout = original + end +end + +# Base test class for tests that use the database +class Git::Pkgs::DatabaseTest < Minitest::Test + include TestHelpers + + def setup + create_test_repo + @git_dir = File.join(@test_dir, ".git") + Git::Pkgs::Database.connect(@git_dir) + Git::Pkgs::Database.create_schema + end + + def teardown + cleanup_test_repo + end end