Unified · Split
| @@ -13,7 +13,7 @@ jobs: · 12 unmodified lines | ||
| 13 | 13 | steps: |
| 14 | 14 | - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2.7.0 |
| 15 | 15 | - name: Set up Ruby |
| 16 | − uses: ruby/setup-ruby@161cd54b698f1fb3ea539faab2e036d409550e3c # v1.187.0 | |
| 16 | + uses: ruby/setup-ruby@50ba3386b050ad5b97a41fcb81240cbee1d1821f # v1.188.0 | |
| 17 | 17 | with: |
| 18 | 18 | bundler-cache: true # runs 'bundle install' and caches installed gems automatically |
| 19 | 19 | - name: Run tests |
| @@ -11,7 +11,7 @@ jobs: · 10 unmodified lines | ||
| 11 | 11 | steps: |
| 12 | 12 | - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2.7.0 |
| 13 | 13 | - name: Set up Ruby |
| 14 | − uses: ruby/setup-ruby@161cd54b698f1fb3ea539faab2e036d409550e3c # v1.187.0 | |
| 14 | + uses: ruby/setup-ruby@50ba3386b050ad5b97a41fcb81240cbee1d1821f # v1.188.0 | |
| 15 | 15 | with: |
| 16 | 16 | bundler-cache: true # runs 'bundle install' and caches installed gems automatically |
| 17 | 17 | - name: Generate report |
| @@ -11,7 +11,7 @@ jobs: · 10 unmodified lines | ||
| 11 | 11 | steps: |
| 12 | 12 | - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 |
| 13 | 13 | - name: Set up Ruby |
| 14 | − uses: ruby/setup-ruby@161cd54b698f1fb3ea539faab2e036d409550e3c # v1.187.0 | |
| 14 | + uses: ruby/setup-ruby@50ba3386b050ad5b97a41fcb81240cbee1d1821f # v1.188.0 | |
| 15 | 15 | with: |
| 16 | 16 | bundler-cache: true # runs 'bundle install' and caches installed gems automatically |
| 17 | 17 | - name: Run tests |
| @@ -39,7 +39,6 @@ group :development do · 38 unmodified lines | ||
| 39 | 39 | end |
| 40 | 40 | |
| 41 | 41 | group :docs do |
| 42 | − gem 'net-sftp', require: false | |
| 43 | 42 | gem 'progress_bar', require: false |
| 44 | 43 | gem 'redcarpet' |
| 45 | 44 | gem 'tty-pager', require: false |
| @@ -57,9 +57,6 @@ GEM · 56 unmodified lines | ||
| 57 | 57 | mustermann (3.0.0) |
| 58 | 58 | ruby2_keywords (~> 0.0.1) |
| 59 | 59 | mutex_m (0.2.0) |
| 60 | − net-sftp (4.0.0) | |
| 61 | − net-ssh (>= 5.0.0, < 8.0.0) | |
| 62 | − net-ssh (7.0.1) | |
| 63 | 60 | newrelic_rpm (8.16.0) |
| 64 | 61 | nokogiri (1.16.6) |
| 65 | 62 | mini_portile2 (~> 2.8.2) |
| @@ -159,7 +156,6 @@ DEPENDENCIES · 93 unmodified lines | ||
| 159 | 156 | image_optim |
| 160 | 157 | image_optim_pack |
| 161 | 158 | minitest |
| 162 | − net-sftp | |
| 163 | 159 | newrelic_rpm |
| 164 | 160 | nokogiri |
| 165 | 161 | progress_bar |
| @@ -196,10 +196,13 @@ mv ./usr/share/doc/openjdk-16-jre-headless/api/ docs/openjdk~$VERSION · 195 unmodified lines | ||
| 196 | 196 | |
| 197 | 197 | ## Pandas |
| 198 | 198 | |
| 199 | +From the home directory; `devdocs`, execute below: | |
| 200 | + | |
| 199 | 201 | ```sh |
| 200 | −curl https://pandas.pydata.org/docs/pandas.zip | bsdtar --extract --file - --directory=docs/pandas~1 | |
| 202 | +curl https://pandas.pydata.org/docs/pandas.zip -o tmp.zip && unzip tmp.zip -d docs/pandas~2 && rm tmp.zip | |
| 201 | 203 | ``` |
| 202 | 204 | |
| 205 | + | |
| 203 | 206 | ## PHP |
| 204 | 207 | Click the link under the "Many HTML files" column on https://www.php.net/download-docs.php, extract the tarball, change its name to `php` and put it in `docs/`. |
| 205 | 208 | |
| @@ -2,32 +2,12 @@ module Docs · 1 unmodified line | ||
| 2 | 2 | class Elixir |
| 3 | 3 | class CleanHtmlFilter < Filter |
| 4 | 4 | def call |
| 5 | − if current_url.path.start_with?('/getting-started') | |
| 6 | − guide | |
| 7 | − else | |
| 8 | − api | |
| 9 | − end | |
| 5 | + api | |
| 10 | 6 | doc |
| 11 | 7 | end |
| 12 | 8 | |
| 13 | − def guide | |
| 14 | − @doc = at_css('#content article') | |
| 15 | − | |
| 16 | − css('pre > code').each do |node| | |
| 17 | − node.parent.content = node.content | |
| 18 | − end | |
| 19 | − | |
| 20 | − css('div > pre.highlight').each do |node| | |
| 21 | − node.content = node.content | |
| 22 | − node['data-language'] = node.parent['class'][/language-(\w+)/, 1] | |
| 23 | − node.parent.before(node).remove | |
| 24 | − end | |
| 25 | − end | |
| 26 | − | |
| 27 | 9 | def api |
| 28 | − css('.hover-link', 'footer', ':not(.detail-header) > .view-source').remove | |
| 29 | − | |
| 30 | − css('h1 .settings').remove | |
| 10 | + css('.top-search').remove | |
| 31 | 11 | |
| 32 | 12 | css('.summary').each do |node| |
| 33 | 13 | node.name = 'dl' |
| @@ -65,6 +45,11 @@ module Docs · 31 unmodified lines | ||
| 65 | 45 | end |
| 66 | 46 | end |
| 67 | 47 | |
| 48 | + css('h1 a.icon-action[title="View Source"]').each do |node| | |
| 49 | + node['class'] = 'source' | |
| 50 | + node.content = "Source" | |
| 51 | + end | |
| 52 | + | |
| 68 | 53 | css('pre').each do |node| |
| 69 | 54 | node['data-language'] = 'elixir' |
| 70 | 55 | node.content = node.content |
| @@ -3,25 +3,21 @@ module Docs · 2 unmodified lines | ||
| 3 | 3 | class EntriesFilter < Docs::EntriesFilter |
| 4 | 4 | def get_name |
| 5 | 5 | css('h1 .app-vsn').remove |
| 6 | − name = (at_css('h1 > span') or at_css('h1')).content.strip | |
| 7 | − | |
| 8 | − if current_url.path.start_with?('/getting-started') | |
| 9 | − name.remove(/\.\z/) | |
| 10 | − else | |
| 11 | − name = name.split(' ').first unless name.start_with?('mix ') # ecto | |
| 12 | − name | |
| 13 | − end | |
| 6 | + (at_css('h1 > span') or at_css('h1')).content.strip | |
| 14 | 7 | end |
| 15 | 8 | |
| 16 | 9 | def get_type |
| 17 | − if current_url.path.start_with?('/getting-started') | |
| 18 | − if subpath.start_with?('mix-otp') | |
| 19 | − 'Guide: Mix & OTP' | |
| 20 | − elsif subpath.start_with?('meta') | |
| 21 | − 'Guide: Metaprogramming' | |
| 22 | − else | |
| 23 | − 'Guide' | |
| 24 | − end | |
| 10 | + section = at_css('h1 a.source').attr('href').match('elixir/pages/([^/]+)/')&.captures&.first | |
| 11 | + if section == "mix-and-otp" | |
| 12 | + return "Mix & OTP" | |
| 13 | + elsif section | |
| 14 | + return section.gsub("-", " ").capitalize | |
| 15 | + end | |
| 16 | + | |
| 17 | + name = at_css('h1 span').text | |
| 18 | + case name.split(' ').first | |
| 19 | + when 'mix' then 'Mix Tasks' | |
| 20 | + when 'Changelog' then 'References' | |
| 25 | 21 | else |
| 26 | 22 | case at_css('h1 small').try(:content) |
| 27 | 23 | when 'exception' |
| @@ -29,19 +25,13 @@ module Docs · 1 unmodified line | ||
| 29 | 25 | when 'protocol' |
| 30 | 26 | 'Protocols' |
| 31 | 27 | else |
| 32 | − if name.start_with?('Phoenix') | |
| 33 | − name.split('.')[0..2].join('.') | |
| 34 | − elsif name.start_with?('mix ') | |
| 35 | − 'Mix Tasks' | |
| 36 | − else | |
| 37 | − name.split('.').first | |
| 38 | − end | |
| 28 | + name | |
| 39 | 29 | end |
| 40 | 30 | end |
| 41 | 31 | end |
| 42 | 32 | |
| 43 | 33 | def additional_entries |
| 44 | − return [] if type == 'Exceptions' || type == 'Guide' || root_page? | |
| 34 | + return [] if root_page? | |
| 45 | 35 | |
| 46 | 36 | css('.detail-header').map do |node| |
| 47 | 37 | id = node['id'] |
| @@ -4,7 +4,7 @@ module Docs · 3 unmodified lines | ||
| 4 | 4 | class Qunit |
| 5 | 5 | class CleanHtmlFilter < Filter |
| 6 | 6 | def call |
| 7 | − @doc = at_css('.content[role="main"]') | |
| 7 | + @doc = at_css('.content[role="main"] > article') | |
| 8 | 8 | css('.sidebar').remove |
| 9 | 9 | css('pre').each do |node| |
| 10 | 10 | node['data-language'] = 'javascript' |
| @@ -4,7 +4,7 @@ module Docs · 3 unmodified lines | ||
| 4 | 4 | |
| 5 | 5 | self.name = 'Elixir' |
| 6 | 6 | self.type = 'elixir' |
| 7 | − self.root_path = 'api-reference.html' | |
| 7 | + self.root_path = 'introduction.html' | |
| 8 | 8 | self.links = { |
| 9 | 9 | home: 'https://elixir-lang.org/', |
| 10 | 10 | code: 'https://github.com/elixir-lang/elixir' |
| @@ -12,27 +12,50 @@ module Docs · 1 unmodified line | ||
| 12 | 12 | |
| 13 | 13 | html_filters.push 'elixir/clean_html', 'elixir/entries', 'title' |
| 14 | 14 | |
| 15 | − options[:container] = ->(filter) { | |
| 16 | − filter.current_url.path.start_with?('/getting-started') ? '#main' : '#content' | |
| 17 | − } | |
| 15 | + options[:container] = '#content' | |
| 18 | 16 | options[:title] = false |
| 19 | 17 | options[:root_title] = 'Elixir' |
| 20 | 18 | |
| 21 | 19 | options[:attribution] = <<-HTML |
| 22 | − © 2012 Plataformatec<br> | |
| 20 | + © 2012-2024 The Elixir Team<br> | |
| 23 | 21 | Licensed under the Apache License, Version 2.0. |
| 24 | 22 | HTML |
| 25 | 23 | |
| 26 | 24 | def initial_urls |
| 27 | − [ "https://hexdocs.pm/elixir/#{self.class.release}/api-reference.html", | |
| 25 | + [ "https://hexdocs.pm/elixir/#{self.class.release}/introduction.html", | |
| 28 | 26 | "https://hexdocs.pm/eex/#{self.class.release}/EEx.html", |
| 29 | 27 | "https://hexdocs.pm/ex_unit/#{self.class.release}/ExUnit.html", |
| 30 | 28 | "https://hexdocs.pm/iex/#{self.class.release}/IEx.html", |
| 31 | 29 | "https://hexdocs.pm/logger/#{self.class.release}/Logger.html", |
| 32 | − "https://hexdocs.pm/mix/#{self.class.release}/Mix.html", | |
| 33 | − "https://elixir-lang.org/getting-started/introduction.html" ] | |
| 30 | + "https://hexdocs.pm/mix/#{self.class.release}/Mix.html" ] | |
| 31 | + end | |
| 32 | + | |
| 33 | + version '1.17' do | |
| 34 | + self.release = '1.17.2' | |
| 35 | + self.base_urls = [ | |
| 36 | + "https://hexdocs.pm/elixir/#{release}/", | |
| 37 | + "https://hexdocs.pm/eex/#{release}/", | |
| 38 | + "https://hexdocs.pm/ex_unit/#{release}/", | |
| 39 | + "https://hexdocs.pm/iex/#{release}/", | |
| 40 | + "https://hexdocs.pm/logger/#{release}/", | |
| 41 | + "https://hexdocs.pm/mix/#{release}/" | |
| 42 | + ] | |
| 34 | 43 | end |
| 35 | 44 | |
| 45 | + version '1.16' do | |
| 46 | + self.release = '1.16.3' | |
| 47 | + self.base_urls = [ | |
| 48 | + "https://hexdocs.pm/elixir/#{release}/", | |
| 49 | + "https://hexdocs.pm/eex/#{release}/", | |
| 50 | + "https://hexdocs.pm/ex_unit/#{release}/", | |
| 51 | + "https://hexdocs.pm/iex/#{release}/", | |
| 52 | + "https://hexdocs.pm/logger/#{release}/", | |
| 53 | + "https://hexdocs.pm/mix/#{release}/" | |
| 54 | + ] | |
| 55 | + end | |
| 56 | + | |
| 57 | + # scraping of older versions is no longer supported! | |
| 58 | + | |
| 36 | 59 | version '1.15' do |
| 37 | 60 | self.release = '1.15.4' |
| 38 | 61 | self.base_urls = [ |
| @@ -2,7 +2,7 @@ module Docs · 1 unmodified line | ||
| 2 | 2 | class Fastapi < UrlScraper |
| 3 | 3 | self.name = 'FastAPI' |
| 4 | 4 | self.type = 'fastapi' |
| 5 | − self.release = '0.100.1' | |
| 5 | + self.release = '0.111.1' | |
| 6 | 6 | self.base_url = 'https://fastapi.tiangolo.com/' |
| 7 | 7 | self.root_path = '/' |
| 8 | 8 | self.links = { |
| @@ -29,8 +29,22 @@ module Docs · 28 unmodified lines | ||
| 29 | 29 | Laravel is a trademark of Taylor Otwell. |
| 30 | 30 | HTML |
| 31 | 31 | |
| 32 | + version '11' do | |
| 33 | + self.release = '11.11.1' | |
| 34 | + self.root_path = '/api/11.x/index.html' | |
| 35 | + self.initial_paths = %w(/docs/11.x/installation /api/11.x/classes.html) | |
| 36 | + | |
| 37 | + options[:only_patterns] = [%r{\A/api/11\.x/}, %r{\A/docs/11\.x/}] | |
| 38 | + | |
| 39 | + options[:fix_urls] = ->(url) do | |
| 40 | + url.sub! %r{11.x/+}, "11.x/" | |
| 41 | + url.sub! %r{#{Regexp.escape(Laravel.base_url)}/docs\/(?![1-9]?\d)}, "#{Laravel.base_url}/docs/11.x/" | |
| 42 | + url | |
| 43 | + end | |
| 44 | + end | |
| 45 | + | |
| 32 | 46 | version '10' do |
| 33 | − self.release = '10.13.0' | |
| 47 | + self.release = '10.48.14' | |
| 34 | 48 | self.root_path = '/api/10.x/index.html' |
| 35 | 49 | self.initial_paths = %w(/docs/10.x/installation /api/10.x/classes.html) |
| 36 | 50 | |
| @@ -44,7 +58,7 @@ module Docs · 7 unmodified lines | ||
| 44 | 58 | end |
| 45 | 59 | |
| 46 | 60 | version '9' do |
| 47 | − self.release = '9.52.8' | |
| 61 | + self.release = '9.52.16' | |
| 48 | 62 | self.root_path = '/api/9.x/index.html' |
| 49 | 63 | self.initial_paths = %w(/docs/9.x/installation /api/9.x/classes.html) |
| 50 | 64 | |
| @@ -16,6 +16,29 @@ module Docs · 15 unmodified lines | ||
| 16 | 16 | Licensed under the 3-clause BSD License. |
| 17 | 17 | HTML |
| 18 | 18 | |
| 19 | + version '2' do | |
| 20 | + self.release = '2.2.2' | |
| 21 | + self.base_url = "https://pandas.pydata.org/pandas-docs/version/#{self.release}/" | |
| 22 | + | |
| 23 | + html_filters.push 'pandas/clean_html', 'pandas/entries' | |
| 24 | + | |
| 25 | + options[:container] = 'main section' | |
| 26 | + | |
| 27 | + options[:skip_patterns] = [ | |
| 28 | + /development/, | |
| 29 | + /getting_started/, | |
| 30 | + /whatsnew/ | |
| 31 | + ] | |
| 32 | + | |
| 33 | + options[:skip] = [ | |
| 34 | + 'panel.html', | |
| 35 | + 'pandas.pdf', | |
| 36 | + 'pandas.zip', | |
| 37 | + 'ecosystem.html' | |
| 38 | + ] | |
| 39 | + | |
| 40 | + end | |
| 41 | + | |
| 19 | 42 | version '1' do |
| 20 | 43 | self.release = '1.5.0' |
| 21 | 44 | self.base_url = "https://pandas.pydata.org/pandas-docs/version/#{self.release}/" |
| @@ -1,5 +1,5 @@ | ||
| 1 | 1 | module Docs |
| 2 | − class Python < FileScraper | |
| 2 | + class Python < UrlScraper | |
| 3 | 3 | self.type = 'python' |
| 4 | 4 | self.root_path = 'index.html' |
| 5 | 5 | self.links = { |
| @@ -23,12 +23,12 @@ module Docs · 17 unmodified lines | ||
| 23 | 23 | library/sunau.html) |
| 24 | 24 | |
| 25 | 25 | options[:attribution] = <<-HTML |
| 26 | − © 2001–2023 Python Software Foundation<br> | |
| 26 | + © 2001–2024 Python Software Foundation<br> | |
| 27 | 27 | Licensed under the PSF License. |
| 28 | 28 | HTML |
| 29 | 29 | |
| 30 | 30 | version '3.12' do |
| 31 | − self.release = '3.12.1' | |
| 31 | + self.release = '3.12.4' | |
| 32 | 32 | self.base_url = "https://docs.python.org/#{self.version}/" |
| 33 | 33 | |
| 34 | 34 | html_filters.push 'python/entries_v3', 'sphinx/clean_html', 'python/clean_html' |
| @@ -4,8 +4,8 @@ module Docs · 3 unmodified lines | ||
| 4 | 4 | class Qunit < UrlScraper |
| 5 | 5 | self.name = 'QUnit' |
| 6 | 6 | self.type = 'qunit' |
| 7 | − self.release = '2.19.3' | |
| 8 | − self.base_url = 'https://api.qunitjs.com/' | |
| 7 | + self.release = '2.21.0' | |
| 8 | + self.base_url = 'https://qunitjs.com/api/' | |
| 9 | 9 | self.root_path = '/' |
| 10 | 10 | self.links = { |
| 11 | 11 | home: 'https://qunitjs.com/', |
| @@ -18,13 +18,14 @@ module Docs · 6 unmodified lines | ||
| 18 | 18 | |
| 19 | 19 | options[:container] = '.main' |
| 20 | 20 | options[:skip_patterns] = [ |
| 21 | − /deprecated/, | |
| 22 | 21 | /^QUnit$/, |
| 23 | 22 | /^assert$/, |
| 24 | 23 | /^callbacks$/, |
| 25 | 24 | /^async$/, |
| 26 | 25 | /^config$/, |
| 27 | 26 | /^extension$/, |
| 27 | + /^deprecated$/, | |
| 28 | + /^removed$/, | |
| 28 | 29 | ] |
| 29 | 30 | |
| 30 | 31 | options[:attribution] = <<-HTML |
| @@ -3,7 +3,7 @@ · 2 unmodified lines | ||
| 3 | 3 | module Docs |
| 4 | 4 | class Rust < UrlScraper |
| 5 | 5 | self.type = 'rust' |
| 6 | − self.release = '1.75.0' | |
| 6 | + self.release = '1.79.0' | |
| 7 | 7 | self.base_url = 'https://doc.rust-lang.org/' |
| 8 | 8 | self.root_path = 'book/index.html' |
| 9 | 9 | self.initial_paths = %w( |
| @@ -7,7 +7,7 @@ module Docs · 6 unmodified lines | ||
| 7 | 7 | self.name = 'Support Tables' |
| 8 | 8 | self.slug = 'browser_support_tables' |
| 9 | 9 | self.type = 'support_tables' |
| 10 | − self.release = '1.0.30001574' | |
| 10 | + self.release = '1.0.30001642' | |
| 11 | 11 | self.base_url = 'https://github.com/Fyrd/caniuse/raw/main/' |
| 12 | 12 | |
| 13 | 13 | # https://github.com/Fyrd/caniuse/blob/main/LICENSE |
| @@ -17,7 +17,7 @@ module Docs · 16 unmodified lines | ||
| 17 | 17 | Licensed under the MIT License. |
| 18 | 18 | HTML |
| 19 | 19 | |
| 20 | − self.release = '1.2.2' | |
| 20 | + self.release = '2.0.3' | |
| 21 | 21 | self.base_url = 'https://vitest.dev/' |
| 22 | 22 | self.initial_paths = %w(guide/) |
| 23 | 23 | html_filters.push 'vitest/entries', 'vite/clean_html' |
| @@ -162,8 +162,6 @@ class DocsCLI < Thor · 161 unmodified lines | ||
| 162 | 162 | option :dryrun, type: :boolean |
| 163 | 163 | option :packaged, type: :boolean |
| 164 | 164 | def upload(*names) |
| 165 | − require 'net/sftp' | |
| 166 | − | |
| 167 | 165 | if options[:packaged] |
| 168 | 166 | slugs = Dir[File.join(Docs.store_path, '*.tar.gz')].map { |f| File.basename(f, '.tar.gz') } |
| 169 | 167 | docs = find_docs_by_slugs(slugs) |
| @@ -159,7 +159,6 @@ Full tech stack [here](/techstack.md) · 158 unmodified lines | ||
| 159 | 159 | |[image_optim](https://rubygems.org/image_optim)|v0.31.3|11/14/22|Paul Sernatinger |MIT|N/A| |
| 160 | 160 | |[image_optim_pack](https://rubygems.org/image_optim_pack)|v0.10.1|11/14/22|Paul Sernatinger |MIT|N/A| |
| 161 | 161 | |[minitest](https://rubygems.org/minitest)|v5.21.2|01/03/15|Thibaut |MIT|N/A| |
| 162 | −|[net-sftp](https://rubygems.org/net-sftp)|v4.0.0|11/14/22|Paul Sernatinger |MIT|N/A| | |
| 163 | 162 | |[newrelic_rpm](https://rubygems.org/newrelic_rpm)|v8.16.0|03/24/18|Thibaut Courouble |Apache-2.0|N/A| |
| 164 | 163 | |[nokogiri](https://rubygems.org/nokogiri)|v1.16.0|10/21/18|Thibaut Courouble |MIT|N/A| |
| 165 | 164 | |[progress_bar](https://rubygems.org/progress_bar)|v1.3.3|01/26/14|Thibaut |WTFPL|N/A| |
| @@ -268,20 +268,6 @@ tools: · 267 unmodified lines | ||
| 268 | 268 | detection_source: Gemfile |
| 269 | 269 | last_updated_by: Thibaut |
| 270 | 270 | last_updated_on: 2015-01-03 15:38:22.000000000 Z |
| 271 | −- name: net-sftp | |
| 272 | − description: A pure Ruby implementation of the SFTP client protocol | |
| 273 | − package_url: https://rubygems.org/net-sftp | |
| 274 | − version: 4.0.0 | |
| 275 | − license: MIT | |
| 276 | − open_source: true | |
| 277 | − hosted_saas: false | |
| 278 | − category: Libraries | |
| 279 | − sub_category: RubyGems Packages | |
| 280 | − image_url: https://img.stackshare.io/package/19106/default_92b412aaa5197ea1fc2d70a503018e4186714110.png | |
| 281 | − detection_source_url: https://github.com/freeCodeCamp/devdocs/blob/main/Gemfile.lock | |
| 282 | − detection_source: Gemfile | |
| 283 | − last_updated_by: Paul Sernatinger | |
| 284 | − last_updated_on: 2022-11-14 14:30:30.000000000 Z | |
| 285 | 271 | - name: newrelic_rpm |
| 286 | 272 | description: New Relic is a performance management system, developed by New Relic, |
| 287 | 273 | Inc |