git-pkgs 0.5.0 → 0.6.0

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.
@@ -1,7 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_record"
4
- require "sqlite3"
3
+ require "sequel"
4
+
5
+ # Set up a placeholder DB so models can be defined before connection
6
+ DB = Sequel.sqlite
7
+ Sequel::Model.plugin :timestamps, update_on_create: true
8
+ Sequel::Model.plugin :update_or_create
9
+ Sequel::Model.require_valid_table = false
10
+ Sequel::Model.unrestrict_primary_key
11
+ # Allow mass assignment of any column (similar to AR default behavior)
12
+ Sequel::Model.strict_param_setting = false
5
13
 
6
14
  module Git
7
15
  module Pkgs
@@ -9,6 +17,10 @@ module Git
9
17
  DB_FILE = "pkgs.sqlite3"
10
18
  SCHEMA_VERSION = 1
11
19
 
20
+ class << self
21
+ attr_accessor :db
22
+ end
23
+
12
24
  def self.path(git_dir = nil)
13
25
  return Git::Pkgs.db_path if Git::Pkgs.db_path
14
26
 
@@ -23,7 +35,6 @@ module Git
23
35
  raise NotInGitRepoError, "GIT_DIR '#{Git::Pkgs.git_dir}' does not exist"
24
36
  end
25
37
 
26
- # Start from work_tree if set, otherwise current directory
27
38
  dir = Git::Pkgs.work_tree || Dir.pwd
28
39
  loop do
29
40
  git_dir = File.join(dir, ".git")
@@ -37,20 +48,54 @@ module Git
37
48
  end
38
49
 
39
50
  def self.connect(git_dir = nil, check_version: true)
51
+ disconnect
40
52
  db_path = path(git_dir)
41
- ActiveRecord::Base.establish_connection(
42
- adapter: "sqlite3",
43
- database: db_path
44
- )
53
+ @db = Sequel.sqlite(db_path)
54
+ Sequel::Model.db = @db
55
+ refresh_models
45
56
  check_version! if check_version
57
+ @db
46
58
  end
47
59
 
48
60
  def self.connect_memory
49
- ActiveRecord::Base.establish_connection(
50
- adapter: "sqlite3",
51
- database: ":memory:"
52
- )
61
+ disconnect
62
+ @db = Sequel.sqlite
63
+ Sequel::Model.db = @db
64
+ refresh_models
53
65
  create_schema
66
+ @db
67
+ end
68
+
69
+ def self.disconnect
70
+ return unless @db
71
+
72
+ Sequel::DATABASES.delete(@db)
73
+ @db.disconnect rescue nil
74
+ @db = nil
75
+ end
76
+
77
+ def self.refresh_models
78
+ # Force models to use the new database connection
79
+ [
80
+ Git::Pkgs::Models::Branch,
81
+ Git::Pkgs::Models::BranchCommit,
82
+ Git::Pkgs::Models::Commit,
83
+ Git::Pkgs::Models::Manifest,
84
+ Git::Pkgs::Models::DependencyChange,
85
+ Git::Pkgs::Models::DependencySnapshot
86
+ ].each do |model|
87
+ model.dataset = @db[model.table_name]
88
+ # Clear cached association datasets and loaders that may reference old db
89
+ model.association_reflections.each_value do |reflection|
90
+ if reflection[:cache]
91
+ reflection[:cache].delete(:_dataset)
92
+ reflection[:cache].delete(:associated_eager_dataset)
93
+ reflection[:cache].delete(:placeholder_eager_loader)
94
+ end
95
+ end
96
+ rescue Sequel::Error
97
+ # Table may not exist yet
98
+ end
54
99
  end
55
100
 
56
101
  def self.exists?(git_dir = nil)
@@ -58,111 +103,110 @@ module Git
58
103
  end
59
104
 
60
105
  def self.create_schema(with_indexes: true)
61
- ActiveRecord::Schema.verbose = false
62
- ActiveRecord::Schema.define do
63
- create_table :schema_info, if_not_exists: true do |t|
64
- t.integer :version, null: false
65
- end
106
+ @db.create_table?(:schema_info) do
107
+ Integer :version, null: false
108
+ end
66
109
 
67
- create_table :branches, if_not_exists: true do |t|
68
- t.string :name, null: false
69
- t.string :last_analyzed_sha
70
- t.timestamps
71
- end
72
- add_index :branches, :name, unique: true, if_not_exists: true
73
-
74
- create_table :commits, if_not_exists: true do |t|
75
- t.string :sha, null: false
76
- t.text :message
77
- t.string :author_name
78
- t.string :author_email
79
- t.datetime :committed_at
80
- t.boolean :has_dependency_changes, default: false
81
- t.timestamps
82
- end
83
- add_index :commits, :sha, unique: true, if_not_exists: true
110
+ @db.create_table?(:branches) do
111
+ primary_key :id
112
+ String :name, null: false
113
+ String :last_analyzed_sha
114
+ DateTime :created_at
115
+ DateTime :updated_at
116
+ index :name, unique: true
117
+ end
84
118
 
85
- create_table :branch_commits, if_not_exists: true do |t|
86
- t.references :branch, foreign_key: true
87
- t.references :commit, foreign_key: true
88
- t.integer :position
89
- end
90
- add_index :branch_commits, [:branch_id, :commit_id], unique: true, if_not_exists: true
119
+ @db.create_table?(:commits) do
120
+ primary_key :id
121
+ String :sha, null: false
122
+ String :message, text: true
123
+ String :author_name
124
+ String :author_email
125
+ DateTime :committed_at
126
+ TrueClass :has_dependency_changes, default: false
127
+ DateTime :created_at
128
+ DateTime :updated_at
129
+ index :sha, unique: true
130
+ end
91
131
 
92
- create_table :manifests, if_not_exists: true do |t|
93
- t.string :path, null: false
94
- t.string :ecosystem
95
- t.string :kind
96
- t.timestamps
97
- end
98
- add_index :manifests, :path, if_not_exists: true
99
-
100
- create_table :dependency_changes, if_not_exists: true do |t|
101
- t.references :commit, foreign_key: true
102
- t.references :manifest, foreign_key: true
103
- t.string :name, null: false
104
- t.string :ecosystem
105
- t.string :change_type, null: false
106
- t.string :requirement
107
- t.string :previous_requirement
108
- t.string :dependency_type
109
- t.timestamps
110
- end
132
+ @db.create_table?(:branch_commits) do
133
+ primary_key :id
134
+ foreign_key :branch_id, :branches
135
+ foreign_key :commit_id, :commits
136
+ Integer :position
137
+ index [:branch_id, :commit_id], unique: true
138
+ end
111
139
 
112
- create_table :dependency_snapshots, if_not_exists: true do |t|
113
- t.references :commit, foreign_key: true
114
- t.references :manifest, foreign_key: true
115
- t.string :name, null: false
116
- t.string :ecosystem
117
- t.string :requirement
118
- t.string :dependency_type
119
- t.timestamps
120
- end
140
+ @db.create_table?(:manifests) do
141
+ primary_key :id
142
+ String :path, null: false
143
+ String :ecosystem
144
+ String :kind
145
+ DateTime :created_at
146
+ DateTime :updated_at
147
+ index :path
148
+ end
149
+
150
+ @db.create_table?(:dependency_changes) do
151
+ primary_key :id
152
+ foreign_key :commit_id, :commits
153
+ foreign_key :manifest_id, :manifests
154
+ String :name, null: false
155
+ String :ecosystem
156
+ String :change_type, null: false
157
+ String :requirement
158
+ String :previous_requirement
159
+ String :dependency_type
160
+ DateTime :created_at
161
+ DateTime :updated_at
162
+ end
163
+
164
+ @db.create_table?(:dependency_snapshots) do
165
+ primary_key :id
166
+ foreign_key :commit_id, :commits
167
+ foreign_key :manifest_id, :manifests
168
+ String :name, null: false
169
+ String :ecosystem
170
+ String :requirement
171
+ String :dependency_type
172
+ DateTime :created_at
173
+ DateTime :updated_at
121
174
  end
122
175
 
123
176
  set_version
124
177
  create_bulk_indexes if with_indexes
178
+ refresh_models
125
179
  end
126
180
 
127
181
  def self.create_bulk_indexes
128
- conn = ActiveRecord::Base.connection
129
-
130
- # dependency_changes indexes
131
- conn.add_index :dependency_changes, :name, if_not_exists: true
132
- conn.add_index :dependency_changes, :ecosystem, if_not_exists: true
133
- conn.add_index :dependency_changes, [:commit_id, :name], if_not_exists: true
182
+ @db.alter_table(:dependency_changes) do
183
+ add_index :name, if_not_exists: true
184
+ add_index :ecosystem, if_not_exists: true
185
+ add_index [:commit_id, :name], if_not_exists: true
186
+ end
134
187
 
135
- # dependency_snapshots indexes
136
- conn.add_index :dependency_snapshots, [:commit_id, :manifest_id, :name],
137
- unique: true, name: "idx_snapshots_unique", if_not_exists: true
138
- conn.add_index :dependency_snapshots, :name, if_not_exists: true
139
- conn.add_index :dependency_snapshots, :ecosystem, if_not_exists: true
188
+ @db.alter_table(:dependency_snapshots) do
189
+ add_index [:commit_id, :manifest_id, :name], unique: true, name: "idx_snapshots_unique", if_not_exists: true
190
+ add_index :name, if_not_exists: true
191
+ add_index :ecosystem, if_not_exists: true
192
+ end
140
193
  end
141
194
 
142
195
  def self.stored_version
143
- conn = ActiveRecord::Base.connection
144
- return nil unless conn.table_exists?(:schema_info)
196
+ return nil unless @db.table_exists?(:schema_info)
145
197
 
146
- result = conn.select_value("SELECT version FROM schema_info LIMIT 1")
147
- result&.to_i
198
+ @db[:schema_info].get(:version)
148
199
  end
149
200
 
150
201
  def self.set_version(version = SCHEMA_VERSION)
151
- conn = ActiveRecord::Base.connection
152
- conn.execute("DELETE FROM schema_info")
153
- conn.execute("INSERT INTO schema_info (version) VALUES (#{version})")
202
+ @db[:schema_info].delete
203
+ @db[:schema_info].insert(version: version)
154
204
  end
155
205
 
156
206
  def self.needs_upgrade?
157
- conn = ActiveRecord::Base.connection
158
-
159
- # No tables at all = fresh database, no upgrade needed
160
- return false unless conn.table_exists?(:commits)
161
-
162
- # Has commits table but no schema_info = old database, needs upgrade
163
- return true unless conn.table_exists?(:schema_info)
207
+ return false unless @db.table_exists?(:commits)
208
+ return true unless @db.table_exists?(:schema_info)
164
209
 
165
- # Check version
166
210
  stored = stored_version || 0
167
211
  stored < SCHEMA_VERSION
168
212
  end
@@ -177,19 +221,18 @@ module Git
177
221
  end
178
222
 
179
223
  def self.optimize_for_bulk_writes
180
- conn = ActiveRecord::Base.connection
181
- conn.execute("PRAGMA synchronous = OFF")
182
- conn.execute("PRAGMA journal_mode = WAL")
183
- conn.execute("PRAGMA cache_size = -64000") # 64MB cache
224
+ @db.run("PRAGMA synchronous = OFF")
225
+ @db.run("PRAGMA journal_mode = WAL")
226
+ @db.run("PRAGMA cache_size = -64000")
184
227
  end
185
228
 
186
229
  def self.optimize_for_reads
187
- conn = ActiveRecord::Base.connection
188
- conn.execute("PRAGMA synchronous = NORMAL")
230
+ @db.run("PRAGMA synchronous = NORMAL")
189
231
  end
190
232
 
191
233
  def self.drop(git_dir = nil)
192
- ActiveRecord::Base.connection.close if ActiveRecord::Base.connected?
234
+ @db&.disconnect
235
+ @db = nil
193
236
  File.delete(path(git_dir)) if exists?(git_dir)
194
237
  end
195
238
  end
@@ -3,14 +3,12 @@
3
3
  module Git
4
4
  module Pkgs
5
5
  module Models
6
- class Branch < ActiveRecord::Base
7
- has_many :branch_commits, dependent: :destroy
8
- has_many :commits, through: :branch_commits
9
-
10
- validates :name, presence: true, uniqueness: true
6
+ class Branch < Sequel::Model
7
+ one_to_many :branch_commits
8
+ many_to_many :commits, join_table: :branch_commits
11
9
 
12
10
  def self.find_or_create(name)
13
- find_or_create_by(name: name)
11
+ first(name: name) || create(name: name)
14
12
  end
15
13
  end
16
14
  end
@@ -3,11 +3,9 @@
3
3
  module Git
4
4
  module Pkgs
5
5
  module Models
6
- class BranchCommit < ActiveRecord::Base
7
- belongs_to :branch
8
- belongs_to :commit
9
-
10
- validates :branch_id, uniqueness: { scope: :commit_id }
6
+ class BranchCommit < Sequel::Model
7
+ many_to_one :branch
8
+ many_to_one :commit
11
9
  end
12
10
  end
13
11
  end
@@ -3,16 +3,17 @@
3
3
  module Git
4
4
  module Pkgs
5
5
  module Models
6
- class Commit < ActiveRecord::Base
7
- has_many :branch_commits, dependent: :destroy
8
- has_many :branches, through: :branch_commits
9
- has_many :dependency_changes, dependent: :destroy
10
- has_many :dependency_snapshots, dependent: :destroy
6
+ class Commit < Sequel::Model
7
+ unrestrict_primary_key
8
+ plugin :validation_helpers
11
9
 
12
- validates :sha, presence: true, uniqueness: true
10
+ one_to_many :branch_commits
11
+ one_to_many :dependency_changes
12
+ one_to_many :dependency_snapshots
13
+ many_to_many :branches, join_table: :branch_commits
13
14
 
14
15
  def self.find_or_create_from_rugged(rugged_commit)
15
- find_or_create_by(sha: rugged_commit.oid) do |commit|
16
+ find_or_create(sha: rugged_commit.oid) do |commit|
16
17
  commit.message = rugged_commit.message&.strip
17
18
  commit.author_name = rugged_commit.author[:name]
18
19
  commit.author_email = rugged_commit.author[:email]
@@ -21,13 +22,13 @@ module Git
21
22
  end
22
23
 
23
24
  def self.find_or_create_from_repo(repo, sha)
24
- commit = find_by(sha: sha) || where("sha LIKE ?", "#{sha}%").first
25
+ commit = first(sha: sha) || where(Sequel.like(:sha, "#{sha}%")).first
25
26
  return commit if commit
26
27
 
27
28
  rugged_commit = repo.lookup(sha)
28
29
  return nil unless rugged_commit
29
30
 
30
- create!(
31
+ create(
31
32
  sha: rugged_commit.oid,
32
33
  message: rugged_commit.message,
33
34
  author_name: rugged_commit.author[:name],
@@ -3,18 +3,31 @@
3
3
  module Git
4
4
  module Pkgs
5
5
  module Models
6
- class DependencyChange < ActiveRecord::Base
7
- belongs_to :commit
8
- belongs_to :manifest
6
+ class DependencyChange < Sequel::Model
7
+ many_to_one :commit
8
+ many_to_one :manifest
9
9
 
10
- validates :name, presence: true
11
- validates :change_type, presence: true, inclusion: { in: %w[added modified removed] }
10
+ dataset_module do
11
+ def added
12
+ where(change_type: "added")
13
+ end
12
14
 
13
- scope :added, -> { where(change_type: "added") }
14
- scope :modified, -> { where(change_type: "modified") }
15
- scope :removed, -> { where(change_type: "removed") }
16
- scope :for_package, ->(name) { where(name: name) }
17
- scope :for_platform, ->(platform) { where(ecosystem: platform) }
15
+ def modified
16
+ where(change_type: "modified")
17
+ end
18
+
19
+ def removed
20
+ where(change_type: "removed")
21
+ end
22
+
23
+ def for_package(name)
24
+ where(name: name)
25
+ end
26
+
27
+ def for_platform(platform)
28
+ where(ecosystem: platform)
29
+ end
30
+ end
18
31
  end
19
32
  end
20
33
  end
@@ -3,21 +3,29 @@
3
3
  module Git
4
4
  module Pkgs
5
5
  module Models
6
- class DependencySnapshot < ActiveRecord::Base
7
- belongs_to :commit
8
- belongs_to :manifest
6
+ class DependencySnapshot < Sequel::Model
7
+ many_to_one :commit
8
+ many_to_one :manifest
9
9
 
10
- validates :name, presence: true
10
+ dataset_module do
11
+ def for_package(name)
12
+ where(name: name)
13
+ end
11
14
 
12
- scope :for_package, ->(name) { where(name: name) }
13
- scope :for_platform, ->(platform) { where(ecosystem: platform) }
14
- scope :at_commit, ->(commit) { where(commit: commit) }
15
+ def for_platform(platform)
16
+ where(ecosystem: platform)
17
+ end
18
+
19
+ def at_commit(commit)
20
+ where(commit: commit)
21
+ end
22
+ end
15
23
 
16
24
  def self.current_for_branch(branch)
17
- return none unless branch.last_analyzed_sha
25
+ return dataset.where(false) unless branch.last_analyzed_sha
18
26
 
19
- commit = Commit.find_by(sha: branch.last_analyzed_sha)
20
- return none unless commit
27
+ commit = Commit.first(sha: branch.last_analyzed_sha)
28
+ return dataset.where(false) unless commit
21
29
 
22
30
  where(commit: commit)
23
31
  end
@@ -3,17 +3,15 @@
3
3
  module Git
4
4
  module Pkgs
5
5
  module Models
6
- class Manifest < ActiveRecord::Base
7
- has_many :dependency_changes, dependent: :destroy
8
- has_many :dependency_snapshots, dependent: :destroy
6
+ class Manifest < Sequel::Model
7
+ one_to_many :dependency_changes
8
+ one_to_many :dependency_snapshots
9
9
 
10
- validates :path, presence: true
10
+ def self.find_or_create(path:, ecosystem: nil, kind: nil)
11
+ existing = first(path: path)
12
+ return existing if existing
11
13
 
12
- def self.find_or_create(path:, ecosystem:, kind:)
13
- find_or_create_by(path: path) do |m|
14
- m.ecosystem = ecosystem
15
- m.kind = kind
16
- end
14
+ create(path: path, ecosystem: ecosystem, kind: kind)
17
15
  end
18
16
  end
19
17
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Git
4
4
  module Pkgs
5
- VERSION = "0.5.0"
5
+ VERSION = "0.6.0"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: git-pkgs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Nesbitt
@@ -24,19 +24,19 @@ dependencies:
24
24
  - !ruby/object:Gem::Version
25
25
  version: '1.0'
26
26
  - !ruby/object:Gem::Dependency
27
- name: activerecord
27
+ name: sequel
28
28
  requirement: !ruby/object:Gem::Requirement
29
29
  requirements:
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
- version: '7.0'
32
+ version: '5.0'
33
33
  type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - ">="
38
38
  - !ruby/object:Gem::Version
39
- version: '7.0'
39
+ version: '5.0'
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: sqlite3
42
42
  requirement: !ruby/object:Gem::Requirement
@@ -55,16 +55,16 @@ dependencies:
55
55
  name: ecosystems-bibliothecary
56
56
  requirement: !ruby/object:Gem::Requirement
57
57
  requirements:
58
- - - ">="
58
+ - - "~>"
59
59
  - !ruby/object:Gem::Version
60
- version: '0'
60
+ version: '15.0'
61
61
  type: :runtime
62
62
  prerelease: false
63
63
  version_requirements: !ruby/object:Gem::Requirement
64
64
  requirements:
65
- - - ">="
65
+ - - "~>"
66
66
  - !ruby/object:Gem::Version
67
- version: '0'
67
+ version: '15.0'
68
68
  description: A git subcommand for analyzing package/dependency usage in git repositories
69
69
  over time
70
70
  email: