bundler-git-slim 0.1.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a7851357933fbe0b05a64de7053ea237f81082128f7704785b6ef4804d9a6fad
4
+ data.tar.gz: ef7ff2de1c288a1d3877cbad782f875d800a707e4bea71bb1027c586e439977d
5
+ SHA512:
6
+ metadata.gz: 7aadca013cbdf6117431ebf5ee5257afc1b36600df0c393529219536868d56e52ae6af482c850b99b98c9af4aa30ffe9bb4264ed743159c68e22d202dde2ac0c
7
+ data.tar.gz: bc5e528cdbf8504aa7e342204814951068c928c0f995b40ae7ee3306f80c791dce3943b94150d9c6cecd7b40919e5d80ba1e53c17ebd934740aeb4e6a83d6f31
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Your Name
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,54 @@
1
+ # bundler-git-slim
2
+
3
+ Bundler plugin that slims git-installed gems down to the files listed in `spec.files`, reducing disk usage and cache size.
4
+
5
+ ## Features
6
+
7
+ - Only affects gems installed from `git "..."` sources
8
+ - Only touches the copy under Bundler's `bundle_path`
9
+ - Never modifies `path` sources or your working copies
10
+ - Uses `spec.files` as an allow-list; unlisted files are removed
11
+ - Cleans up empty directories
12
+
13
+ ## Installation
14
+
15
+ Add to your Gemfile:
16
+
17
+ ```ruby
18
+ plugin 'bundler-git-slim'
19
+
20
+ gem 'some_gem', git: 'https://github.com/...'
21
+ ```
22
+
23
+ Then run:
24
+
25
+ ```bash
26
+ bundle install
27
+ ```
28
+
29
+ Or install globally:
30
+
31
+ ```bash
32
+ bundle plugin install bundler-git-slim
33
+ ```
34
+
35
+ ## Slim already-installed gems
36
+
37
+ If you have git gems already installed, run:
38
+
39
+ ```bash
40
+ bundle git-slim
41
+ ```
42
+
43
+ This will prune all git-sourced gems in your current bundle.
44
+
45
+ ## Safety
46
+
47
+ - Scope limited to `Bundler::Source::Git`
48
+ - Only operates when `spec.full_gem_path` is inside `Bundler.bundle_path`
49
+ - If `spec.files` is empty or nil, does nothing
50
+ - Always preserves `*.gemspec` files
51
+
52
+ ## License
53
+
54
+ MIT
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/bundler_git_slim'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'bundler-git-slim'
7
+ spec.version = BundlerGitSlim::VERSION
8
+ spec.authors = ['Sveta Markovic']
9
+ spec.email = ['svetislav.markovic@pm.me']
10
+
11
+ spec.summary = 'Bundler plugin that slims git-installed gems down to spec.files.'
12
+ spec.description = spec.summary
13
+ spec.homepage = 'https://github.com/svetam/bundler-git-slim'
14
+ spec.license = 'MIT'
15
+
16
+ spec.required_ruby_version = '>= 2.7'
17
+
18
+ spec.files = Dir['lib/**/*.rb', 'plugins.rb', 'README.md', 'LICENSE', 'bundler-git-slim.gemspec']
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_dependency 'bundler', '>= 2.0'
22
+
23
+ spec.metadata = {
24
+ 'homepage_uri' => spec.homepage,
25
+ 'source_code_uri' => spec.homepage,
26
+ 'bug_tracker_uri' => "#{spec.homepage}/issues",
27
+ 'bundler_plugin' => 'true',
28
+ 'rubygems_mfa_required' => 'true'
29
+ }
30
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+ require 'set'
5
+ require 'pathname'
6
+
7
+ module BundlerGitSlim
8
+ VERSION = '0.1.0'
9
+ UNITS = %w[B KB MB GB].freeze
10
+
11
+ class << self
12
+ def format_bytes(bytes)
13
+ return '0 B' if bytes.zero?
14
+
15
+ exp = (Math.log(bytes) / Math.log(1024)).to_i
16
+ exp = [exp, UNITS.size - 1].min
17
+ format('%<size>.1f %<unit>s', size: bytes.to_f / (1024**exp), unit: UNITS[exp])
18
+ end
19
+
20
+ def prune(root, allowed_files)
21
+ root = Pathname(root).expand_path
22
+ keep = build_keep_set(root, allowed_files)
23
+ removed_files = 0
24
+ removed_bytes = 0
25
+
26
+ all_paths(root).each do |path|
27
+ next if keep.include?(path)
28
+
29
+ if File.directory?(path) && !File.symlink?(path)
30
+ begin
31
+ Dir.rmdir(path)
32
+ rescue SystemCallError
33
+ # Directory not empty
34
+ end
35
+ else
36
+ removed_bytes += File.size(path) if File.exist?(path)
37
+ FileUtils.rm_f(path)
38
+ removed_files += 1
39
+ end
40
+ end
41
+
42
+ { files: removed_files, bytes: removed_bytes }
43
+ end
44
+
45
+ def build_keep_set(root, allowed_files)
46
+ keep = Set.new
47
+
48
+ allowed_files.each do |rel|
49
+ path = root.join(rel).cleanpath
50
+ keep << path.to_s
51
+ path.ascend { |p| keep << p.to_s }
52
+ end
53
+
54
+ Dir.glob(root.join('*.gemspec')).each { |gs| keep << File.expand_path(gs) }
55
+
56
+ keep
57
+ end
58
+
59
+ def all_paths(root)
60
+ Dir.glob(root.join('**/*'), File::FNM_DOTMATCH)
61
+ .map { |p| File.expand_path(p) }
62
+ .reject { |p| %w[. ..].include?(File.basename(p)) }
63
+ .sort_by(&:length)
64
+ .reverse
65
+ end
66
+
67
+ def prune_spec(spec)
68
+ return nil unless spec.source.is_a?(Bundler::Source::Git)
69
+
70
+ root = Pathname(spec.full_gem_path).expand_path
71
+ bundle_root = Bundler.bundle_path.expand_path
72
+
73
+ return nil unless root.to_s.start_with?("#{bundle_root}#{File::SEPARATOR}")
74
+
75
+ files = spec.files || []
76
+ return nil if files.empty?
77
+
78
+ prune(root, files)
79
+ end
80
+ end
81
+ end
data/plugins.rb ADDED
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/plugin/api'
4
+ require_relative 'lib/bundler_git_slim'
5
+
6
+ Bundler::Plugin.add_hook('after-install') do |spec_install|
7
+ # Unwrap SpecInstallation if needed
8
+ spec = spec_install.respond_to?(:spec) ? spec_install.spec : spec_install
9
+
10
+ result = BundlerGitSlim.prune_spec(spec)
11
+ next unless result&.dig(:files)&.positive?
12
+
13
+ size = BundlerGitSlim.format_bytes(result[:bytes])
14
+ Bundler.ui.warn "Slimmed #{spec.name} #{spec.version} (#{result[:files]} files, #{size})"
15
+ end
16
+
17
+ class GitSlimCommand < Bundler::Plugin::API
18
+ command 'git-slim'
19
+
20
+ def exec(_command, _args)
21
+ Bundler.ui.info 'Slimming installed git gems...'
22
+ specs = git_specs
23
+ return Bundler.ui.info('No git-sourced gems found.') if specs.empty?
24
+
25
+ totals = prune_all(specs)
26
+ report_totals(totals)
27
+ end
28
+
29
+ private
30
+
31
+ def git_specs
32
+ Bundler.load.specs.select { |s| s.source.is_a?(Bundler::Source::Git) }
33
+ end
34
+
35
+ def prune_all(specs)
36
+ totals = { files: 0, bytes: 0 }
37
+
38
+ specs.each do |spec|
39
+ result = BundlerGitSlim.prune_spec(spec)
40
+ next unless result&.dig(:files)&.positive?
41
+
42
+ totals[:files] += result[:files]
43
+ totals[:bytes] += result[:bytes]
44
+ size = BundlerGitSlim.format_bytes(result[:bytes])
45
+ Bundler.ui.warn "Slimmed #{spec.name} #{spec.version} (#{result[:files]} files, #{size})"
46
+ end
47
+
48
+ totals
49
+ end
50
+
51
+ def report_totals(totals)
52
+ if totals[:files].zero?
53
+ Bundler.ui.info 'All git gems already slim.'
54
+ else
55
+ size = BundlerGitSlim.format_bytes(totals[:bytes])
56
+ Bundler.ui.warn "Done. Removed #{totals[:files]} files (#{size}) total."
57
+ end
58
+ end
59
+ end
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bundler-git-slim
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Sveta Markovic
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-01-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ description: Bundler plugin that slims git-installed gems down to spec.files.
28
+ email:
29
+ - svetislav.markovic@pm.me
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - LICENSE
35
+ - README.md
36
+ - bundler-git-slim.gemspec
37
+ - lib/bundler_git_slim.rb
38
+ - plugins.rb
39
+ homepage: https://github.com/svetam/bundler-git-slim
40
+ licenses:
41
+ - MIT
42
+ metadata:
43
+ homepage_uri: https://github.com/svetam/bundler-git-slim
44
+ source_code_uri: https://github.com/svetam/bundler-git-slim
45
+ bug_tracker_uri: https://github.com/svetam/bundler-git-slim/issues
46
+ bundler_plugin: 'true'
47
+ rubygems_mfa_required: 'true'
48
+ post_install_message:
49
+ rdoc_options: []
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '2.7'
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ requirements: []
63
+ rubygems_version: 3.5.22
64
+ signing_key:
65
+ specification_version: 4
66
+ summary: Bundler plugin that slims git-installed gems down to spec.files.
67
+ test_files: []