History

Merge branch 'freeCodeCamp:main' into opengl

Suraj Yadav committed 2 years ago

Commit 20840ba2 parents 0b2cde3 7d24d9721 files · +110 −93Browse files

Unified · Split

21 files
  1. .github/workflows/build.yml +1 −1
  2. .github/workflows/schedule-doc-report.yml +1 −1
  3. .github/workflows/test.yml +1 −1
  4. Gemfile +0 −1
  5. Gemfile.lock +0 −4
  6. docs/file-scrapers.md +4 −1
  7. lib/docs/filters/elixir/clean_html.rb +7 −22
  8. lib/docs/filters/elixir/entries.rb +14 −24
  9. lib/docs/filters/qunit/clean_html.rb +1 −1
  10. lib/docs/scrapers/elixir.rb +31 −8
  11. lib/docs/scrapers/fastapi.rb +1 −1
  12. lib/docs/scrapers/laravel.rb +16 −2
  13. lib/docs/scrapers/pandas.rb +23 −0
  14. lib/docs/scrapers/python.rb +3 −3
  15. lib/docs/scrapers/qunit.rb +4 −3
  16. lib/docs/scrapers/rust.rb +1 −1
  17. lib/docs/scrapers/support_tables.rb +1 −1
  18. lib/docs/scrapers/vitest.rb +1 −1
  19. lib/tasks/docs.thor +0 −2
  20. techstack.md +0 −1
  21. techstack.yml +0 −14
.github/workflows/build.yml+1−1
@@ -13,7 +13,7 @@ jobs: · 12 unmodified lines
13 steps:13 steps:
14 - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2.7.014 - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2.7.0
15 - name: Set up Ruby15 - name: Set up Ruby
16 uses: ruby/setup-ruby@161cd54b698f1fb3ea539faab2e036d409550e3c # v1.187.016 uses: ruby/setup-ruby@50ba3386b050ad5b97a41fcb81240cbee1d1821f # v1.188.0
17 with:17 with:
18 bundler-cache: true # runs 'bundle install' and caches installed gems automatically18 bundler-cache: true # runs 'bundle install' and caches installed gems automatically
19 - name: Run tests19 - name: Run tests
.github/workflows/schedule-doc-report.yml+1−1
@@ -11,7 +11,7 @@ jobs: · 10 unmodified lines
11 steps:11 steps:
12 - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2.7.012 - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2.7.0
13 - name: Set up Ruby13 - name: Set up Ruby
14 uses: ruby/setup-ruby@161cd54b698f1fb3ea539faab2e036d409550e3c # v1.187.014 uses: ruby/setup-ruby@50ba3386b050ad5b97a41fcb81240cbee1d1821f # v1.188.0
15 with:15 with:
16 bundler-cache: true # runs 'bundle install' and caches installed gems automatically16 bundler-cache: true # runs 'bundle install' and caches installed gems automatically
17 - name: Generate report17 - name: Generate report
.github/workflows/test.yml+1−1
@@ -11,7 +11,7 @@ jobs: · 10 unmodified lines
11 steps:11 steps:
12 - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.712 - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
13 - name: Set up Ruby13 - name: Set up Ruby
14 uses: ruby/setup-ruby@161cd54b698f1fb3ea539faab2e036d409550e3c # v1.187.014 uses: ruby/setup-ruby@50ba3386b050ad5b97a41fcb81240cbee1d1821f # v1.188.0
15 with:15 with:
16 bundler-cache: true # runs 'bundle install' and caches installed gems automatically16 bundler-cache: true # runs 'bundle install' and caches installed gems automatically
17 - name: Run tests17 - name: Run tests
Gemfile+0−1
@@ -39,7 +39,6 @@ group :development do · 38 unmodified lines
39end39end
40 40
41group :docs do41group :docs do
42 gem 'net-sftp', require: false
43 gem 'progress_bar', require: false42 gem 'progress_bar', require: false
44 gem 'redcarpet'43 gem 'redcarpet'
45 gem 'tty-pager', require: false44 gem 'tty-pager', require: false
Gemfile.lock+0−4
@@ -57,9 +57,6 @@ GEM · 56 unmodified lines
57 mustermann (3.0.0)57 mustermann (3.0.0)
58 ruby2_keywords (~> 0.0.1)58 ruby2_keywords (~> 0.0.1)
59 mutex_m (0.2.0)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 newrelic_rpm (8.16.0)60 newrelic_rpm (8.16.0)
64 nokogiri (1.16.6)61 nokogiri (1.16.6)
65 mini_portile2 (~> 2.8.2)62 mini_portile2 (~> 2.8.2)
@@ -159,7 +156,6 @@ DEPENDENCIES · 93 unmodified lines
159 image_optim156 image_optim
160 image_optim_pack157 image_optim_pack
161 minitest158 minitest
162 net-sftp
163 newrelic_rpm159 newrelic_rpm
164 nokogiri160 nokogiri
165 progress_bar161 progress_bar
docs/file-scrapers.md+4−1
@@ -196,10 +196,13 @@ mv ./usr/share/doc/openjdk-16-jre-headless/api/ docs/openjdk~$VERSION · 195 unmodified lines
196 196
197## Pandas197## Pandas
198 198
199From the home directory; `devdocs`, execute below:
200
199```sh201```sh
200curl https://pandas.pydata.org/docs/pandas.zip | bsdtar --extract --file - --directory=docs/pandas~1202curl 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## PHP206## PHP
204Click 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/`.207Click 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
lib/docs/filters/elixir/clean_html.rb+7−22
@@ -2,32 +2,12 @@ module Docs · 1 unmodified line
2 class Elixir2 class Elixir
3 class CleanHtmlFilter < Filter3 class CleanHtmlFilter < Filter
4 def call4 def call
5 if current_url.path.start_with?('/getting-started')5 api
6 guide
7 else
8 api
9 end
10 doc6 doc
11 end7 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 def api9 def api
28 css('.hover-link', 'footer', ':not(.detail-header) > .view-source').remove10 css('.top-search').remove
29
30 css('h1 .settings').remove
31 11
32 css('.summary').each do |node|12 css('.summary').each do |node|
33 node.name = 'dl'13 node.name = 'dl'
@@ -65,6 +45,11 @@ module Docs · 31 unmodified lines
65 end45 end
66 end46 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 css('pre').each do |node|53 css('pre').each do |node|
69 node['data-language'] = 'elixir'54 node['data-language'] = 'elixir'
70 node.content = node.content55 node.content = node.content
lib/docs/filters/elixir/entries.rb+14−24
@@ -3,25 +3,21 @@ module Docs · 2 unmodified lines
3 class EntriesFilter < Docs::EntriesFilter3 class EntriesFilter < Docs::EntriesFilter
4 def get_name4 def get_name
5 css('h1 .app-vsn').remove5 css('h1 .app-vsn').remove
6 name = (at_css('h1 > span') or at_css('h1')).content.strip6 (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
14 end7 end
15 8
16 def get_type9 def get_type
17 if current_url.path.start_with?('/getting-started')10 section = at_css('h1 a.source').attr('href').match('elixir/pages/([^/]+)/')&.captures&.first
18 if subpath.start_with?('mix-otp')11 if section == "mix-and-otp"
19 'Guide: Mix & OTP'12 return "Mix & OTP"
20 elsif subpath.start_with?('meta')13 elsif section
21 'Guide: Metaprogramming'14 return section.gsub("-", " ").capitalize
22 else15 end
23 'Guide'16
24 end17 name = at_css('h1 span').text
18 case name.split(' ').first
19 when 'mix' then 'Mix Tasks'
20 when 'Changelog' then 'References'
25 else21 else
26 case at_css('h1 small').try(:content)22 case at_css('h1 small').try(:content)
27 when 'exception'23 when 'exception'
@@ -29,19 +25,13 @@ module Docs · 1 unmodified line
29 when 'protocol'25 when 'protocol'
30 'Protocols'26 'Protocols'
31 else27 else
32 if name.start_with?('Phoenix')28 name
33 name.split('.')[0..2].join('.')
34 elsif name.start_with?('mix ')
35 'Mix Tasks'
36 else
37 name.split('.').first
38 end
39 end29 end
40 end30 end
41 end31 end
42 32
43 def additional_entries33 def additional_entries
44 return [] if type == 'Exceptions' || type == 'Guide' || root_page?34 return [] if root_page?
45 35
46 css('.detail-header').map do |node|36 css('.detail-header').map do |node|
47 id = node['id']37 id = node['id']
lib/docs/filters/qunit/clean_html.rb+1−1
@@ -4,7 +4,7 @@ module Docs · 3 unmodified lines
4 class Qunit4 class Qunit
5 class CleanHtmlFilter < Filter5 class CleanHtmlFilter < Filter
6 def call6 def call
7 @doc = at_css('.content[role="main"]')7 @doc = at_css('.content[role="main"] > article')
8 css('.sidebar').remove8 css('.sidebar').remove
9 css('pre').each do |node|9 css('pre').each do |node|
10 node['data-language'] = 'javascript'10 node['data-language'] = 'javascript'
lib/docs/scrapers/elixir.rb+31−8
@@ -4,7 +4,7 @@ module Docs · 3 unmodified lines
4 4
5 self.name = 'Elixir'5 self.name = 'Elixir'
6 self.type = 'elixir'6 self.type = 'elixir'
7 self.root_path = 'api-reference.html'7 self.root_path = 'introduction.html'
8 self.links = {8 self.links = {
9 home: 'https://elixir-lang.org/',9 home: 'https://elixir-lang.org/',
10 code: 'https://github.com/elixir-lang/elixir'10 code: 'https://github.com/elixir-lang/elixir'
@@ -12,27 +12,50 @@ module Docs · 1 unmodified line
12 12
13 html_filters.push 'elixir/clean_html', 'elixir/entries', 'title'13 html_filters.push 'elixir/clean_html', 'elixir/entries', 'title'
14 14
15 options[:container] = ->(filter) {15 options[:container] = '#content'
16 filter.current_url.path.start_with?('/getting-started') ? '#main' : '#content'
17 }
18 options[:title] = false16 options[:title] = false
19 options[:root_title] = 'Elixir'17 options[:root_title] = 'Elixir'
20 18
21 options[:attribution] = <<-HTML19 options[:attribution] = <<-HTML
22 &copy; 2012 Plataformatec<br>20 &copy; 2012-2024 The Elixir Team<br>
23 Licensed under the Apache License, Version 2.0.21 Licensed under the Apache License, Version 2.0.
24 HTML22 HTML
25 23
26 def initial_urls24 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 "https://hexdocs.pm/eex/#{self.class.release}/EEx.html",26 "https://hexdocs.pm/eex/#{self.class.release}/EEx.html",
29 "https://hexdocs.pm/ex_unit/#{self.class.release}/ExUnit.html",27 "https://hexdocs.pm/ex_unit/#{self.class.release}/ExUnit.html",
30 "https://hexdocs.pm/iex/#{self.class.release}/IEx.html",28 "https://hexdocs.pm/iex/#{self.class.release}/IEx.html",
31 "https://hexdocs.pm/logger/#{self.class.release}/Logger.html",29 "https://hexdocs.pm/logger/#{self.class.release}/Logger.html",
32 "https://hexdocs.pm/mix/#{self.class.release}/Mix.html",30 "https://hexdocs.pm/mix/#{self.class.release}/Mix.html" ]
33 "https://elixir-lang.org/getting-started/introduction.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 end43 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 version '1.15' do59 version '1.15' do
37 self.release = '1.15.4'60 self.release = '1.15.4'
38 self.base_urls = [61 self.base_urls = [
lib/docs/scrapers/fastapi.rb+1−1
@@ -2,7 +2,7 @@ module Docs · 1 unmodified line
2 class Fastapi < UrlScraper2 class Fastapi < UrlScraper
3 self.name = 'FastAPI'3 self.name = 'FastAPI'
4 self.type = 'fastapi'4 self.type = 'fastapi'
5 self.release = '0.100.1'5 self.release = '0.111.1'
6 self.base_url = 'https://fastapi.tiangolo.com/'6 self.base_url = 'https://fastapi.tiangolo.com/'
7 self.root_path = '/'7 self.root_path = '/'
8 self.links = {8 self.links = {
lib/docs/scrapers/laravel.rb+16−2
@@ -29,8 +29,22 @@ module Docs · 28 unmodified lines
29 Laravel is a trademark of Taylor Otwell.29 Laravel is a trademark of Taylor Otwell.
30 HTML30 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 version '10' do46 version '10' do
33 self.release = '10.13.0'47 self.release = '10.48.14'
34 self.root_path = '/api/10.x/index.html'48 self.root_path = '/api/10.x/index.html'
35 self.initial_paths = %w(/docs/10.x/installation /api/10.x/classes.html)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 end58 end
45 59
46 version '9' do60 version '9' do
47 self.release = '9.52.8'61 self.release = '9.52.16'
48 self.root_path = '/api/9.x/index.html'62 self.root_path = '/api/9.x/index.html'
49 self.initial_paths = %w(/docs/9.x/installation /api/9.x/classes.html)63 self.initial_paths = %w(/docs/9.x/installation /api/9.x/classes.html)
50 64
lib/docs/scrapers/pandas.rb+23−0
@@ -16,6 +16,29 @@ module Docs · 15 unmodified lines
16 Licensed under the 3-clause BSD License.16 Licensed under the 3-clause BSD License.
17 HTML17 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 version '1' do42 version '1' do
20 self.release = '1.5.0'43 self.release = '1.5.0'
21 self.base_url = "https://pandas.pydata.org/pandas-docs/version/#{self.release}/"44 self.base_url = "https://pandas.pydata.org/pandas-docs/version/#{self.release}/"
lib/docs/scrapers/python.rb+3−3
@@ -1,5 +1,5 @@
1module Docs1module Docs
2 class Python < FileScraper2 class Python < UrlScraper
3 self.type = 'python'3 self.type = 'python'
4 self.root_path = 'index.html'4 self.root_path = 'index.html'
5 self.links = {5 self.links = {
@@ -23,12 +23,12 @@ module Docs · 17 unmodified lines
23 library/sunau.html)23 library/sunau.html)
24 24
25 options[:attribution] = <<-HTML25 options[:attribution] = <<-HTML
26 &copy; 2001&ndash;2023 Python Software Foundation<br>26 &copy; 2001&ndash;2024 Python Software Foundation<br>
27 Licensed under the PSF License.27 Licensed under the PSF License.
28 HTML28 HTML
29 29
30 version '3.12' do30 version '3.12' do
31 self.release = '3.12.1'31 self.release = '3.12.4'
32 self.base_url = "https://docs.python.org/#{self.version}/"32 self.base_url = "https://docs.python.org/#{self.version}/"
33 33
34 html_filters.push 'python/entries_v3', 'sphinx/clean_html', 'python/clean_html'34 html_filters.push 'python/entries_v3', 'sphinx/clean_html', 'python/clean_html'
lib/docs/scrapers/qunit.rb+4−3
@@ -4,8 +4,8 @@ module Docs · 3 unmodified lines
4 class Qunit < UrlScraper4 class Qunit < UrlScraper
5 self.name = 'QUnit'5 self.name = 'QUnit'
6 self.type = 'qunit'6 self.type = 'qunit'
7 self.release = '2.19.3'7 self.release = '2.21.0'
8 self.base_url = 'https://api.qunitjs.com/'8 self.base_url = 'https://qunitjs.com/api/'
9 self.root_path = '/'9 self.root_path = '/'
10 self.links = {10 self.links = {
11 home: 'https://qunitjs.com/',11 home: 'https://qunitjs.com/',
@@ -18,13 +18,14 @@ module Docs · 6 unmodified lines
18 18
19 options[:container] = '.main'19 options[:container] = '.main'
20 options[:skip_patterns] = [20 options[:skip_patterns] = [
21 /deprecated/,
22 /^QUnit$/,21 /^QUnit$/,
23 /^assert$/,22 /^assert$/,
24 /^callbacks$/,23 /^callbacks$/,
25 /^async$/,24 /^async$/,
26 /^config$/,25 /^config$/,
27 /^extension$/,26 /^extension$/,
27 /^deprecated$/,
28 /^removed$/,
28 ]29 ]
29 30
30 options[:attribution] = <<-HTML31 options[:attribution] = <<-HTML
lib/docs/scrapers/rust.rb+1−1
@@ -3,7 +3,7 @@ · 2 unmodified lines
3module Docs3module Docs
4 class Rust < UrlScraper4 class Rust < UrlScraper
5 self.type = 'rust'5 self.type = 'rust'
6 self.release = '1.75.0'6 self.release = '1.79.0'
7 self.base_url = 'https://doc.rust-lang.org/'7 self.base_url = 'https://doc.rust-lang.org/'
8 self.root_path = 'book/index.html'8 self.root_path = 'book/index.html'
9 self.initial_paths = %w(9 self.initial_paths = %w(
lib/docs/scrapers/support_tables.rb+1−1
@@ -7,7 +7,7 @@ module Docs · 6 unmodified lines
7 self.name = 'Support Tables'7 self.name = 'Support Tables'
8 self.slug = 'browser_support_tables'8 self.slug = 'browser_support_tables'
9 self.type = 'support_tables'9 self.type = 'support_tables'
10 self.release = '1.0.30001574'10 self.release = '1.0.30001642'
11 self.base_url = 'https://github.com/Fyrd/caniuse/raw/main/'11 self.base_url = 'https://github.com/Fyrd/caniuse/raw/main/'
12 12
13 # https://github.com/Fyrd/caniuse/blob/main/LICENSE13 # https://github.com/Fyrd/caniuse/blob/main/LICENSE
lib/docs/scrapers/vitest.rb+1−1
@@ -17,7 +17,7 @@ module Docs · 16 unmodified lines
17 Licensed under the MIT License.17 Licensed under the MIT License.
18 HTML18 HTML
19 19
20 self.release = '1.2.2'20 self.release = '2.0.3'
21 self.base_url = 'https://vitest.dev/'21 self.base_url = 'https://vitest.dev/'
22 self.initial_paths = %w(guide/)22 self.initial_paths = %w(guide/)
23 html_filters.push 'vitest/entries', 'vite/clean_html'23 html_filters.push 'vitest/entries', 'vite/clean_html'
lib/tasks/docs.thor+0−2
@@ -162,8 +162,6 @@ class DocsCLI < Thor · 161 unmodified lines
162 option :dryrun, type: :boolean162 option :dryrun, type: :boolean
163 option :packaged, type: :boolean163 option :packaged, type: :boolean
164 def upload(*names)164 def upload(*names)
165 require 'net/sftp'
166
167 if options[:packaged]165 if options[:packaged]
168 slugs = Dir[File.join(Docs.store_path, '*.tar.gz')].map { |f| File.basename(f, '.tar.gz') }166 slugs = Dir[File.join(Docs.store_path, '*.tar.gz')].map { |f| File.basename(f, '.tar.gz') }
169 docs = find_docs_by_slugs(slugs)167 docs = find_docs_by_slugs(slugs)
techstack.md+0−1
@@ -159,7 +159,6 @@ Full tech stack [here](/techstack.md) · 158 unmodified lines
159|[image_optim](https://rubygems.org/image_optim)|v0.31.3|11/14/22|Paul Sernatinger |MIT|N/A|159|[image_optim](https://rubygems.org/image_optim)|v0.31.3|11/14/22|Paul Sernatinger |MIT|N/A|
160|[image_optim_pack](https://rubygems.org/image_optim_pack)|v0.10.1|11/14/22|Paul Sernatinger |MIT|N/A|160|[image_optim_pack](https://rubygems.org/image_optim_pack)|v0.10.1|11/14/22|Paul Sernatinger |MIT|N/A|
161|[minitest](https://rubygems.org/minitest)|v5.21.2|01/03/15|Thibaut |MIT|N/A|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|[newrelic_rpm](https://rubygems.org/newrelic_rpm)|v8.16.0|03/24/18|Thibaut Courouble |Apache-2.0|N/A|162|[newrelic_rpm](https://rubygems.org/newrelic_rpm)|v8.16.0|03/24/18|Thibaut Courouble |Apache-2.0|N/A|
164|[nokogiri](https://rubygems.org/nokogiri)|v1.16.0|10/21/18|Thibaut Courouble |MIT|N/A|163|[nokogiri](https://rubygems.org/nokogiri)|v1.16.0|10/21/18|Thibaut Courouble |MIT|N/A|
165|[progress_bar](https://rubygems.org/progress_bar)|v1.3.3|01/26/14|Thibaut |WTFPL|N/A|164|[progress_bar](https://rubygems.org/progress_bar)|v1.3.3|01/26/14|Thibaut |WTFPL|N/A|
techstack.yml+0−14
@@ -268,20 +268,6 @@ tools: · 267 unmodified lines
268 detection_source: Gemfile268 detection_source: Gemfile
269 last_updated_by: Thibaut269 last_updated_by: Thibaut
270 last_updated_on: 2015-01-03 15:38:22.000000000 Z270 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- name: newrelic_rpm271- name: newrelic_rpm
286 description: New Relic is a performance management system, developed by New Relic,272 description: New Relic is a performance management system, developed by New Relic,
287 Inc273 Inc