Class: Homebrew::Livecheck::Strategy::Git

Inherits:
Object
  • Object
show all
Extended by:
SystemCommand::Mixin
Defined in:
livecheck/strategy/git.rb

Overview

The Git strategy identifies versions of software in a Git repository by checking the tags using git ls-remote --tags.

Livecheck has historically prioritized the Git strategy over others and this behavior was continued when the priority setup was created. This is partly related to Livecheck checking formula URLs in order of head, stable and then homepage. The higher priority here may be removed (or altered) in the future if we reevaluate this particular behavior.

This strategy does not have a default regex. Instead, it simply removes any non-digit text from the start of tags and parses the rest as a Version. This works for some simple situations but even one unusual tag can cause a bad result. It's better to provide a regex in a livecheck block, so livecheck only matches what we really want.

Constant Summary collapse

PRIORITY =

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

The priority of the strategy on an informal scale of 1 to 10 (from lowest to highest).

8
DEFAULT_REGEX =

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

The default regex used to naively identify versions from tags when a regex isn't provided.

/\D*(.+)/

Class Method Summary collapse

Methods included from SystemCommand::Mixin

system_command, system_command!

Class Method Details

.find_versions(url:, regex: nil, **_unused, &block) ⇒ Hash{Symbol => T.untyped}

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.

Checks the Git tags for new versions. When a regex isn't provided, this strategy simply removes non-digits from the start of tag strings and parses the remaining text as a Version.

Parameters:

  • url (String)

    the URL of the Git repository to check

  • regex (Regexp, nil) (defaults to: nil)

    a regex used for matching versions

  • _unused (T.untyped)
  • block (Proc, nil)

Returns:



198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# File 'livecheck/strategy/git.rb', line 198

def self.find_versions(url:, regex: nil, **_unused, &block)
  match_data = { matches: {}, regex:, url: }

  tags_data = tag_info(url, regex)
  tags = tags_data[:tags]

  if tags_data.key?(:messages)
    match_data[:messages] = tags_data[:messages]
    return match_data if tags.blank?
  end

  versions_from_tags(tags, regex, &block).each do |version_text|
    match_data[:matches][version_text] = Version.new(version_text)
  rescue TypeError
    next
  end

  match_data
end

.match?(url) ⇒ Boolean

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.

Whether the strategy can be applied to the provided URL.

Parameters:

  • url (String)

    the URL to match against

Returns:

  • (Boolean)


108
109
110
111
# File 'livecheck/strategy/git.rb', line 108

def self.match?(url)
  url = preprocess_url(url)
  (DownloadStrategyDetector.detect(url) <= GitDownloadStrategy) == true
end

.preprocess_url(url) ⇒ String

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.

Processes and returns the URL used by livecheck.

Parameters:

Returns:



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
# File 'livecheck/strategy/git.rb', line 56

def self.preprocess_url(url)
  processed_url = @processed_urls[url]
  return processed_url if processed_url

  begin
    uri = Addressable::URI.parse url
  rescue Addressable::URI::InvalidURIError
    return url
  end

  host = uri.host
  path = uri.path
  return url if host.nil? || path.nil?

  host = "github.com" if host == "github.s3.amazonaws.com"
  path = path.delete_prefix("/").delete_suffix(".git")
  scheme = uri.scheme

  if host == "github.com"
    return url if path.match? %r{/releases/latest/?$}

    owner, repo = path.delete_prefix("downloads/").split("/")
    processed_url = "#{scheme}://#{host}/#{owner}/#{repo}.git"
  elsif GITEA_INSTANCES.include?(host)
    return url if path.match? %r{/releases/latest/?$}

    owner, repo = path.split("/")
    processed_url = "#{scheme}://#{host}/#{owner}/#{repo}.git"
  elsif GOGS_INSTANCES.include?(host)
    owner, repo = path.split("/")
    processed_url = "#{scheme}://#{host}/#{owner}/#{repo}.git"
  # sourcehut
  elsif host == "git.sr.ht"
    owner, repo = path.split("/")
    processed_url = "#{scheme}://#{host}/#{owner}/#{repo}"
  # GitLab (gitlab.com or self-hosted)
  elsif path.include?("/-/archive/")
    processed_url = url.sub(%r{/-/archive/.*$}i, ".git")
  end

  if processed_url && (processed_url != url)
    @processed_urls[url] = processed_url
  else
    url
  end
end

.tag_info(url, regex = nil) ⇒ Hash{Symbol => T.untyped}

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.

Fetches a remote Git repository's tags using git ls-remote --tags and parses the command's output. If a regex is provided, it will be used to filter out any tags that don't match it.

Parameters:

  • url (String)

    the URL of the Git repository to check

  • regex (Regexp, nil) (defaults to: nil)

    the regex to use for filtering tags

Returns:



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'livecheck/strategy/git.rb', line 121

def self.tag_info(url, regex = nil)
  stdout, stderr, _status = system_command(
    "git",
    args:         ["ls-remote", "--tags", url],
    env:          { "GIT_TERMINAL_PROMPT" => "0" },
    print_stdout: false,
    print_stderr: false,
    debug:        false,
    verbose:      false,
  )

  tags_data = { tags: [] }
  tags_data[:messages] = stderr.split("\n") if stderr.present?
  return tags_data if stdout.blank?

  # Isolate tag strings and filter by regex
  tags = stdout.gsub(%r{^.*\trefs/tags/|\^{}$}, "").split("\n").uniq.sort
  tags.select! { |t| regex.match?(t) } if regex
  tags_data[:tags] = tags

  tags_data
end

.versions_from_tags(tags, regex = nil, &block) ⇒ Array<String>

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.

Identify versions from tag strings using a provided regex or the DEFAULT_REGEX. The regex is expected to use a capture group around the version text.

Parameters:

  • tags (Array<String>)

    the tags to identify versions from

  • regex (Regexp, nil) (defaults to: nil)

    a regex to identify versions

  • block (Proc, nil)

Returns:



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'livecheck/strategy/git.rb', line 158

def self.versions_from_tags(tags, regex = nil, &block)
  if block
    block_return_value = if regex.present?
      yield(tags, regex)
    elsif block.arity == 2
      yield(tags, DEFAULT_REGEX)
    else
      yield(tags)
    end
    return Strategy.handle_block_return(block_return_value)
  end

  tags.filter_map do |tag|
    if regex
      # Use the first capture group (the version)
      # This code is not typesafe unless the regex includes a capture group
      T.unsafe(tag.scan(regex).first)&.first
    else
      # Remove non-digits from the start of the tag and use that as the
      # version text
      tag[DEFAULT_REGEX, 1]
    end
  end.uniq
end