Skip to content
Draft
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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Dependency installations and lockfiles
Gemfile.lock
/Gemfile.lock
.bundle/
/*/vendor/bundle/
/*/lib/bundler/man/
Expand Down
112 changes: 112 additions & 0 deletions seed_lockfiles.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#!/usr/bin/env ruby
# seed_lockfiles.rb
#
# Usage:
# ruby seed_lockfiles.rb --continue --size 15 --push

require 'optparse'
require 'fileutils'

options = {
batch_index: 0,
batch_size: 15,
auto_continue: false,
push: false
}

OptionParser.new do |opts|
opts.banner = "Usage: ruby seed_lockfiles.rb [options]"

opts.on("-s", "--size N", Integer, "Number of libraries in the batch (default: 15)") do |s|
options[:batch_size] = s
end

opts.on("-i", "--index N", Integer, "Manual batch index (0-indexed)") do |i|
options[:batch_index] = i
end

opts.on("-c", "--continue", "Automatically resume from the first unseeded library") do
options[:auto_continue] = true
end

opts.on("-p", "--push", "Create a branch, commit (but do not push automatically to allow review)") do
options[:push] = true
end
end.parse!

# 1. Discover all libraries
all_gems = Dir.glob("*/").map { |d| d.chomp('/') }.select do |dir|
File.exist?(File.join(dir, "#{dir}.gemspec"))
end.sort

# 2. Filter unseeded & eligible libraries
unseeded_gems = all_gems.select do |dir|
lockfile_path = File.join(dir, "Gemfile.lock")

# Skip if it already has a lockfile physically present
next false if File.exist?(lockfile_path)

# Ask Git directly if this path is ignored
is_ignored = system("git check-ignore -q #{lockfile_path}")

next false if is_ignored

true
end

puts "🔍 [Seeder] Found #{unseeded_gems.size} eligible libraries (no lockfile AND not .gitignored)"

if unseeded_gems.empty?
puts "🎉 All eligible libraries have been seeded!"
exit 0
end

if options[:auto_continue]
puts "⏭️ [Seeder] --continue flag detected."
options[:batch_index] = 0
end

start_offset = options[:batch_index] * options[:batch_size]
batch_gems = unseeded_gems[start_offset, options[:batch_size]]

if batch_gems.nil? || batch_gems.empty?
puts "❌ [Seeder] Batch index #{options[:batch_index]} is out of bounds!"
exit 1
end

branch_name = "chore/seed-lockfiles-#{Time.now.to_i}"
puts "📦 [Seeder] Batch targets #{batch_gems.size} gems:"
batch_gems.each { |g| puts " - #{g}" }

# 3. Create branch if requested
if options[:push]
system("git checkout main && git pull", exception: true)
system("git checkout -b #{branch_name}", exception: true)
end

# 4. Generate lockfiles SEQUENTIALLY
batch_gems.each_with_index do |gem_name, i|
puts "⏳ [#{i + 1}/#{batch_gems.size}] Seeding #{gem_name}..."

Dir.chdir(gem_name) do
# Run bundle lock synchronously
system("bundle lock", exception: true)
end

# Stage the file sequentially so we safely avoid Git index lock conflicts
system("git add #{gem_name}/Gemfile.lock", exception: true) if options[:push]
end

# 5. Commit if requested
if options[:push]
commit_msg = "chore(lockfiles): seed Gemfile.lock for #{batch_gems.size} libraries\n\nGems included in this batch:\n" + batch_gems.map { |g| "- #{g}" }.join("\n")
File.write(".git/COMMIT_EDITMSG", commit_msg)
system("git commit -F .git/COMMIT_EDITMSG", exception: true)
File.delete(".git/COMMIT_EDITMSG")

puts "\n✅ Batch committed to local branch #{branch_name}!"
puts "👉 You can now review the commit and push it:"
puts " git push -u origin #{branch_name}"
else
puts "\n✅ Batch seeded locally. Run with --push to automatically branch and commit."
end
Loading