github-pages-health-check 0.6.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +6 -0
  3. data/.rspec +2 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +6 -0
  6. data/Gemfile +3 -0
  7. data/README.md +83 -0
  8. data/github-pages-health-check.gemspec +28 -0
  9. data/lib/github-pages-health-check.rb +18 -245
  10. data/lib/github-pages-health-check/checkable.rb +62 -0
  11. data/lib/github-pages-health-check/cloudflare.rb +11 -7
  12. data/lib/github-pages-health-check/domain.rb +260 -0
  13. data/lib/github-pages-health-check/error.rb +7 -6
  14. data/lib/github-pages-health-check/errors.rb +13 -0
  15. data/lib/github-pages-health-check/errors/build_error.rb +8 -0
  16. data/lib/github-pages-health-check/errors/deprecated_ip_error.rb +11 -0
  17. data/lib/github-pages-health-check/errors/invalid_a_record_error.rb +11 -0
  18. data/lib/github-pages-health-check/errors/invalid_cname_error.rb +11 -0
  19. data/lib/github-pages-health-check/errors/invalid_dns_error.rb +11 -0
  20. data/lib/github-pages-health-check/errors/invalid_domain_error.rb +11 -0
  21. data/lib/github-pages-health-check/errors/invalid_repository_error.rb +11 -0
  22. data/lib/github-pages-health-check/errors/missing_access_token_error.rb +11 -0
  23. data/lib/github-pages-health-check/errors/not_served_by_pages_error.rb +11 -0
  24. data/lib/github-pages-health-check/printer.rb +71 -0
  25. data/lib/github-pages-health-check/repository.rb +74 -0
  26. data/lib/github-pages-health-check/site.rb +32 -0
  27. data/lib/github-pages-health-check/version.rb +3 -3
  28. data/script/bootstrap +5 -0
  29. data/script/check +11 -0
  30. data/script/check-cloudflare-ips +17 -0
  31. data/script/cibuild +9 -0
  32. data/script/console +5 -0
  33. data/script/release +42 -0
  34. data/script/test +2 -0
  35. data/script/update-cloudflare-ips +14 -0
  36. metadata +60 -7
  37. data/lib/github-pages-health-check/errors/deprecated_ip.rb +0 -9
  38. data/lib/github-pages-health-check/errors/invalid_a_record.rb +0 -9
  39. data/lib/github-pages-health-check/errors/invalid_cname.rb +0 -9
  40. data/lib/github-pages-health-check/errors/invalid_dns.rb +0 -9
  41. data/lib/github-pages-health-check/errors/not_served_by_pages.rb +0 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6a76a2726cf101e5e0b31295968533bc118eca9b
4
- data.tar.gz: 78b1175bfb6c4de74c3a8a4303ab25a644805d86
3
+ metadata.gz: f39bbd059617a66074ecfefd2e099a8642f8f02a
4
+ data.tar.gz: 3d515c8fa894e296b042c23ca6729849f1fcc114
5
5
  SHA512:
6
- metadata.gz: 4d121a12f58aabe94583eac1561bd76e87805ce0712aada99c31d84918c02717a46a226191b8a409adecb23f55c5007d7f23e7929099f34f3a5966b7e1dfd076
7
- data.tar.gz: c25ace49dcb5c559791dc471b5ef847f0fb4a9b3d2a7718b21c489fbee01ec31e9bce61001fac55646932562b10f5bcdbe919a2dcd9681988fe52e7751c0905e
6
+ metadata.gz: 4687d324883562a5424f698643b1beed8bd737581e8d07ba0a5090990016dd68409790c4f075ca96e4fccef7c49b94ae0063cd7eaa0aa1cd3313e0216c55aac9
7
+ data.tar.gz: d3545668fa5b774dbf5b7d74ecbb1fc37ea97d36e55242f32221e087ba1bca1cc26f315126d4b9b3059a806f99175774c2b777918e4a2f4f87c4168ca6ee93eb
@@ -0,0 +1,6 @@
1
+ /*.gem
2
+ *.lock
3
+ .bundle
4
+ vendor/gems
5
+ /bin
6
+ .env
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
@@ -0,0 +1 @@
1
+ 2.1.7-github
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ script: "script/cibuild"
3
+ install:
4
+ - sudo bash -c 'echo search github.com >> /etc/resolv.conf'
5
+ cache: bundler
6
+ rvm: 2.1.7
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
@@ -0,0 +1,83 @@
1
+ # GitHub Pages Health Check
2
+
3
+ *Checks your GitHub Pages site for common DNS configuration issues*
4
+
5
+ [![Build Status](https://travis-ci.org/github/pages-health-check.svg)](https://travis-ci.org/github/pages-health-check) [![Gem Version](https://badge.fury.io/rb/github-pages-health-check.svg)](http://badge.fury.io/rb/github-pages-health-check)
6
+
7
+ ## Installation
8
+
9
+ `gem install github-pages-health-check`
10
+
11
+ ## Usage
12
+
13
+ ### Basic Usage
14
+
15
+ ```ruby
16
+ > check = GitHubPages::HealthCheck::Site.new("choosealicense.com")
17
+ => #<GitHubPages::HealthCheck::Site @domain="choosealicense.com" valid?=true>
18
+ > check.valid?
19
+ => true
20
+ ```
21
+
22
+ ### An invalid domain
23
+
24
+ ```ruby
25
+ > check = GitHubPages::HealthCheck::Site.new("foo.github.com")
26
+ > check.valid?
27
+ => false
28
+ > check.valid!
29
+ raises GitHubPages::HealthCheck::Errors::InvalidCNAMEError
30
+ ```
31
+
32
+
33
+ ### Retrieving specific checks
34
+
35
+ ``` ruby
36
+ > check.domain.should_be_a_record?
37
+ => true
38
+ > check.domain.a_record?
39
+ => true
40
+ ```
41
+
42
+ ### Getting checks in bulk
43
+
44
+ ```ruby
45
+ > check.to_hash
46
+ => {
47
+ :cloudflare_ip?=>false,
48
+ :old_ip_address?=>false,
49
+ :a_record?=>true,
50
+ :cname_record?=>false,
51
+ :valid_domain?=>true,
52
+ :apex_domain?=>true,
53
+ :should_be_a_record?=>true,
54
+ :pointed_to_github_user_domain?=>false,
55
+ :pointed_to_github_pages_ip?=>false,
56
+ :pages_domain?=>false,
57
+ :valid?=>true
58
+ }
59
+ > check.to_json
60
+ => "{\"cloudflare_ip?\":false,\"old_ip_address?\":false,\"a_record?\":true,\"cname_record?\":false,\"valid_domain?\":true,\"apex_domain?\":true,\"should_be_a_record?\":true,\"pointed_to_github_user_domain?\":false,\"pointed_to_github_pages_ip?\":false,\"pages_domain?\":false,\"valid?\":true}"
61
+ ```
62
+
63
+ ### Getting the reason a domain is invalid
64
+
65
+ ```ruby
66
+ > check = GitHubPages::HealthCheck::Site.new "developer.facebook.com"
67
+ > check.valid?
68
+ => false
69
+ > check.reason
70
+ => #<GitHubPages::HealthCheck::InvalidCNAME>
71
+ > check.reason.message
72
+ => "CNAME does not point to GitHub Pages"
73
+ ```
74
+
75
+ ### Repository checks
76
+
77
+ Repository checks require a personal access or OAuth token with `repo` or scope. This can be passed as the second argument to the Site or Repository constructors like so:
78
+
79
+ ```ruby
80
+ check = GitHubPages::HealthCheck::Site.new "github/pages-health-check", access_token: "1234
81
+ ```
82
+
83
+ You can also set `OCTOKIT_ACCESS_TOKEN` as an environmental variable, or via a `.env` file in your working directory.
@@ -0,0 +1,28 @@
1
+ require File.expand_path("../lib/github-pages-health-check/version", __FILE__)
2
+
3
+ Gem::Specification.new do |s|
4
+ s.required_ruby_version = ">= 1.9.3"
5
+
6
+ s.name = "github-pages-health-check"
7
+ s.version = GitHubPages::HealthCheck::VERSION
8
+ s.summary = "Checks your GitHub Pages site for commons DNS configuration issues"
9
+ s.description = "Checks your GitHub Pages site for commons DNS configuration issues."
10
+ s.authors = "GitHub, Inc."
11
+ s.email = "support@github.com"
12
+ s.homepage = "https://github.com/github/github-pages-health-check"
13
+ s.license = "MIT"
14
+ s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
15
+ s.require_paths = ['lib']
16
+
17
+ s.add_dependency("net-dns", "~> 0.8")
18
+ s.add_dependency("public_suffix", "~> 1.4")
19
+ s.add_dependency("typhoeus", "~> 0.7")
20
+ s.add_dependency("addressable", "~> 2.3")
21
+ s.add_dependency("octokit", "~> 4.0")
22
+
23
+ s.add_development_dependency("rspec", "~> 3.0")
24
+ s.add_development_dependency("pry", "~> 0.10")
25
+ s.add_development_dependency("gem-release", "~> 0.7")
26
+ s.add_development_dependency("webmock", "~> 1.21")
27
+ s.add_development_dependency("dotenv", "~> 1.0")
28
+ end
@@ -8,30 +8,25 @@ require "net/http"
8
8
  require "typhoeus"
9
9
  require "resolv"
10
10
  require "timeout"
11
+ require "octokit"
11
12
  require_relative "github-pages-health-check/version"
12
- require_relative "github-pages-health-check/cloudflare"
13
- require_relative "github-pages-health-check/error"
14
- require_relative "github-pages-health-check/errors/deprecated_ip"
15
- require_relative "github-pages-health-check/errors/invalid_a_record"
16
- require_relative "github-pages-health-check/errors/invalid_cname"
17
- require_relative "github-pages-health-check/errors/invalid_dns"
18
- require_relative "github-pages-health-check/errors/not_served_by_pages"
19
13
 
20
- class GitHubPages
21
- class HealthCheck
22
-
23
- attr_accessor :domain
14
+ if File.exists?(File.expand_path "../.env", File.dirname(__FILE__))
15
+ require 'dotenv'
16
+ Dotenv.load
17
+ end
24
18
 
25
- LEGACY_IP_ADDRESSES = %w[
26
- 207.97.227.245
27
- 204.232.175.78
28
- 199.27.73.133
29
- ]
19
+ module GitHubPages
20
+ module HealthCheck
30
21
 
31
- CURRENT_IP_ADDRESSES = %w[
32
- 192.30.252.153
33
- 192.30.252.154
34
- ]
22
+ autoload :CloudFlare, "github-pages-health-check/cloudflare"
23
+ autoload :Error, "github-pages-health-check/error"
24
+ autoload :Errors, "github-pages-health-check/errors"
25
+ autoload :Checkable, "github-pages-health-check/checkable"
26
+ autoload :Domain, "github-pages-health-check/domain"
27
+ autoload :Repository, "github-pages-health-check/repository"
28
+ autoload :Site, "github-pages-health-check/site"
29
+ autoload :Printer, "github-pages-health-check/printer"
35
30
 
36
31
  # DNS and HTTP timeout, in seconds
37
32
  TIMEOUT = 10
@@ -46,238 +41,16 @@ class GitHubPages
46
41
  }
47
42
  }
48
43
 
49
- def initialize(domain)
50
- @domain = host_from_uri(domain)
51
- end
52
-
53
- def cloudflare_ip?
54
- dns.all? do |answer|
55
- answer.class == Net::DNS::RR::A && CloudFlare.controls_ip?(answer.address)
56
- end if dns?
57
- end
58
-
59
- # Does this non-GitHub-pages domain proxy a GitHub Pages site?
60
- #
61
- # This can be:
62
- # 1. A Cloudflare-owned IP address
63
- # 2. A site that returns GitHub.com server headers, but isn't CNAME'd to a GitHub domain
64
- # 3. A site that returns GitHub.com server headers, but isn't CNAME'd to a GitHub IP
65
- def proxied?
66
- return unless dns?
67
- return true if cloudflare_ip?
68
- return false if pointed_to_github_pages_ip? || pointed_to_github_user_domain?
69
- served_by_pages?
70
- end
71
-
72
- # Returns an array of DNS answers
73
- def dns
74
- if @dns.nil?
75
- begin
76
- @dns = Timeout::timeout(TIMEOUT) do
77
- without_warnings do
78
- Net::DNS::Resolver.start(absolute_domain).answer if domain
79
- end
80
- end
81
- @dns ||= false
82
- rescue Exception
83
- @dns = false
84
- end
85
- end
86
- @dns || nil
87
- end
88
-
89
- # Are we even able to get the DNS record?
90
- def dns?
91
- !dns.nil? && !dns.empty?
92
- end
93
- alias_method :dns_resolves?, :dns
94
-
95
- # Does this domain have *any* A record that points to the legacy IPs?
96
- def old_ip_address?
97
- dns.any? do |answer|
98
- answer.class == Net::DNS::RR::A && LEGACY_IP_ADDRESSES.include?(answer.address.to_s)
99
- end if dns?
100
- end
101
-
102
- # Is this domain's first response an A record?
103
- def a_record?
104
- dns.first.class == Net::DNS::RR::A if dns?
105
- end
106
-
107
- # Is this domain's first response a CNAME record?
108
- def cname_record?
109
- dns.first.class == Net::DNS::RR::CNAME if dns?
110
- end
111
-
112
- # Is this a valid domain that PublicSuffix recognizes?
113
- # Used as an escape hatch to prevent false positives on DNS checkes
114
- def valid_domain?
115
- PublicSuffix.valid? domain
116
- end
117
-
118
- # Is this domain an apex domain, meaning a CNAME would be innapropriate
119
- def apex_domain?
120
- return @apex_domain if defined?(@apex_domain)
121
-
122
- answers = Resolv::DNS.open { |dns|
123
- dns.getresources(absolute_domain, Resolv::DNS::Resource::IN::NS)
124
- }
125
-
126
- @apex_domain = answers.any?
127
- end
128
-
129
- # Should the domain be an apex record?
130
- def should_be_a_record?
131
- !pages_domain? && apex_domain?
132
- end
133
-
134
- # Is the domain's first response a CNAME to a pages domain?
135
- def pointed_to_github_user_domain?
136
- dns.first.class == Net::DNS::RR::CNAME && pages_domain?(dns.first.cname.to_s) if dns?
137
- end
138
-
139
- # Is the domain's first response an A record to a valid GitHub Pages IP?
140
- def pointed_to_github_pages_ip?
141
- dns.first.class == Net::DNS::RR::A && CURRENT_IP_ADDRESSES.include?(dns.first.value) if dns?
142
- end
143
-
144
- # Is the given cname a pages domain?
145
- #
146
- # domain - the domain to check, generaly the target of a cname
147
- def pages_domain?(domain = domain())
148
- !!domain.match(/^[\w-]+\.github\.(io|com)\.?$/i)
149
- end
150
-
151
- # Is this domain owned by GitHub?
152
- def github_domain?
153
- !!domain.match(/\.github\.com$/)
154
- end
155
-
156
- def to_hash
157
- {
158
- :uri => uri.to_s,
159
- :dns_resolves? => dns?,
160
- :proxied? => proxied?,
161
- :cloudflare_ip? => cloudflare_ip?,
162
- :old_ip_address? => old_ip_address?,
163
- :a_record? => a_record?,
164
- :cname_record? => cname_record?,
165
- :valid_domain? => valid_domain?,
166
- :apex_domain? => apex_domain?,
167
- :should_be_a_record? => should_be_a_record?,
168
- :pointed_to_github_user_domain? => pointed_to_github_user_domain?,
169
- :pointed_to_github_pages_ip? => pointed_to_github_pages_ip?,
170
- :pages_domain? => pages_domain?,
171
- :served_by_pages? => served_by_pages?,
172
- :valid? => valid?,
173
- :reason => reason
174
- }
175
- end
176
- alias_method :to_h, :to_hash
177
-
178
- def served_by_pages?
179
- return @served_by_pages if defined? @served_by_pages
180
- @served_by_pages = begin
181
- response = Typhoeus.head(uri, TYPHOEUS_OPTIONS)
182
- # Workaround for webmock not playing nicely with Typhoeus redirects
183
- # See https://github.com/bblimke/webmock/issues/237
184
- if response.mock? && response.headers["Location"]
185
- response = Typhoeus.head(response.headers["Location"], TYPHOEUS_OPTIONS)
186
- end
187
-
188
- return false unless response.mock? || response.return_code == :ok
189
- return true if response.headers["Server"] == "GitHub.com"
190
-
191
- # Typhoeus mangles the case of the header, compare insensitively
192
- response.headers.any? { |k,v| k =~ /X-GitHub-Request-Id/i }
193
- end
194
- end
195
-
196
- def to_json
197
- to_hash.to_json
198
- end
199
-
200
- # Runs all checks, raises an error if invalid
201
- def check!
202
- raise InvalidDNS unless dns?
203
- return if proxied?
204
- raise DeprecatedIP if a_record? && old_ip_address?
205
- raise InvalidARecord if valid_domain? && a_record? && !should_be_a_record?
206
- raise InvalidCNAME if valid_domain? && !github_domain? && !apex_domain? && !pointed_to_github_user_domain?
207
- raise NotServedByPages unless served_by_pages?
208
- true
209
- end
210
- alias_method :valid!, :check!
211
-
212
- # Runs all checks, returns true if valid, otherwise false
213
- def valid?
214
- check!
215
- true
216
- rescue
217
- false
218
- end
219
-
220
- # Return the error, if any
221
- def reason
222
- check!
223
- nil
224
- rescue GitHubPages::HealthCheck::Error => e
225
- e
226
- end
227
-
228
- def inspect
229
- "#<GitHubPages::HealthCheck @domain=\"#{domain}\" valid?=#{valid?}>"
230
- end
231
-
232
- def to_s
233
- to_hash.inject(Array.new) do |all, pair|
234
- all.push pair.join(": ")
235
- end.join("\n")
236
- end
237
-
238
- private
239
-
240
44
  # surpress warn-level feedback due to unsupported record types
241
- def without_warnings(&block)
45
+ def self.without_warnings(&block)
242
46
  warn_level, $VERBOSE = $VERBOSE, nil
243
47
  result = block.call
244
48
  $VERBOSE = warn_level
245
49
  result
246
50
  end
247
51
 
248
- # Adjust `domain` so that it won't be searched for with /etc/resolv.conf's search rules.
249
- #
250
- # GitHubPages::HealthCheck.new("anything.io").absolute_domain
251
- # => "anything.io."
252
- def absolute_domain
253
- domain.end_with?(".") ? domain : "#{domain}."
254
- end
255
-
256
- def scheme
257
- @scheme ||= github_domain? ? "https" : "http"
258
- end
259
-
260
- def uri
261
- @uri ||= Addressable::URI.new(:host => domain, :scheme => scheme, :path => "/").normalize
262
- end
263
-
264
- # Parse the URI. Accept either domain names or full URI's.
265
- # Used by the initializer so we can be more flexible with inputs.
266
- #
267
- # domain - a URI or domain name.
268
- #
269
- # Examples
270
- #
271
- # host_from_uri("benbalter.github.com")
272
- # # => 'benbalter.github.com'
273
- # host_from_uri("https://benbalter.github.com")
274
- # # => 'benbalter.github.com'
275
- # host_from_uri("benbalter.github.com/help-me-im-a-path/")
276
- # # => 'benbalter.github.com'
277
- #
278
- # Return the hostname.
279
- def host_from_uri(domain)
280
- Addressable::URI.parse(domain).host || Addressable::URI.parse("http://#{domain}").host
52
+ def self.check(repository_or_domain, access_token: nil)
53
+ Site.new repository_or_domain, access_token: access_token
281
54
  end
282
55
  end
283
56
  end
@@ -0,0 +1,62 @@
1
+ module GitHubPages
2
+ module HealthCheck
3
+ class Checkable
4
+
5
+ # Array of symbolized methods to be included in the output hash
6
+ HASH_METHODS = []
7
+
8
+ def check!
9
+ raise "Not implemented"
10
+ end
11
+ alias_method :valid!, :check!
12
+
13
+ # Runs all checks, returns true if valid, otherwise false
14
+ def valid?
15
+ check!
16
+ true
17
+ rescue GitHubPages::HealthCheck::Error
18
+ false
19
+ end
20
+
21
+ # Returns the reason the check failed, if any
22
+ def reason
23
+ check!
24
+ nil
25
+ rescue GitHubPages::HealthCheck::Error => e
26
+ e
27
+ end
28
+
29
+ def to_hash
30
+ @hash ||= begin
31
+ hash = {}
32
+ self.class::HASH_METHODS.each do |method|
33
+ hash[method] = public_send(method)
34
+ end
35
+ hash
36
+ end
37
+ end
38
+ alias_method :[], :to_hash
39
+ alias_method :to_h, :to_hash
40
+
41
+ def to_json
42
+ require 'json'
43
+ to_hash.to_json
44
+ end
45
+
46
+ def to_s
47
+ printer.simple_string
48
+ end
49
+
50
+ def to_s_pretty
51
+ printer.pretty_print
52
+ end
53
+ alias_method :pretty_print, :to_s_pretty
54
+
55
+ private
56
+
57
+ def printer
58
+ @printer ||= GitHubPages::HealthCheck::Printer.new(self)
59
+ end
60
+ end
61
+ end
62
+ end