├── log └── .keep ├── tmp └── .keep ├── vendor └── .keep ├── lib ├── assets │ └── .keep └── tasks │ └── .keep ├── public ├── favicon.ico ├── apple-touch-icon.png ├── apple-touch-icon-precomposed.png ├── robots.txt ├── 500.html ├── 422.html └── 404.html ├── test ├── helpers │ └── .keep ├── mailers │ └── .keep ├── models │ └── .keep ├── system │ └── .keep ├── controllers │ └── .keep ├── fixtures │ ├── .keep │ └── files │ │ └── .keep ├── integration │ └── .keep ├── application_system_test_case.rb └── test_helper.rb ├── .ruby-version ├── app ├── assets │ ├── images │ │ ├── .keep │ │ └── og-image.png │ ├── config │ │ └── manifest.js │ ├── javascripts │ │ ├── package_form_validation.js │ │ └── application.js │ └── stylesheets │ │ └── application.css ├── models │ ├── concerns │ │ └── .keep │ ├── application_record.rb │ ├── project_package.rb │ ├── project.rb │ ├── package_source.rb │ ├── metric.rb │ ├── packary │ │ ├── metrics │ │ │ ├── registry_package │ │ │ │ ├── releases_metric.rb │ │ │ │ ├── downloads_metric.rb │ │ │ │ ├── latest_release_metric.rb │ │ │ │ └── releases_in_metric.rb │ │ │ └── repository │ │ │ │ ├── contributors_metric.rb │ │ │ │ ├── status_metric.rb │ │ │ │ ├── star_metric.rb │ │ │ │ ├── last_commit_metric.rb │ │ │ │ └── last_closed_issue_metric.rb │ │ ├── metric.rb │ │ ├── repository.rb │ │ ├── cis │ │ │ └── travis_ci.rb │ │ ├── registry_packages │ │ │ ├── rubygems_package.rb │ │ │ └── npm_package.rb │ │ └── repositories │ │ │ └── github_repository.rb │ ├── dummy_project.rb │ ├── package.rb │ └── metrics_fetcher.rb ├── controllers │ ├── concerns │ │ └── .keep │ ├── application_controller.rb │ ├── top_pages_controller.rb │ ├── packages_controller.rb │ └── projects_controller.rb ├── views │ ├── layouts │ │ ├── mailer.text.erb │ │ ├── _footer.html.erb │ │ ├── mailer.html.erb │ │ ├── _navigation.html.erb │ │ └── application.html.erb │ ├── packages │ │ └── show.html.erb │ ├── projects │ │ ├── show.html.erb │ │ ├── _detail.html.erb │ │ └── _package.html.erb │ ├── kaminari │ │ ├── _gap.html.erb │ │ ├── _last_page.html.erb │ │ ├── _first_page.html.erb │ │ ├── _next_page.html.erb │ │ ├── _prev_page.html.erb │ │ ├── _page.html.erb │ │ └── _paginator.html.erb │ └── top_pages │ │ └── index.html.erb ├── jobs │ └── application_job.rb ├── workers │ ├── application_worker.rb │ └── fetch_metrics_worker.rb ├── mailers │ └── application_mailer.rb └── helpers │ ├── packages_helper.rb │ └── application_helper.rb ├── .rspec ├── config ├── sidekiq.yml ├── initializers │ ├── sidekiq.rb │ ├── mime_types.rb │ ├── filter_parameter_logging.rb │ ├── application_controller_renderer.rb │ ├── cookies_serializer.rb │ ├── backtrace_silencers.rb │ ├── wrap_parameters.rb │ ├── assets.rb │ ├── inflections.rb │ ├── content_security_policy.rb │ └── meta_tags.rb ├── environment.rb ├── boot.rb ├── credentials.yml.enc ├── routes.rb ├── database.yml ├── locales │ └── en.yml ├── storage.yml ├── application.rb ├── puma.rb └── environments │ ├── test.rb │ ├── development.rb │ └── production.rb ├── package.json ├── bin ├── rake ├── rails ├── yarn ├── erd ├── sidekiq ├── sidekiqctl ├── update ├── setup └── bundle ├── config.ru ├── Procfile ├── spec ├── models │ ├── metric_spec.rb │ ├── package_spec.rb │ ├── project_spec.rb │ ├── package_source_spec.rb │ └── project_package_spec.rb ├── system │ └── top_pages_spec.rb ├── controllers │ └── top_pages_controller_spec.rb ├── support │ └── capybara.rb ├── rails_helper.rb └── spec_helper.rb ├── db ├── migrate │ ├── 20180315153147_enable_extension_for_uuid.rb │ ├── 20180315154301_create_projects.rb │ ├── 20180428144053_add_expired_at_to_package_source.rb │ ├── 20180418154130_add_metric_type_and_value_to_metric.rb │ ├── 20180418123357_create_metrics.rb │ ├── 20180315161352_create_packages.rb │ ├── 20180315161622_create_project_packages.rb │ └── 20180418143357_create_package_sources.rb ├── seeds.rb ├── static │ ├── mastodon-rubygems.list │ └── mastodon-npm.list └── schema.rb ├── Rakefile ├── docker-compose.yml ├── README.md ├── .gitignore ├── Gemfile └── Gemfile.lock /log/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tmp/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vendor/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/assets/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/tasks/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/helpers/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/mailers/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/models/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/system/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.6.5 2 | -------------------------------------------------------------------------------- /app/assets/images/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/controllers/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/integration/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/models/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/files/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --require spec_helper 2 | -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /config/sidekiq.yml: -------------------------------------------------------------------------------- 1 | :concurrency: 1 2 | -------------------------------------------------------------------------------- /public/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.text.erb: -------------------------------------------------------------------------------- 1 | <%= yield %> 2 | -------------------------------------------------------------------------------- /config/initializers/sidekiq.rb: -------------------------------------------------------------------------------- 1 | require 'sidekiq/web' 2 | -------------------------------------------------------------------------------- /app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base 2 | end 3 | -------------------------------------------------------------------------------- /app/workers/application_worker.rb: -------------------------------------------------------------------------------- 1 | class ApplicationWorker 2 | include Sidekiq::Worker 3 | end 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pkgstatus", 3 | "private": true, 4 | "dependencies": {} 5 | } 6 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | end 3 | -------------------------------------------------------------------------------- /app/assets/images/og-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meganemura/pkgstatus/HEAD/app/assets/images/og-image.png -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../config/boot' 3 | require 'rake' 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | self.abstract_class = true 3 | end 4 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | -------------------------------------------------------------------------------- /app/controllers/top_pages_controller.rb: -------------------------------------------------------------------------------- 1 | class TopPagesController < ApplicationController 2 | def index 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/models/project_package.rb: -------------------------------------------------------------------------------- 1 | class ProjectPackage < ApplicationRecord 2 | belongs_to :project 3 | belongs_to :package 4 | end 5 | -------------------------------------------------------------------------------- /app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_tree ../images 2 | //= link_directory ../javascripts .js 3 | //= link_directory ../stylesheets .css 4 | -------------------------------------------------------------------------------- /app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: 'from@example.com' 3 | layout 'mailer' 4 | end 5 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require_relative 'config/environment' 4 | 5 | run Rails.application 6 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path('../config/application', __dir__) 3 | require_relative '../config/boot' 4 | require 'rails/commands' 5 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | release: bundle exec rails db:migrate 2 | web: bundle exec puma -R config.ru -C config/puma.rb -p $PORT 3 | worker: bundle exec sidekiq -C config/sidekiq.yml 4 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative 'application' 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /spec/models/metric_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe Metric, type: :model do 4 | pending "add some examples to (or delete) #{__FILE__}" 5 | end 6 | -------------------------------------------------------------------------------- /spec/models/package_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe Package, type: :model do 4 | pending "add some examples to (or delete) #{__FILE__}" 5 | end 6 | -------------------------------------------------------------------------------- /spec/models/project_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe Project, type: :model do 4 | pending "add some examples to (or delete) #{__FILE__}" 5 | end 6 | -------------------------------------------------------------------------------- /app/models/project.rb: -------------------------------------------------------------------------------- 1 | class Project < ApplicationRecord 2 | has_many :project_packages, dependent: :destroy 3 | has_many :packages, through: :project_packages 4 | end 5 | -------------------------------------------------------------------------------- /spec/models/package_source_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe PackageSource, type: :model do 4 | pending "add some examples to (or delete) #{__FILE__}" 5 | end 6 | -------------------------------------------------------------------------------- /spec/models/project_package_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe ProjectPackage, type: :model do 4 | pending "add some examples to (or delete) #{__FILE__}" 5 | end 6 | -------------------------------------------------------------------------------- /config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | -------------------------------------------------------------------------------- /app/controllers/packages_controller.rb: -------------------------------------------------------------------------------- 1 | class PackagesController < ApplicationController 2 | def show 3 | @package = Package.find_by(registry: params[:registry], name: params[:package]) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /test/application_system_test_case.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class ApplicationSystemTestCase < ActionDispatch::SystemTestCase 4 | driven_by :selenium, using: :chrome, screen_size: [1400, 1400] 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20180315153147_enable_extension_for_uuid.rb: -------------------------------------------------------------------------------- 1 | class EnableExtensionForUuid < ActiveRecord::Migration[5.2] 2 | def change 3 | enable_extension 'pgcrypto' unless extension_enabled?('pgcrypto') 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20180315154301_create_projects.rb: -------------------------------------------------------------------------------- 1 | class CreateProjects < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :projects, id: :uuid do |t| 4 | 5 | t.timestamps 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20180428144053_add_expired_at_to_package_source.rb: -------------------------------------------------------------------------------- 1 | class AddExpiredAtToPackageSource < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :package_sources, :expired_at, :datetime 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 2 | 3 | require 'bundler/setup' # Set up gems listed in the Gemfile. 4 | require 'bootsnap/setup' # Speed up boot time by caching expensive operations. 5 | -------------------------------------------------------------------------------- /app/views/packages/show.html.erb: -------------------------------------------------------------------------------- 1 | <%= @package.metrics.inspect %> 2 |
3 | 10 | -------------------------------------------------------------------------------- /spec/system/top_pages_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | describe 'root', type: :system, js: true do 4 | xit 'show login' do 5 | visit root_url 6 | expect(page).to have_content 'Monitoring the health of packages' 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require_relative 'config/application' 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /db/migrate/20180418154130_add_metric_type_and_value_to_metric.rb: -------------------------------------------------------------------------------- 1 | class AddMetricTypeAndValueToMetric < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :metrics, :metric_type, :string 4 | add_column :metrics, :value, :string 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /app/models/package_source.rb: -------------------------------------------------------------------------------- 1 | class PackageSource < ApplicationRecord 2 | belongs_to :package 3 | 4 | def expired? 5 | return true unless expired_at 6 | 7 | expired_at < Time.current 8 | end 9 | 10 | def self.ttl 11 | 7.days 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/helpers/packages_helper.rb: -------------------------------------------------------------------------------- 1 | module PackagesHelper 2 | def icon(package) 3 | case package.registry 4 | when 'rubygems' 5 | 'far fa-gem' 6 | when 'npm' 7 | 'fab fa-npm' 8 | else 9 | raise NotImplementedError 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20180418123357_create_metrics.rb: -------------------------------------------------------------------------------- 1 | class CreateMetrics < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :metrics, id: :uuid do |t| 4 | t.belongs_to :package, foreign_key: true, type: :uuid 5 | 6 | t.timestamps 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /config/initializers/application_controller_renderer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # ActiveSupport::Reloader.to_prepare do 4 | # ApplicationController.renderer.defaults.merge!( 5 | # http_host: 'example.org', 6 | # https: false 7 | # ) 8 | # end 9 | -------------------------------------------------------------------------------- /config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Specify a serializer for the signed and encrypted cookie jars. 4 | # Valid options are :json, :marshal, and :hybrid. 5 | Rails.application.config.action_dispatch.cookies_serializer = :json 6 | -------------------------------------------------------------------------------- /app/views/layouts/_footer.html.erb: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | -------------------------------------------------------------------------------- /spec/controllers/top_pages_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe TopPagesController, type: :controller do 4 | 5 | describe "GET #index" do 6 | it "returns http success" do 7 | get :index 8 | expect(response).to have_http_status(:success) 9 | end 10 | end 11 | 12 | end 13 | -------------------------------------------------------------------------------- /app/views/projects/show.html.erb: -------------------------------------------------------------------------------- 1 | <%= render partial: 'detail', locals: { project: @project } %> 2 |
3 |
4 |
5 | <%= render partial: 'package', collection: @packages %> 6 | <%= paginate(@packages) %> 7 |
8 |
9 |
10 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2.1' 2 | services: 3 | db: 4 | image: postgres:latest 5 | ports: 6 | - "5432:5432" 7 | volumes: 8 | - db:/var/lib/postgresql/data 9 | redis: 10 | image: redis:alpine 11 | ports: 12 | - "6379:6379" 13 | volumes: 14 | - redis:/data 15 | volumes: 16 | db: 17 | redis: 18 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | ENV['RAILS_ENV'] ||= 'test' 2 | require_relative '../config/environment' 3 | require 'rails/test_help' 4 | 5 | class ActiveSupport::TestCase 6 | # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. 7 | fixtures :all 8 | 9 | # Add more helper methods to be used by all tests here... 10 | end 11 | -------------------------------------------------------------------------------- /bin/yarn: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_ROOT = File.expand_path('..', __dir__) 3 | Dir.chdir(APP_ROOT) do 4 | begin 5 | exec "yarnpkg", *ARGV 6 | rescue Errno::ENOENT 7 | $stderr.puts "Yarn executable was not detected in the system." 8 | $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install" 9 | exit 1 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/models/metric.rb: -------------------------------------------------------------------------------- 1 | class Metric < ApplicationRecord 2 | belongs_to :package 3 | 4 | def title 5 | klass.title 6 | end 7 | 8 | def class_name 9 | instance.class_name 10 | end 11 | 12 | private 13 | 14 | def instance 15 | klass.new(value: value) 16 | end 17 | 18 | def klass 19 | metric_type.constantize 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /db/migrate/20180315161352_create_packages.rb: -------------------------------------------------------------------------------- 1 | class CreatePackages < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :packages, id: :uuid do |t| 4 | t.string :registry, null: false 5 | t.string :name, null: false 6 | 7 | t.timestamps 8 | end 9 | 10 | add_index :packages, [:registry, :name], unique: true 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20180315161622_create_project_packages.rb: -------------------------------------------------------------------------------- 1 | class CreateProjectPackages < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :project_packages, id: :uuid do |t| 4 | t.belongs_to :project, foreign_key: true, type: :uuid 5 | t.belongs_to :package, foreign_key: true, type: :uuid 6 | 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) 7 | # Character.create(name: 'Luke', movie: movies.first) 8 | -------------------------------------------------------------------------------- /db/migrate/20180418143357_create_package_sources.rb: -------------------------------------------------------------------------------- 1 | class CreatePackageSources < ActiveRecord::Migration[5.2] 2 | def change 3 | create_table :package_sources, id: :uuid do |t| 4 | t.belongs_to :package, foreign_key: true, type: :uuid 5 | t.string :repository_url 6 | t.string :registry_url 7 | t.string :ci_url 8 | 9 | t.timestamps 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | def default_meta_tags 3 | { 4 | site: 'pkgstatus.org', 5 | og: default_og, 6 | } 7 | end 8 | 9 | def default_og 10 | { 11 | title: 'pkgstatus.org', 12 | image: image_url('og-image.png'), 13 | url: request.original_url, 14 | description: 'Monitoring the health of packages', 15 | } 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/models/packary/metrics/registry_package/releases_metric.rb: -------------------------------------------------------------------------------- 1 | module Packary 2 | class Metrics::RegistryPackage::ReleasesMetric < Packary::Metric 3 | def self.group 4 | :registry 5 | end 6 | 7 | def self.title 8 | 'Releases' 9 | end 10 | 11 | def read(source) 12 | source.gem_versions&.size 13 | end 14 | 15 | def status 16 | :info 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /app/views/kaminari/_gap.html.erb: -------------------------------------------------------------------------------- 1 | <%# Non-link tag that stands for skipped pages... 2 | - available local variables 3 | current_page: a page object for the currently displayed page 4 | total_pages: total number of pages 5 | per_page: number of items to fetch per page 6 | remote: data-remote 7 | -%> 8 | <%= t('views.pagination.truncate').html_safe %> 9 | -------------------------------------------------------------------------------- /app/views/kaminari/_last_page.html.erb: -------------------------------------------------------------------------------- 1 | <%# Link to the "Last" page 2 | - available local variables 3 | url: url to the last page 4 | current_page: a page object for the currently displayed page 5 | total_pages: total number of pages 6 | per_page: number of items to fetch per page 7 | remote: data-remote 8 | -%> 9 | <%= link_to_unless current_page.last?, t('views.pagination.last').html_safe, url, remote: remote %> 10 | -------------------------------------------------------------------------------- /config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /app/views/kaminari/_first_page.html.erb: -------------------------------------------------------------------------------- 1 | <%# Link to the "First" page 2 | - available local variables 3 | url: url to the first page 4 | current_page: a page object for the currently displayed page 5 | total_pages: total number of pages 6 | per_page: number of items to fetch per page 7 | remote: data-remote 8 | -%> 9 | <%= link_to_unless current_page.first?, t('views.pagination.first').html_safe, url, remote: remote %> 10 | -------------------------------------------------------------------------------- /config/credentials.yml.enc: -------------------------------------------------------------------------------- 1 | 2Uocf/XY4aPFQ1kELYOUXdWWMsLWWq/xGWTB0fzlUCAub6qdArrQ06RmYv9jJuToQcS8vNGgsLxoo8HzFqkZ7Iv2ppQ3FasURjDtiJfBgIxrd6+U9O0riJWht9copDqpB0IdI0G84a6HUxJQSNxDyrkdhkadSGomKaDaS9dP/nIvnAwCssat3/JwLQ4G8zputhtxUzevtGYXVZ2Mm+cyvzsd/3zqd131fEIPZOGk7XmQycypQf9eVztgNReNngIWE6q+I1LK5J3wZBEVA4I8ftPWTxOyaSZYZUpK1gFbgqyTMq2iWjGfQkLo3Q6H2BIvMdB1tp7w0hMA+BRydf3wY4IVyeXE5ntQ7v5aylizp1GsO6fHeRuarOiCCgqJuN1jwm/s1eP2KjmKc74wvwcnYorxcWdzMdOlx0no--2PbAepduj01mfd1s--QazRM0nj03BZyxT0IzV3WA== -------------------------------------------------------------------------------- /app/views/kaminari/_next_page.html.erb: -------------------------------------------------------------------------------- 1 | <%# Link to the "Next" page 2 | - available local variables 3 | url: url to the next page 4 | current_page: a page object for the currently displayed page 5 | total_pages: total number of pages 6 | per_page: number of items to fetch per page 7 | remote: data-remote 8 | -%> 9 | <%= link_to t('views.pagination.next').html_safe, url, rel: 'next', remote: remote, class: "pagination-next#{' is-disabled' if current_page.last?}" %> 10 | -------------------------------------------------------------------------------- /app/views/kaminari/_prev_page.html.erb: -------------------------------------------------------------------------------- 1 | <%# Link to the "Previous" page 2 | - available local variables 3 | url: url to the previous page 4 | current_page: a page object for the currently displayed page 5 | total_pages: total number of pages 6 | per_page: number of items to fetch per page 7 | remote: data-remote 8 | -%> 9 | <%= link_to t('views.pagination.previous').html_safe, url, rel: 'prev', remote: remote, class: "pagination-previous#{' is-disabled' if current_page.first?}" %> 10 | -------------------------------------------------------------------------------- /app/models/packary/metrics/repository/contributors_metric.rb: -------------------------------------------------------------------------------- 1 | module Packary 2 | class Metrics::Repository::ContributorsMetric < Packary::Metric 3 | def self.group 4 | :repository 5 | end 6 | 7 | def self.title 8 | 'Contributors' 9 | end 10 | 11 | def read(source) 12 | source.contributors.size 13 | end 14 | 15 | def value 16 | return '> 99' if original_value.to_i > 99 17 | original_value.to_i 18 | end 19 | 20 | def status 21 | :info 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /app/views/kaminari/_page.html.erb: -------------------------------------------------------------------------------- 1 | <%# Link showing page number 2 | - available local variables 3 | page: a page object for "this" page 4 | url: url to this page 5 | current_page: a page object for the currently displayed page 6 | total_pages: total number of pages 7 | per_page: number of items to fetch per page 8 | remote: data-remote 9 | -%> 10 |
  • 11 | <%= link_to page, url, {remote: remote, rel: page.rel, class: "pagination-link#{' is-current' if page.current?}"} %> 12 |
  • 13 | -------------------------------------------------------------------------------- /spec/support/capybara.rb: -------------------------------------------------------------------------------- 1 | chrome_bin = ENV.fetch('GOOGLE_CHROME_SHIM', nil) 2 | 3 | chrome_opts = chrome_bin ? { "chromeOptions" => { "binary" => chrome_bin } } : {} 4 | 5 | Capybara.register_driver :chrome do |app| 6 | Capybara::Selenium::Driver.new( 7 | app, 8 | browser: :chrome, 9 | desired_capabilities: Selenium::WebDriver::Remote::Capabilities.chrome(chrome_opts) 10 | ) 11 | end 12 | 13 | RSpec.configure do |config| 14 | config.before(:each, type: :system, js: true) do 15 | driven_by :chrome 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | root 'top_pages#index' 3 | 4 | # Sidekiq 5 | if Rails.env.development? 6 | mount Sidekiq::Web => '/sidekiq' 7 | end 8 | 9 | get '/projects/:id', to: 'projects#show', as: 'project' 10 | delete '/projects/:id', to: 'projects#destroy' 11 | post '/projects', to: 'projects#create' 12 | 13 | constraints = { 14 | registry: %r{(?!stylesheets|javascripts)[^/]*}, 15 | package: /.*/, 16 | } 17 | get '/:registry/:package', to: 'packages#show', constraints: constraints 18 | end 19 | -------------------------------------------------------------------------------- /app/models/packary/metrics/repository/status_metric.rb: -------------------------------------------------------------------------------- 1 | module Packary 2 | class Metrics::Repository::StatusMetric < Packary::Metric 3 | def self.group 4 | :repository 5 | end 6 | 7 | def self.title 8 | 'CI Status' 9 | end 10 | 11 | def read(source) 12 | source.status 13 | end 14 | 15 | def status 16 | case value 17 | when 'pending' 18 | :warning 19 | when 'success' 20 | :success 21 | when 'failure' 22 | :danger 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /app/assets/javascripts/package_form_validation.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | const validatePackages = () => { 3 | let textareas = Array.from(document.querySelectorAll('.js-package-form')); 4 | 5 | if ( textareas.every(isTextareaEmpty) ) { 6 | alert('Require at least 1 package'); 7 | return false; 8 | } else { 9 | return true; 10 | } 11 | }; 12 | 13 | const isTextareaEmpty = (area) => { 14 | let text = area.value.trim(); 15 | return text.length === 0; 16 | }; 17 | 18 | window.validatePackages = validatePackages; 19 | }()); 20 | -------------------------------------------------------------------------------- /config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /app/models/packary/metrics/repository/star_metric.rb: -------------------------------------------------------------------------------- 1 | module Packary 2 | class Metrics::Repository::StarMetric < Packary::Metric 3 | def self.group 4 | :repository 5 | end 6 | 7 | def self.title 8 | 'GitHub Stars' 9 | end 10 | 11 | def read(source) 12 | source.repository&.dig(:stargazers_count) 13 | end 14 | 15 | # Use ActiveModel::Attribute? 16 | def value 17 | original_value.to_i 18 | end 19 | 20 | def status 21 | return :success if value > 1_000 22 | return :warning if value > 50 23 | :danger 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /app/models/packary/metrics/registry_package/downloads_metric.rb: -------------------------------------------------------------------------------- 1 | module Packary 2 | class Metrics::RegistryPackage::DownloadsMetric < Packary::Metric 3 | def self.group 4 | :registry 5 | end 6 | 7 | def self.title 8 | 'Downloads' 9 | end 10 | 11 | def read(source) 12 | source.downloads 13 | end 14 | 15 | # Use ActiveModel::Attribute? 16 | def value 17 | original_value.to_i 18 | end 19 | 20 | def status 21 | return :warning unless value 22 | 23 | return :success if value > 1_000_000 24 | return :warning if value > 10_000 25 | :failure 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /app/models/packary/metric.rb: -------------------------------------------------------------------------------- 1 | module Packary 2 | class Metric 3 | attr_reader :value 4 | 5 | def initialize(value: nil) 6 | @value = value if value 7 | end 8 | 9 | def preload(source) 10 | @value = read(source).to_s 11 | self 12 | end 13 | 14 | def self.title 15 | end 16 | 17 | def title 18 | self.class.title 19 | end 20 | 21 | # FIXME 22 | def status 23 | :warning 24 | end 25 | 26 | # TODO: Move into decorator 27 | def class_name 28 | status 29 | end 30 | 31 | private 32 | 33 | # Use this if override value 34 | def original_value 35 | @value 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Version of your assets, change this if you want to expire all your assets. 4 | Rails.application.config.assets.version = '1.0' 5 | 6 | # Add additional assets to the asset load path. 7 | # Rails.application.config.assets.paths << Emoji.images_path 8 | # Add Yarn node_modules folder to the asset load path. 9 | Rails.application.config.assets.paths << Rails.root.join('node_modules') 10 | 11 | # Precompile additional assets. 12 | # application.js, application.css, and all non-JS/CSS in the app/assets 13 | # folder are already added. 14 | # Rails.application.config.assets.precompile += %w( admin.js admin.css ) 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pkgstatus.org 2 | 3 | [![Build Status](https://semaphoreci.com/api/v1/meganemura/pkgstatus/branches/master/badge.svg)](https://semaphoreci.com/meganemura/pkgstatus) 4 | [![Maintainability](https://api.codeclimate.com/v1/badges/df76a6e11180d644801c/maintainability)](https://codeclimate.com/github/meganemura/pkgstatus/maintainability) 5 | [![Test Coverage](https://api.codeclimate.com/v1/badges/df76a6e11180d644801c/test_coverage)](https://codeclimate.com/github/meganemura/pkgstatus/test_coverage) 6 | [![codecov](https://codecov.io/gh/meganemura/pkgstatus/branch/master/graph/badge.svg)](https://codecov.io/gh/meganemura/pkgstatus) 7 | 8 | [pkgstatus.org](https://pkgstatus.org/) is the service for monitoring the health of packages. 9 | -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, '\1en' 8 | # inflect.singular /^(ox)en/i, '\1' 9 | # inflect.irregular 'person', 'people' 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym 'RESTful' 16 | # end 17 | -------------------------------------------------------------------------------- /app/models/packary/metrics/registry_package/latest_release_metric.rb: -------------------------------------------------------------------------------- 1 | module Packary 2 | class Metrics::RegistryPackage::LatestReleaseMetric < Packary::Metric 3 | def self.group 4 | :registry 5 | end 6 | 7 | def self.title 8 | 'Latest Releases' 9 | end 10 | 11 | def read(source) 12 | return nil unless source.gem_versions 13 | 14 | Time.parse(source.gem_versions.first['built_at']) 15 | end 16 | 17 | # Use ActiveModel::Attribute? 18 | def value 19 | Time.parse(original_value) if original_value.present? 20 | end 21 | 22 | def status 23 | return :warning unless value 24 | 25 | return :success if value > 180.days.ago 26 | :warning 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /app/models/packary/metrics/repository/last_commit_metric.rb: -------------------------------------------------------------------------------- 1 | module Packary 2 | class Metrics::Repository::LastCommitMetric < Packary::Metric 3 | def self.group 4 | :repository 5 | end 6 | 7 | def self.title 8 | 'Last commit' 9 | end 10 | 11 | def read(source) 12 | source.last_commit&.dig(:commit, :committer, :date) 13 | end 14 | 15 | # Use ActiveModel::Attribute? 16 | def value 17 | Time.parse(original_value) if original_value.present? 18 | end 19 | 20 | def status 21 | return :success if elapsed_days < 30 22 | return :warning if elapsed_days < 90 23 | :danger 24 | end 25 | 26 | def elapsed_days 27 | (Time.zone.now - value) / 86400 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /app/models/packary/metrics/registry_package/releases_in_metric.rb: -------------------------------------------------------------------------------- 1 | module Packary 2 | class Metrics::RegistryPackage::ReleasesInMetric < Packary::Metric 3 | def self.group 4 | :registry 5 | end 6 | 7 | def self.title 8 | "Releases in #{DAYS} days" 9 | end 10 | 11 | DAYS = 180 12 | DURATION = DAYS.days 13 | 14 | def read(source) 15 | versions = source.gem_versions 16 | versions&.count {|x| Time.parse(x['built_at']) > (Time.current - DURATION) } 17 | end 18 | 19 | # Use ActiveModel::Attribute? 20 | def value 21 | original_value.to_i 22 | end 23 | 24 | def status 25 | return :warning unless value 26 | 27 | return :success if value > 0 28 | :warning 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into application.js, which will include all the files 2 | // listed below. 3 | // 4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's 5 | // vendor/assets/javascripts directory can be referenced here using a relative path. 6 | // 7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 8 | // compiled file. JavaScript code in this file should be added after the last require_* statement. 9 | // 10 | // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details 11 | // about supported directives. 12 | // 13 | //= require rails-ujs 14 | //= require activestorage 15 | //= require_tree . 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile '~/.gitignore_global' 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore all logfiles and tempfiles. 11 | /log/* 12 | /tmp/* 13 | !/log/.keep 14 | !/tmp/.keep 15 | 16 | # Ignore uploaded files in development 17 | /storage/* 18 | 19 | /node_modules 20 | /yarn-error.log 21 | 22 | /public/assets 23 | .byebug_history 24 | 25 | # Ignore master key for decrypting credentials and more. 26 | /config/master.key 27 | 28 | # application specific 29 | .env.development 30 | 31 | erd.pdf 32 | -------------------------------------------------------------------------------- /app/models/dummy_project.rb: -------------------------------------------------------------------------------- 1 | class DummyProject 2 | def self.find(id) 3 | new 4 | end 5 | 6 | def packages 7 | x = sample_rubygems_packages.map { |x| ['rubygems', x] } 8 | y = sample_npm_packages.map { |x| ['npm', x] } 9 | 10 | if Rails.env.development? || ENV['PKGSTATUS_MIN_PACKAGES'] 11 | x = x.first(6) 12 | y = y.first(3) 13 | end 14 | 15 | pkgs = x + y 16 | 17 | pkgs.map { |registry, name| Package.new(registry: registry, name: name) } 18 | end 19 | 20 | private 21 | 22 | def sample_rubygems_packages 23 | File.read(Rails.root.join('db', 'static', 'mastodon-rubygems.list')).lines.map(&:chomp) 24 | end 25 | 26 | def sample_npm_packages 27 | File.read(Rails.root.join('db', 'static', 'mastodon-npm.list')).lines.map(&:chomp) 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /app/models/packary/metrics/repository/last_closed_issue_metric.rb: -------------------------------------------------------------------------------- 1 | module Packary 2 | class Metrics::Repository::LastClosedIssueMetric < Packary::Metric 3 | def self.group 4 | :repository 5 | end 6 | 7 | def self.title 8 | 'Last Closed Issue' 9 | end 10 | 11 | def read(source) 12 | source.last_closed_issue&.dig(:closed_at) 13 | end 14 | 15 | # Use ActiveModel::Attribute? 16 | def value 17 | Time.parse(original_value) if original_value.present? 18 | end 19 | 20 | def status 21 | return :danger unless value 22 | 23 | return :success if elapsed_days < 90 24 | return :warning if elapsed_days < 180 25 | :danger 26 | end 27 | 28 | def elapsed_days 29 | (Time.zone.now - value) / 86400 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /app/workers/fetch_metrics_worker.rb: -------------------------------------------------------------------------------- 1 | class FetchMetricsWorker < ApplicationWorker 2 | def perform(registry, name) 3 | # XXX: Do not raise errors because it cause exceeding of sentry quota... 4 | return if rate_limited? 5 | 6 | package = Package.find_by(registry: registry, name: name) 7 | MetricsFetcher.new(package.id).fetch 8 | rescue Octokit::TooManyRequests => e 9 | self.rate_limit_reset = e.response_headers['x-ratelimit-reset'].to_i 10 | end 11 | 12 | def rate_limit_reset=(reset_time) 13 | reset_in = [reset_time - Time.now.to_i, 300].max 14 | Rails.cache.write(cache_key, reset_time, expires_in: reset_in) 15 | end 16 | 17 | def rate_limited? 18 | Rails.cache.exist?(cache_key) 19 | end 20 | 21 | def cache_key 22 | [self.class.name, 'rate_limit_reset'].join(':') 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's 6 | * vendor/assets/stylesheets directory can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the 9 | * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS 10 | * files in this directory. Styles in this file should be added after the last require_* statement. 11 | * It is generally better to create a new file per style scope. 12 | * 13 | *= require_tree . 14 | *= require_self 15 | */ 16 | .brand { 17 | font-family: 'Lato', sans-serif; 18 | } 19 | -------------------------------------------------------------------------------- /app/views/projects/_detail.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 | 13 | 14 |

    15 | This project is created at <%= project.created_at.to_date.to_s %>. 16 |

    17 |

    18 | Fetched 19 | <%= project.packages.select(&:cached?).count %> 20 | / 21 | <%= project.packages.count %> 22 | packages 23 |

    24 |
    25 |
    26 |
    27 | -------------------------------------------------------------------------------- /app/views/layouts/_navigation.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | 25 |
    26 |
    27 | -------------------------------------------------------------------------------- /bin/erd: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'erd' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | require "pathname" 12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 13 | Pathname.new(__FILE__).realpath) 14 | 15 | bundle_binstub = File.expand_path("../bundle", __FILE__) 16 | 17 | if File.file?(bundle_binstub) 18 | if File.read(bundle_binstub, 150) =~ /This file was generated by Bundler/ 19 | load(bundle_binstub) 20 | else 21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 23 | end 24 | end 25 | 26 | require "rubygems" 27 | require "bundler/setup" 28 | 29 | load Gem.bin_path("rails-erd", "erd") 30 | -------------------------------------------------------------------------------- /bin/sidekiq: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'sidekiq' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | require "pathname" 12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 13 | Pathname.new(__FILE__).realpath) 14 | 15 | bundle_binstub = File.expand_path("../bundle", __FILE__) 16 | 17 | if File.file?(bundle_binstub) 18 | if File.read(bundle_binstub, 150) =~ /This file was generated by Bundler/ 19 | load(bundle_binstub) 20 | else 21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 23 | end 24 | end 25 | 26 | require "rubygems" 27 | require "bundler/setup" 28 | 29 | load Gem.bin_path("sidekiq", "sidekiq") 30 | -------------------------------------------------------------------------------- /bin/sidekiqctl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'sidekiqctl' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | require "pathname" 12 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", 13 | Pathname.new(__FILE__).realpath) 14 | 15 | bundle_binstub = File.expand_path("../bundle", __FILE__) 16 | 17 | if File.file?(bundle_binstub) 18 | if File.read(bundle_binstub, 150) =~ /This file was generated by Bundler/ 19 | load(bundle_binstub) 20 | else 21 | abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. 22 | Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") 23 | end 24 | end 25 | 26 | require "rubygems" 27 | require "bundler/setup" 28 | 29 | load Gem.bin_path("sidekiq", "sidekiqctl") 30 | -------------------------------------------------------------------------------- /bin/update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'fileutils' 3 | include FileUtils 4 | 5 | # path to your application root. 6 | APP_ROOT = File.expand_path('..', __dir__) 7 | 8 | def system!(*args) 9 | system(*args) || abort("\n== Command #{args} failed ==") 10 | end 11 | 12 | chdir APP_ROOT do 13 | # This script is a way to update your development environment automatically. 14 | # Add necessary update steps to this file. 15 | 16 | puts '== Installing dependencies ==' 17 | system! 'gem install bundler --conservative' 18 | system('bundle check') || system!('bundle install') 19 | 20 | # Install JavaScript dependencies if using Yarn 21 | # system('bin/yarn') 22 | 23 | puts "\n== Updating database ==" 24 | system! 'bin/rails db:migrate' 25 | 26 | puts "\n== Removing old logs and tempfiles ==" 27 | system! 'bin/rails log:clear tmp:clear' 28 | 29 | puts "\n== Restarting application server ==" 30 | system! 'bin/rails restart' 31 | end 32 | -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | default: &default 2 | adapter: postgresql 3 | encoding: unicode 4 | # For details on connection pooling, see Rails configuration guide 5 | # http://guides.rubyonrails.org/configuring.html#database-pooling 6 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> 7 | url: <%= ENV['DATABASE_URL'] || 'postgresql://postgres@localhost:5432' %> 8 | 9 | development: 10 | <<: *default 11 | database: pkgstatus_development 12 | 13 | # Schema search path. The server defaults to $user,public 14 | #schema_search_path: myapp,sharedapp,public 15 | 16 | # Minimum log levels, in increasing order: 17 | # debug5, debug4, debug3, debug2, debug1, 18 | # log, notice, warning, error, fatal, and panic 19 | # Defaults to warning. 20 | #min_messages: notice 21 | 22 | test: 23 | <<: *default 24 | database: pkgstatus_test 25 | 26 | production: 27 | <<: *default 28 | database: pkgstatus_production 29 | url: <%= ENV['DATABASE_URL'] %> 30 | -------------------------------------------------------------------------------- /app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | pkgstatus.org 5 | <%= csrf_meta_tags %> 6 | 7 | <%= display_meta_tags(default_meta_tags) %> 8 | <%= stylesheet_link_tag 'application', media: 'all' %> 9 | <%= javascript_include_tag 'application' %> 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | <%= render partial: 'layouts/navigation' %> 18 | <%= yield %> 19 | <%= render partial: 'layouts/footer' %> 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/views/kaminari/_paginator.html.erb: -------------------------------------------------------------------------------- 1 | <%# The container tag 2 | - available local variables 3 | current_page: a page object for the currently displayed page 4 | total_pages: total number of pages 5 | per_page: number of items to fetch per page 6 | remote: data-remote 7 | paginator: the paginator that renders the pagination tags inside 8 | -%> 9 | <%= paginator.render do -%> 10 | 27 | <% end -%> 28 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # The following keys must be escaped otherwise they will not be retrieved by 20 | # the default I18n backend: 21 | # 22 | # true, false, on, off, yes, no 23 | # 24 | # Instead, surround them with single quotes. 25 | # 26 | # en: 27 | # 'true': 'foo' 28 | # 29 | # To learn more, please read the Rails Internationalization guide 30 | # available at http://guides.rubyonrails.org/i18n.html. 31 | 32 | en: 33 | hello: "Hello world" 34 | -------------------------------------------------------------------------------- /config/initializers/content_security_policy.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Define an application-wide content security policy 4 | # For further information see the following documentation 5 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy 6 | 7 | # Rails.application.config.content_security_policy do |policy| 8 | # policy.default_src :self, :https 9 | # policy.font_src :self, :https, :data 10 | # policy.img_src :self, :https, :data 11 | # policy.object_src :none 12 | # policy.script_src :self, :https 13 | # policy.style_src :self, :https, :unsafe_inline 14 | 15 | # # Specify URI for violation reports 16 | # # policy.report_uri "/csp-violation-report-endpoint" 17 | # end 18 | 19 | # Report CSP violations to a specified URI 20 | # For further information see the following documentation: 21 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only 22 | # Rails.application.config.content_security_policy_report_only = true 23 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'fileutils' 3 | include FileUtils 4 | 5 | # path to your application root. 6 | APP_ROOT = File.expand_path('..', __dir__) 7 | 8 | def system!(*args) 9 | system(*args) || abort("\n== Command #{args} failed ==") 10 | end 11 | 12 | chdir APP_ROOT do 13 | # This script is a starting point to setup your application. 14 | # Add necessary setup steps to this file. 15 | 16 | puts '== Installing dependencies ==' 17 | system! 'gem install bundler --conservative' 18 | system('bundle check') || system!('bundle install') 19 | 20 | # Install JavaScript dependencies if using Yarn 21 | # system('bin/yarn') 22 | 23 | # puts "\n== Copying sample files ==" 24 | # unless File.exist?('config/database.yml') 25 | # cp 'config/database.yml.sample', 'config/database.yml' 26 | # end 27 | 28 | puts "\n== Preparing database ==" 29 | system! 'bin/rails db:setup' 30 | 31 | puts "\n== Removing old logs and tempfiles ==" 32 | system! 'bin/rails log:clear tmp:clear' 33 | 34 | puts "\n== Restarting application server ==" 35 | system! 'bin/rails restart' 36 | end 37 | -------------------------------------------------------------------------------- /config/storage.yml: -------------------------------------------------------------------------------- 1 | test: 2 | service: Disk 3 | root: <%= Rails.root.join("tmp/storage") %> 4 | 5 | local: 6 | service: Disk 7 | root: <%= Rails.root.join("storage") %> 8 | 9 | # Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) 10 | # amazon: 11 | # service: S3 12 | # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> 13 | # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> 14 | # region: us-east-1 15 | # bucket: your_own_bucket 16 | 17 | # Remember not to checkin your GCS keyfile to a repository 18 | # google: 19 | # service: GCS 20 | # project: your_project 21 | # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %> 22 | # bucket: your_own_bucket 23 | 24 | # Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key) 25 | # microsoft: 26 | # service: AzureStorage 27 | # path: your_azure_storage_path 28 | # storage_account_name: your_account_name 29 | # storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %> 30 | # container: your_container_name 31 | 32 | # mirror: 33 | # service: Mirror 34 | # primary: local 35 | # mirrors: [ amazon, google, microsoft ] 36 | -------------------------------------------------------------------------------- /app/models/packary/repository.rb: -------------------------------------------------------------------------------- 1 | module Packary 2 | class Repository 3 | # FIXME: Detect repository service from uri 4 | def self.from_package(registry, name) 5 | record = Resolver.new(registry, name).resolve 6 | Repositories::GithubRepository.new(record[:slug]) 7 | end 8 | 9 | def initialize(slug) 10 | @slug = slug 11 | end 12 | 13 | attr_reader :slug 14 | 15 | def self.metric_classes 16 | [] 17 | end 18 | 19 | def metrics 20 | self.class.metric_classes.map { |klass| klass.new.preload(self) } 21 | end 22 | 23 | def resource 24 | @resource ||= {} 25 | end 26 | attr_writer :resource 27 | 28 | class Resolver 29 | def initialize(registry, name) 30 | @registry = registry 31 | @name = name 32 | end 33 | 34 | attr_reader :registry, :name 35 | 36 | # XXX: Use pkgns.org to resolve 37 | def resolve 38 | resolved = Codestatus.resolver(registry).resolve!(name) 39 | host = if resolved.class == Codestatus::Repositories::GitHubRepository 40 | :github 41 | else 42 | nil 43 | end 44 | 45 | { 46 | host: host, 47 | slug: resolved.slug, 48 | } 49 | end 50 | end 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /config/initializers/meta_tags.rb: -------------------------------------------------------------------------------- 1 | # Use this setup block to configure all options available in MetaTags. 2 | MetaTags.configure do |config| 3 | # How many characters should the title meta tag have at most. Default is 70. 4 | # Set to nil or 0 to remove limits. 5 | # config.title_limit = 70 6 | 7 | # When true, site title will be truncated instead of title. Default is false. 8 | # config.truncate_site_title_first = false 9 | 10 | # Maximum length of the page description. Default is 300. 11 | # Set to nil or 0 to remove limits. 12 | # config.description_limit = 300 13 | 14 | # Maximum length of the keywords meta tag. Default is 255. 15 | # config.keywords_limit = 255 16 | 17 | # Default separator for keywords meta tag (used when an Array passed with 18 | # the list of keywords). Default is ", ". 19 | # config.keywords_separator = ', ' 20 | 21 | # When true, keywords will be converted to lowercase, otherwise they will 22 | # appear on the page as is. Default is true. 23 | # config.keywords_lowercase = true 24 | 25 | # When false, generated meta tags will be self-closing () instead 26 | # of open (``). Default is true. 27 | # config.open_meta_tags = true 28 | 29 | # List of additional meta tags that should use "property" attribute instead 30 | # of "name" attribute in tags. 31 | # config.property_tags.push( 32 | # 'x-hearthstone:deck', 33 | # ) 34 | end 35 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative 'boot' 2 | 3 | require "rails" 4 | # Pick the frameworks you want: 5 | require "active_model/railtie" 6 | require "active_job/railtie" 7 | require "active_record/railtie" 8 | require "active_storage/engine" 9 | require "action_controller/railtie" 10 | require "action_mailer/railtie" 11 | require "action_view/railtie" 12 | # require "action_cable/engine" 13 | require "sprockets/railtie" 14 | require "rails/test_unit/railtie" 15 | 16 | # Require the gems listed in Gemfile, including any gems 17 | # you've limited to :test, :development, or :production. 18 | Bundler.require(*Rails.groups) 19 | 20 | module Pkgstatus 21 | class Application < Rails::Application 22 | # Initialize configuration defaults for originally generated Rails version. 23 | config.load_defaults 5.2 24 | 25 | # Settings in config/environments/* take precedence over those specified here. 26 | # Application configuration can go into files in config/initializers 27 | # -- all .rb files in that directory are automatically loaded after loading 28 | # the framework and any gems in your application. 29 | 30 | config.generators do |generator| 31 | generator.orm :active_record, primary_key_type: :uuid 32 | generator.javascripts false 33 | generator.stylesheets false 34 | generator.helper false 35 | generator.test_framework :rspec, view_specs: false, helper_specs: false, controller_specs: false 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /app/controllers/projects_controller.rb: -------------------------------------------------------------------------------- 1 | class ProjectsController < ApplicationController 2 | def show 3 | @project = Project.find(params[:id]) 4 | @packages = @project.packages.order(:registry, :name).page(params[:page]).per(params[:per]) 5 | @project.packages.each(&:cache_later) # Use &:cache to cache now 6 | end 7 | 8 | def create 9 | project = Project.new 10 | project.packages = 11 | packages_for(registry: 'rubygems', names: rubygems_package_names) + 12 | packages_for(registry: 'npm', names: npm_package_names) 13 | 14 | if project.save 15 | redirect_to project_path(project) 16 | else 17 | redirect_to root_path 18 | end 19 | end 20 | 21 | def destroy 22 | project = Project.find(params[:id]) 23 | if project.destroy 24 | redirect_to root_path 25 | else 26 | redirect_to project_path(project) 27 | end 28 | end 29 | 30 | private 31 | 32 | def packages_param 33 | params.require(:packages).permit(:rubygems, :npm) 34 | end 35 | 36 | def packages_for(registry:, names:) 37 | names.map do |name| 38 | Package.find_or_initialize_by(registry: registry, name: name) 39 | end 40 | end 41 | 42 | def rubygems_package_names 43 | package_names_for(:rubygems) 44 | end 45 | 46 | def npm_package_names 47 | package_names_for(:npm) 48 | end 49 | 50 | def package_names_for(registry) 51 | packages_param[registry].lines.flat_map(&:split).uniq 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /config/puma.rb: -------------------------------------------------------------------------------- 1 | # Puma can serve each request in a thread from an internal thread pool. 2 | # The `threads` method setting takes two numbers: a minimum and maximum. 3 | # Any libraries that use thread pools should be configured to match 4 | # the maximum value specified for Puma. Default is set to 5 threads for minimum 5 | # and maximum; this matches the default thread size of Active Record. 6 | # 7 | threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } 8 | threads threads_count, threads_count 9 | 10 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000. 11 | # 12 | port ENV.fetch("PORT") { 3000 } 13 | 14 | # Specifies the `environment` that Puma will run in. 15 | # 16 | environment ENV.fetch("RAILS_ENV") { "development" } 17 | 18 | # Specifies the number of `workers` to boot in clustered mode. 19 | # Workers are forked webserver processes. If using threads and workers together 20 | # the concurrency of the application would be max `threads` * `workers`. 21 | # Workers do not work on JRuby or Windows (both of which do not support 22 | # processes). 23 | # 24 | # workers ENV.fetch("WEB_CONCURRENCY") { 2 } 25 | 26 | # Use the `preload_app!` method when specifying a `workers` number. 27 | # This directive tells Puma to first boot the application and load code 28 | # before forking the application. This takes advantage of Copy On Write 29 | # process behavior so workers use less memory. 30 | # 31 | # preload_app! 32 | 33 | # Allow puma to be restarted by `rails restart` command. 34 | plugin :tmp_restart 35 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | git_source(:github) { |repo| "https://github.com/#{repo}.git" } 3 | 4 | if v = ENV['CUSTOM_RUBY_VERSION'] 5 | ruby v 6 | end 7 | 8 | gem 'rails', '~> 5.2.4' 9 | 10 | gem 'bootsnap', '>= 1.1.0', require: false 11 | gem 'jbuilder', '~> 2.5' 12 | gem 'pg', '>= 0.18', '< 2.0' 13 | gem 'puma', '~> 3.12' 14 | gem 'redis-rails' 15 | gem 'sass-rails', '~> 5.0' 16 | gem 'uglifier', '>= 1.3.0' 17 | gem 'rest-client' 18 | gem 'sidekiq' 19 | gem 'travis' 20 | gem 'sentry-raven' 21 | gem 'meta-tags' 22 | gem 'kaminari' 23 | 24 | # These will be removed 25 | gem 'octokit' # RepoClinic::Repositories::GithubRepository 26 | gem 'gems' # RepoClinic::RegistryPackages::RubygemsPackage 27 | gem 'codestatus' # RepoClinic::Repository 28 | 29 | group :development, :test do 30 | gem 'pry-byebug' 31 | gem 'pry-rails' 32 | gem 'dotenv-rails' 33 | gem 'rspec-rails', '~> 3.8' 34 | end 35 | 36 | group :development do 37 | # Access an interactive console on exception pages or by calling 'console' anywhere in the code. 38 | gem 'web-console', '>= 3.3.0' 39 | gem 'listen', '>= 3.0.5', '< 3.2' 40 | 41 | gem 'derailed_benchmarks' 42 | gem 'stackprof' 43 | gem 'pp_sql' 44 | gem 'rails-erd' 45 | end 46 | 47 | group :test do 48 | # Adds support for Capybara system testing and selenium driver 49 | gem 'capybara', '~> 3.29' 50 | gem 'selenium-webdriver' 51 | # Easy installation and use of chromedriver to run system tests with Chrome 52 | gem 'chromedriver-helper' 53 | gem 'simplecov', require: false 54 | gem 'codecov', require: false 55 | end 56 | -------------------------------------------------------------------------------- /db/static/mastodon-rubygems.list: -------------------------------------------------------------------------------- 1 | active_model_serializers 2 | active_record_query_trace 3 | addressable 4 | annotate 5 | aws-sdk 6 | better_errors 7 | binding_of_caller 8 | bootsnap 9 | brakeman 10 | browser 11 | bullet 12 | bundler-audit 13 | capistrano 14 | capistrano-rails 15 | capistrano-rbenv 16 | capistrano-yarn 17 | capybara 18 | charlock_holmes 19 | cld3 20 | climate_control 21 | devise 22 | devise-two-factor 23 | doorkeeper 24 | dotenv-rails 25 | fabrication 26 | faker 27 | fast_blank 28 | fog-openstack 29 | fuubar 30 | goldfinger 31 | hamlit-rails 32 | hiredis 33 | htmlentities 34 | http 35 | http_accept_language 36 | httplog 37 | i18n-tasks 38 | idn-ruby 39 | iso-639 40 | json-ld-preloaded 41 | kaminari 42 | letter_opener 43 | letter_opener_web 44 | link_header 45 | lograge 46 | mario-redis-lock 47 | microformats 48 | mime-types 49 | nokogiri 50 | nsa 51 | oj 52 | ostatus2 53 | ox 54 | paperclip 55 | paperclip-av-transcoder 56 | parallel_tests 57 | pg 58 | pghero 59 | pkg-config 60 | pry-rails 61 | puma 62 | pundit 63 | rabl 64 | rack-attack 65 | rack-cors 66 | rack-timeout 67 | rails 68 | rails-controller-testing 69 | rails-i18n 70 | rails-settings-cached 71 | rdf-normalize 72 | redis 73 | redis-namespace 74 | redis-rails 75 | rqrcode 76 | rspec-rails 77 | rspec-sidekiq 78 | rubocop 79 | ruby-oembed 80 | sanitize 81 | scss_lint 82 | sidekiq 83 | sidekiq-bulk 84 | sidekiq-scheduler 85 | sidekiq-unique-jobs 86 | simple-navigation 87 | simple_form 88 | simplecov 89 | sprockets-rails 90 | strong_migrations 91 | twitter-text 92 | tzinfo-data 93 | uglifier 94 | webmock 95 | webpacker 96 | webpush 97 | -------------------------------------------------------------------------------- /app/models/packary/cis/travis_ci.rb: -------------------------------------------------------------------------------- 1 | module Packary 2 | module Cis 3 | class TravisCi 4 | def initialize(slug, default_branch) 5 | @slug = slug 6 | @default_branch = default_branch 7 | end 8 | 9 | attr_reader :slug, :default_branch 10 | 11 | def load_resource 12 | last_build 13 | end 14 | 15 | def self.metric_classes 16 | [ 17 | ] 18 | end 19 | 20 | def metrics 21 | self.class.metric_classes.map { |klass| klass.new.preload(self) } 22 | end 23 | 24 | def resource 25 | @resource ||= {} 26 | end 27 | attr_writer :resource 28 | 29 | def html_url 30 | "https://travis-ci.org/#{slug}" if active? 31 | end 32 | 33 | # FIXME: Ruby specific 34 | def configured_versions 35 | last_build&.dig('config', 'rvm') || [] 36 | end 37 | 38 | def last_build 39 | resource[:last_build] ||= begin 40 | repository&.branch(default_branch)&.to_h 41 | rescue ::Travis::Client::NotFound 42 | nil 43 | end 44 | end 45 | 46 | def repository 47 | client.repo(slug).tap do |repo| 48 | resource[:active] ||= repo.active? 49 | end 50 | rescue ::Travis::Client::NotFound 51 | nil 52 | end 53 | 54 | def active? 55 | # load repository 56 | repository unless resource.key?(:active) 57 | 58 | resource[:active] 59 | end 60 | 61 | def client 62 | @client ||= ::Travis::Client.new 63 | end 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
    60 |
    61 |

    We're sorry, but something went wrong.

    62 |
    63 |

    If you are the application owner check the logs for more information.

    64 |
    65 | 66 | 67 | -------------------------------------------------------------------------------- /public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
    60 |
    61 |

    The change you wanted was rejected.

    62 |

    Maybe you tried to change something you didn't have access to.

    63 |
    64 |

    If you are the application owner check the logs for more information.

    65 |
    66 | 67 | 68 | -------------------------------------------------------------------------------- /app/models/packary/registry_packages/rubygems_package.rb: -------------------------------------------------------------------------------- 1 | module Packary 2 | module RegistryPackages 3 | class RubygemsPackage 4 | def initialize(name) 5 | @name = name 6 | end 7 | 8 | attr_reader :name 9 | 10 | def self.metric_classes 11 | [ 12 | ::Packary::Metrics::RegistryPackage::DownloadsMetric, 13 | ::Packary::Metrics::RegistryPackage::ReleasesMetric, 14 | ::Packary::Metrics::RegistryPackage::ReleasesInMetric, 15 | ::Packary::Metrics::RegistryPackage::LatestReleaseMetric, 16 | ] 17 | end 18 | 19 | # Attach resource from cache 20 | attr_writer :resource 21 | def resource 22 | @resource ||= {} 23 | end 24 | 25 | def metrics 26 | self.class.metric_classes.map { |klass| klass.new.preload(self) } 27 | end 28 | 29 | def gem_info 30 | resource[:info] ||= begin 31 | Gems.info(name) 32 | rescue JSON::ParserError 33 | # When the package is not found on rubygems, 34 | # Gems does try to parse html as json and raise JSON::ParserError :sob: 35 | nil 36 | end 37 | end 38 | 39 | def gem_versions 40 | resource[:versions] ||= begin 41 | Gems.versions(name) 42 | rescue JSON::ParserError 43 | # When the package is not found on rubygems, 44 | # Gems does try to parse html as json and raise JSON::ParserError :sob: 45 | nil 46 | end 47 | end 48 | 49 | # for metric 50 | def downloads 51 | gem_info&.dig('downloads') 52 | end 53 | 54 | def html_url 55 | "https://rubygems.org/gems/#{name}" 56 | end 57 | end 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
    60 |
    61 |

    The page you were looking for doesn't exist.

    62 |

    You may have mistyped the address or the page may have moved.

    63 |
    64 |

    If you are the application owner check the logs for more information.

    65 |
    66 | 67 | 68 | -------------------------------------------------------------------------------- /db/static/mastodon-npm.list: -------------------------------------------------------------------------------- 1 | array-includes 2 | autoprefixer 3 | axios 4 | babel-core 5 | babel-loader 6 | babel-plugin-lodash 7 | babel-plugin-preval 8 | babel-plugin-react-intl 9 | babel-plugin-syntax-dynamic-import 10 | babel-plugin-transform-class-properties 11 | babel-plugin-transform-decorators-legacy 12 | babel-plugin-transform-es2015-modules-commonjs 13 | babel-plugin-transform-object-rest-spread 14 | babel-plugin-transform-react-inline-elements 15 | babel-plugin-transform-react-jsx-self 16 | babel-plugin-transform-react-jsx-source 17 | babel-plugin-transform-react-remove-prop-types 18 | babel-plugin-transform-runtime 19 | babel-preset-env 20 | babel-preset-react 21 | classnames 22 | compression-webpack-plugin 23 | cross-env 24 | css-loader 25 | detect-passive-events 26 | dotenv 27 | emoji-mart 28 | es6-symbol 29 | escape-html 30 | express 31 | extract-text-webpack-plugin 32 | file-loader 33 | font-awesome 34 | glob 35 | http-link-header 36 | immutable 37 | intersection-observer 38 | intl 39 | intl-messageformat 40 | intl-relativeformat 41 | is-nan 42 | js-yaml 43 | lodash 44 | mark-loader 45 | marky 46 | mkdirp 47 | node-sass 48 | npmlog 49 | object-assign 50 | object-fit-images 51 | offline-plugin 52 | path-complete-extname 53 | pg 54 | postcss-loader 55 | postcss-object-fit-images 56 | postcss-smart-import 57 | precss 58 | prop-types 59 | punycode 60 | rails-ujs 61 | react 62 | react-dom 63 | react-hotkeys 64 | react-immutable-proptypes 65 | react-immutable-pure-component 66 | react-intl 67 | react-motion 68 | react-notification 69 | react-overlays 70 | react-redux 71 | react-redux-loading-bar 72 | react-router-dom 73 | react-router-scroll-4 74 | react-swipeable-views 75 | react-textarea-autosize 76 | react-toggle 77 | redis 78 | redux 79 | redux-immutable 80 | redux-thunk 81 | requestidlecallback 82 | reselect 83 | resolve-url-loader 84 | rimraf 85 | sass-loader 86 | stringz 87 | style-loader 88 | substring-trie 89 | throng 90 | tiny-queue 91 | uuid 92 | uws 93 | webpack 94 | webpack-bundle-analyzer 95 | webpack-manifest-plugin 96 | webpack-merge 97 | websocket.js -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Do not eager load code on boot. This avoids loading your whole application 11 | # just for the purpose of running a single test. If you are using a tool that 12 | # preloads Rails for running tests, you may have to set it to true. 13 | config.eager_load = false 14 | 15 | # Configure public file server for tests with Cache-Control for performance. 16 | config.public_file_server.enabled = true 17 | config.public_file_server.headers = { 18 | 'Cache-Control' => "public, max-age=#{1.hour.to_i}" 19 | } 20 | 21 | # Show full error reports and disable caching. 22 | config.consider_all_requests_local = true 23 | config.action_controller.perform_caching = false 24 | 25 | # Raise exceptions instead of rendering exception templates. 26 | config.action_dispatch.show_exceptions = false 27 | 28 | # Disable request forgery protection in test environment. 29 | config.action_controller.allow_forgery_protection = false 30 | 31 | # Store uploaded files on the local file system in a temporary directory 32 | config.active_storage.service = :test 33 | 34 | config.action_mailer.perform_caching = false 35 | 36 | # Tell Action Mailer not to deliver emails to the real world. 37 | # The :test delivery method accumulates sent emails in the 38 | # ActionMailer::Base.deliveries array. 39 | config.action_mailer.delivery_method = :test 40 | 41 | # Print deprecation notices to the stderr. 42 | config.active_support.deprecation = :stderr 43 | 44 | # Raises error for missing translations 45 | # config.action_view.raise_on_missing_translations = true 46 | end 47 | -------------------------------------------------------------------------------- /app/views/top_pages/index.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 |
    4 |

    5 |

    6 | pkgstatus.org 7 |

    8 |

    9 |

    10 | Monitoring the health of packages 11 |

    12 |
    13 |
    14 |
    15 |
    16 |
    17 | <%= form_tag("/projects", method: :post, onsubmit: 'return validatePackages()') do %> 18 |
    19 |
    20 |

    21 | 22 | rubygems 23 |

    24 | 25 |
    26 |
    27 | <%- placeholder = %w(rails jekyll rubocop ...).join(" \n") -%> 28 | 29 |
    30 |
    31 |
    32 | 33 |
    34 |

    35 | 36 | npm 37 |

    38 | 39 |
    40 |
    41 | <%- placeholder = %w(express react lodash ...).join(" \n") -%> 42 | 43 |
    44 |
    45 |
    46 |
    47 | 48 |
    49 | 52 |
    53 | <%- end -%> 54 |
    55 | 56 | 57 | How to list dependencies 58 | 59 | 60 |
    61 |
    62 |
    63 | -------------------------------------------------------------------------------- /app/models/packary/registry_packages/npm_package.rb: -------------------------------------------------------------------------------- 1 | module Packary 2 | module RegistryPackages 3 | class NpmPackage 4 | NPM_REGISTRY_ENDPOINT = 'https://registry.npmjs.org/'.freeze 5 | 6 | def initialize(name) 7 | @name = name 8 | end 9 | 10 | attr_reader :name 11 | 12 | def self.metric_classes 13 | [ 14 | ::Packary::Metrics::RegistryPackage::DownloadsMetric, 15 | ] 16 | end 17 | 18 | # Attach resource from cache 19 | attr_writer :resource 20 | def resource 21 | @resource ||= {} 22 | end 23 | 24 | def metrics 25 | self.class.metric_classes.map { |klass| klass.new.preload(self) } 26 | end 27 | 28 | # https://github.com/npm/registry/blob/master/docs/download-counts.md 29 | # https://github.com/npm/download-counts/issues/1#issuecomment-293756533 30 | def downloads 31 | return resource[:downloads] if resource.key?(:downloads) 32 | 33 | responses = (2015..Date.today.year).map do |year| 34 | uri = "https://api.npmjs.org/downloads/point/#{year}-01-01:#{year}-12-31/#{slash_escaped_package}" 35 | request(uri) 36 | end 37 | 38 | resource[:downloads] = responses.inject(0) { |value, hash| value + hash['downloads'] } 39 | end 40 | 41 | def html_url 42 | "https://www.npmjs.com/package/#{name}" 43 | end 44 | 45 | private 46 | 47 | def package_info 48 | @package_info ||= request(package_uri) 49 | end 50 | 51 | def request(uri) 52 | response = RestClient.get(uri) 53 | JSON.parse(response) 54 | rescue RestClient::NotFound 55 | nil 56 | end 57 | 58 | def package_uri 59 | File.join(NPM_REGISTRY_ENDPOINT, slash_escaped_package) 60 | end 61 | 62 | # for scoped package 63 | # For example, to access @atlassian/aui information, 64 | # we must use https://registry.npmjs.org/@atlassian%2Faui, 65 | # not https://registry.npmjs.org/%40atlassian%2Faui. 66 | def slash_escaped_package 67 | name.gsub('/', CGI.escape('/')) 68 | end 69 | end 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /app/models/packary/repositories/github_repository.rb: -------------------------------------------------------------------------------- 1 | module Packary 2 | class Repositories::GithubRepository < Packary::Repository 3 | def self.metric_classes 4 | [ 5 | Packary::Metrics::Repository::StarMetric, 6 | Packary::Metrics::Repository::LastCommitMetric, 7 | Packary::Metrics::Repository::StatusMetric, 8 | Packary::Metrics::Repository::ContributorsMetric, 9 | Packary::Metrics::Repository::LastClosedIssueMetric, 10 | ] 11 | end 12 | 13 | def repository 14 | resource[:repository] ||= begin 15 | # Sawyer::Resource -> Hash 16 | client.repository(slug).to_hash 17 | rescue Octokit::NotFound 18 | nil 19 | end 20 | end 21 | 22 | def last_commit 23 | resource[:last_commit] ||= begin 24 | client.commits(slug, per_page: 1).first.to_hash 25 | rescue Octokit::NotFound 26 | nil 27 | end 28 | end 29 | 30 | def status 31 | combined_status&.dig(:state) 32 | end 33 | 34 | def combined_status 35 | resource[:combined_status] ||= client.combined_status(slug, default_branch).to_hash 36 | rescue Octokit::NotFound 37 | nil 38 | end 39 | 40 | def contributors 41 | resource[:contributors] ||= client.contributors(slug) 42 | end 43 | 44 | def last_closed_issue 45 | closed_issues.first 46 | end 47 | 48 | def closed_issues 49 | resource[:closed_issues] ||= client.issues(slug, state: 'closed', per_page: 50).map(&:to_h) 50 | end 51 | 52 | def html_url 53 | repository&.dig(:html_url) 54 | end 55 | 56 | def default_branch 57 | repository&.dig(:default_branch) 58 | end 59 | 60 | private 61 | 62 | def client 63 | @client ||= Octokit::Client.new(access_token: access_token, per_page: 100).tap do 64 | puts "client: #{slug}" if Rails.env.development? 65 | end 66 | end 67 | 68 | def access_token 69 | ENV['PACKARY_GITHUB_TOKEN'] 70 | end 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Do not eager load code on boot. 10 | config.eager_load = false 11 | 12 | # Show full error reports. 13 | config.consider_all_requests_local = true 14 | 15 | # Enable/disable caching. By default caching is disabled. 16 | # Run rails dev:cache to toggle caching. 17 | if Rails.root.join('tmp', 'caching-dev.txt').exist? 18 | config.action_controller.perform_caching = true 19 | else 20 | config.action_controller.perform_caching = false 21 | end 22 | config.cache_store = :redis_store, File.join((ENV['REDIS_URL'].presence || 'redis://localhost:6379/0'), 'cache'), { expires_in: 90.minutes } 23 | 24 | # Store uploaded files on the local file system (see config/storage.yml for options) 25 | config.active_storage.service = :local 26 | 27 | # Don't care if the mailer can't send. 28 | config.action_mailer.raise_delivery_errors = false 29 | 30 | config.action_mailer.perform_caching = false 31 | 32 | # Print deprecation notices to the Rails logger. 33 | config.active_support.deprecation = :log 34 | 35 | # Raise an error on page load if there are pending migrations. 36 | config.active_record.migration_error = :page_load 37 | 38 | # Highlight code that triggered database queries in logs. 39 | config.active_record.verbose_query_logs = true 40 | 41 | # Debug mode disables concatenation and preprocessing of assets. 42 | # This option may cause significant delays in view rendering with a large 43 | # number of complex assets. 44 | config.assets.debug = true 45 | 46 | # Suppress logger output for asset requests. 47 | config.assets.quiet = true 48 | 49 | # Raises error for missing translations 50 | # config.action_view.raise_on_missing_translations = true 51 | 52 | # Use an evented file watcher to asynchronously detect changes in source code, 53 | # routes, locales, etc. This feature depends on the listen gem. 54 | config.file_watcher = ActiveSupport::EventedFileUpdateChecker 55 | end 56 | -------------------------------------------------------------------------------- /app/models/package.rb: -------------------------------------------------------------------------------- 1 | class Package < ApplicationRecord 2 | has_many :project_packages 3 | has_many :projects, through: :project_packages 4 | has_many :metrics 5 | has_one :package_source 6 | 7 | # https://github.com/rubygems/rubygems.org/blob/master/lib/patterns.rb 8 | RUBYGEMS_SPECIAL_CHARACTERS = ".-_".freeze 9 | RUBYGEMS_ALLOWED_CHARACTERS = "[A-Za-z0-9#{Regexp.escape(RUBYGEMS_SPECIAL_CHARACTERS)}]+".freeze 10 | RUBYGEMS_NAME_PATTERN = /\A#{RUBYGEMS_ALLOWED_CHARACTERS}\Z/ 11 | 12 | NPM_DISALLOWED_CHARACTERS = /~()'!*/ 13 | 14 | validates :registry, presence: true 15 | validates :name, presence: true 16 | validate :package_naming 17 | 18 | def package_naming 19 | if rubygem? 20 | rubygem_naming 21 | elsif npm? 22 | npm_naming 23 | end 24 | end 25 | 26 | def rubygem_naming 27 | return if name.match?(RUBYGEMS_NAME_PATTERN) 28 | errors.add(:name, 'Given name is not allowed.') 29 | end 30 | 31 | def rubygem? 32 | registry == 'rubygems' 33 | end 34 | 35 | # https://github.com/npm/validate-npm-package-name#naming-rules 36 | def npm_naming 37 | if name.length == 0 || 38 | name.length > 214 || 39 | name.strip != name || 40 | name.match(/^\./) || 41 | name.match(/^_/) || 42 | name.match(NPM_DISALLOWED_CHARACTERS) 43 | errors.add(:name, 'Given name is not allowed.') 44 | end 45 | end 46 | 47 | def npm? 48 | registry == 'npm' 49 | end 50 | 51 | def cached? 52 | # TODO: Add expiration condition 53 | metrics.present? 54 | end 55 | 56 | def cache_later 57 | store_metrics_later if !package_source || package_source.expired? 58 | end 59 | 60 | def store_metrics_later 61 | FetchMetricsWorker.perform_async(registry, name) 62 | end 63 | 64 | def self.metric_classes 65 | registry_metric_classes + repository_metric_classes 66 | end 67 | 68 | def self.registry_metric_classes 69 | Packary::RegistryPackages::RubygemsPackage.metric_classes 70 | end 71 | 72 | def self.repository_metric_classes 73 | Packary::Repositories::GithubRepository.metric_classes 74 | end 75 | 76 | def metric_collection 77 | metrics.each_with_object({}) do |metric, hash| 78 | hash[metric.metric_type] = metric 79 | end 80 | end 81 | 82 | def repository_url 83 | package_source&.repository_url 84 | end 85 | 86 | def registry_url 87 | package_source&.registry_url 88 | end 89 | 90 | def ci_url 91 | package_source&.ci_url 92 | end 93 | 94 | end 95 | -------------------------------------------------------------------------------- /spec/rails_helper.rb: -------------------------------------------------------------------------------- 1 | # This file is copied to spec/ when you run 'rails generate rspec:install' 2 | require 'spec_helper' 3 | ENV['RAILS_ENV'] ||= 'test' 4 | require File.expand_path('../../config/environment', __FILE__) 5 | # Prevent database truncation if the environment is production 6 | abort("The Rails environment is running in production mode!") if Rails.env.production? 7 | require 'rspec/rails' 8 | # Add additional requires below this line. Rails is not loaded until this point! 9 | 10 | # Requires supporting ruby files with custom matchers and macros, etc, in 11 | # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are 12 | # run as spec files by default. This means that files in spec/support that end 13 | # in _spec.rb will both be required and run as specs, causing the specs to be 14 | # run twice. It is recommended that you do not name files matching this glob to 15 | # end with _spec.rb. You can configure this pattern with the --pattern 16 | # option on the command line or in ~/.rspec, .rspec or `.rspec-local`. 17 | # 18 | # The following line is provided for convenience purposes. It has the downside 19 | # of increasing the boot-up time by auto-requiring all files in the support 20 | # directory. Alternatively, in the individual `*_spec.rb` files, manually 21 | # require only the support files necessary. 22 | # 23 | Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f } 24 | 25 | # Checks for pending migrations and applies them before tests are run. 26 | # If you are not using ActiveRecord, you can remove this line. 27 | ActiveRecord::Migration.maintain_test_schema! 28 | 29 | RSpec.configure do |config| 30 | # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures 31 | config.fixture_path = "#{::Rails.root}/spec/fixtures" 32 | 33 | # If you're not using ActiveRecord, or you'd prefer not to run each of your 34 | # examples within a transaction, remove the following line or assign false 35 | # instead of true. 36 | config.use_transactional_fixtures = true 37 | 38 | # RSpec Rails can automatically mix in different behaviours to your tests 39 | # based on their file location, for example enabling you to call `get` and 40 | # `post` in specs under `spec/controllers`. 41 | # 42 | # You can disable this behaviour by removing the line below, and instead 43 | # explicitly tag your specs with their type, e.g.: 44 | # 45 | # RSpec.describe UsersController, :type => :controller do 46 | # # ... 47 | # end 48 | # 49 | # The different available types are documented in the features, such as in 50 | # https://relishapp.com/rspec/rspec-rails/docs 51 | config.infer_spec_type_from_file_location! 52 | 53 | # Filter lines from Rails gems in backtraces. 54 | config.filter_rails_from_backtrace! 55 | # arbitrary gems may also be filtered via: 56 | # config.filter_gems_from_backtrace("gem name") 57 | end 58 | -------------------------------------------------------------------------------- /db/schema.rb: -------------------------------------------------------------------------------- 1 | # This file is auto-generated from the current state of the database. Instead 2 | # of editing this file, please use the migrations feature of Active Record to 3 | # incrementally modify your database, and then regenerate this schema definition. 4 | # 5 | # Note that this schema.rb definition is the authoritative source for your 6 | # database schema. If you need to create the application database on another 7 | # system, you should be using db:schema:load, not running all the migrations 8 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 9 | # you'll amass, the slower it'll run and the greater likelihood for issues). 10 | # 11 | # It's strongly recommended that you check this file into your version control system. 12 | 13 | ActiveRecord::Schema.define(version: 2018_04_28_144053) do 14 | 15 | # These are extensions that must be enabled in order to support this database 16 | enable_extension "pgcrypto" 17 | enable_extension "plpgsql" 18 | 19 | create_table "metrics", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| 20 | t.uuid "package_id" 21 | t.datetime "created_at", null: false 22 | t.datetime "updated_at", null: false 23 | t.string "metric_type" 24 | t.string "value" 25 | t.index ["package_id"], name: "index_metrics_on_package_id" 26 | end 27 | 28 | create_table "package_sources", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| 29 | t.uuid "package_id" 30 | t.string "repository_url" 31 | t.string "registry_url" 32 | t.string "ci_url" 33 | t.datetime "created_at", null: false 34 | t.datetime "updated_at", null: false 35 | t.datetime "expired_at" 36 | t.index ["package_id"], name: "index_package_sources_on_package_id" 37 | end 38 | 39 | create_table "packages", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| 40 | t.string "registry", null: false 41 | t.string "name", null: false 42 | t.datetime "created_at", null: false 43 | t.datetime "updated_at", null: false 44 | t.index ["registry", "name"], name: "index_packages_on_registry_and_name", unique: true 45 | end 46 | 47 | create_table "project_packages", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| 48 | t.uuid "project_id" 49 | t.uuid "package_id" 50 | t.datetime "created_at", null: false 51 | t.datetime "updated_at", null: false 52 | t.index ["package_id"], name: "index_project_packages_on_package_id" 53 | t.index ["project_id"], name: "index_project_packages_on_project_id" 54 | end 55 | 56 | create_table "projects", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| 57 | t.datetime "created_at", null: false 58 | t.datetime "updated_at", null: false 59 | end 60 | 61 | add_foreign_key "metrics", "packages" 62 | add_foreign_key "package_sources", "packages" 63 | add_foreign_key "project_packages", "packages" 64 | add_foreign_key "project_packages", "projects" 65 | end 66 | -------------------------------------------------------------------------------- /app/models/metrics_fetcher.rb: -------------------------------------------------------------------------------- 1 | class MetricsFetcher 2 | def initialize(package_id) 3 | @package = Package.find(package_id) 4 | end 5 | 6 | def fetch 7 | return unless package_source_expired? 8 | 9 | store_metrics 10 | update_package_source 11 | end 12 | 13 | private 14 | 15 | attr_reader :package 16 | 17 | attr_writer :resources 18 | def resources 19 | @resources ||= {} 20 | end 21 | 22 | def package_source_expired? 23 | return false unless package_source 24 | package_source.expired? 25 | end 26 | 27 | def store_metrics 28 | # FIXME: Implement #cache to registry_package, repository 29 | fetch_metrics.each do |metric| 30 | mtr = Metric.find_or_initialize_by(package_id: id, metric_type: metric.class.name) 31 | mtr.value = metric.value.to_s 32 | mtr.save! 33 | end 34 | end 35 | 36 | def update_package_source 37 | package_source.update!(repository_url: fetch_repository_url, 38 | registry_url: fetch_registry_url, 39 | ci_url: fetch_ci_url, 40 | expired_at: Time.current + PackageSource.ttl) 41 | end 42 | 43 | def package_source 44 | @package_source ||= PackageSource.find_or_initialize_by(package_id: id) 45 | end 46 | 47 | def fetch_metrics 48 | registry_metrics + repository_metrics 49 | end 50 | 51 | def registry_metrics 52 | registry_package.metrics 53 | end 54 | 55 | def repository_metrics 56 | repository&.metrics || [] 57 | end 58 | 59 | def registry_package 60 | return @registry_package if @registry_package 61 | 62 | # XXX: Where to separate registries 63 | # TODO: Detect registry_package class 64 | klass = case registry.to_s 65 | when 'rubygems' 66 | Packary::RegistryPackages::RubygemsPackage 67 | when 'npm' 68 | Packary::RegistryPackages::NpmPackage 69 | else 70 | nil 71 | end 72 | 73 | return unless klass 74 | 75 | @registry_package = klass.new(name).tap do |pkg| 76 | pkg.resource = resources[:registry] 77 | end 78 | end 79 | 80 | def repository 81 | @repository ||= Packary::Repository.from_package(registry, name).tap do |repo| 82 | repo.resource = resources[:repository] 83 | end 84 | rescue 85 | nil 86 | end 87 | 88 | def ci 89 | return nil unless repository 90 | 91 | @ci ||= Packary::Cis::TravisCi.new(repository.slug, repository.default_branch).tap do |c| 92 | c.resource = resources[:ci] 93 | end 94 | end 95 | 96 | def fetch_repository_url 97 | repository&.html_url 98 | end 99 | 100 | def fetch_registry_url 101 | registry_package.html_url 102 | end 103 | 104 | def fetch_ci_url 105 | ci&.html_url 106 | end 107 | 108 | def id 109 | package.id 110 | end 111 | 112 | def name 113 | package.name 114 | end 115 | 116 | def registry 117 | package.registry 118 | end 119 | end 120 | -------------------------------------------------------------------------------- /bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | # 5 | # This file was generated by Bundler. 6 | # 7 | # The application 'bundle' is installed as part of a gem, and 8 | # this file is here to facilitate running it. 9 | # 10 | 11 | require "rubygems" 12 | 13 | m = Module.new do 14 | module_function 15 | 16 | def invoked_as_script? 17 | File.expand_path($0) == File.expand_path(__FILE__) 18 | end 19 | 20 | def env_var_version 21 | ENV["BUNDLER_VERSION"] 22 | end 23 | 24 | def cli_arg_version 25 | return unless invoked_as_script? # don't want to hijack other binstubs 26 | return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update` 27 | bundler_version = nil 28 | update_index = nil 29 | ARGV.each_with_index do |a, i| 30 | if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN 31 | bundler_version = a 32 | end 33 | next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/ 34 | bundler_version = $1 || ">= 0.a" 35 | update_index = i 36 | end 37 | bundler_version 38 | end 39 | 40 | def gemfile 41 | gemfile = ENV["BUNDLE_GEMFILE"] 42 | return gemfile if gemfile && !gemfile.empty? 43 | 44 | File.expand_path("../../Gemfile", __FILE__) 45 | end 46 | 47 | def lockfile 48 | lockfile = 49 | case File.basename(gemfile) 50 | when "gems.rb" then gemfile.sub(/\.rb$/, gemfile) 51 | else "#{gemfile}.lock" 52 | end 53 | File.expand_path(lockfile) 54 | end 55 | 56 | def lockfile_version 57 | return unless File.file?(lockfile) 58 | lockfile_contents = File.read(lockfile) 59 | return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/ 60 | Regexp.last_match(1) 61 | end 62 | 63 | def bundler_version 64 | @bundler_version ||= begin 65 | env_var_version || cli_arg_version || 66 | lockfile_version || "#{Gem::Requirement.default}.a" 67 | end 68 | end 69 | 70 | def load_bundler! 71 | ENV["BUNDLE_GEMFILE"] ||= gemfile 72 | 73 | # must dup string for RG < 1.8 compatibility 74 | activate_bundler(bundler_version.dup) 75 | end 76 | 77 | def activate_bundler(bundler_version) 78 | if Gem::Version.correct?(bundler_version) && Gem::Version.new(bundler_version).release < Gem::Version.new("2.0") 79 | bundler_version = "< 2" 80 | end 81 | gem_error = activation_error_handling do 82 | gem "bundler", bundler_version 83 | end 84 | return if gem_error.nil? 85 | require_error = activation_error_handling do 86 | require "bundler/version" 87 | end 88 | return if require_error.nil? && Gem::Requirement.new(bundler_version).satisfied_by?(Gem::Version.new(Bundler::VERSION)) 89 | warn "Activating bundler (#{bundler_version}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_version}'`" 90 | exit 42 91 | end 92 | 93 | def activation_error_handling 94 | yield 95 | nil 96 | rescue StandardError, LoadError => e 97 | e 98 | end 99 | end 100 | 101 | m.load_bundler! 102 | 103 | if m.invoked_as_script? 104 | load Gem.bin_path("bundler", "bundle") 105 | end 106 | -------------------------------------------------------------------------------- /app/views/projects/_package.html.erb: -------------------------------------------------------------------------------- 1 | 2 |
    3 |
    4 |
    5 |
    6 |

    7 | <%= package.name %> 8 |

    9 |

    sources

    10 | 11 | 12 | 13 | 14 | 15 | <%= package.registry %> 16 | 17 | 18 | 19 | 20 | <%- if package.cached? %> 21 | 22 | <%- if package.repository_url -%> 23 | 24 | 25 | 26 | GitHub 27 | 28 | 29 | <%- else -%> 30 | 31 | Repository detection error 32 | 33 | <%- end -%> 34 | 35 | 36 | 37 | <%- if package.repository_url -%> 38 | <%- if package.ci_url %> 39 | 40 | 41 | 42 | CI 43 | <%- versions = nil#package.send(:ci).configured_versions %> 44 | <%- if versions.present? %> 45 | (<%= versions.size %> ruby versions) 46 | <%- end %> 47 | 48 | 49 | <%- else %> 50 | 51 | CI detection warning 52 | 53 | <%- end %> 54 | <%- end -%> 55 | 56 | <%- end %> 57 |
    58 | 59 | <%- if package.cached? %> 60 | <%- Package.metric_classes.group_by(&:group).each do |group, metric_classes| -%> 61 |
    62 |

    63 | <%= group %> 64 |

    65 | 66 | 91 | 92 |
    93 | <%- end -%> 94 | <%- else -%> 95 |

    Please wait a while until loading.

    96 | <%= link_to 'reload', "?t=#{rand}##{[package.registry, package.name].join('-')}" %> 97 | <%- end -%> 98 |

    99 |
    100 |
    101 |
    102 |
    103 | -------------------------------------------------------------------------------- /config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # Code is not reloaded between requests. 5 | config.cache_classes = true 6 | 7 | # Eager load code on boot. This eager loads most of Rails and 8 | # your application in memory, allowing both threaded web servers 9 | # and those relying on copy on write to perform better. 10 | # Rake tasks automatically ignore this option for performance. 11 | config.eager_load = true 12 | 13 | # Full error reports are disabled and caching is turned on. 14 | config.consider_all_requests_local = false 15 | config.action_controller.perform_caching = true 16 | 17 | # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"] 18 | # or in config/master.key. This key is used to decrypt credentials (and other encrypted files). 19 | # config.require_master_key = true 20 | 21 | # Disable serving static files from the `/public` folder by default since 22 | # Apache or NGINX already handles this. 23 | config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? 24 | 25 | # Compress JavaScripts and CSS. 26 | config.assets.js_compressor = Uglifier.new(harmony: true) 27 | # config.assets.css_compressor = :sass 28 | 29 | # Do not fallback to assets pipeline if a precompiled asset is missed. 30 | config.assets.compile = false 31 | 32 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb 33 | 34 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 35 | # config.action_controller.asset_host = 'http://assets.example.com' 36 | 37 | # Specifies the header that your server uses for sending files. 38 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache 39 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX 40 | 41 | # Store uploaded files on the local file system (see config/storage.yml for options) 42 | config.active_storage.service = :local 43 | 44 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 45 | config.force_ssl = true 46 | 47 | # Use the lowest log level to ensure availability of diagnostic information 48 | # when problems arise. 49 | config.log_level = :debug 50 | 51 | # Prepend all log lines with the following tags. 52 | config.log_tags = [ :request_id ] 53 | 54 | # Use redis as a cache store 55 | config.cache_store = :redis_store, File.join(ENV['REDIS_URL'].presence, 'cache'), { expires_in: 90.minutes } 56 | 57 | # Use a real queuing backend for Active Job (and separate queues per environment) 58 | # config.active_job.queue_adapter = :resque 59 | # config.active_job.queue_name_prefix = "pkgstatus_#{Rails.env}" 60 | 61 | config.action_mailer.perform_caching = false 62 | 63 | # Ignore bad email addresses and do not raise email delivery errors. 64 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 65 | # config.action_mailer.raise_delivery_errors = false 66 | 67 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 68 | # the I18n.default_locale when a translation cannot be found). 69 | config.i18n.fallbacks = true 70 | 71 | # Send deprecation notices to registered listeners. 72 | config.active_support.deprecation = :notify 73 | 74 | # Use default logging formatter so that PID and timestamp are not suppressed. 75 | config.log_formatter = ::Logger::Formatter.new 76 | 77 | # Use a different logger for distributed setups. 78 | # require 'syslog/logger' 79 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') 80 | 81 | if ENV["RAILS_LOG_TO_STDOUT"].present? 82 | logger = ActiveSupport::Logger.new(STDOUT) 83 | logger.formatter = config.log_formatter 84 | config.logger = ActiveSupport::TaggedLogging.new(logger) 85 | end 86 | 87 | # Do not dump schema after migrations. 88 | config.active_record.dump_schema_after_migration = false 89 | end 90 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # This file was generated by the `rails generate rspec:install` command. Conventionally, all 2 | # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. 3 | # The generated `.rspec` file contains `--require spec_helper` which will cause 4 | # this file to always be loaded, without a need to explicitly require it in any 5 | # files. 6 | # 7 | # Given that it is always loaded, you are encouraged to keep this file as 8 | # light-weight as possible. Requiring heavyweight dependencies from this file 9 | # will add to the boot time of your test suite on EVERY test run, even for an 10 | # individual file that may not need all of that loaded. Instead, consider making 11 | # a separate helper file that requires the additional dependencies and performs 12 | # the additional setup, and require it from the spec files that actually need 13 | # it. 14 | # 15 | # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration 16 | 17 | if ENV['CI'] == 'true' 18 | require "simplecov" 19 | SimpleCov.start 20 | 21 | require 'codecov' 22 | SimpleCov.formatter = SimpleCov::Formatter::Codecov 23 | end 24 | 25 | RSpec.configure do |config| 26 | # rspec-expectations config goes here. You can use an alternate 27 | # assertion/expectation library such as wrong or the stdlib/minitest 28 | # assertions if you prefer. 29 | config.expect_with :rspec do |expectations| 30 | # This option will default to `true` in RSpec 4. It makes the `description` 31 | # and `failure_message` of custom matchers include text for helper methods 32 | # defined using `chain`, e.g.: 33 | # be_bigger_than(2).and_smaller_than(4).description 34 | # # => "be bigger than 2 and smaller than 4" 35 | # ...rather than: 36 | # # => "be bigger than 2" 37 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true 38 | end 39 | 40 | # rspec-mocks config goes here. You can use an alternate test double 41 | # library (such as bogus or mocha) by changing the `mock_with` option here. 42 | config.mock_with :rspec do |mocks| 43 | # Prevents you from mocking or stubbing a method that does not exist on 44 | # a real object. This is generally recommended, and will default to 45 | # `true` in RSpec 4. 46 | mocks.verify_partial_doubles = true 47 | end 48 | 49 | # This option will default to `:apply_to_host_groups` in RSpec 4 (and will 50 | # have no way to turn it off -- the option exists only for backwards 51 | # compatibility in RSpec 3). It causes shared context metadata to be 52 | # inherited by the metadata hash of host groups and examples, rather than 53 | # triggering implicit auto-inclusion in groups with matching metadata. 54 | config.shared_context_metadata_behavior = :apply_to_host_groups 55 | 56 | # The settings below are suggested to provide a good initial experience 57 | # with RSpec, but feel free to customize to your heart's content. 58 | =begin 59 | # This allows you to limit a spec run to individual examples or groups 60 | # you care about by tagging them with `:focus` metadata. When nothing 61 | # is tagged with `:focus`, all examples get run. RSpec also provides 62 | # aliases for `it`, `describe`, and `context` that include `:focus` 63 | # metadata: `fit`, `fdescribe` and `fcontext`, respectively. 64 | config.filter_run_when_matching :focus 65 | 66 | # Allows RSpec to persist some state between runs in order to support 67 | # the `--only-failures` and `--next-failure` CLI options. We recommend 68 | # you configure your source control system to ignore this file. 69 | config.example_status_persistence_file_path = "spec/examples.txt" 70 | 71 | # Limits the available syntax to the non-monkey patched syntax that is 72 | # recommended. For more details, see: 73 | # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ 74 | # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ 75 | # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode 76 | config.disable_monkey_patching! 77 | 78 | # Many RSpec users commonly either run the entire suite or an individual 79 | # file, and it's useful to allow more verbose output when running an 80 | # individual spec file. 81 | if config.files_to_run.one? 82 | # Use the documentation formatter for detailed output, 83 | # unless a formatter has already been configured 84 | # (e.g. via a command-line flag). 85 | config.default_formatter = "doc" 86 | end 87 | 88 | # Print the 10 slowest examples and example groups at the 89 | # end of the spec run, to help surface which specs are running 90 | # particularly slow. 91 | config.profile_examples = 10 92 | 93 | # Run specs in random order to surface order dependencies. If you find an 94 | # order dependency and want to debug it, you can fix the order by providing 95 | # the seed, which is printed after each run. 96 | # --seed 1234 97 | config.order = :random 98 | 99 | # Seed global randomization in this process using the `--seed` CLI option. 100 | # Setting this allows you to use `--seed` to deterministically reproduce 101 | # test failures related to randomization by passing the same `--seed` value 102 | # as the one that triggered the failure. 103 | Kernel.srand config.seed 104 | =end 105 | end 106 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | actioncable (5.2.4.1) 5 | actionpack (= 5.2.4.1) 6 | nio4r (~> 2.0) 7 | websocket-driver (>= 0.6.1) 8 | actionmailer (5.2.4.1) 9 | actionpack (= 5.2.4.1) 10 | actionview (= 5.2.4.1) 11 | activejob (= 5.2.4.1) 12 | mail (~> 2.5, >= 2.5.4) 13 | rails-dom-testing (~> 2.0) 14 | actionpack (5.2.4.1) 15 | actionview (= 5.2.4.1) 16 | activesupport (= 5.2.4.1) 17 | rack (~> 2.0, >= 2.0.8) 18 | rack-test (>= 0.6.3) 19 | rails-dom-testing (~> 2.0) 20 | rails-html-sanitizer (~> 1.0, >= 1.0.2) 21 | actionview (5.2.4.1) 22 | activesupport (= 5.2.4.1) 23 | builder (~> 3.1) 24 | erubi (~> 1.4) 25 | rails-dom-testing (~> 2.0) 26 | rails-html-sanitizer (~> 1.0, >= 1.0.3) 27 | activejob (5.2.4.1) 28 | activesupport (= 5.2.4.1) 29 | globalid (>= 0.3.6) 30 | activemodel (5.2.4.1) 31 | activesupport (= 5.2.4.1) 32 | activerecord (5.2.4.1) 33 | activemodel (= 5.2.4.1) 34 | activesupport (= 5.2.4.1) 35 | arel (>= 9.0) 36 | activestorage (5.2.4.1) 37 | actionpack (= 5.2.4.1) 38 | activerecord (= 5.2.4.1) 39 | marcel (~> 0.3.1) 40 | activesupport (5.2.4.1) 41 | concurrent-ruby (~> 1.0, >= 1.0.2) 42 | i18n (>= 0.7, < 2) 43 | minitest (~> 5.1) 44 | tzinfo (~> 1.1) 45 | addressable (2.5.2) 46 | public_suffix (>= 2.0.2, < 4.0) 47 | anbt-sql-formatter (0.0.7) 48 | archive-zip (0.12.0) 49 | io-like (~> 0.3.0) 50 | arel (9.0.0) 51 | backports (3.11.1) 52 | benchmark-ips (2.7.2) 53 | bindex (0.5.0) 54 | bootsnap (1.4.4) 55 | msgpack (~> 1.0) 56 | builder (3.2.4) 57 | byebug (11.0.1) 58 | capybara (3.29.0) 59 | addressable 60 | mini_mime (>= 0.1.3) 61 | nokogiri (~> 1.8) 62 | rack (>= 1.6.0) 63 | rack-test (>= 0.6.3) 64 | regexp_parser (~> 1.5) 65 | xpath (~> 3.2) 66 | childprocess (1.0.1) 67 | rake (< 13.0) 68 | choice (0.2.0) 69 | chromedriver-helper (2.1.1) 70 | archive-zip (~> 0.10) 71 | nokogiri (~> 1.8) 72 | codecov (0.1.14) 73 | json 74 | simplecov 75 | url 76 | coderay (1.1.2) 77 | codestatus (0.1.3) 78 | gems 79 | octokit 80 | rest-client 81 | thor 82 | concurrent-ruby (1.1.5) 83 | connection_pool (2.2.2) 84 | crass (1.0.5) 85 | derailed_benchmarks (1.3.5) 86 | benchmark-ips (~> 2) 87 | get_process_mem (~> 0) 88 | heapy (~> 0) 89 | memory_profiler (~> 0) 90 | rack (>= 1) 91 | rake (> 10, < 13) 92 | thor (~> 0.19) 93 | diff-lcs (1.3) 94 | docile (1.3.2) 95 | domain_name (0.5.20170404) 96 | unf (>= 0.0.5, < 1.0.0) 97 | dotenv (2.7.5) 98 | dotenv-rails (2.7.5) 99 | dotenv (= 2.7.5) 100 | railties (>= 3.2, < 6.1) 101 | erubi (1.9.0) 102 | ethon (0.11.0) 103 | ffi (>= 1.3.0) 104 | execjs (2.7.0) 105 | faraday (0.15.4) 106 | multipart-post (>= 1.2, < 3) 107 | faraday_middleware (0.12.2) 108 | faraday (>= 0.7.4, < 1.0) 109 | ffi (1.10.0) 110 | gems (1.0.0) 111 | json 112 | get_process_mem (0.2.3) 113 | gh (0.14.0) 114 | addressable 115 | backports 116 | faraday (~> 0.8) 117 | multi_json (~> 1.0) 118 | net-http-persistent (>= 2.7) 119 | net-http-pipeline 120 | globalid (0.4.2) 121 | activesupport (>= 4.2.0) 122 | heapy (0.1.4) 123 | highline (1.7.10) 124 | http-cookie (1.0.3) 125 | domain_name (~> 0.5) 126 | i18n (1.7.0) 127 | concurrent-ruby (~> 1.0) 128 | io-like (0.3.0) 129 | jbuilder (2.7.0) 130 | activesupport (>= 4.2.0) 131 | multi_json (>= 1.2) 132 | json (2.2.0) 133 | kaminari (1.1.1) 134 | activesupport (>= 4.1.0) 135 | kaminari-actionview (= 1.1.1) 136 | kaminari-activerecord (= 1.1.1) 137 | kaminari-core (= 1.1.1) 138 | kaminari-actionview (1.1.1) 139 | actionview 140 | kaminari-core (= 1.1.1) 141 | kaminari-activerecord (1.1.1) 142 | activerecord 143 | kaminari-core (= 1.1.1) 144 | kaminari-core (1.1.1) 145 | launchy (2.4.3) 146 | addressable (~> 2.3) 147 | listen (3.1.5) 148 | rb-fsevent (~> 0.9, >= 0.9.4) 149 | rb-inotify (~> 0.9, >= 0.9.7) 150 | ruby_dep (~> 1.2) 151 | loofah (2.4.0) 152 | crass (~> 1.0.2) 153 | nokogiri (>= 1.5.9) 154 | mail (2.7.1) 155 | mini_mime (>= 0.1.1) 156 | marcel (0.3.3) 157 | mimemagic (~> 0.3.2) 158 | memory_profiler (0.9.13) 159 | meta-tags (2.11.1) 160 | actionpack (>= 3.2.0, < 6.1) 161 | method_source (0.9.2) 162 | mime-types (3.1) 163 | mime-types-data (~> 3.2015) 164 | mime-types-data (3.2016.0521) 165 | mimemagic (0.3.3) 166 | mini_mime (1.0.2) 167 | mini_portile2 (2.4.0) 168 | minitest (5.13.0) 169 | msgpack (1.2.10) 170 | multi_json (1.13.1) 171 | multipart-post (2.1.1) 172 | net-http-persistent (3.0.0) 173 | connection_pool (~> 2.2) 174 | net-http-pipeline (1.0.1) 175 | netrc (0.11.0) 176 | nio4r (2.5.2) 177 | nokogiri (1.10.7) 178 | mini_portile2 (~> 2.4.0) 179 | octokit (4.9.0) 180 | sawyer (~> 0.8.0, >= 0.5.3) 181 | pg (1.0.0) 182 | pp_sql (0.2.10) 183 | anbt-sql-formatter (~> 0.0.6) 184 | rails 185 | pry (0.12.2) 186 | coderay (~> 1.1.0) 187 | method_source (~> 0.9.0) 188 | pry-byebug (3.7.0) 189 | byebug (~> 11.0) 190 | pry (~> 0.10) 191 | pry-rails (0.3.9) 192 | pry (>= 0.10.4) 193 | public_suffix (3.1.1) 194 | puma (3.12.2) 195 | pusher-client (0.6.2) 196 | json 197 | websocket (~> 1.0) 198 | rack (2.0.8) 199 | rack-protection (2.0.5) 200 | rack 201 | rack-test (1.1.0) 202 | rack (>= 1.0, < 3) 203 | rails (5.2.4.1) 204 | actioncable (= 5.2.4.1) 205 | actionmailer (= 5.2.4.1) 206 | actionpack (= 5.2.4.1) 207 | actionview (= 5.2.4.1) 208 | activejob (= 5.2.4.1) 209 | activemodel (= 5.2.4.1) 210 | activerecord (= 5.2.4.1) 211 | activestorage (= 5.2.4.1) 212 | activesupport (= 5.2.4.1) 213 | bundler (>= 1.3.0) 214 | railties (= 5.2.4.1) 215 | sprockets-rails (>= 2.0.0) 216 | rails-dom-testing (2.0.3) 217 | activesupport (>= 4.2.0) 218 | nokogiri (>= 1.6) 219 | rails-erd (1.6.0) 220 | activerecord (>= 4.2) 221 | activesupport (>= 4.2) 222 | choice (~> 0.2.0) 223 | ruby-graphviz (~> 1.2) 224 | rails-html-sanitizer (1.3.0) 225 | loofah (~> 2.3) 226 | railties (5.2.4.1) 227 | actionpack (= 5.2.4.1) 228 | activesupport (= 5.2.4.1) 229 | method_source 230 | rake (>= 0.8.7) 231 | thor (>= 0.19.0, < 2.0) 232 | rake (12.3.3) 233 | rb-fsevent (0.10.2) 234 | rb-inotify (0.9.10) 235 | ffi (>= 0.5.0, < 2) 236 | redis (4.1.0) 237 | redis-actionpack (5.0.2) 238 | actionpack (>= 4.0, < 6) 239 | redis-rack (>= 1, < 3) 240 | redis-store (>= 1.1.0, < 2) 241 | redis-activesupport (5.0.4) 242 | activesupport (>= 3, < 6) 243 | redis-store (>= 1.3, < 2) 244 | redis-rack (2.0.4) 245 | rack (>= 1.5, < 3) 246 | redis-store (>= 1.2, < 2) 247 | redis-rails (5.0.2) 248 | redis-actionpack (>= 5.0, < 6) 249 | redis-activesupport (>= 5.0, < 6) 250 | redis-store (>= 1.2, < 2) 251 | redis-store (1.4.1) 252 | redis (>= 2.2, < 5) 253 | regexp_parser (1.6.0) 254 | rest-client (2.0.2) 255 | http-cookie (>= 1.0.2, < 2.0) 256 | mime-types (>= 1.16, < 4.0) 257 | netrc (~> 0.8) 258 | rspec-core (3.8.0) 259 | rspec-support (~> 3.8.0) 260 | rspec-expectations (3.8.2) 261 | diff-lcs (>= 1.2.0, < 2.0) 262 | rspec-support (~> 3.8.0) 263 | rspec-mocks (3.8.0) 264 | diff-lcs (>= 1.2.0, < 2.0) 265 | rspec-support (~> 3.8.0) 266 | rspec-rails (3.8.2) 267 | actionpack (>= 3.0) 268 | activesupport (>= 3.0) 269 | railties (>= 3.0) 270 | rspec-core (~> 3.8.0) 271 | rspec-expectations (~> 3.8.0) 272 | rspec-mocks (~> 3.8.0) 273 | rspec-support (~> 3.8.0) 274 | rspec-support (3.8.0) 275 | ruby-graphviz (1.2.4) 276 | ruby_dep (1.5.0) 277 | rubyzip (1.3.0) 278 | sass (3.5.5) 279 | sass-listen (~> 4.0.0) 280 | sass-listen (4.0.0) 281 | rb-fsevent (~> 0.9, >= 0.9.4) 282 | rb-inotify (~> 0.9, >= 0.9.7) 283 | sass-rails (5.0.7) 284 | railties (>= 4.0.0, < 6) 285 | sass (~> 3.1) 286 | sprockets (>= 2.8, < 4.0) 287 | sprockets-rails (>= 2.0, < 4.0) 288 | tilt (>= 1.1, < 3) 289 | sawyer (0.8.1) 290 | addressable (>= 2.3.5, < 2.6) 291 | faraday (~> 0.8, < 1.0) 292 | selenium-webdriver (3.142.3) 293 | childprocess (>= 0.5, < 2.0) 294 | rubyzip (~> 1.2, >= 1.2.2) 295 | sentry-raven (2.11.1) 296 | faraday (>= 0.7.6, < 1.0) 297 | sidekiq (5.2.7) 298 | connection_pool (~> 2.2, >= 2.2.2) 299 | rack (>= 1.5.0) 300 | rack-protection (>= 1.5.0) 301 | redis (>= 3.3.5, < 5) 302 | simplecov (0.17.0) 303 | docile (~> 1.1) 304 | json (>= 1.8, < 3) 305 | simplecov-html (~> 0.10.0) 306 | simplecov-html (0.10.2) 307 | sprockets (3.7.2) 308 | concurrent-ruby (~> 1.0) 309 | rack (> 1, < 3) 310 | sprockets-rails (3.2.1) 311 | actionpack (>= 4.0) 312 | activesupport (>= 4.0) 313 | sprockets (>= 3.0.0) 314 | stackprof (0.2.12) 315 | thor (0.20.3) 316 | thread_safe (0.3.6) 317 | tilt (2.0.8) 318 | travis (1.8.8) 319 | backports 320 | faraday (~> 0.9) 321 | faraday_middleware (~> 0.9, >= 0.9.1) 322 | gh (~> 0.13) 323 | highline (~> 1.6) 324 | launchy (~> 2.1) 325 | pusher-client (~> 0.4) 326 | typhoeus (~> 0.6, >= 0.6.8) 327 | typhoeus (0.8.0) 328 | ethon (>= 0.8.0) 329 | tzinfo (1.2.5) 330 | thread_safe (~> 0.1) 331 | uglifier (4.1.20) 332 | execjs (>= 0.3.0, < 3) 333 | unf (0.1.4) 334 | unf_ext 335 | unf_ext (0.0.7.5) 336 | url (0.3.2) 337 | web-console (3.7.0) 338 | actionview (>= 5.0) 339 | activemodel (>= 5.0) 340 | bindex (>= 0.4.0) 341 | railties (>= 5.0) 342 | websocket (1.2.5) 343 | websocket-driver (0.7.1) 344 | websocket-extensions (>= 0.1.0) 345 | websocket-extensions (0.1.4) 346 | xpath (3.2.0) 347 | nokogiri (~> 1.8) 348 | 349 | PLATFORMS 350 | ruby 351 | 352 | DEPENDENCIES 353 | bootsnap (>= 1.1.0) 354 | capybara (~> 3.29) 355 | chromedriver-helper 356 | codecov 357 | codestatus 358 | derailed_benchmarks 359 | dotenv-rails 360 | gems 361 | jbuilder (~> 2.5) 362 | kaminari 363 | listen (>= 3.0.5, < 3.2) 364 | meta-tags 365 | octokit 366 | pg (>= 0.18, < 2.0) 367 | pp_sql 368 | pry-byebug 369 | pry-rails 370 | puma (~> 3.12) 371 | rails (~> 5.2.4) 372 | rails-erd 373 | redis-rails 374 | rest-client 375 | rspec-rails (~> 3.8) 376 | sass-rails (~> 5.0) 377 | selenium-webdriver 378 | sentry-raven 379 | sidekiq 380 | simplecov 381 | stackprof 382 | travis 383 | uglifier (>= 1.3.0) 384 | web-console (>= 3.3.0) 385 | 386 | BUNDLED WITH 387 | 1.16.1 388 | --------------------------------------------------------------------------------