summaryrefslogtreecommitdiff
path: root/lib/license/finder/ext/bundler.rb
blob: b8c755bf5dff7f382edf6b7ff60984dc0a0bbf64 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# frozen_string_literal: true

module LicenseFinder
  class Bundler
    def prepare
      create_vendor_path

      with_clean_bundler do
        tool_box.install(tool: :ruby, version: ruby_version, env: default_env)
        Pathname.glob('/opt/toolcache/*.gem').each do |gem|
          shell.execute([
            :gem, :install, gem,
            '--no-document',
            '--verbose',
            '--no-update-sources',
            '--ignore-dependencies',
            '--no-suggestions',
            '--local'
          ], env: default_env)
        end
        shell.execute([:asdf, :reshim], env: default_env)
        shell.execute([:bundle, :config, '--local', :path, vendor_path], env: default_env)
        shell.execute([:bundle, :install, '--verbose'], env: default_env)
      end
    end

    def current_packages
      with_clean_bundler do
        stdout, _stderr, status = shell.execute(scan_command, env: default_env)
        return [] unless status.success?

        stdout.each_line.map do |line|
          map_from(JSON.parse(line, symbolize_names: true))
        end
      end
    end

    def possible_package_paths
      if ENV['BUNDLE_GEMFILE'] && File.exist?(ENV['BUNDLE_GEMFILE'])
        [project_path.join(File.basename(ENV['BUNDLE_GEMFILE']))]
      else
        [project_path.join('Gemfile'), project_path.join('gems.rb')]
      end
    end

    private

    def gemfile
      if ENV['BUNDLE_GEMFILE']
        custom_gemfile = project_path.join(File.basename(ENV['BUNDLE_GEMFILE']))
        return custom_gemfile.basename.to_s if custom_gemfile.exist?
      end

      project_path.join("gems.rb").exist? ? "gems.rb" : "Gemfile"
    end

    def lockfile
      gemfile == 'gems.rb' ? 'gems.locked' : "#{gemfile}.lock"
    end

    def lockfile_path
      project_path.join(lockfile)
    end

    def scan_command
      [
        :ruby,
        '-W0',
        ::License::Management.root.join('exe', 'scan_bundler'),
        detected_package_path,
        lockfile_path
      ]
    end

    def default_env
      @default_env ||= {
        'BUNDLE_ALLOW_OFFLINE_INSTALL' => 'true',
        'BUNDLE_DISABLE_VERSION_CHECK' => 'true',
        'BUNDLE_ERROR_ON_STDERR' => 'true',
        'BUNDLE_GEMFILE' => "#{project_path}/#{gemfile}",
        'BUNDLE_IGNORE_MESSAGES' => 'true',
        'BUNDLE_JOBS' => ENV.fetch('BUNDLE_JOBS', `nproc`.chomp),
        'BUNDLE_SILENCE_ROOT_WARNING' => 'true',
        'BUNDLE_SUPPRESS_INSTALL_USING_MESSAGES' => 'true',
        'BUNDLE_WITHOUT' => ENV.fetch('BUNDLE_WITHOUT', ignored_groups.to_a.join(':')),
        'PATH' => "/opt/asdf/shims:/opt/asdf/bin:#{ENV['PATH']}"
      }.tap do |env|
        env['BUNDLE_FROZEN'] = 'true' if lockfile_path.exist?
        env['BUNDLE_SSL_CA_CERT'] = shell.custom_certificate_path.to_s if shell.custom_certificate_installed?
      end
    end

    def with_clean_bundler
      Dir.chdir(project_path) do
        ::Gem.clear_paths
        ::Bundler.reset!
        if ::Bundler.respond_to?(:with_unbundled_env)
          ::Bundler.with_unbundled_env { yield }
        else
          ::Bundler.with_clean_env { yield }
        end
      end
    end

    def ruby_version
      @ruby_version ||= gemfile_ruby_version || tool_box.version_of(:ruby)
    end

    def map_from(gem)
      Dependency.new(
        'Bundler',
        gem[:name],
        gem[:version],
        description: gem[:description],
        detection_path: lockfile_path,
        homepage: gem[:homepage],
        install_path: gem[:full_gem_path] || '/dev/null',
        spec_licenses: gem[:licenses],
        summary: gem[:summary]
      )
    end

    def gemfile_ruby_version
      with_clean_bundler do
        stdout, _stderr, status = shell.execute([:embedded_bundle, :platform, '--ruby'])
        return unless status.success?

        match = stdout.chomp.match(/\Aruby (?<version>\d+\.\d+.\d+).*\z/)
        match[:version] if match
      end
    end
  end
end