Skip to content

Specs, refactor and cleanup #5

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ jobs:
ruby-version: ${{ matrix.ruby }}
bundler-cache: true

- name: Configure Git
run: |
git config --global user.email "shayonj@gmail.com"
git config --global user.name "Shayon Mukherjee"
git config --global init.defaultBranch main

- name: Bundle install
env:
RAILS_ENV: test
Expand All @@ -38,10 +44,10 @@ jobs:

- name: Run Lint
run: bundle exec rubocop
# - name: Setup upterm session
# uses: lhotari/action-upterm@v1

- name: Run RSpec
run: bundle exec rspec

build-push-image:
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-latest
Expand Down
8 changes: 8 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,14 @@ PATH
GEM
remote: https://rubygems.org/
specs:
addressable (2.8.5)
public_suffix (>= 2.0.2, < 6.0)
ast (2.4.2)
coderay (1.1.3)
diff-lcs (1.5.0)
git (1.18.0)
addressable (~> 2.8)
rchardet (~> 1.8)
haml (6.0.7)
temple (>= 0.8.2)
thor
Expand All @@ -27,10 +32,12 @@ GEM
pry (0.14.2)
coderay (~> 1.1)
method_source (~> 1.0)
public_suffix (5.0.4)
racc (1.7.3)
rainbow (3.1.1)
rake (13.1.0)
rbs (1.0.0)
rchardet (1.8.0)
regexp_parser (2.8.2)
rexml (3.2.6)
rspec (3.12.0)
Expand Down Expand Up @@ -99,6 +106,7 @@ PLATFORMS

DEPENDENCIES
branch_base!
git
prettier_print
pry
rake
Expand Down
1 change: 1 addition & 0 deletions branch_base.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Gem::Specification.new do |spec|

spec.metadata = { "rubygems_mfa_required" => "true" }

spec.add_development_dependency("git")
spec.add_development_dependency("prettier_print")
spec.add_development_dependency("pry")
spec.add_development_dependency("rake")
Expand Down
2 changes: 0 additions & 2 deletions lib/branch_base.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# frozen_string_literal: true

# lib/branch_base.rb

require "branch_base/database"
require "branch_base/repository"
require "branch_base/sync"
Expand Down
32 changes: 0 additions & 32 deletions lib/branch_base/database.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,38 +25,6 @@ def last_insert_row_id
@db.last_insert_row_id
end

def commit_exists?(commit_hash)
query = "SELECT COUNT(*) FROM commits WHERE commit_hash = ?"
execute(query, commit_hash).first[0].positive?
end

def insert_commit(repo_id, commit)
return if commit_exists?(commit.oid)

query =
"INSERT INTO commits (commit_hash, repo_id, author, committer, message, timestamp) VALUES (?, ?, ?, ?, ?, ?)"
execute(
query,
commit.oid,
repo_id,
commit.author[:name],
commit.committer[:name],
commit.message,
commit.time.to_s
)
end

def get_or_insert_repo_id(name, path)
query = "SELECT repo_id FROM repositories WHERE url = ?"
result = execute(query, path).first
return result[0] if result

execute("INSERT INTO repositories (name, url) VALUES (?, ?)", name, path)
last_insert_row_id
end

private

def setup_schema
@db.execute_batch(<<-SQL)
CREATE TABLE IF NOT EXISTS repositories (
Expand Down
32 changes: 14 additions & 18 deletions lib/branch_base/sync.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,37 @@

module BranchBase
class Sync
BATCH_SIZE = 10_000 # Adjust this based on performance testing
BATCH_SIZE = 1000

def initialize(database, repository)
@db = database
@repo = repository
end

def run
# Disable foreign key checks for performance
@db.execute("PRAGMA foreign_keys = OFF")

# @db.transaction do
repo_id = sync_repository
sync_branches(repo_id)
sync_commits(repo_id)
# end

# Re-enable foreign key checks
@db.execute("PRAGMA foreign_keys = ON")
end

private

def sync_repository
repo_path = @repo.path.chomp(".git/")
repo_name = File.basename(repo_path)

existing_repo_id =
@db.execute(
"SELECT repo_id FROM repositories WHERE url = ?",
[repo_path]
[repo_path],
).first
return existing_repo_id[0] if existing_repo_id

@db.execute(
"INSERT INTO repositories (name, url) VALUES (?, ?)",
[repo_name, repo_path]
[repo_name, repo_path],
)
@db.last_insert_row_id
end
Expand Down Expand Up @@ -81,7 +75,7 @@ def sync_commits(repo_id)
commit.author[:name],
commit.committer[:name],
commit.message,
commit.time.to_s
commit.time.to_s,
]

if batched_commits.size >= BATCH_SIZE
Expand All @@ -106,10 +100,12 @@ def sync_commits(repo_id)
insert_files_and_commit_files(batched_files) unless batched_files.empty?
end

private

def commit_exists?(commit_hash)
@db.execute(
"SELECT COUNT(*) FROM commits WHERE commit_hash = ?",
[commit_hash]
[commit_hash],
).first[
0
].positive?
Expand All @@ -120,12 +116,12 @@ def insert_commit_files(commit, repo_id)
file_path = patch.delta.new_file[:path]
@db.execute(
"INSERT OR IGNORE INTO files (repo_id, file_path, latest_commit) VALUES (?, ?, ?)",
[repo_id, file_path, commit.oid]
[repo_id, file_path, commit.oid],
)
file_id = @db.last_insert_row_id
@db.execute(
"INSERT INTO commit_files (commit_hash, file_id, changes) VALUES (?, ?, ?)",
[commit.oid, file_id, patch.to_s]
[commit.oid, file_id, patch.to_s],
)
end
end
Expand All @@ -134,7 +130,7 @@ def insert_commit_parents(commit)
commit.parent_ids.each do |parent_id|
@db.execute(
"INSERT INTO commit_parents (commit_hash, parent_hash) VALUES (?, ?)",
[commit.oid, parent_id]
[commit.oid, parent_id],
)
end
end
Expand All @@ -144,7 +140,7 @@ def insert_branches(batched_branches)
batched_branches.each do |data|
@db.execute(
"INSERT OR IGNORE INTO branches (repo_id, name, head_commit) VALUES (?, ?, ?)",
data
data,
)
end
end
Expand All @@ -155,7 +151,7 @@ def insert_commits(batched_commits)
batched_commits.each do |data|
@db.execute(
"INSERT INTO commits (commit_hash, repo_id, author, committer, message, timestamp) VALUES (?, ?, ?, ?, ?, ?)",
data
data,
)
end
end
Expand All @@ -167,12 +163,12 @@ def insert_files_and_commit_files(batched_data)
repo_id, file_path, commit_hash, changes = data
@db.execute(
"INSERT OR IGNORE INTO files (repo_id, file_path, latest_commit) VALUES (?, ?, ?)",
[repo_id, file_path, commit_hash]
[repo_id, file_path, commit_hash],
)
file_id = @db.last_insert_row_id
@db.execute(
"INSERT INTO commit_files (commit_hash, file_id, changes) VALUES (?, ?, ?)",
[commit_hash, file_id, changes]
[commit_hash, file_id, changes],
)
end
end
Expand Down
File renamed without changes.
52 changes: 52 additions & 0 deletions spec/branch_base/cli_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# frozen_string_literal: true
require "branch_base/cli"
require "branch_base/database"
require "branch_base/repository"
require "branch_base/sync"
require "thor"
require "rspec"

RSpec.describe(BranchBase::CLI) do
let(:cli) { BranchBase::CLI.new }
let(:repo_path) { "path/to/repo" }
let(:expanded_repo_path) { File.expand_path(repo_path) }
let(:repo_name) { File.basename(expanded_repo_path) }
let(:db_filename) { "#{repo_name}_git_data.db" }

describe "#sync" do
context "when the repository path is valid" do
before do
allow(File).to receive(:directory?).and_call_original
allow(File).to receive(:directory?).with(
"#{expanded_repo_path}/.git"
).and_return(true)
allow(BranchBase::Database).to receive(:new).with(
db_filename
).and_return(double("Database"))
allow(BranchBase::Repository).to receive(:new).with(
expanded_repo_path
).and_return(double("Repository"))
allow(BranchBase::Sync).to receive(:new).and_return(
double("Sync", run: nil)
)
end

it "syncs the repository data" do
expect { cli.sync(repo_path) }.to output(
"Repository data synced successfully for\n"
).to_stdout
end
end

context "when the repository path is not valid" do
it "exits with an error message" do
allow(File).to receive(:directory?).with(
"#{expanded_repo_path}/.git"
).and_return(false)
expect { cli.sync(repo_path) }.to output(
"The specified path is not a valid Git repository: #{expanded_repo_path}\n"
).to_stdout.and raise_error(SystemExit)
end
end
end
end
70 changes: 70 additions & 0 deletions spec/branch_base/database_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# frozen_string_literal: true
require "branch_base/database"
require "rspec"

RSpec.describe(BranchBase::Database) do
let(:database) { BranchBase::Database.new(":memory:") }

describe "#initialize" do
it "initializes with the correct schema" do
tables =
database.execute("SELECT name FROM sqlite_master WHERE type='table'")
expected_tables = %w[
repositories
commits
branches
files
commit_files
commit_parents
sqlite_sequence
]
expect(tables.flatten).to match_array(expected_tables)
end
end

describe "#execute" do
it "executes a given SQL statement" do
result =
database.execute(
"INSERT INTO repositories (name, url) VALUES (?, ?)",
%w[mock_repo mock_repo/],
)
expect(result).to be_empty
end
end

describe "#prepare" do
it "prepares a SQL statement" do
statement =
database.prepare("INSERT INTO repositories (name, url) VALUES (?, ?)")
expect(statement).to be_a(SQLite3::Statement)
end
end

describe "#transaction" do
it "executes a block within a transaction" do
expect {
database.transaction do
database.execute(
"INSERT INTO repositories (name, url) VALUES (?, ?)",
%w[mock_repo mock_repo/],
)
raise "Rollback transaction"
end
}.to raise_error(RuntimeError, "Rollback transaction")

count = database.execute("SELECT COUNT(*) FROM repositories").first.first
expect(count).to eq(0)
end
end

describe "#last_insert_row_id" do
it "returns the last insert row ID" do
database.execute(
"INSERT INTO repositories (name, url) VALUES (?, ?)",
%w[mock_repo mock_repo/],
)
expect(database.last_insert_row_id).to be > 0
end
end
end
Loading