github-pages-health-check-1.18.5/0000755000004100000410000000000014712471256016544 5ustar www-datawww-datagithub-pages-health-check-1.18.5/.gitignore0000644000004100000410000000007614712471256020537 0ustar www-datawww-data/*.gem *.lock .bundle vendor/gems /bin .env spec/examples.txt github-pages-health-check-1.18.5/Dockerfile0000644000004100000410000000076014712471256020541 0ustar www-datawww-dataARG RUBY_VERSION FROM ruby:$RUBY_VERSION-slim RUN set -ex \ && gem update --system --silent --quiet \ && apt-get update -y \ && apt-get upgrade -y \ && apt-get install -y \ build-essential \ git \ libcurl4-openssl-dev \ && apt-get clean WORKDIR /app/github-pages-health-check COPY Gemfile . COPY github-pages-health-check.gemspec . COPY lib/github-pages-health-check/version.rb lib/github-pages-health-check/version.rb RUN bundle install COPY . . ENTRYPOINT [ "/bin/bash" ] github-pages-health-check-1.18.5/.github/0000755000004100000410000000000014712471256020104 5ustar www-datawww-datagithub-pages-health-check-1.18.5/.github/dependabot.yml0000644000004100000410000000067414712471256022743 0ustar www-datawww-dataversion: 2 updates: - package-ecosystem: bundler directory: "/" schedule: interval: daily time: "10:00" timezone: Europe/Vienna pull-request-branch-name: separator: "-" open-pull-requests-limit: 99 allow: - dependency-type: direct - dependency-type: indirect rebase-strategy: disabled - package-ecosystem: "github-actions" directory: "/" schedule: interval: weekly github-pages-health-check-1.18.5/.github/CODEOWNERS0000644000004100000410000000013114712471256021472 0ustar www-datawww-data# Automatically add our team to each pull request in this repo * @github/pages-reviewers github-pages-health-check-1.18.5/.github/workflows/0000755000004100000410000000000014712471256022141 5ustar www-datawww-datagithub-pages-health-check-1.18.5/.github/workflows/push-cibuild.yml0000644000004100000410000000070214712471256025253 0ustar www-datawww-dataon: push name: "GitHub Pages Health Check Tests" permissions: contents: read jobs: build: name: "GitHub Pages Health Check Tests" runs-on: ubuntu-latest strategy: fail-fast: false matrix: ruby: - '3.0' - '3.1' - '3.2' steps: - uses: actions/checkout@master - name: script/cibuild-docker run: script/cibuild-docker env: RUBY_VERSION: ${{ matrix.ruby }} github-pages-health-check-1.18.5/.github/workflows/pages-gem.yml0000644000004100000410000000114514712471256024532 0ustar www-datawww-dataname: Publish Gem on: release: types: [released] permissions: contents: read jobs: release: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Setup Ruby uses: ruby/setup-ruby@52753b7da854d5c07df37391a986c76ab4615999 # v1.191.0 with: ruby-version: '3.2' - name: Build gem run: | gem build github-pages-health-check.gemspec - name: Publish gem env: GEM_HOST_API_KEY: ${{ secrets.PAGES_GEM_PUBLISHING }} run: | gem push github-pages-health-check-*.gem github-pages-health-check-1.18.5/lib/0000755000004100000410000000000014712471256017312 5ustar www-datawww-datagithub-pages-health-check-1.18.5/lib/github-pages-health-check.rb0000644000004100000410000000513114712471256024534 0ustar www-datawww-data# frozen_string_literal: true require "dnsruby" require "addressable/idna" require "addressable/uri" require "ipaddr" require "public_suffix" require "singleton" require "net/http" require "typhoeus" require "resolv" require "timeout" require "octokit" require_relative "github-pages-health-check/version" if File.exist?(File.expand_path("../.env", File.dirname(__FILE__))) require "dotenv" Dotenv.load end module GitHubPages module HealthCheck autoload :CDN, "github-pages-health-check/cdn" autoload :CloudFlare, "github-pages-health-check/cdns/cloudflare" autoload :Fastly, "github-pages-health-check/cdns/fastly" autoload :Error, "github-pages-health-check/error" autoload :Errors, "github-pages-health-check/errors" autoload :CAA, "github-pages-health-check/caa" autoload :Checkable, "github-pages-health-check/checkable" autoload :Domain, "github-pages-health-check/domain" autoload :RedundantCheck, "github-pages-health-check/redundant_check" autoload :Repository, "github-pages-health-check/repository" autoload :Resolver, "github-pages-health-check/resolver" autoload :Site, "github-pages-health-check/site" autoload :Printer, "github-pages-health-check/printer" # DNS and HTTP timeout, in seconds TIMEOUT = 7 HUMAN_NAME = "GitHub Pages Health Check" URL = "https://github.com/github/pages-health-check" USER_AGENT = "Mozilla/5.0 (compatible; #{HUMAN_NAME}/#{VERSION}; +#{URL})" # suppress warn-level feedback due to unsupported record types def self.without_warnings(&block) warn_level = $VERBOSE $VERBOSE = nil result = block.call $VERBOSE = warn_level result end def self.check(repository_or_domain, access_token: nil) Site.new repository_or_domain, :access_token => access_token end # rubocop:disable Naming/AccessorMethodName (this is not an accessor method) def self.set_proxy(proxy_url) @typhoeus_options = typhoeus_options.merge(:proxy => proxy_url).freeze nil end # rubocop:enable Naming/AccessorMethodName def self.typhoeus_options return @typhoeus_options if defined?(@typhoeus_options) @typhoeus_options = { :followlocation => true, :redir_protocols => %i[http https], # don't allow non-http protocols on redirections :timeout => TIMEOUT, :accept_encoding => "gzip", :method => :head, :headers => { "User-Agent" => USER_AGENT }, :proxy => nil }.freeze end end end github-pages-health-check-1.18.5/lib/github-pages-health-check/0000755000004100000410000000000014712471256024207 5ustar www-datawww-datagithub-pages-health-check-1.18.5/lib/github-pages-health-check/error.rb0000644000004100000410000000277714712471256025702 0ustar www-datawww-data# frozen_string_literal: true module GitHubPages module HealthCheck class Error < StandardError DOCUMENTATION_BASE = "https://help.github.com" DOCUMENTATION_PATH = "/categories/github-pages-basics/" LOCAL_ONLY = false # Error is only used when running locally attr_reader :repository, :domain def initialize(repository: nil, domain: nil) super @repository = repository @domain = domain end def self.inherited(base) subclasses << base end def self.subclasses @subclasses ||= [] end def message "Something's wrong with your GitHub Pages site." end # Error message, with get more info URL appended def message_with_url msg = message.gsub(/\s+/, " ").squeeze(" ").strip msg << "." unless msg.end_with?(".") # add trailing period if not there "#{msg} #{more_info}" end alias message_formatted message_with_url def to_s "#{message_with_url} (#{name})".tr("\n", " ").squeeze(" ").strip end private def name self.class.name.split("::").last end def username if repository.nil? "[YOUR USERNAME]" else repository.owner end end def more_info "For more information, see #{documentation_url}." end def documentation_url URI.join(Error::DOCUMENTATION_BASE, self.class::DOCUMENTATION_PATH).to_s end end end end github-pages-health-check-1.18.5/lib/github-pages-health-check/cdn.rb0000644000004100000410000000220514712471256025277 0ustar www-datawww-data# frozen_string_literal: true module GitHubPages module HealthCheck class CDN include Singleton # Internal: The path of the config file. attr_reader :name, :path # Public: Does cloudflare control this address? def self.controls_ip?(address) instance.controls_ip?(address) end # Internal: Create a new CDN info instance. def initialize(options = {}) @name = options.fetch(:name) { self.class.name.split("::").last.downcase } @path = options.fetch(:path) { default_config_path } end # Internal: Does this CDN control this address? def controls_ip?(address) ranges.any? { |range| range.include?(address.to_s) } end private # Internal: The IP address ranges that cloudflare controls. def ranges @ranges ||= load_ranges end # Internal: Load IPAddr ranges from #path def load_ranges File.read(path).lines.map { |line| IPAddr.new(line.chomp) } end def default_config_path File.expand_path("../../config/#{name}-ips.txt", File.dirname(__FILE__)) end end end end github-pages-health-check-1.18.5/lib/github-pages-health-check/repository.rb0000644000004100000410000000340014712471256026750 0ustar www-datawww-data# frozen_string_literal: true module GitHubPages module HealthCheck class Repository < Checkable attr_reader :name, :owner REPO_REGEX = %r{\A[a-z0-9_\-]+/[a-z0-9_\-\.]+\z}i.freeze HASH_METHODS = %i[ name_with_owner built? last_built build_duration build_error ].freeze def initialize(name_with_owner, access_token: nil) unless name_with_owner.match(REPO_REGEX) raise Errors::InvalidRepositoryError end parts = name_with_owner.split("/") @owner = parts.first @name = parts.last @access_token = access_token || ENV["OCTOKIT_ACCESS_TOKEN"] end def name_with_owner @name_with_owner ||= [owner, name].join("/") end alias nwo name_with_owner def check! raise Errors::BuildError.new(:repository => self), build_error unless built? true end def last_build @last_build ||= client.latest_pages_build(name_with_owner) end def built? last_build && last_build.status == "built" end def build_error last_build.error["message"] unless built? end alias reason build_error def build_duration last_build&.duration end def last_built last_build&.updated_at end def domain return if cname.nil? @domain ||= GitHubPages::HealthCheck::Domain.redundant(cname) end private def client raise Errors::MissingAccessTokenError if @access_token.nil? @client ||= Octokit::Client.new(:access_token => @access_token) end def pages_info @pages_info ||= client.pages(name_with_owner) end def cname pages_info.cname end end end end github-pages-health-check-1.18.5/lib/github-pages-health-check/cdns/0000755000004100000410000000000014712471256025136 5ustar www-datawww-datagithub-pages-health-check-1.18.5/lib/github-pages-health-check/cdns/fastly.rb0000644000004100000410000000067114712471256026771 0ustar www-datawww-data# frozen_string_literal: true module GitHubPages module HealthCheck # Instance of the Fastly CDN for checking IP ownership # Specifically not namespaced to avoid a breaking change class Fastly < CDN # Fastly maps used by GitHub Pages. HOSTNAMES = %w( github.map.fastly.net github.map.fastly.net. sni.github.map.fastly.net sni.github.map.fastly.net. ).freeze end end end github-pages-health-check-1.18.5/lib/github-pages-health-check/cdns/cloudflare.rb0000644000004100000410000000036014712471256027602 0ustar www-datawww-data# frozen_string_literal: true module GitHubPages module HealthCheck # Instance of the CloudFlare CDN for checking IP ownership # Specifically not namespaced to avoid a breaking change class CloudFlare < CDN end end end github-pages-health-check-1.18.5/lib/github-pages-health-check/domain.rb0000644000004100000410000004473614712471256026021 0ustar www-datawww-data# frozen_string_literal: true module GitHubPages module HealthCheck class Domain < Checkable attr_reader :host, :resolver, :nameservers LEGACY_IP_ADDRESSES = [ # Legacy GitHub Datacenter "207.97.227.245", "204.232.175.78", # Aug. 2016 Fastly datacenter deprecation "199.27.73.133", "199.27.76.133", # Feb. 2017 Fastly datacenter deprecation "103.245.222.133", "103.245.223.133", "103.245.224.133", "104.156.81.133", "104.156.82.133", "104.156.83.133", "104.156.85.133", "104.156.87.133", "104.156.88.133", "104.156.89.133", "104.156.90.133", "104.156.91.133", "104.156.92.133", "104.156.93.133", "104.156.94.133", "104.156.95.133", "104.37.95.133", "157.52.64.133", "157.52.66.133", "157.52.67.133", "157.52.68.133", "157.52.69.133", "157.52.96.133", "172.111.64.133", "172.111.96.133", "185.31.16.133", "185.31.17.133", "185.31.18.133", "185.31.19.133", "199.27.74.133", "199.27.75.133", "199.27.76.133", "199.27.78.133", "199.27.79.133", "23.235.33.133", "23.235.37.133", "23.235.39.133", "23.235.40.133", "23.235.41.133", "23.235.43.133", "23.235.44.133", "23.235.45.133", "23.235.46.133", "23.235.47.133", "23.235.47.133", "43.249.72.133", "43.249.73.133", "43.249.74.133", "43.249.75.133", # 2018 Move to GitHub assigned IP space "192.30.252.153", "192.30.252.154" ].freeze CURRENT_IP_ADDRESSES = %w( 185.199.108.153 185.199.109.153 185.199.110.153 185.199.111.153 ).freeze CURRENT_IPV6_ADDRESSES = %w( 2606:50c0:8000::153 2606:50c0:8001::153 2606:50c0:8002::153 2606:50c0:8003::153 ).freeze CURRENT_IP_ADDRESSES_ALL = (CURRENT_IP_ADDRESSES + CURRENT_IPV6_ADDRESSES).freeze HASH_METHODS = %i[ host uri nameservers dns_resolves? proxied? cloudflare_ip? fastly_ip? old_ip_address? a_record? aaaa_record? a_record_present? aaaa_record_present? cname_record? mx_records_present? valid_domain? apex_domain? should_be_a_record? cname_to_github_user_domain? cname_to_domain_to_pages? cname_to_pages_dot_github_dot_com? cname_to_fastly? pointed_to_github_pages_ip? non_github_pages_ip_present? pages_domain? served_by_pages? valid? reason valid_domain? https? enforces_https? https_error https_eligible? caa_error dns_zone_soa? dns_zone_ns? ].freeze def self.redundant(host) GitHubPages::HealthCheck::RedundantCheck.new(host).check end def initialize(host, nameservers: :default) unless host.is_a? String raise ArgumentError, "Expected string, got #{host.class}" end @host = normalize_host(host) @nameservers = nameservers @resolver = GitHubPages::HealthCheck::Resolver.new(self.host, :nameservers => nameservers) end # Runs all checks, raises an error if invalid # rubocop:disable Metrics/AbcSize def check! raise Errors::InvalidDomainError.new :domain => self unless valid_domain? raise Errors::InvalidDNSError.new :domain => self unless dns_resolves? raise Errors::DeprecatedIPError.new :domain => self if deprecated_ip? return true if proxied? raise Errors::InvalidARecordError.new :domain => self if invalid_a_record? raise Errors::InvalidCNAMEError.new :domain => self if invalid_cname? raise Errors::InvalidAAAARecordError.new :domain => self if invalid_aaaa_record? raise Errors::NotServedByPagesError.new :domain => self unless served_by_pages? true end # rubocop:enable Metrics/AbcSize def deprecated_ip? return @deprecated_ip if defined? @deprecated_ip @deprecated_ip = (valid_domain? && a_record? && old_ip_address?) end def invalid_aaaa_record? return @invalid_aaaa_record if defined? @invalid_aaaa_record @invalid_aaaa_record = (valid_domain? && aaaa_record_present? && !should_be_a_record?) end def invalid_a_record? return @invalid_a_record if defined? @invalid_a_record @invalid_a_record = (valid_domain? && a_record_present? && !should_be_a_record?) end def invalid_cname? return @invalid_cname if defined? @invalid_cname @invalid_cname = begin return false unless valid_domain? return false if github_domain? || apex_domain? return true if cname_to_pages_dot_github_dot_com? || cname_to_fastly? !cname_to_github_user_domain? && should_be_cname_record? end end # Is this a valid domain that PublicSuffix recognizes? # Used as an escape hatch to prevent false positives on DNS checks def valid_domain? return @valid if defined? @valid unicode_host = Addressable::IDNA.to_unicode(host) @valid = PublicSuffix.valid?(unicode_host, :default_rule => nil, :ignore_private => true) end # Is this domain an apex domain, meaning a CNAME would be inappropriate def apex_domain? return @apex_domain if defined?(@apex_domain) return false unless valid_domain? return true if dns_zone_soa? && dns_zone_ns? # PublicSuffix.domain pulls out the apex-level domain name. # E.g. PublicSuffix.domain("techblog.netflix.com") # => "netflix.com" # It's aware of multi-step top-level domain names: # E.g. PublicSuffix.domain("blog.digital.gov.uk") # => "digital.gov.uk" # For apex-level domain names, DNS providers do not support CNAME records. unicode_host = Addressable::IDNA.to_unicode(host) PublicSuffix.domain(unicode_host, :default_rule => nil, :ignore_private => true) == unicode_host end # # Does the domain have an associated SOA record? # def dns_zone_soa? return @soa_records if defined?(@soa_records) return false unless dns? @soa_records = dns.any? do |answer| answer.type == Dnsruby::Types::SOA && answer.name.to_s == host end end # # Does the domain have associated NS records? # def dns_zone_ns? return @ns_records if defined?(@ns_records) return false unless dns? @ns_records = dns.any? do |answer| answer.type == Dnsruby::Types::NS && answer.name.to_s == host end end # Should the domain use an A record? def should_be_a_record? !pages_io_domain? && (apex_domain? || mx_records_present?) end def should_be_cname_record? !should_be_a_record? end # Is the domain's first response an A or AAAA record to a valid GitHub Pages IP? def pointed_to_github_pages_ip? return false unless address_record? CURRENT_IP_ADDRESSES_ALL.include?(dns.first.address.to_s.downcase) end # Are any of the domain's A or AAAA records pointing elsewhere? def non_github_pages_ip_present? return unless dns? dns .select { |a| Dnsruby::Types::A == a.type || Dnsruby::Types::AAAA == a.type } .any? { |a| !github_pages_ip?(a.address.to_s) } end # Is the domain's first response a CNAME to a pages domain? def cname_to_github_user_domain? cname? && !cname_to_pages_dot_github_dot_com? && cname.pages_domain? end # Check if the CNAME points to a Domain that points to pages # e.g. CNAME -> Domain -> Pages # rubocop:disable Metrics/AbcSize def cname_to_domain_to_pages? return false unless dns? a_record_to_pages = dns.select { |d| d.type == Dnsruby::Types::A && d.name.to_s == host }.first return false unless a_record_to_pages && cname? && !cname_to_pages_dot_github_dot_com? && @www_cname CURRENT_IP_ADDRESSES.include?(a_record_to_pages.address.to_s.downcase) end # rubocop:enable Metrics/AbcSize # Is the given domain a CNAME to pages.github.(io|com) # instead of being CNAME'd to the user's subdomain? # # domain - the domain to check, generally the target of a cname def cname_to_pages_dot_github_dot_com? cname? && cname.pages_dot_github_dot_com? end # Is the given domain CNAME'd directly to our Fastly account? def cname_to_fastly? cname? && !pages_domain? && cname.fastly? end # Is the host a *.github.io domain? def pages_io_domain? !!host.match(/\A[\w-]+\.github\.(io)\.?\z/i) end # Is the host a *.github.(io|com) domain? def pages_domain? !!host.match(/\A[\w-]+\.github\.(io|com)\.?\z/i) end # Is the host pages.github.com or pages.github.io? def pages_dot_github_dot_com? !!host.match(/\Apages\.github\.(io|com)\.?\z/i) end # Is this domain owned by GitHub? def github_domain? host.downcase.eql?("github.com") || host.downcase.end_with?(".github.com") end # Is the host our Fastly CNAME? def fastly? !!host.match(/\A#{Regexp.union(Fastly::HOSTNAMES)}\z/i) end # Does the domain resolve to a CloudFlare-owned IP def cloudflare_ip? cdn_ip?(CloudFlare) end # Does the domain resolve to a Fastly-owned IP def fastly_ip? cdn_ip?(Fastly) end # Does this non-GitHub-pages domain proxy a GitHub Pages site? # # This can be: # 1. A Cloudflare-owned IP address # 2. A site that returns GitHub.com server headers, but # isn't CNAME'd to a GitHub domain # 3. A site that returns GitHub.com server headers, but # isn't CNAME'd to a GitHub IP def proxied? return unless dns? return true if cloudflare_ip? return false if pointed_to_github_pages_ip? return false if cname_to_github_user_domain? return false if cname_to_domain_to_pages? return false if cname_to_pages_dot_github_dot_com? return false if cname_to_fastly? || fastly_ip? served_by_pages? end REQUESTED_RECORD_TYPES = [ Dnsruby::Types::A, Dnsruby::Types::AAAA, Dnsruby::Types::CNAME, Dnsruby::Types::MX, Dnsruby::Types::NS, Dnsruby::Types::SOA ].freeze # Returns an array of DNS answers def dns return @dns if defined? @dns return unless valid_domain? @dns = Timeout.timeout(TIMEOUT) do GitHubPages::HealthCheck.without_warnings do next if host.nil? REQUESTED_RECORD_TYPES .map { |type| resolver.query(type) } .flatten.uniq end end rescue StandardError @dns = nil end # Are we even able to get the DNS record? def dns? !(dns.nil? || dns.empty?) end alias dns_resolves? dns? # Does this domain have *any* A record that points to the legacy IPs? def old_ip_address? return unless dns? dns.any? do |answer| answer.type == Dnsruby::Types::A && legacy_ip?(answer.address.to_s) end end # Is this domain's first response an A record? def a_record? return @is_a_record if defined?(@is_a_record) return unless dns? @is_a_record = Dnsruby::Types::A == dns.first.type end # Is this domain's first response an AAAA record? def aaaa_record? return @is_aaaa_record if defined?(@is_aaaa_record) return unless dns? @is_aaaa_record = Dnsruby::Types::AAAA == dns.first.type end # Does this domain has an A record setup (not necessarily as the first record)? def a_record_present? return unless dns? dns.any? { |answer| answer.type == Dnsruby::Types::A && answer.name.to_s == host } end # Does this domain has an AAAA record setup (not necessarily as the first record)? def aaaa_record_present? return unless dns? dns.any? { |answer| answer.type == Dnsruby::Types::AAAA && answer.name.to_s == host } end # Is this domain's first response a CNAME record? def cname_record? return unless dns? return false unless cname cname.valid_domain? end alias cname? cname_record? # The domain to which this domain's CNAME resolves # Returns nil if the domain is not a CNAME def cname return unless dns? cnames = dns.take_while { |answer| answer.type == Dnsruby::Types::CNAME } return if cnames.empty? www_cname(cnames.last) @cname ||= Domain.new(cnames.last.cname.to_s) end # Check if we have a 'www.' CNAME that matches the domain def www_cname(cname) @www_cname ||= cname.name.to_s.start_with?("www.") && cname.name.to_s.end_with?(cname.domainname.to_s) end def mx_records_present? return unless dns? dns.any? { |answer| answer.type == Dnsruby::Types::MX } end def served_by_pages? return @served_by_pages if defined? @served_by_pages return unless dns_resolves? @served_by_pages = begin return true if response.headers["Server"] == "GitHub.com" # Typhoeus mangles the case of the header, compare insensitively response.headers.any? { |k, _v| k.downcase == "x-github-request-id" } end end def uri(overrides = {}) options = { :host => host, :scheme => scheme, :path => "/" } options = options.merge(overrides) Addressable::URI.new(options).normalize.to_s end # Does this domain respond to HTTPS requests with a valid cert? def https? https_response.return_code == :ok end # The response code of the HTTPS request, if it failed. # Useful for diagnosing cert errors def https_error https_response.return_code unless https? end # Does this domain redirect HTTP requests to HTTPS? def enforces_https? return false unless https? && http_response.headers["Location"] redirect = Addressable::URI.parse(http_response.headers["Location"]) redirect.scheme == "https" && redirect.host == host end # Can an HTTPS certificate be issued for this domain? def https_eligible? # Can't have any IP's which aren't GitHub's present. return false if non_github_pages_ip_present? # Can't have underscores in the domain name (Let's Encrypt does not allow it) return false if host.include?("_") # Must be a CNAME or point to our IPs. return true if cname_to_github_user_domain? || cname_to_domain_to_pages? # Check CAA records for the full domain and its parent domain. pointed_to_github_pages_ip? && caa.lets_encrypt_allowed? end # Any errors querying CAA records def caa_error return nil unless caa&.errored? caa.error.class.name end private def address_record? a_record? || aaaa_record? end def caa @caa ||= GitHubPages::HealthCheck::CAA.new( :host => cname&.host || host, :nameservers => nameservers ) end # The domain's response to HTTP(S) requests, following redirects def response return @response if defined? @response @response = Typhoeus.head(uri, GitHubPages::HealthCheck.typhoeus_options) # Workaround for webmock not playing nicely with Typhoeus redirects # See https://github.com/bblimke/webmock/issues/237 if @response.mock? && @response.headers["Location"] @response = Typhoeus.head(response.headers["Location"], GitHubPages::HealthCheck.typhoeus_options) end @response end # The domain's response to HTTP requests, without following redirects def http_response options = GitHubPages::HealthCheck.typhoeus_options.merge(:followlocation => false) @http_response ||= Typhoeus.head(uri(:scheme => "http"), options) end # The domain's response to HTTPS requests, without following redirects def https_response options = GitHubPages::HealthCheck.typhoeus_options.merge(:followlocation => false) @https_response ||= Typhoeus.head(uri(:scheme => "https"), options) end # Parse the URI. Accept either domain names or full URI's. # Used by the initializer so we can be more flexible with inputs. # # domain - a URI or domain name. # # Examples # # normalize_host("benbalter.github.com") # # => 'benbalter.github.com' # normalize_host("https://benbalter.github.com") # # => 'benbalter.github.com' # normalize_host("benbalter.github.com/help-me-im-a-path/") # # => 'benbalter.github.com' # # Return the hostname. def normalize_host(domain) domain = domain.strip.chomp(".") host = Addressable::URI.parse(domain).normalized_host host ||= Addressable::URI.parse("http://#{domain}").normalized_host host unless host.to_s.empty? rescue Addressable::URI::InvalidURIError nil end # Adjust `domain` so that it won't be searched for with /etc/resolv.conf # # GitHubPages::HealthCheck.new("anything.io").absolute_domain # => "anything.io." def absolute_domain host.end_with?(".") ? host : "#{host}." end def scheme @scheme ||= github_domain? ? "https" : "http" end # Does the domain resolve to a CDN-owned IP def cdn_ip?(cdn) return unless dns? address_records = dns.select do |answer| Dnsruby::Types::A == answer.type || Dnsruby::Types::AAAA == answer.type end return false if !address_records || address_records.empty? address_records.all? do |answer| cdn.controls_ip?(answer.address) end end def legacy_ip?(ip_addr) LEGACY_IP_ADDRESSES.include?(ip_addr) end def github_pages_ip?(ip_addr) CURRENT_IP_ADDRESSES_ALL.include?(ip_addr&.to_s&.downcase) end end end end github-pages-health-check-1.18.5/lib/github-pages-health-check/version.rb0000644000004100000410000000015014712471256026215 0ustar www-datawww-data# frozen_string_literal: true module GitHubPages module HealthCheck VERSION = "1.18.5" end end github-pages-health-check-1.18.5/lib/github-pages-health-check/redundant_check.rb0000644000004100000410000000175714712471256027667 0ustar www-datawww-data# frozen_string_literal: true module GitHubPages module HealthCheck class RedundantCheck extend Forwardable TIMEOUT = 5 # seconds attr_reader :domain def initialize(domain) @domain = domain end def check @check ||= (checks.find(&:valid?) || check_with_default_nameservers) end def_delegator :check, :reason, :reason def_delegator :check, :valid?, :valid? def https_eligible? checks.any?(&:https_eligible?) end private def checks @checks ||= %i[default authoritative public].map do |ns| GitHubPages::HealthCheck::Domain.new(domain, :nameservers => ns) end end def check_with_default_nameservers @check_with_default_nameservers ||= checks.find { |c| c.nameservers == :default } end def check_with_public_nameservers @check_with_public_nameservers ||= checks.find { |c| c.nameservers == :public } end end end end github-pages-health-check-1.18.5/lib/github-pages-health-check/checkable.rb0000644000004100000410000000236214712471256026440 0ustar www-datawww-data# frozen_string_literal: true module GitHubPages module HealthCheck class Checkable # Array of symbolized methods to be included in the output hash HASH_METHODS = [].freeze def check! raise "Not implemented" end alias valid! check! # Runs all checks, returns true if valid, otherwise false def valid? check! true rescue GitHubPages::HealthCheck::Error false end # Returns the reason the check failed, if any def reason check! nil rescue GitHubPages::HealthCheck::Error => e e end def to_hash @to_hash ||= begin hash = {} self.class::HASH_METHODS.each do |method| hash[method] = public_send(method) end hash end end alias [] to_hash alias to_h to_hash def to_json(state = nil) require "json" to_hash.to_json(state) end def to_s printer.simple_string end def to_s_pretty printer.pretty_print end alias pretty_print to_s_pretty private def printer @printer ||= GitHubPages::HealthCheck::Printer.new(self) end end end end github-pages-health-check-1.18.5/lib/github-pages-health-check/errors/0000755000004100000410000000000014712471256025523 5ustar www-datawww-datagithub-pages-health-check-1.18.5/lib/github-pages-health-check/errors/invalid_aaaa_record_error.rb0000644000004100000410000000105114712471256033205 0ustar www-datawww-data# frozen_string_literal: true module GitHubPages module HealthCheck module Errors class InvalidAAAARecordError < GitHubPages::HealthCheck::Error DOCUMENTATION_PATH = "/articles/setting-up-a-custom-domain-with-github-pages/" def message <<-MSG Your site's DNS settings are using a custom subdomain, #{domain.host}, that's set up as an AAAA record. We recommend you change this to a CNAME record pointing at #{username}.github.io. MSG end end end end end github-pages-health-check-1.18.5/lib/github-pages-health-check/errors/not_served_by_pages_error.rb0000644000004100000410000000056014712471256033303 0ustar www-datawww-data# frozen_string_literal: true module GitHubPages module HealthCheck module Errors class NotServedByPagesError < GitHubPages::HealthCheck::Error DOCUMENTATION_PATH = "/articles/setting-up-a-custom-domain-with-github-pages/" def message "Domain does not resolve to the GitHub Pages server" end end end end end github-pages-health-check-1.18.5/lib/github-pages-health-check/errors/invalid_a_record_error.rb0000644000004100000410000000105414712471256032545 0ustar www-datawww-data# frozen_string_literal: true module GitHubPages module HealthCheck module Errors class InvalidARecordError < GitHubPages::HealthCheck::Error DOCUMENTATION_PATH = "/articles/setting-up-a-custom-domain-with-github-pages/" def message <<-MSG Your site's DNS settings are using a custom subdomain, #{domain.host}, that's set up as an A record. We recommend you change this to a CNAME record pointing at #{username}.github.io. MSG end end end end end github-pages-health-check-1.18.5/lib/github-pages-health-check/errors/build_error.rb0000644000004100000410000000042014712471256030354 0ustar www-datawww-data# frozen_string_literal: true module GitHubPages module HealthCheck module Errors class BuildError < GitHubPages::HealthCheck::Error DOCUMENTATION_PATH = "/articles/troubleshooting-jekyll-builds/" LOCAL_ONLY = true end end end end github-pages-health-check-1.18.5/lib/github-pages-health-check/errors/invalid_dns_error.rb0000644000004100000410000000054214712471256031554 0ustar www-datawww-data# frozen_string_literal: true module GitHubPages module HealthCheck module Errors class InvalidDNSError < GitHubPages::HealthCheck::Error DOCUMENTATION_PATH = "/articles/setting-up-a-custom-domain-with-github-pages/" def message "Domain's DNS record could not be retrieved" end end end end end github-pages-health-check-1.18.5/lib/github-pages-health-check/errors/invalid_repository_error.rb0000644000004100000410000000044514712471256033211 0ustar www-datawww-data# frozen_string_literal: true module GitHubPages module HealthCheck module Errors class InvalidRepositoryError < GitHubPages::HealthCheck::Error LOCAL_ONLY = true def message "Repository is not a valid repository" end end end end end github-pages-health-check-1.18.5/lib/github-pages-health-check/errors/deprecated_ip_error.rb0000644000004100000410000000103014712471256032043 0ustar www-datawww-data# frozen_string_literal: true module GitHubPages module HealthCheck module Errors class DeprecatedIPError < GitHubPages::HealthCheck::Error DOCUMENTATION_PATH = "/articles/setting-up-a-custom-domain-with-github-pages/" def message <<-MSG The custom domain for your GitHub Pages site is pointed at an outdated IP address. You must update your site's DNS records if you'd like it to be available via your custom domain. MSG end end end end end github-pages-health-check-1.18.5/lib/github-pages-health-check/errors/invalid_cname_error.rb0000644000004100000410000000106414712471256032053 0ustar www-datawww-data# frozen_string_literal: true module GitHubPages module HealthCheck module Errors class InvalidCNAMEError < GitHubPages::HealthCheck::Error DOCUMENTATION_PATH = "/articles/setting-up-a-custom-domain-with-github-pages/" def message <<-MSG Your site's DNS settings are using a custom subdomain, #{domain.host}, that's not set up with a correct CNAME record. We recommend you set this CNAME record to point at #{username}.github.io. MSG end end end end end github-pages-health-check-1.18.5/lib/github-pages-health-check/errors/invalid_domain_error.rb0000644000004100000410000000052714712471256032242 0ustar www-datawww-data# frozen_string_literal: true module GitHubPages module HealthCheck module Errors class InvalidDomainError < GitHubPages::HealthCheck::Error DOCUMENTATION_PATH = "/articles/setting-up-a-custom-domain-with-github-pages/" def message "Domain is not a valid domain" end end end end end github-pages-health-check-1.18.5/lib/github-pages-health-check/errors/missing_access_token_error.rb0000644000004100000410000000050214712471256033450 0ustar www-datawww-data# frozen_string_literal: true module GitHubPages module HealthCheck module Errors class MissingAccessTokenError < GitHubPages::HealthCheck::Error LOCAL_ONLY = true def message "Cannot retrieve repository information with a valid access token" end end end end end github-pages-health-check-1.18.5/lib/github-pages-health-check/errors.rb0000644000004100000410000000036714712471256026056 0ustar www-datawww-data# frozen_string_literal: true Dir[File.expand_path("errors/*_error.rb", __dir__)].sort.each do |f| require f end module GitHubPages module HealthCheck module Errors def self.all Error.subclasses end end end end github-pages-health-check-1.18.5/lib/github-pages-health-check/site.rb0000644000004100000410000000154514712471256025505 0ustar www-datawww-data# frozen_string_literal: true module GitHubPages module HealthCheck class Site < Checkable attr_reader :repository, :domain def initialize(repository_or_domain, access_token: nil) @repository = Repository.new(repository_or_domain, :access_token => access_token) @domain = @repository.domain rescue GitHubPages::HealthCheck::Errors::InvalidRepositoryError @repository = nil @domain = Domain.redundant(repository_or_domain) end def check! [domain, repository].compact.each(&:check!) true end def to_hash hash = (domain || {}).to_hash.dup hash = hash.merge(repository.to_hash) unless repository.nil? hash[:valid?] = valid? hash[:reason] = reason hash end alias to_h to_hash alias as_json to_hash end end end github-pages-health-check-1.18.5/lib/github-pages-health-check/resolver.rb0000644000004100000410000000426414712471256026403 0ustar www-datawww-data# frozen_string_literal: true module GitHubPages module HealthCheck class Resolver DEFAULT_RESOLVER_OPTIONS = { :retry_times => 2, :query_timeout => 5, :dnssec => false, :do_caching => false }.freeze PUBLIC_NAMESERVERS = %w( 8.8.8.8 1.1.1.1 ).freeze class << self def default_resolver @default_resolver ||= Dnsruby::Resolver.new(DEFAULT_RESOLVER_OPTIONS) end end attr_reader :domain, :nameservers # Create a new resolver. # # domain - the domain we're getting answers for # nameserver - (optional) a case def initialize(domain, nameservers: :default) @domain = domain @nameservers = nameservers end def query(type) resolver.query(Addressable::IDNA.to_ascii(domain), type).answer end private def resolver @resolver ||= case nameservers when :default self.class.default_resolver when :authoritative Dnsruby::Resolver.new(DEFAULT_RESOLVER_OPTIONS.merge( :nameservers => authoritative_nameservers )) when :public Dnsruby::Resolver.new(DEFAULT_RESOLVER_OPTIONS.merge( :nameservers => PUBLIC_NAMESERVERS )) when Array Dnsruby::Resolver.new(DEFAULT_RESOLVER_OPTIONS.merge( :nameservers => nameservers )) else raise "Invalid nameserver type: #{nameservers.inspect}" end end def authoritative_nameservers @authoritative_nameservers ||= begin self.class.default_resolver.query(domain, Dnsruby::Types::NS).answer.map do |rr| next rr.nsdname.to_s if rr.type == Dnsruby::Types::NS end.compact end end end end end github-pages-health-check-1.18.5/lib/github-pages-health-check/printer.rb0000644000004100000410000000540214712471256026220 0ustar www-datawww-data# frozen_string_literal: true module GitHubPages module HealthCheck class Printer PRETTY_LEFT_WIDTH = 11 PRETTY_JOINER = " | " attr_reader :health_check def initialize(health_check) @health_check = health_check end def simple_string require "yaml" hash = health_check.to_hash hash[:reason] = hash[:reason].to_s if hash[:reason] hash.to_yaml.sub(/\A---\n/, "").gsub(/^:/, "") end def pretty_print values = health_check.to_hash output = StringIO.new # Header output.puts new_line "Domain", (values[:uri]).to_s output.puts "-" * (PRETTY_LEFT_WIDTH + 1) + "|" + "-" * 50 output.puts new_line "DNS", "does not resolve" unless values[:dns_resolves?] # Valid? output.write new_line "State", (values[:valid?] ? "valid" : "invalid").to_s output.puts " - is #{"NOT " unless values[:served_by_pages?]}served by Pages" # What's wrong? output.puts new_line "Reason", (values[:reason]).to_s unless values[:valid?] if values[:pointed_to_github_user_domain?] output.puts new_line nil, "pointed to user domain" end if values[:pointed_to_github_pages_ip?] output.puts new_line nil, "pointed to pages IP" end # DNS Record info record_type = if values[:a_record?] "A" elsif values[:cname_record?] "CNAME" else "other" end output.write new_line "Record Type", record_type should_be = values[:should_be_a_record?] ? "A record" : "CNAME" output.puts ", should be #{should_be}" ip_problems = [] ip_problems << "not apex domain" unless values[:apex_domain?] ip_problems << "invalid domain" unless values[:valid_domain?] ip_problems << "old ip address used" if values[:old_ip_address?] ip_problems_string = !ip_problems.empty? ? ip_problems.join(", ") : "none" output.puts new_line "IP Problems", ip_problems_string if values[:proxied?] proxy = values[:cloudflare_ip?] ? "CloudFlare" : "unknown" output.puts new_line "Proxied", "yes, through #{proxy}" end output.puts new_line "Domain", "*.github.com/io domain" if values[:pages_domain?] output.string end def new_line(left = nil, right = nil) if left && right ljust(left) + PRETTY_JOINER + right elsif left ljust(left) elsif right " " * (PRETTY_LEFT_WIDTH + PRETTY_JOINER.size) + right end end def ljust(line) line.ljust(PRETTY_LEFT_WIDTH) end end end end github-pages-health-check-1.18.5/lib/github-pages-health-check/caa.rb0000644000004100000410000000321514712471256025261 0ustar www-datawww-data# frozen_string_literal: true require "dnsruby" require "public_suffix" require "github-pages-health-check/resolver" module GitHubPages module HealthCheck class CAA attr_reader :host, :error, :nameservers def initialize(host:, nameservers: :default) raise ArgumentError, "host cannot be nil" if host.nil? @host = host @nameservers = nameservers end def errored? records # load the records first !error.nil? end def lets_encrypt_allowed? return false if errored? return true unless records_present? records.any? { |r| r.property_value == "letsencrypt.org" } end def records_present? return false if errored? records && !records.empty? end def records return @records if defined?(@records) @records = get_caa_records(host) @records = get_caa_records(parent_host) if @records.nil? || @records.empty? @records end private def get_caa_records(domain) return [] if domain.nil? query(domain).select { |r| issue_caa_record?(r) } end def issue_caa_record?(record) record.type == Dnsruby::Types::CAA && record.property_tag == "issue" end def query(domain) resolver(domain).query(Dnsruby::Types::CAA) rescue Dnsruby::ResolvError, Dnsruby::ResolvTimeout => e @error = e [] end def resolver(domain) GitHubPages::HealthCheck::Resolver.new(domain, :nameservers => nameservers) end def parent_host host.split(".").drop(1).join(".") end end end end github-pages-health-check-1.18.5/github-pages-health-check.gemspec0000644000004100000410000000206414712471256025010 0ustar www-datawww-data# frozen_string_literal: true require File.expand_path("lib/github-pages-health-check/version", __dir__) Gem::Specification.new do |s| s.required_ruby_version = ">= 2.2.0" s.name = "github-pages-health-check" s.version = GitHubPages::HealthCheck::VERSION s.summary = "Checks your GitHub Pages site for commons DNS configuration issues" s.description = "Checks your GitHub Pages site for commons DNS configuration issues." s.authors = "GitHub, Inc." s.email = "support@github.com" s.homepage = "https://github.com/github/github-pages-health-check" s.license = "MIT" s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } s.require_paths = ["lib"] s.add_dependency("addressable", "~> 2.3") s.add_dependency("dnsruby", "~> 1.60") s.add_dependency("octokit", ">= 4", "< 10") s.add_dependency("public_suffix", ">= 3.0", "< 7.0") s.add_dependency("typhoeus", "~> 1.3") end github-pages-health-check-1.18.5/.rspec0000644000004100000410000000003614712471256017660 0ustar www-datawww-data--color --require spec_helper github-pages-health-check-1.18.5/LICENSE.md0000644000004100000410000000207614712471256020155 0ustar www-datawww-dataThe MIT License (MIT) Copyright (c) 2014 - 2017 GitHub, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. github-pages-health-check-1.18.5/Gemfile0000644000004100000410000000045514712471256020043 0ustar www-datawww-data# frozen_string_literal: true source "https://rubygems.org" group :development do gem "dotenv", "~> 2.7" gem "gem-release", "~> 2.1" gem "pry", "~> 0.10" gem "pry-byebug" gem "rspec", "~> 3.0" gem "rspec-retry", "~> 0.6" gem "rubocop", "~> 0.52" gem "webmock", "~> 3.8" end gemspec github-pages-health-check-1.18.5/README.md0000644000004100000410000000600514712471256020024 0ustar www-datawww-data# GitHub Pages Health Check *Checks your GitHub Pages site for common DNS configuration issues* [![Build Status](https://github.com/github/pages-health-check/actions/workflows/push-cibuild.yml/badge.svg)](https://github.com/github/pages-health-check/actions/workflows/push-cibuild.yml) [![Gem Version](https://badge.fury.io/rb/github-pages-health-check.svg)](http://badge.fury.io/rb/github-pages-health-check) ## Installation `gem install github-pages-health-check` ## Usage ### Basic Usage ```ruby > check = GitHubPages::HealthCheck::Site.new("choosealicense.com") => # > check.valid? => true ``` ### An invalid domain ```ruby > check = GitHubPages::HealthCheck::Site.new("foo.github.com") > check.valid? => false > check.valid! raises GitHubPages::HealthCheck::Errors::InvalidCNAMEError ``` ### Retrieving specific checks ``` ruby > check.domain.should_be_a_record? => true > check.domain.a_record? => true ``` ### Getting checks in bulk ```ruby > check.to_hash => { :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 } > check.to_json => "{\"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}" ``` ### Getting the reason a domain is invalid ```ruby > check = GitHubPages::HealthCheck::Site.new "developer.facebook.com" > check.valid? => false > check.reason => # > check.reason.message => "CNAME does not point to GitHub Pages" ``` ### Repository checks 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: ```ruby check = GitHubPages::HealthCheck::Site.new "github/pages-health-check", access_token: "1234 ``` You can also set `OCTOKIT_ACCESS_TOKEN` as an environmental variable, or via a `.env` file in your working directory. ### Command Line ``` ./script/check pages.github.com host: pages.github.com uri: https://pages.github.com/ nameservers: :default dns_resolves?: true proxied?: false cloudflare_ip?: false fastly_ip?: false old_ip_address?: false a_record?: false cname_record?: true mx_records_present?: false valid_domain?: true apex_domain?: false should_be_a_record?: false cname_to_github_user_domain?: true cname_to_pages_dot_github_dot_com?: false cname_to_fastly?: false pointed_to_github_pages_ip?: false non_github_pages_ip_present?: false pages_domain?: true served_by_pages?: true valid?: true reason: https?: true enforces_https?: true https_error: https_eligible?: true caa_error: dns_zone_soa?: false dns_zone_ns?: false ``` github-pages-health-check-1.18.5/config/0000755000004100000410000000000014712471256020011 5ustar www-datawww-datagithub-pages-health-check-1.18.5/config/fastly-ips.txt0000644000004100000410000000050514712471256022645 0ustar www-datawww-data23.235.32.0/20 43.249.72.0/22 103.244.50.0/24 103.245.222.0/23 103.245.224.0/24 104.156.80.0/20 140.248.64.0/18 140.248.128.0/17 146.75.0.0/17 151.101.0.0/16 157.52.64.0/18 167.82.0.0/17 167.82.128.0/20 167.82.160.0/20 167.82.224.0/20 172.111.64.0/18 185.31.16.0/22 199.27.72.0/21 199.232.0.0/16 2a04:4e40::/32 2a04:4e42::/32github-pages-health-check-1.18.5/config/cloudflare-ips.txt0000644000004100000410000000051714712471256023466 0ustar www-datawww-data173.245.48.0/20 103.21.244.0/22 103.22.200.0/22 103.31.4.0/22 141.101.64.0/18 108.162.192.0/18 190.93.240.0/20 188.114.96.0/20 197.234.240.0/22 198.41.128.0/17 162.158.0.0/15 104.16.0.0/13 104.24.0.0/14 172.64.0.0/13 131.0.72.0/22 2400:cb00::/32 2606:4700::/32 2803:f800::/32 2405:b500::/32 2405:8100::/32 2a06:98c0::/29 2c0f:f248::/32github-pages-health-check-1.18.5/.dockerignore0000644000004100000410000000001514712471256021214 0ustar www-datawww-dataGemfile.lock github-pages-health-check-1.18.5/.ruby-version0000644000004100000410000000000414712471256021203 0ustar www-datawww-data3.2 github-pages-health-check-1.18.5/.rubocop.yml0000644000004100000410000000717214712471256021025 0ustar www-datawww-data# Ruby linting configuration. # See https://github.com/styleguide/ruby for the Ruby style guide # We only worry about two kinds of issues: 'error' and anything less than that. # Error is not about severity, but about taste. Simple style choices that # never have a great excuse to be broken, such as 1.9 JSON-like hash syntax, # are errors. Choices that tend to have good exceptions in practice, such as # line length, are warnings. # If you'd like to make changes, a full list of available issues is at # https://github.com/bbatsov/rubocop/blob/master/config/enabled.yml # # A list of configurable issues is at: # https://github.com/bbatsov/rubocop/blob/master/config/default.yml # # If you disable a check, document why. # AllCops: TargetRubyVersion: 3.0 Exclude: - 'bin/**/*' - 'script/**/*' - 'vendor/**/*' - 'test-site/**/*' Layout/EndAlignment: Severity: error Lint/UnreachableCode: Severity: error Style/StringLiteralsInInterpolation: EnforcedStyle: double_quotes Style/HashSyntax: EnforcedStyle: hash_rockets Severity: error Layout/HashAlignment: SupportedLastArgumentHashStyles: always_ignore Layout/ParameterAlignment: Enabled: false # This is usually true, but we often want to roll back to # the start of a line. Style/Attr: Enabled: false # We have no styleguide guidance here, and it seems to be # in frequent use. Style/ClassAndModuleChildren: Enabled: false # module X<\n>module Y is just as good as module X::Y. Layout/LineLength: Max: 120 Severity: warning Exclude: - github-pages-health-check.gemspec - lib/github-pages-health-check/errors/*.rb Metrics/BlockLength: Enabled: false Style/MultilineTernaryOperator: Severity: error Style/AndOr: Severity: error Layout/IndentationWidth: Severity: error Metrics/MethodLength: CountComments: false # count full line comments? Max: 20 Severity: error Exclude: - lib/github-pages-health-check/printer.rb Style/Alias: Enabled: false # We have no guidance on alias vs alias_method Style/RedundantSelf: Enabled: false # Sometimes a self.field is a bit more clear Style/IfUnlessModifier: Enabled: false Naming/FileName: #Rubocop doesn't like the Git*H*ub namespace Enabled: false Metrics/ParameterLists: { Max: 4 } Layout/FirstHashElementIndentation: { EnforcedStyle: consistent } Layout/MultilineMethodCallIndentation: { EnforcedStyle: indented } Layout/MultilineOperationIndentation: { EnforcedStyle: indented } Layout/FirstParameterIndentation: { EnforcedStyle: consistent } Layout/FirstArrayElementIndentation: { EnforcedStyle: consistent } Layout/ExtraSpacing: { AllowForAlignment: true } Style/SignalException: { EnforcedStyle: only_raise } Style/StringLiterals: { EnforcedStyle: double_quotes } Style/PercentLiteralDelimiters: PreferredDelimiters: '%q': '{}' '%Q': '{}' '%r': '{}' '%s': '()' '%w': '()' '%W': '()' '%x': '()' Style/Documentation: Enabled: false Metrics/ClassLength: Exclude: - lib/github-pages-health-check/domain.rb Metrics/CyclomaticComplexity: Max: 9 Exclude: - lib/github-pages-health-check/printer.rb Metrics/PerceivedComplexity: Max: 9 Exclude: - lib/github-pages-health-check/printer.rb Metrics/AbcSize: Max: 17 Exclude: - lib/github-pages-health-check/printer.rb Style/DoubleNegation: Enabled: false Layout/EmptyLineAfterMagicComment: Exclude: - script/* Style/FrozenStringLiteralComment: Enabled: true Severity: error Gemspec/RequiredRubyVersion: Enabled: false Style/HashEachMethods: Enabled: false Style/HashTransformKeys: Enabled: false Style/HashTransformValues: Enabled: false github-pages-health-check-1.18.5/script/0000755000004100000410000000000014712471256020050 5ustar www-datawww-datagithub-pages-health-check-1.18.5/script/release0000755000004100000410000000153114712471256021416 0ustar www-datawww-data#!/bin/sh # Tag and push a release. set -e # Make sure we're in the project root. cd $(dirname "$0")/.. # Make sure the darn thing works bundle update # Build a new gem archive. rm -rf github-pages-health-check-*.gem gem build -q github-pages-health-check.gemspec # Make sure we're on the master branch. (git branch | grep -q '* master') || { echo "Only release from the master branch." exit 1 } # Figure out what version we're releasing. tag=v`ls github-pages-health-check-*.gem | sed 's/^github-pages-health-check-\(.*\)\.gem$/\1/'` # Make sure we haven't released this version before. git fetch -t origin (git tag -l | grep -q "$tag") && { echo "Whoops, there's already a '${tag}' tag." exit 1 } # Tag it and bag it. gem push github-pages-health-check-*.gem && git tag "$tag" && git push origin master && git push origin "$tag" github-pages-health-check-1.18.5/script/cibuild-docker0000755000004100000410000000054014712471256022655 0ustar www-datawww-data#!/bin/bash : ${RUBY_VERSION:="3.2"} docker build -t github-pages-health-check --build-arg RUBY_VERSION=$RUBY_VERSION . if [ -n "$DEBUG" ]; then # Run a shell. docker run -it --rm -v $(pwd):/app/github-pages-health-check github-pages-health-check else # Run CI docker run --rm github-pages-health-check script/cibuild --profile --fail-fast fi github-pages-health-check-1.18.5/script/update-cdn-ips0000755000004100000410000000213014712471256022607 0ustar www-datawww-data#!/usr/bin/env ruby # frozen_string_literal: true # # Usage script/update-ips # updates config/cloudflare-ips.txt and config/fastly-ips.txt require "open-uri" require "json" SOURCES = { :cloudflare => ["https://www.cloudflare.com/ips-v4", "https://www.cloudflare.com/ips-v6"], :fastly => ["https://api.fastly.com/public-ip-list"] }.freeze def parse_fastly(data) json_data = JSON.parse(data) (json_data["addresses"] + json_data["ipv6_addresses"]).join("\n") end def parse_cloudflare(data) data end def fetch_ips_from_cdn(urls) urls.map do |url| puts "Fetching #{url}..." URI.parse(url).open.read end.join("\n") end def update_cdn_file(source, data) file = "config/#{source}-ips.txt" File.write(file, data) puts "Writing contents to #{file} and staging changes." `git add --verbose #{file}` end def parse_cdn_response(source, ips) send("parse_#{source}", ips) end def update_cdn_ips(source, urls) ips = fetch_ips_from_cdn(urls) data = parse_cdn_response(source, ips) update_cdn_file(source, data) end SOURCES.each do |source, urls| update_cdn_ips(source, urls) end github-pages-health-check-1.18.5/script/bootstrap0000755000004100000410000000004314712471256022010 0ustar www-datawww-data#!/bin/sh set -ex bundle install github-pages-health-check-1.18.5/script/test0000755000004100000410000000006314712471256020754 0ustar www-datawww-data#!/bin/sh set -e bundle exec rspec $@ script/fmt github-pages-health-check-1.18.5/script/check-cdn-ips0000755000004100000410000000075714712471256022417 0ustar www-datawww-data#!/bin/bash -e script/update-cdn-ips >/dev/null 2>&1 files=( cloudflare fastly) # `git diff --quiet` suppresses output and sets a return code # 0 - no changes # 1 - changes for file in "${files[@]}" do if git diff -w --quiet --cached "config/$file-ips.txt" then echo "$file IP list is up-to-date." else echo git reset "config/$file-ips.txt" git reset --quiet "config/$file-ips.txt" echo "*** $file IP list is out of date! Run script/update-cdn-ips!" exit 1 fi done github-pages-health-check-1.18.5/script/console0000755000004100000410000000011114712471256021431 0ustar www-datawww-data#!/bin/sh set -ex bundle exec pry -r "./lib/github-pages-health-check" github-pages-health-check-1.18.5/script/check0000755000004100000410000000045014712471256021052 0ustar www-datawww-data#!/usr/bin/env ruby # frozen_string_literal: true # # Usage: script/check [DOMAIN] require "rubygems" require "bundler/setup" require_relative "../lib/github-pages-health-check" if ARGV.count != 1 puts "Usage: script/check [DOMAIN]" exit 1 end puts GitHubPages::HealthCheck.check(ARGV[0]) github-pages-health-check-1.18.5/script/fmt0000755000004100000410000000016014712471256020561 0ustar www-datawww-data#!/bin/bash #/ Usage: script/fmt [args] #/ Runs rubocop with the given arguments. bundle exec rubocop -D -S $@ github-pages-health-check-1.18.5/script/cibuild0000755000004100000410000000040214712471256021405 0ustar www-datawww-data#!/bin/sh set -ex script/bootstrap script/test $@ script/check-cdn-ips bundle exec script/check www.parkermoore.de | grep 'valid?: true' bundle exec script/check ben.balter.com | grep 'valid?: true' bundle exec gem build github-pages-health-check.gemspec