summaryrefslogtreecommitdiff
path: root/lib/up2date.rb
blob: 33879414c337e1d9957b808c4027c088aead69b5 (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
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at https://mozilla.org/MPL/2.0/.

# frozen_string_literal: true

require 'net/http'
require 'json'
require_relative '../config/config'
require 'term/ansicolor'

module SlackBuilder
  extend Term::ANSIColor

  # Remote repository for a single package.
  class Repository
    # Request the latest tag in the given repository.
    def latest
      raise NotImplementedError
    end
  end

  # Reads the list fo tags from the GitHub API.
  class GitHub < Repository
    GITHUB_QUERY = <<~GQL
      query ($name: String!, $owner: String!) {
        repository(name: $name, owner: $owner) {
          refs(last: 10, refPrefix: "refs/tags/", orderBy: { field: TAG_COMMIT_DATE, direction: ASC }) {
            nodes {
              id,
              name
            }
          }
        }
      }
    GQL
    private_constant :GITHUB_QUERY

    def initialize(owner, name, version_transform = ->(v) { v.delete_prefix 'v' })
      super()

      @owner = owner
      @name = name
      @version_transform = version_transform
    end

    def latest
      post_data = {
        'query' => GITHUB_QUERY,
        'variables' => { 'name' => @name, 'owner' => @owner }
      }
      uri = URI('https://api.github.com/graphql')
      response = Net::HTTP.post uri, post_data.to_json, {
        'content-type' => 'application/json',
        'authorization' => "Bearer #{CONFIG[:gh_token]}"
      }
      filter_versions_from_response JSON.parse(response.body)
    end

    private

    def filter_versions_from_response(response)
      response['data']['repository']['refs']['nodes']
        .map { |node| @version_transform.call node['name'] }
        .compact
        .last
    end
  end

  # Request the latest version from the packagist API.
  class Packagist < Repository
    def initialize(vendor, name)
      super()

      @vendor = vendor
      @name = name
    end

    def latest
      full_name = [@vendor, @name].join '/'
      uri = URI "https://repo.packagist.org/p2/#{full_name}.json"
      response = Net::HTTP.get uri, {
        'content-type' => 'application/json'
      }
      JSON.parse(response)['packages'][full_name].first['version']
    end
  end

  # Reads a remote LATEST file.
  class LatestText < Repository
    def initialize(latest_url)
      super()

      @latest_url = URI latest_url
    end

    def latest
      response = Net::HTTP.get @latest_url, {
        'content-type' => 'text/plain'
      }
      response.strip
    end
  end

  module_function

  # Checks if there is a new version for the package and returns the latest
  # version if an update is available, otherwise returns nil.
  def check_for_latest(package_name, repository)
    package = find_package_info package_name
    latest_version = repository.latest

    if package.version == latest_version
      puts green "#{package_name} is up to date (Version #{package.version})."
      nil
    else
      puts red "#{package_name}: Current version is #{package.version}, #{latest_version} is available."
      latest_version
    end
  end

  private_class_method def find_package_info(package_name)
    package_path = Pathname.new Dir.glob("#{CONFIG[:repository]}/*/#{package_name}").first
    package_category = package_path.dirname.basename.to_s
    Package.parse("#{package_category}/#{package_name}", File.read("#{package_path}/#{package_name}.info"))
  end
end