Class: Homebrew::DownloadQueue Private

Inherits:
Object
  • Object
show all
Defined in:
download_queue.rb

This class is part of a private API. This class may only be used in the Homebrew/brew repository. Third parties should avoid using this class if possible, as it may be removed or changed without warning.

Defined Under Namespace

Classes: Spinner

Instance Method Summary collapse

Constructor Details

#initialize(retries: 1, force: false, pour: false) ⇒ void

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Parameters:

  • retries (Integer) (defaults to: 1)
  • force (Boolean) (defaults to: false)
  • pour (Boolean) (defaults to: false)


12
13
14
15
16
17
18
19
# File 'download_queue.rb', line 12

def initialize(retries: 1, force: false, pour: false)
  @concurrency = T.let(EnvConfig.download_concurrency, Integer)
  @quiet = T.let(@concurrency > 1, T::Boolean)
  @tries = T.let(retries + 1, Integer)
  @force = force
  @pour = pour
  @pool = T.let(Concurrent::FixedThreadPool.new(concurrency), Concurrent::FixedThreadPool)
end

Instance Method Details

#enqueue(downloadable) ⇒ void

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

This method returns an undefined value.

Parameters:



22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'download_queue.rb', line 22

def enqueue(downloadable)
  downloads[downloadable] ||= Concurrent::Promises.future_on(
    pool, RetryableDownload.new(downloadable, tries:), force, quiet
  ) do |download, force, quiet|
    download.clear_cache if force
    download.fetch(quiet:)
    if pour && download.bottle?
      UnpackStrategy.detect(download.cached_download, prioritize_extension: true)
                    .extract_nestedly(to: HOMEBREW_CELLAR)
    elsif download.api?
      FileUtils.touch(download.cached_download, mtime: Time.now)
    end
  end
end

#fetchvoid

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

This method returns an undefined value.



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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'download_queue.rb', line 38

def fetch
  return if downloads.empty?

  if concurrency == 1 || downloads.one?
    downloads.each do |downloadable, promise|
      promise.wait!
    rescue ChecksumMismatchError => e
      opoo "#{downloadable.download_type.capitalize} reports different checksum: #{e.expected}"
      Homebrew.failed = true if downloadable.is_a?(Resource::Patch)
    end
  else
    spinner = Spinner.new
    remaining_downloads = downloads.dup.to_a
    previous_pending_line_count = 0

    begin
      $stdout.print Tty.hide_cursor
      $stdout.flush

      output_message = lambda do |downloadable, future, last|
        status = case future.state
        when :fulfilled
          "#{Tty.green}✔︎#{Tty.reset}"
        when :rejected
          "#{Tty.red}#{Tty.reset}"
        when :pending, :processing
          "#{Tty.blue}#{spinner}#{Tty.reset}"
        else
          raise future.state.to_s
        end

        message = "#{downloadable.download_type.capitalize} #{downloadable.name}"
        $stdout.print "#{status} #{message}#{"\n" unless last}"
        $stdout.flush

        if future.rejected?
          if (e = future.reason).is_a?(ChecksumMismatchError)
            opoo "#{downloadable.download_type.capitalize} reports different checksum: #{e.expected}"
            Homebrew.failed = true if downloadable.is_a?(Resource::Patch)
            next 2
          else
            message = future.reason.to_s
            onoe message
            Homebrew.failed = true
            next message.count("\n")
          end
        end

        1
      end

      until remaining_downloads.empty?
        begin
          finished_states = [:fulfilled, :rejected]

          finished_downloads, remaining_downloads = remaining_downloads.partition do |_, future|
            finished_states.include?(future.state)
          end

          finished_downloads.each do |downloadable, future|
            previous_pending_line_count -= 1
            $stdout.print Tty.clear_to_end
            $stdout.flush
            output_message.call(downloadable, future, false)
          end

          previous_pending_line_count = 0
          max_lines = [concurrency, Tty.height].min
          remaining_downloads.each_with_index do |(downloadable, future), i|
            break if previous_pending_line_count >= max_lines

            $stdout.print Tty.clear_to_end
            $stdout.flush
            last = i == max_lines - 1 || i == remaining_downloads.count - 1
            previous_pending_line_count += output_message.call(downloadable, future, last)
          end

          if previous_pending_line_count.positive?
            if (previous_pending_line_count - 1).zero?
              $stdout.print Tty.move_cursor_beginning
            else
              $stdout.print Tty.move_cursor_up_beginning(previous_pending_line_count - 1)
            end
            $stdout.flush
          end

          sleep 0.05
        rescue Interrupt
          remaining_downloads.each do |_, future|
            # FIXME: Implement cancellation of running downloads.
          end

          cancel

          if previous_pending_line_count.positive?
            $stdout.print Tty.move_cursor_down(previous_pending_line_count - 1)
            $stdout.flush
          end

          raise
        end
      end
    ensure
      $stdout.print Tty.show_cursor
      $stdout.flush
    end
  end

  downloads.clear
end

#shutdownvoid

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

This method returns an undefined value.



150
151
152
153
# File 'download_queue.rb', line 150

def shutdown
  pool.shutdown
  pool.wait_for_termination
end