File: repo.rb

package info (click to toggle)
librarian-puppet 5.0.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 512 kB
  • sloc: ruby: 1,428; sh: 37; makefile: 9
file content (172 lines) | stat: -rw-r--r-- 5,626 bytes parent folder | download | duplicates (2)
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
require 'uri'
require 'net/https'
require 'open-uri'
require 'json'

require 'librarian/puppet/version'
require 'librarian/puppet/source/repo'

module Librarian
  module Puppet
    module Source
      class GitHubTarball
        class Repo < Librarian::Puppet::Source::Repo
          include Librarian::Puppet::Util

          TOKEN_KEY = 'GITHUB_API_TOKEN'

          def versions
            return @versions if @versions
            data = api_call("/repos/#{source.uri}/tags")
            if data.nil?
              raise Error, "Unable to find module '#{source.uri}' on https://github.com"
            end

            all_versions = data.map { |r| r['name'].gsub(/^v/, '') }.sort.reverse

            all_versions.delete_if do |version|
              version !~ /\A\d+\.\d+(\.\d+.*)?\z/
            end

            @versions = all_versions.compact
            debug { "  Module #{name} found versions: #{@versions.join(", ")}" }
            @versions
          end

          def manifests
            versions.map do |version|
              Manifest.new(source, name, version)
            end
          end

          def install_version!(version, install_path)
            if environment.local? && !vendored?(vendored_name, version)
              raise Error, "Could not find a local copy of #{source.uri} at #{version}."
            end

            vendor_cache(source.uri.to_s, version) unless vendored?(vendored_name, version)

            cache_version_unpacked! version

            if install_path.exist? && rsync? != true
              install_path.rmtree
            end

            unpacked_path = version_unpacked_cache_path(version).children.first
            cp_r(unpacked_path, install_path)
          end

          def cache_version_unpacked!(version)
            path = version_unpacked_cache_path(version)
            return if path.directory?

            path.mkpath

            target = vendored?(vendored_name, version) ? vendored_path(vendored_name, version) : name

            Librarian::Posix.run!(%W{tar xzf #{target} -C #{path}})
          end

          def vendor_cache(name, version)
            clean_up_old_cached_versions(vendored_name(name))

            url = "https://api.github.com/repos/#{name}/tarball/#{version}"
            add_api_token_to_url(url)

            environment.vendor!
            File.open(vendored_path(vendored_name(name), version).to_s, 'wb') do |f|
              begin
                debug { "Downloading <#{url}> to <#{f.path}>" }
                URI.open(url,
                  "User-Agent" => "librarian-puppet v#{Librarian::Puppet::VERSION}") do |res|
                  while buffer = res.read(8192)
                    f.write(buffer)
                  end
                end
              rescue OpenURI::HTTPError => e
                raise e, "Error requesting <#{url}>: #{e.to_s}"
              end
            end
          end

          def clean_up_old_cached_versions(name)
            Dir["#{environment.vendor_cache}/#{name}*.tar.gz"].each do |old_version|
              FileUtils.rm old_version
            end
          end

          def token_key_value
            ENV[TOKEN_KEY]
          end

          def token_key_nil?
            token_key_value.nil? || token_key_value.empty?
          end

          def add_api_token_to_url url
            if token_key_nil?
              debug { "#{TOKEN_KEY} environment value is empty or missing" }
            elsif url.include? "?"
              url << "&access_token=#{ENV[TOKEN_KEY]}"
            else
              url << "?access_token=#{ENV[TOKEN_KEY]}"
            end
            url
          end

        private

          def api_call(path)
            tags = []
            url = "https://api.github.com#{path}?page=1&per_page=100"
            while true do
              debug { "  Module #{name} getting tags at: #{url}" }
              add_api_token_to_url(url)
              response = http_get(url, :headers => {
                "User-Agent" => "librarian-puppet v#{Librarian::Puppet::VERSION}"
              })

              code, data = response.code.to_i, response.body

              if code == 200
                tags.concat JSON.parse(data)
              else
                begin
                  message = JSON.parse(data)['message']
                  if code == 403 && message && message.include?('API rate limit exceeded')
                    raise Error, message + " -- increase limit by authenticating via #{TOKEN_KEY}=your-token"
                  elsif message
                    raise Error, "Error fetching #{url}: [#{code}] #{message}"
                  end
                rescue JSON::ParserError
                  # response does not return json
                end
                raise Error, "Error fetching #{url}: [#{code}] #{response.body}"
              end

              # next page
              break if response["link"].nil?
              next_link = response["link"].split(",").select{|l| l.match /rel=.*next.*/}
              break if next_link.empty?
              url = next_link.first.match(/<(.*)>/)[1]
            end
            return tags
          end

          def http_get(url, options)
            uri = URI.parse(url)
            http = Net::HTTP.new(uri.host, uri.port)
            http.use_ssl = true
            request = Net::HTTP::Get.new(uri.request_uri)
            options[:headers].each { |k, v| request.add_field k, v }
            http.request(request)
          end

          def vendored_name(name = source.uri.to_s)
            name.sub('/','-')
          end
        end
      end
    end
  end
end