├── data └── .keep ├── log └── .keep ├── tmp ├── .keep └── pids │ └── .keep ├── vendor └── .keep ├── lib ├── assets │ └── .keep ├── tasks │ ├── .keep │ ├── search.rake │ ├── orgs.rake │ ├── cleanup.rake │ ├── contributions.rake │ ├── stewards.rake │ ├── repositories.rake │ ├── issues.rake │ ├── research.rake │ ├── contributors.rake │ ├── upgrades.rake │ ├── backfill.rake │ └── changelog.rake └── templates │ └── erb │ └── scaffold │ └── _form.html.erb ├── 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 │ ├── auth_token_test.rb │ ├── tag_test.rb │ ├── search_query_test.rb │ ├── search_result_test.rb │ ├── contributor_test.rb │ ├── version_test.rb │ ├── dependency_test.rb │ ├── manifest_test.rb │ ├── event_test.rb │ ├── issue_test.rb │ ├── repository_dependency_test.rb │ ├── package_test.rb │ ├── organization_test.rb │ └── repository_test.rb ├── system │ └── .keep ├── controllers │ ├── .keep │ ├── events_controller_test.rb │ ├── issues_controller_test.rb │ ├── tags_controller_test.rb │ ├── packages_controller_test.rb │ ├── versions_controller_test.rb │ ├── manifests_controller_test.rb │ ├── repositors_controller_test.rb │ ├── contributors_controller_test.rb │ ├── organizations_controller_test.rb │ ├── repositories_controller_test.rb │ ├── search_queries_controller_test.rb │ ├── search_results_controller_test.rb │ └── repository_dependencies_controller_test.rb ├── fixtures │ ├── .keep │ ├── files │ │ └── .keep │ ├── auth_tokens.yml │ ├── search_queries.yml │ ├── search_results.yml │ ├── tags.yml │ ├── contributors.yml │ ├── manifests.yml │ ├── organizations.yml │ ├── events.yml │ ├── dependencies.yml │ ├── versions.yml │ ├── repository_dependencies.yml │ ├── issues.yml │ ├── repositories.yml │ └── packages.yml ├── integration │ └── .keep ├── application_system_test_case.rb └── test_helper.rb ├── .ruby-version ├── app ├── assets │ ├── images │ │ └── .keep │ ├── config │ │ └── manifest.js │ └── javascripts │ │ └── application.js ├── models │ ├── concerns │ │ ├── .keep │ │ ├── releaseable.rb │ │ ├── dependency_checks.rb │ │ └── releases.rb │ ├── application_record.rb │ ├── search_result.rb │ ├── manifest.rb │ ├── auth_token.rb │ ├── package_manager │ │ ├── clojars.rb │ │ ├── carthage.rb │ │ └── docker.rb │ ├── tag.rb │ ├── dependency.rb │ └── repository_dependency.rb ├── controllers │ ├── concerns │ │ └── .keep │ ├── admin │ │ ├── application_controller.rb │ │ ├── contributors_controller.rb │ │ └── organizations_controller.rb │ ├── versions_controller.rb │ ├── users_controller.rb │ ├── forage_controller.rb │ ├── application_controller.rb │ └── sessions_controller.rb ├── views │ ├── events │ │ ├── _watch_event.html.erb │ │ ├── _member_event.html.erb │ │ ├── _public_event.html.erb │ │ ├── _delete_event.html.erb │ │ ├── _fork_event.html.erb │ │ ├── _commit_comment_event.html.erb │ │ ├── _issue_comment_event.html.erb │ │ ├── _gollum_event.html.erb │ │ ├── _issues_event.html.erb │ │ ├── _pull_request_review_event.html.erb │ │ ├── _release_event.html.erb │ │ ├── _create_event.html.erb │ │ ├── _pull_request_review_comment_event.html.erb │ │ ├── _pull_request_event.html.erb │ │ ├── newsletter.html.erb │ │ ├── index.rss.builder │ │ ├── _push_event.html.erb │ │ ├── _newsletter_releases_markdown.html.erb │ │ ├── _sort_filter.html.erb │ │ └── _event.html.erb │ ├── layouts │ │ ├── mailer.text.erb │ │ └── mailer.html.erb │ ├── admin │ │ ├── contributors │ │ │ ├── edit.html.erb │ │ │ ├── new.html.erb │ │ │ ├── _form.html.erb │ │ │ ├── index.html.erb │ │ │ └── _contributor.html.erb │ │ └── organizations │ │ │ ├── new.html.erb │ │ │ ├── edit.html.erb │ │ │ ├── _form.html.erb │ │ │ ├── _tabs.html.erb │ │ │ ├── index.html.erb │ │ │ └── _organization.html.erb │ ├── packages │ │ ├── search.html.erb │ │ ├── index.rss.builder │ │ ├── _usage.html.erb │ │ ├── _external_dependents.html.erb │ │ ├── _package.html.erb │ │ ├── _internal_dependents.html.erb │ │ ├── index.html.erb │ │ └── outdated.html.erb │ ├── home │ │ └── sitemap.html.erb │ ├── organizations │ │ ├── _events.html.erb │ │ ├── _search.html.erb │ │ ├── index.rss.builder │ │ ├── _organization.html.erb │ │ ├── collabs.html.erb │ │ ├── _packages.html.erb │ │ └── dependencies.html.erb │ ├── repositories │ │ ├── _search.html.erb │ │ ├── _packages.html.erb │ │ ├── _score_breakdown.html.erb │ │ ├── _events.html.erb │ │ ├── index.rss.builder │ │ ├── hackathons.html.erb │ │ ├── discover.html.erb │ │ ├── contributors.html.erb │ │ ├── _internal_direct.html.erb │ │ ├── _internal_indirect.html.erb │ │ ├── _dependencies.html.erb │ │ └── _repository.html.erb │ ├── versions │ │ ├── show.html.erb │ │ ├── _version.html.erb │ │ ├── index.html.erb │ │ └── index.rss.builder │ ├── issues │ │ ├── all.rss.builder │ │ ├── _collabs.html.erb │ │ ├── _contributors.html.erb │ │ ├── _range_filter.html.erb │ │ ├── _state_filter.html.erb │ │ ├── _type_filter.html.erb │ │ ├── _excludable_filter.html.erb │ │ └── _sort_filter.html.erb │ ├── search │ │ ├── index.rss.builder │ │ ├── highlights.html.erb │ │ └── index.html.erb │ ├── contributors │ │ ├── _contributor.html.erb │ │ ├── show.html.erb │ │ ├── index.html.erb │ │ ├── collabs.html.erb │ │ └── new.html.erb │ ├── users │ │ ├── hackathons.html.erb │ │ └── show.html.erb │ └── search_results │ │ └── _search_result.html.erb ├── workers │ ├── org_setup_worker.rb │ ├── repo_setup_worker.rb │ ├── contributor_download_worker.rb │ ├── repository_download_worker.rb │ └── backfill_worker.rb └── services │ ├── gitlab_url_parser.rb │ ├── bitbucket_url_parser.rb │ └── github_url_parser.rb ├── config ├── initializers │ ├── bibliothecary.rb │ ├── redis.rb │ ├── omniauth.rb │ ├── mime_types.rb │ ├── cors.rb │ ├── filter_parameter_logging.rb │ ├── application_controller_renderer.rb │ ├── cookies_serializer.rb │ ├── bugsnag.rb │ ├── pagy.rb │ ├── backtrace_silencers.rb │ ├── wrap_parameters.rb │ ├── assets.rb │ ├── inflections.rb │ └── content_security_policy.rb ├── sidekiq.yml ├── spring.rb ├── environment.rb ├── boot.rb ├── credentials.yml.enc ├── locales │ ├── simple_form.en.yml │ └── en.yml ├── application.rb ├── puma.rb └── environments │ ├── test.rb │ └── development.rb ├── package.json ├── Procfile ├── config.ru ├── db ├── migrate │ ├── 20200210153918_add_org_to_issues.rb │ ├── 20220630100708_add_core_to_events.rb │ ├── 20220630102511_add_bot_to_events.rb │ ├── 20200427094656_add_draft_to_issues.rb │ ├── 20240213202203_drop_dependency_events.rb │ ├── 20200321182941_add_locked_to_issues.rb │ ├── 20240213204535_drop_pmf_index.rb │ ├── 20240213205402_remove_pmf_from_events.rb │ ├── 20200424150239_add_merged_at_to_issues.rb │ ├── 20200514152951_add_github_id_to_issues.rb │ ├── 20200723123556_add_org_index_to_events.rb │ ├── 20221019100045_add_pmf_index_to_events.rb │ ├── 20240213203557_drop_pmf_active_repo_dates.rb │ ├── 20200428091703_add_etag_to_repositories.rb │ ├── 20200812101306_add_etag_to_organizations.rb │ ├── 20210304125036_add_created_at_index_on_events.rb │ ├── 20200429150809_add_response_time_to_issues.rb │ ├── 20200603101333_add_events_indexes.rb │ ├── 20200702145622_add_last_synced_at_to_issues.rb │ ├── 20200825101811_add_repository_id_index_to_events.rb │ ├── 20220712105536_add_pmf_to_events.rb │ ├── 20200214102051_add_collabs_to_issues.rb │ ├── 20200309105246_add_labels_to_issues.rb │ ├── 20200729125520_add_score_to_repositories.rb │ ├── 20220224133551_add_github_id_index_on_repositories.rb │ ├── 20220224133608_add_html_url_index_on_search_results.rb │ ├── 20200427130444_add_first_response_at_to_issues.rb │ ├── 20200512142647_add_outdated_percentage_to_packages.rb │ ├── 20200827145235_add_triage_to_repositories.rb │ ├── 20210408154253_add_board_ids_to_issues.rb │ ├── 20210408102457_add_partner_to_organization.rb │ ├── 20210513134413_add_repository_dependencies_index.rb │ ├── 20220630121629_add_contributors_index.rb │ ├── 20200220174016_add_index_on_html_url.rb │ ├── 20200706105811_add_docker_hub_org_to_organizations.rb │ ├── 20200812152608_add_topics_to_repositories.rb │ ├── 20200907144549_add_html_url_unique_constraint_to_issues.rb │ ├── 20210504143325_add_sol_files_to_repositories.rb │ ├── 20211027085630_add_keyword_matches_to_repositories.rb │ ├── 20220223180022_add_github_username_index_on_contributors.rb │ ├── 20211115153014_add_discovered_to_repositories.rb │ ├── 20220923140234_add_collabs_to_contributors.rb │ ├── 20200430133327_add_created_at_index_to_issues.rb │ ├── 20200501173219_add_license_normalized_to_packages.rb │ ├── 20210316112110_add_last_events_sync_at_to_repositories.rb │ ├── 20200309103547_add_milestone_to_issues.rb │ ├── 20200518093851_add_direct_to_repository_dependencies.rb │ ├── 20211115160932_create_auth_tokens.rb │ ├── 20210623115717_add_review_time_to_issues.rb │ ├── 20200512103305_add_collab_dependent_repositories_count_to_packages.rb │ ├── 20200212161037_add_org_index_to_issues.rb │ ├── 20200720144706_add_counters_to_orgs.rb │ ├── 20210325151130_add_missing_indexes.rb │ ├── 20211103112236_add_last_events_sync_at_to_contributors.rb │ ├── 20210301142256_add_latest_commit_sha_to_repositories.rb │ ├── 20210302134323_add_first_added_internal_deps_to_repositories.rb │ ├── 20200707081456_create_search_queries.rb │ ├── 20200501161411_create_tags.rb │ ├── 20221019102726_create_pmf_active_repo_dates.rb │ ├── 20200615115133_create_contributors.rb │ ├── 20200615133314_create_organizations.rb │ ├── 20200501145035_create_manifests.rb │ ├── 20210104132331_add_internal_dependency_arrays_to_repositories.rb │ ├── 20200501161338_create_versions.rb │ ├── 20200501162244_create_dependencies.rb │ ├── 20200825094122_add_file_fields_to_repositories.rb │ ├── 20200210150021_create_issues.rb │ ├── 20200707083613_create_search_results.rb │ ├── 20200430152230_create_events.rb │ ├── 20200501145133_create_repository_dependencies.rb │ ├── 20200505133355_add_package_indexes.rb │ ├── 20200904122106_add_details_to_organizations.rb │ ├── 20200428083829_create_repositories.rb │ ├── 20210219150421_create_dependency_events.rb │ └── 20200501155652_create_packages.rb └── seeds.rb ├── Rakefile ├── bin ├── rake ├── rails ├── yarn ├── docker-start ├── spring └── setup ├── .github ├── workflows │ ├── stale.yml │ ├── generated-pr.yml │ └── ci.yml └── dependabot.yml ├── .gitignore ├── docker-compose.yml ├── Dockerfile ├── Gemfile └── app.json /data/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /tmp/pids/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 3.4.7 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 | -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/views/events/_watch_event.html.erb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/views/events/_member_event.html.erb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/views/events/_public_event.html.erb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /config/initializers/bibliothecary.rb: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.text.erb: -------------------------------------------------------------------------------- 1 | <%= yield %> 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { "engines" : { "node": "15.4.0" } } 2 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: / 3 | -------------------------------------------------------------------------------- /config/sidekiq.yml: -------------------------------------------------------------------------------- 1 | --- 2 | :queues: 3 | - critical 4 | - default 5 | -------------------------------------------------------------------------------- /app/views/events/_delete_event.html.erb: -------------------------------------------------------------------------------- 1 |

2 | <%= event.payload['ref'] %> 3 |

4 | -------------------------------------------------------------------------------- /config/initializers/redis.rb: -------------------------------------------------------------------------------- 1 | uri = ENV['REDISCLOUD_URL'] || 'redis://localhost:6379/' 2 | REDIS = Redis.new(url: uri) -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: bundle exec puma -C config/puma.rb 2 | worker: bundle exec sidekiq 3 | release: bundle exec rake db:migrate 4 | -------------------------------------------------------------------------------- /config/spring.rb: -------------------------------------------------------------------------------- 1 | Spring.watch( 2 | ".ruby-version", 3 | ".rbenv-vars", 4 | "tmp/restart.txt", 5 | "tmp/caching-dev.txt" 6 | ) 7 | -------------------------------------------------------------------------------- /app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_tree ../images 2 | //= link_directory ../stylesheets .css 3 | //= link_directory ../javascripts .js 4 | -------------------------------------------------------------------------------- /app/views/admin/contributors/edit.html.erb: -------------------------------------------------------------------------------- 1 | <%= render 'admin/organizations/tabs' %> 2 | 3 |

Edit Contributor

4 | 5 | <%= render 'form' %> 6 | -------------------------------------------------------------------------------- /app/views/admin/contributors/new.html.erb: -------------------------------------------------------------------------------- 1 | <%= render 'admin/organizations/tabs' %> 2 | 3 |

New Contributor

4 | 5 | <%= render 'form' %> 6 | -------------------------------------------------------------------------------- /app/views/admin/organizations/new.html.erb: -------------------------------------------------------------------------------- 1 | <%= render 'admin/organizations/tabs' %> 2 | 3 |

New Organization

4 | 5 | <%= render 'form' %> 6 | -------------------------------------------------------------------------------- /app/views/events/_fork_event.html.erb: -------------------------------------------------------------------------------- 1 |

2 | <%= link_to event.payload['forkee']['full_name'], event.payload['forkee']['html_url'] %> 3 |

4 | -------------------------------------------------------------------------------- /app/controllers/admin/application_controller.rb: -------------------------------------------------------------------------------- 1 | class Admin::ApplicationController < ApplicationController 2 | before_action :authenticate_user! 3 | end 4 | -------------------------------------------------------------------------------- /app/views/admin/organizations/edit.html.erb: -------------------------------------------------------------------------------- 1 | <%= render 'admin/organizations/tabs' %> 2 | 3 |

Edit Organization

4 | 5 | <%= render 'form' %> 6 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/tasks/search.rake: -------------------------------------------------------------------------------- 1 | namespace :search do 2 | desc 'peform all search queries' 3 | task run_all: :environment do 4 | SearchQuery.run_all 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /app/workers/org_setup_worker.rb: -------------------------------------------------------------------------------- 1 | class OrgSetupWorker 2 | include Sidekiq::Worker 3 | 4 | def perform(id) 5 | Organization.find(id).try(:setup) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/workers/repo_setup_worker.rb: -------------------------------------------------------------------------------- 1 | class RepoSetupWorker 2 | include Sidekiq::Worker 3 | 4 | def perform(id) 5 | Repository.find(id).try(:setup) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative 'application' 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /app/views/packages/search.html.erb: -------------------------------------------------------------------------------- 1 |

Packages matching "<%= params[:query] %>"

2 | 3 | <%= render @packages %> 4 | <%== pagy_bootstrap_nav(@pagy) if @pagy.pages > 1 %> 5 | -------------------------------------------------------------------------------- /db/migrate/20200210153918_add_org_to_issues.rb: -------------------------------------------------------------------------------- 1 | class AddOrgToIssues < ActiveRecord::Migration[6.0] 2 | def change 3 | add_column :issues, :org, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20220630100708_add_core_to_events.rb: -------------------------------------------------------------------------------- 1 | class AddCoreToEvents < ActiveRecord::Migration[7.0] 2 | def change 3 | add_column :events, :core, :boolean 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20220630102511_add_bot_to_events.rb: -------------------------------------------------------------------------------- 1 | class AddBotToEvents < ActiveRecord::Migration[7.0] 2 | def change 3 | add_column :events, :bot, :boolean 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /test/models/auth_token_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class AuthTokenTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20200427094656_add_draft_to_issues.rb: -------------------------------------------------------------------------------- 1 | class AddDraftToIssues < ActiveRecord::Migration[6.0] 2 | def change 3 | add_column :issues, :draft, :boolean 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20240213202203_drop_dependency_events.rb: -------------------------------------------------------------------------------- 1 | class DropDependencyEvents < ActiveRecord::Migration[7.1] 2 | def change 3 | drop_table :dependency_events 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /test/models/tag_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class TagTest < ActiveSupport::TestCase 4 | context 'associations' do 5 | should belong_to(:repository) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20200321182941_add_locked_to_issues.rb: -------------------------------------------------------------------------------- 1 | class AddLockedToIssues < ActiveRecord::Migration[6.0] 2 | def change 3 | add_column :issues, :locked, :boolean 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20240213204535_drop_pmf_index.rb: -------------------------------------------------------------------------------- 1 | class DropPmfIndex < ActiveRecord::Migration[7.1] 2 | def change 3 | remove_index :events, name: "index_events_on_pmf" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20240213205402_remove_pmf_from_events.rb: -------------------------------------------------------------------------------- 1 | class RemovePmfFromEvents < ActiveRecord::Migration[7.1] 2 | def change 3 | remove_column :events, :pmf, :boolean 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20200424150239_add_merged_at_to_issues.rb: -------------------------------------------------------------------------------- 1 | class AddMergedAtToIssues < ActiveRecord::Migration[6.0] 2 | def change 3 | add_column :issues, :merged_at, :datetime 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20200514152951_add_github_id_to_issues.rb: -------------------------------------------------------------------------------- 1 | class AddGithubIdToIssues < ActiveRecord::Migration[6.0] 2 | def change 3 | add_column :issues, :github_id, :bigint 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20200723123556_add_org_index_to_events.rb: -------------------------------------------------------------------------------- 1 | class AddOrgIndexToEvents < ActiveRecord::Migration[6.0] 2 | def change 3 | add_index :events, [:org, :event_type] 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20221019100045_add_pmf_index_to_events.rb: -------------------------------------------------------------------------------- 1 | class AddPmfIndexToEvents < ActiveRecord::Migration[7.0] 2 | def change 3 | add_index :events, 'date(created_at)' 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20240213203557_drop_pmf_active_repo_dates.rb: -------------------------------------------------------------------------------- 1 | class DropPmfActiveRepoDates < ActiveRecord::Migration[7.1] 2 | def change 3 | drop_table :pmf_active_repo_dates 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /test/fixtures/auth_tokens.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 2 | 3 | one: 4 | token: MyString 5 | 6 | two: 7 | token: MyString 8 | -------------------------------------------------------------------------------- /app/views/home/sitemap.html.erb: -------------------------------------------------------------------------------- 1 |

Sitemap

2 | 3 | -------------------------------------------------------------------------------- /config/initializers/omniauth.rb: -------------------------------------------------------------------------------- 1 | Rails.application.config.middleware.use OmniAuth::Builder do 2 | provider :github, ENV['GITHUB_CLIENT_ID'], ENV['GITHUB_CLIENT_SECRET'], scope: 'read:org' 3 | end 4 | -------------------------------------------------------------------------------- /db/migrate/20200428091703_add_etag_to_repositories.rb: -------------------------------------------------------------------------------- 1 | class AddEtagToRepositories < ActiveRecord::Migration[6.0] 2 | def change 3 | add_column :repositories, :etag, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20200812101306_add_etag_to_organizations.rb: -------------------------------------------------------------------------------- 1 | class AddEtagToOrganizations < ActiveRecord::Migration[6.0] 2 | def change 3 | add_column :organizations, :etag, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20210304125036_add_created_at_index_on_events.rb: -------------------------------------------------------------------------------- 1 | class AddCreatedAtIndexOnEvents < ActiveRecord::Migration[6.1] 2 | def change 3 | add_index :events, :created_at 4 | end 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 | -------------------------------------------------------------------------------- /test/controllers/events_controller_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class EventsControllerTest < ActionDispatch::IntegrationTest 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/controllers/issues_controller_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class IssuesControllerTest < ActionDispatch::IntegrationTest 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/controllers/tags_controller_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class TagsControllerTest < ActionDispatch::IntegrationTest 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/models/search_query_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class SearchQueryTest < ActiveSupport::TestCase 4 | context 'associations' do 5 | should have_many(:search_results) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /test/models/search_result_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class SearchResultTest < ActiveSupport::TestCase 4 | context 'associations' do 5 | should belong_to(:search_query) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/views/organizations/_events.html.erb: -------------------------------------------------------------------------------- 1 | <% if @events.any? %> 2 | <%= render @events %> 3 | <% else %> 4 |

No events found.

5 | <% end %> 6 | <%== pagy_bootstrap_nav(@pagy) if @pagy.pages > 1 %> 7 | -------------------------------------------------------------------------------- /app/workers/contributor_download_worker.rb: -------------------------------------------------------------------------------- 1 | class ContributorDownloadWorker 2 | include Sidekiq::Worker 3 | 4 | def perform(github_username) 5 | Contributor.download(github_username) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20200429150809_add_response_time_to_issues.rb: -------------------------------------------------------------------------------- 1 | class AddResponseTimeToIssues < ActiveRecord::Migration[6.0] 2 | def change 3 | add_column :issues, :response_time, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20200603101333_add_events_indexes.rb: -------------------------------------------------------------------------------- 1 | class AddEventsIndexes < ActiveRecord::Migration[6.0] 2 | def change 3 | add_index :events, :actor 4 | add_index :events, :github_id 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20200702145622_add_last_synced_at_to_issues.rb: -------------------------------------------------------------------------------- 1 | class AddLastSyncedAtToIssues < ActiveRecord::Migration[6.0] 2 | def change 3 | add_column :issues, :last_synced_at, :datetime 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20200825101811_add_repository_id_index_to_events.rb: -------------------------------------------------------------------------------- 1 | class AddRepositoryIdIndexToEvents < ActiveRecord::Migration[6.0] 2 | def change 3 | add_index :events, :repository_id 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20220712105536_add_pmf_to_events.rb: -------------------------------------------------------------------------------- 1 | class AddPmfToEvents < ActiveRecord::Migration[7.0] 2 | def change 3 | add_column :events, :pmf, :boolean 4 | add_index :events, :pmf 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /test/controllers/packages_controller_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class PackagesControllerTest < ActionDispatch::IntegrationTest 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/controllers/versions_controller_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class VersionsControllerTest < ActionDispatch::IntegrationTest 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /config/initializers/cors.rb: -------------------------------------------------------------------------------- 1 | Rails.application.config.middleware.insert_before 0, Rack::Cors do 2 | allow do 3 | origins '*' 4 | resource '*.json', headers: :any, methods: [:get, :patch] 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20200214102051_add_collabs_to_issues.rb: -------------------------------------------------------------------------------- 1 | class AddCollabsToIssues < ActiveRecord::Migration[6.0] 2 | def change 3 | add_column :issues, :collabs, :string, default: [], array: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20200309105246_add_labels_to_issues.rb: -------------------------------------------------------------------------------- 1 | class AddLabelsToIssues < ActiveRecord::Migration[6.0] 2 | def change 3 | add_column :issues, :labels, :string, default: [], array: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20200729125520_add_score_to_repositories.rb: -------------------------------------------------------------------------------- 1 | class AddScoreToRepositories < ActiveRecord::Migration[6.0] 2 | def change 3 | add_column :repositories, :score, :integer, default: 0 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20220224133551_add_github_id_index_on_repositories.rb: -------------------------------------------------------------------------------- 1 | class AddGithubIdIndexOnRepositories < ActiveRecord::Migration[7.0] 2 | def change 3 | add_index :repositories, :github_id 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20220224133608_add_html_url_index_on_search_results.rb: -------------------------------------------------------------------------------- 1 | class AddHtmlUrlIndexOnSearchResults < ActiveRecord::Migration[7.0] 2 | def change 3 | add_index :search_results, :html_url 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /test/controllers/manifests_controller_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class ManifestsControllerTest < ActionDispatch::IntegrationTest 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/controllers/repositors_controller_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class RepositorsControllerTest < ActionDispatch::IntegrationTest 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /app/views/events/_commit_comment_event.html.erb: -------------------------------------------------------------------------------- 1 | <%== parse_markdown event.payload['comment']['body'] %> 2 | 3 |

4 | <%= link_to 'View on GitHub', event.payload['comment']['html_url'], target: :_blank %> 5 |

6 | -------------------------------------------------------------------------------- /app/views/events/_issue_comment_event.html.erb: -------------------------------------------------------------------------------- 1 | <%== parse_markdown event.payload['comment']['body'] %> 2 | 3 |

4 | <%= link_to 'View on GitHub', event.payload['comment']['html_url'], target: :_blank %> 5 |

6 | -------------------------------------------------------------------------------- /app/views/organizations/_search.html.erb: -------------------------------------------------------------------------------- 1 | <% if @results.any? %> 2 | <%= render @results %> 3 | <%== pagy_bootstrap_nav(@pagy) if @pagy.pages > 1 %> 4 | <% else %> 5 |

No search results found.

6 | <% end %> 7 | -------------------------------------------------------------------------------- /db/migrate/20200427130444_add_first_response_at_to_issues.rb: -------------------------------------------------------------------------------- 1 | class AddFirstResponseAtToIssues < ActiveRecord::Migration[6.0] 2 | def change 3 | add_column :issues, :first_response_at, :datetime 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20200512142647_add_outdated_percentage_to_packages.rb: -------------------------------------------------------------------------------- 1 | class AddOutdatedPercentageToPackages < ActiveRecord::Migration[6.0] 2 | def change 3 | add_column :packages, :outdated, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20200827145235_add_triage_to_repositories.rb: -------------------------------------------------------------------------------- 1 | class AddTriageToRepositories < ActiveRecord::Migration[6.0] 2 | def change 3 | add_column :repositories, :triage, :boolean, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20210408154253_add_board_ids_to_issues.rb: -------------------------------------------------------------------------------- 1 | class AddBoardIdsToIssues < ActiveRecord::Migration[6.1] 2 | def change 3 | add_column :issues, :board_ids, :integer, default: [], array: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /test/controllers/contributors_controller_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class ContributorsControllerTest < ActionDispatch::IntegrationTest 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/controllers/organizations_controller_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class OrganizationsControllerTest < ActionDispatch::IntegrationTest 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/controllers/repositories_controller_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class RepositoriesControllerTest < ActionDispatch::IntegrationTest 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/controllers/search_queries_controller_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class SearchQueriesControllerTest < ActionDispatch::IntegrationTest 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/controllers/search_results_controller_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class SearchResultsControllerTest < ActionDispatch::IntegrationTest 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /app/views/events/_gollum_event.html.erb: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /app/views/events/_issues_event.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= link_to event.payload['issue']['title'], event.payload['issue']['html_url'], target: :_blank %> 3 |
4 | 5 | <%== parse_markdown event.payload['issue']['body'] %> 6 | -------------------------------------------------------------------------------- /app/views/events/_pull_request_review_event.html.erb: -------------------------------------------------------------------------------- 1 | <%== parse_markdown event.payload['review']['body'] %> 2 | 3 |

4 | <%= link_to 'View on GitHub', event.payload['review']['html_url'], target: :_blank %> 5 |

6 | -------------------------------------------------------------------------------- /db/migrate/20210408102457_add_partner_to_organization.rb: -------------------------------------------------------------------------------- 1 | class AddPartnerToOrganization < ActiveRecord::Migration[6.1] 2 | def change 3 | add_column :organizations, :partner, :boolean, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20210513134413_add_repository_dependencies_index.rb: -------------------------------------------------------------------------------- 1 | class AddRepositoryDependenciesIndex < ActiveRecord::Migration[6.1] 2 | def change 3 | add_index :repository_dependencies, :package_name 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20220630121629_add_contributors_index.rb: -------------------------------------------------------------------------------- 1 | class AddContributorsIndex < ActiveRecord::Migration[7.0] 2 | def change 3 | add_index :contributors, :core 4 | add_index :contributors, :bot 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /app/views/events/_release_event.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= link_to event.payload['release']['name'], event.payload['release']['html_url'], target: :_blank %> 3 |
4 | 5 | <%== parse_markdown event.payload['release']['body'] %> 6 | -------------------------------------------------------------------------------- /db/migrate/20200220174016_add_index_on_html_url.rb: -------------------------------------------------------------------------------- 1 | class AddIndexOnHtmlUrl < ActiveRecord::Migration[6.0] 2 | def change 3 | add_index :issues, :collabs, using: 'gin' 4 | add_index :issues, :html_url 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20200706105811_add_docker_hub_org_to_organizations.rb: -------------------------------------------------------------------------------- 1 | class AddDockerHubOrgToOrganizations < ActiveRecord::Migration[6.0] 2 | def change 3 | add_column :organizations, :docker_hub_org, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20200812152608_add_topics_to_repositories.rb: -------------------------------------------------------------------------------- 1 | class AddTopicsToRepositories < ActiveRecord::Migration[6.0] 2 | def change 3 | add_column :repositories, :topics, :string, default: [], array: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20200907144549_add_html_url_unique_constraint_to_issues.rb: -------------------------------------------------------------------------------- 1 | class AddHtmlUrlUniqueConstraintToIssues < ActiveRecord::Migration[6.0] 2 | def change 3 | add_index :issues, :html_url, unique: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20210504143325_add_sol_files_to_repositories.rb: -------------------------------------------------------------------------------- 1 | class AddSolFilesToRepositories < ActiveRecord::Migration[6.1] 2 | def change 3 | add_column :repositories, :sol_files, :boolean, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20211027085630_add_keyword_matches_to_repositories.rb: -------------------------------------------------------------------------------- 1 | class AddKeywordMatchesToRepositories < ActiveRecord::Migration[6.1] 2 | def change 3 | add_column :repositories, :keyword_matches, :text 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20220223180022_add_github_username_index_on_contributors.rb: -------------------------------------------------------------------------------- 1 | class AddGithubUsernameIndexOnContributors < ActiveRecord::Migration[7.0] 2 | def change 3 | add_index :contributors, :github_username 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /test/models/contributor_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class ContributorTest < ActiveSupport::TestCase 4 | context 'associations' do 5 | should have_many(:events) 6 | should have_many(:issues) 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /test/models/version_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class VersionTest < ActiveSupport::TestCase 4 | context 'associations' do 5 | should belong_to(:package) 6 | should have_many(:dependencies) 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /app/views/admin/contributors/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= simple_form_for [:admin,@contributor] do |f| %> 2 | <%= f.input :github_username %> 3 | <%= f.input :core %> 4 | <%= f.input :bot %> 5 | <%= f.button :submit %> 6 | <% end %> 7 | -------------------------------------------------------------------------------- /app/views/events/_create_event.html.erb: -------------------------------------------------------------------------------- 1 |

2 | <%= link_to event.payload['ref'], "https://github.com/#{event.repository_full_name}/tree/#{event.payload['ref']}", target: :_blank %> - <%= event.payload['description'] %> 3 |

4 | -------------------------------------------------------------------------------- /app/views/events/_pull_request_review_comment_event.html.erb: -------------------------------------------------------------------------------- 1 | <%== parse_markdown event.payload['comment']['body'] %> 2 | 3 |

4 | <%= link_to 'View on GitHub', event.payload['comment']['html_url'], target: :_blank %> 5 |

6 | -------------------------------------------------------------------------------- /app/views/repositories/_search.html.erb: -------------------------------------------------------------------------------- 1 | <% if @results.any? %> 2 | <%= render @results %> 3 | <%== pagy_bootstrap_nav(@results_pagy) if @results_pagy.pages > 1 %> 4 | <% else %> 5 |

No search results found

6 | <% end %> 7 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /db/migrate/20211115153014_add_discovered_to_repositories.rb: -------------------------------------------------------------------------------- 1 | class AddDiscoveredToRepositories < ActiveRecord::Migration[6.1] 2 | def change 3 | add_column :repositories, :discovered, :boolean, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20220923140234_add_collabs_to_contributors.rb: -------------------------------------------------------------------------------- 1 | class AddCollabsToContributors < ActiveRecord::Migration[7.0] 2 | def change 3 | add_column :contributors, :collabs, :string, default: [], array: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20200430133327_add_created_at_index_to_issues.rb: -------------------------------------------------------------------------------- 1 | class AddCreatedAtIndexToIssues < ActiveRecord::Migration[6.0] 2 | def change 3 | add_index :issues, :created_at 4 | remove_index :issues, :html_url 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /test/controllers/repository_dependencies_controller_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class RepositoryDependenciesControllerTest < ActionDispatch::IntegrationTest 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/models/dependency_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class DependencyTest < ActiveSupport::TestCase 4 | context 'associations' do 5 | should belong_to(:package).optional 6 | should belong_to(:version) 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /app/workers/repository_download_worker.rb: -------------------------------------------------------------------------------- 1 | class RepositoryDownloadWorker 2 | include Sidekiq::Worker 3 | 4 | def perform(full_name_or_id, discovered) 5 | Repository.download(full_name_or_id, discovered: discovered) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20200501173219_add_license_normalized_to_packages.rb: -------------------------------------------------------------------------------- 1 | class AddLicenseNormalizedToPackages < ActiveRecord::Migration[6.0] 2 | def change 3 | add_column :packages, :license_normalized, :boolean, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20210316112110_add_last_events_sync_at_to_repositories.rb: -------------------------------------------------------------------------------- 1 | class AddLastEventsSyncAtToRepositories < ActiveRecord::Migration[6.1] 2 | def change 3 | add_column :repositories, :last_events_sync_at, :datetime 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /test/models/manifest_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class ManifestTest < ActiveSupport::TestCase 4 | context 'associations' do 5 | should belong_to(:repository) 6 | should have_many(:repository_dependencies) 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 | -------------------------------------------------------------------------------- /db/migrate/20200309103547_add_milestone_to_issues.rb: -------------------------------------------------------------------------------- 1 | class AddMilestoneToIssues < ActiveRecord::Migration[6.0] 2 | def change 3 | add_column :issues, :milestone_name, :string 4 | add_column :issues, :milestone_id, :integer 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20200518093851_add_direct_to_repository_dependencies.rb: -------------------------------------------------------------------------------- 1 | class AddDirectToRepositoryDependencies < ActiveRecord::Migration[6.0] 2 | def change 3 | add_column :repository_dependencies, :direct, :boolean, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20211115160932_create_auth_tokens.rb: -------------------------------------------------------------------------------- 1 | class CreateAuthTokens < ActiveRecord::Migration[6.1] 2 | def change 3 | create_table :auth_tokens do |t| 4 | t.string :token 5 | 6 | t.timestamps 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /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/20210623115717_add_review_time_to_issues.rb: -------------------------------------------------------------------------------- 1 | class AddReviewTimeToIssues < ActiveRecord::Migration[6.1] 2 | def change 3 | add_column :issues, :review_time, :integer 4 | add_column :issues, :review_requested_at, :datetime 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path('../spring', __FILE__) 4 | rescue LoadError => e 5 | raise unless e.message.include?('spring') 6 | end 7 | require_relative '../config/boot' 8 | require 'rake' 9 | Rake.application.run 10 | -------------------------------------------------------------------------------- /db/migrate/20200512103305_add_collab_dependent_repositories_count_to_packages.rb: -------------------------------------------------------------------------------- 1 | class AddCollabDependentRepositoriesCountToPackages < ActiveRecord::Migration[6.0] 2 | def change 3 | add_column :packages, :collab_dependent_repos_count, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/views/events/_pull_request_event.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= link_to event.payload['pull_request']['html_url'], target: :_blank do %> 3 | <%= event.payload['pull_request']['title'] %> 4 | <% end %> 5 |
6 | 7 | <%== parse_markdown event.payload['pull_request']['body'] %> 8 | -------------------------------------------------------------------------------- /db/migrate/20200212161037_add_org_index_to_issues.rb: -------------------------------------------------------------------------------- 1 | class AddOrgIndexToIssues < ActiveRecord::Migration[6.0] 2 | def change 3 | add_index :issues, :org 4 | add_index :issues, :state 5 | add_index :issues, :user 6 | add_index :issues, :repo_full_name 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20200720144706_add_counters_to_orgs.rb: -------------------------------------------------------------------------------- 1 | class AddCountersToOrgs < ActiveRecord::Migration[6.0] 2 | def change 3 | add_column :organizations, :search_results_count, :integer, default: 0 4 | add_column :organizations, :events_count, :integer, default: 0 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20210325151130_add_missing_indexes.rb: -------------------------------------------------------------------------------- 1 | class AddMissingIndexes < ActiveRecord::Migration[6.1] 2 | def change 3 | add_index :repositories, :full_name 4 | add_index :search_results, :created_at 5 | add_index :search_results, :repository_full_name 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20211103112236_add_last_events_sync_at_to_contributors.rb: -------------------------------------------------------------------------------- 1 | class AddLastEventsSyncAtToContributors < ActiveRecord::Migration[6.1] 2 | def change 3 | add_column :contributors, :last_events_sync_at, :datetime 4 | add_column :contributors, :etag, :string 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | self.abstract_class = true 3 | 4 | def self.fast_total 5 | ActiveRecord::Base.count_by_sql "SELECT (reltuples)::bigint FROM pg_class r WHERE relkind = 'r' AND relname = '#{self.table_name}'" 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /db/migrate/20210301142256_add_latest_commit_sha_to_repositories.rb: -------------------------------------------------------------------------------- 1 | class AddLatestCommitShaToRepositories < ActiveRecord::Migration[6.1] 2 | def change 3 | add_column :repositories, :latest_commit_sha, :string 4 | add_column :repositories, :latest_dependency_mine, :datetime 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /test/models/event_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class EventTest < ActiveSupport::TestCase 4 | context 'associations' do 5 | should belong_to(:repository).optional 6 | should belong_to(:contributor).optional 7 | should belong_to(:organization).optional 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /test/models/issue_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class IssueTest < ActiveSupport::TestCase 4 | context 'associations' do 5 | should belong_to(:repository).optional 6 | should belong_to(:contributor).optional 7 | should belong_to(:organization).optional 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /test/models/repository_dependency_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class RepositoryDependencyTest < ActiveSupport::TestCase 4 | context 'associations' do 5 | should belong_to(:repository) 6 | should belong_to(:package).optional 7 | should belong_to(:manifest) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path('../spring', __FILE__) 4 | rescue LoadError => e 5 | raise unless e.message.include?('spring') 6 | end 7 | APP_PATH = File.expand_path('../config/application', __dir__) 8 | require_relative '../config/boot' 9 | require 'rails/commands' 10 | -------------------------------------------------------------------------------- /config/initializers/bugsnag.rb: -------------------------------------------------------------------------------- 1 | if ENV['BUGSNAG_API_KEY'].present? 2 | Bugsnag.configure do |config| 3 | config.api_key = ENV['BUGSNAG_API_KEY'] 4 | config.release_stage = ENV["RAILS_ENV"] 5 | config.app_version = ENV['HEROKU_RELEASE_VERSION'] 6 | config.auto_capture_sessions = false 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | -------------------------------------------------------------------------------- /test/models/package_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class PackageTest < ActiveSupport::TestCase 4 | context 'associations' do 5 | should belong_to(:repository).optional 6 | should have_one(:organization) 7 | should have_many(:dependencies) 8 | should have_many(:versions) 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20210302134323_add_first_added_internal_deps_to_repositories.rb: -------------------------------------------------------------------------------- 1 | class AddFirstAddedInternalDepsToRepositories < ActiveRecord::Migration[6.1] 2 | def change 3 | add_column :repositories, :first_added_internal_deps, :datetime 4 | add_column :repositories, :last_internal_dep_removed, :datetime 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: Close Stale Issues 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' 6 | workflow_dispatch: 7 | 8 | permissions: 9 | issues: write 10 | pull-requests: write 11 | 12 | jobs: 13 | stale: 14 | uses: ipdxco/unified-github-workflows/.github/workflows/reusable-stale-issue.yml@v1 15 | -------------------------------------------------------------------------------- /config/initializers/pagy.rb: -------------------------------------------------------------------------------- 1 | require 'pagy/extras/bootstrap' 2 | require 'pagy/extras/array' 3 | require 'pagy/extras/headers' 4 | require 'pagy/extras/overflow' 5 | require 'pagy/extras/limit' 6 | 7 | Pagy::DEFAULT[:overflow] = :last_page 8 | 9 | Pagy::DEFAULT[:limit_max] = 1000 10 | Pagy::DEFAULT[:limit_param] = :per_page 11 | -------------------------------------------------------------------------------- /test/fixtures/search_queries.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 2 | 3 | one: 4 | query: MyString 5 | kind: MyString 6 | sort: MyString 7 | order: MyString 8 | 9 | two: 10 | query: MyString 11 | kind: MyString 12 | sort: MyString 13 | order: MyString 14 | -------------------------------------------------------------------------------- /db/migrate/20200707081456_create_search_queries.rb: -------------------------------------------------------------------------------- 1 | class CreateSearchQueries < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :search_queries do |t| 4 | t.string :query 5 | t.string :kind 6 | t.string :sort 7 | t.string :order 8 | 9 | t.timestamps 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /.github/workflows/generated-pr.yml: -------------------------------------------------------------------------------- 1 | name: Close Generated PRs 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' 6 | workflow_dispatch: 7 | 8 | permissions: 9 | issues: write 10 | pull-requests: write 11 | 12 | jobs: 13 | stale: 14 | uses: ipdxco/unified-github-workflows/.github/workflows/reusable-generated-pr.yml@v1 15 | -------------------------------------------------------------------------------- /app/services/gitlab_url_parser.rb: -------------------------------------------------------------------------------- 1 | class GitlabUrlParser < UrlParser 2 | private 3 | 4 | def full_domain 5 | 'https://gitlab.com' 6 | end 7 | 8 | def tlds 9 | %w(com) 10 | end 11 | 12 | def domain 13 | 'gitlab' 14 | end 15 | 16 | def remove_domain 17 | url.gsub!(/(gitlab.com)+?(:|\/)?/i, '') 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /app/views/admin/organizations/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%= simple_form_for [:admin,@organization] do |f| %> 2 | <%= f.input :name, label: 'Github Username' %> 3 | <%= f.input :internal %> 4 | <%= f.input :collaborator %> 5 | <%= f.input :partner %> 6 | <%= f.input :docker_hub_org, label: 'Docker Org Username' %> 7 | <%= f.button :submit %> 8 | <% end %> 9 | -------------------------------------------------------------------------------- /db/migrate/20200501161411_create_tags.rb: -------------------------------------------------------------------------------- 1 | class CreateTags < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :tags do |t| 4 | t.integer :repository_id 5 | t.string :name 6 | t.string :sha 7 | t.string :kind 8 | t.datetime :published_at 9 | 10 | t.timestamps 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /db/migrate/20221019102726_create_pmf_active_repo_dates.rb: -------------------------------------------------------------------------------- 1 | class CreatePmfActiveRepoDates < ActiveRecord::Migration[7.0] 2 | def change 3 | create_table :pmf_active_repo_dates do |t| 4 | t.date :date, index: true 5 | t.string :repository_full_names, array: true, default: [] 6 | 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /test/models/organization_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class OrganizationTest < ActiveSupport::TestCase 4 | context 'associations' do 5 | should have_many(:events) 6 | should have_many(:issues) 7 | should have_many(:repositories) 8 | should have_many(:repository_dependencies) 9 | should have_many(:packages) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20200615115133_create_contributors.rb: -------------------------------------------------------------------------------- 1 | class CreateContributors < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :contributors do |t| 4 | t.string :github_username 5 | t.integer :github_id 6 | t.boolean :core, default: false 7 | t.boolean :bot, default: false 8 | 9 | t.timestamps 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/tasks/orgs.rake: -------------------------------------------------------------------------------- 1 | namespace :orgs do 2 | desc 'update count fields on collabortor organizations' 3 | task update_counts: :environment do 4 | Organization.collaborator.each(&:update_counts) 5 | end 6 | 7 | desc 'setup and sync repositories for all orgs' 8 | task setup: :environment do 9 | Organization.internal.each(&:setup_async) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /test/fixtures/search_results.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 2 | 3 | one: 4 | search_query_id: 1 5 | kind: MyString 6 | repository_full_name: MyString 7 | html_url: MyString 8 | 9 | two: 10 | search_query_id: 1 11 | kind: MyString 12 | repository_full_name: MyString 13 | html_url: MyString 14 | -------------------------------------------------------------------------------- /bin/docker-start: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | db_host=${DATABASE_HOST:-database.service.ecosystem.internal} 5 | while ! nc -z $db_host ${DATABASE_PORT:-5432}; do 6 | echo "Waiting for database to be available..." 7 | sleep 1 8 | done 9 | 10 | bundle exec rake db:create db:migrate 11 | rm -rf tmp/pids 12 | 13 | # exec foreman start -d . 14 | exec bundle exec rails s -b 0.0.0.0 -------------------------------------------------------------------------------- /db/migrate/20200615133314_create_organizations.rb: -------------------------------------------------------------------------------- 1 | class CreateOrganizations < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :organizations do |t| 4 | t.string :name 5 | t.integer :github_id 6 | t.boolean :internal, default: false 7 | t.boolean :collaborator, default: false 8 | 9 | t.timestamps 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/services/bitbucket_url_parser.rb: -------------------------------------------------------------------------------- 1 | class BitbucketUrlParser < UrlParser 2 | private 3 | 4 | def full_domain 5 | 'https://bitbucket.org' 6 | end 7 | 8 | def tlds 9 | %w(com org) 10 | end 11 | 12 | def domain 13 | 'bitbucket' 14 | end 15 | 16 | def remove_domain 17 | url.gsub!(/(bitbucket.com|bitbucket.org)+?(:|\/)?/i, '') 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /db/migrate/20200501145035_create_manifests.rb: -------------------------------------------------------------------------------- 1 | class CreateManifests < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :manifests do |t| 4 | t.integer :repository_id 5 | t.string :platform 6 | t.string :filepath 7 | t.string :sha 8 | t.string :branch 9 | t.string :kind 10 | 11 | t.timestamps 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/views/versions/show.html.erb: -------------------------------------------------------------------------------- 1 | <% @page_title = "#{@version.number} - #{@package.platform.downcase}/#{@package.name}" %> 2 |

3 | <%= link_to "#{@package.platform.downcase}/#{@package.name}", @package %> - 4 | <%= @version.number %> 5 |

6 | 7 |

8 | Published: <%= @version.published_at %>
9 | Runtime dependencies: <%= @version.runtime_dependencies_count %> 10 |

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 | -------------------------------------------------------------------------------- /test/fixtures/tags.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 2 | 3 | one: 4 | repository_id: 1 5 | name: MyString 6 | sha: MyString 7 | kind: MyString 8 | published_at: 2020-05-01 17:14:11 9 | 10 | two: 11 | repository_id: 1 12 | name: MyString 13 | sha: MyString 14 | kind: MyString 15 | published_at: 2020-05-01 17:14:11 16 | -------------------------------------------------------------------------------- /app/services/github_url_parser.rb: -------------------------------------------------------------------------------- 1 | class GithubUrlParser < UrlParser 2 | private 3 | 4 | def full_domain 5 | 'https://github.com' 6 | end 7 | 8 | def tlds 9 | %w(io com org) 10 | end 11 | 12 | def domain 13 | 'github' 14 | end 15 | 16 | def remove_domain 17 | url.gsub!(/(github.io|github.com|github.org|raw.githubusercontent.com)+?(:|\/)?/i, '') 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /db/migrate/20210104132331_add_internal_dependency_arrays_to_repositories.rb: -------------------------------------------------------------------------------- 1 | class AddInternalDependencyArraysToRepositories < ActiveRecord::Migration[6.1] 2 | def change 3 | add_column :repositories, :direct_internal_dependency_package_ids, :integer, default: [], array: true 4 | add_column :repositories, :indirect_internal_dependency_package_ids, :integer, default: [], array: true 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /app/views/repositories/_packages.html.erb: -------------------------------------------------------------------------------- 1 | <% if @repository.packages.present? %> 2 | 9 | <% else %> 10 |

No packages found

11 | <% end %> 12 | -------------------------------------------------------------------------------- /db/migrate/20200501161338_create_versions.rb: -------------------------------------------------------------------------------- 1 | class CreateVersions < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :versions do |t| 4 | t.integer :package_id 5 | t.string :number 6 | t.datetime :published_at 7 | t.integer :runtime_dependencies_count 8 | t.string :spdx_expression 9 | t.jsonb :original_license 10 | 11 | t.timestamps 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /test/fixtures/contributors.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 2 | 3 | # This model initially had no columns defined. If you add columns to the 4 | # model remove the '{}' from the fixture names and add the columns immediately 5 | # below each fixture, per the syntax in the comments below 6 | # 7 | one: {} 8 | # column: value 9 | # 10 | two: {} 11 | # column: value 12 | -------------------------------------------------------------------------------- /test/fixtures/manifests.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 2 | 3 | one: 4 | repository_id: 1 5 | platform: MyString 6 | filepath: MyString 7 | sha: MyString 8 | branch: MyString 9 | kind: MyString 10 | 11 | two: 12 | repository_id: 1 13 | platform: MyString 14 | filepath: MyString 15 | sha: MyString 16 | branch: MyString 17 | kind: MyString 18 | -------------------------------------------------------------------------------- /test/fixtures/organizations.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 2 | 3 | # This model initially had no columns defined. If you add columns to the 4 | # model remove the '{}' from the fixture names and add the columns immediately 5 | # below each fixture, per the syntax in the comments below 6 | # 7 | one: {} 8 | # column: value 9 | # 10 | two: {} 11 | # column: value 12 | -------------------------------------------------------------------------------- /app/views/events/newsletter.html.erb: -------------------------------------------------------------------------------- 1 |

Newsletter

2 | 3 | <%= render_markdown(render('newsletter_releases_markdown').chomp) %> 4 | 5 |

Markdown

6 | 8 | 9 |

HTML

10 | 12 | -------------------------------------------------------------------------------- /app/views/repositories/_score_breakdown.html.erb: -------------------------------------------------------------------------------- 1 | <% ecosystem_parts = @repository.ecosystem_score_parts %> 2 | <% ecosystem_parts.each do |k,v| %> 3 | <%= k.to_s.humanize %>: <%= v.round(1) %>
4 | <% end %> 5 | <% if ecosystem_parts.values.sum > 0 %> 6 | <% @repository.popularity_score_parts.each do |k,v| %> 7 | <%= k.to_s.humanize %>: <%= v.round(1) %>
8 | <% end %> 9 | <% end %> 10 | Total: <%= @repository.score %> 11 | -------------------------------------------------------------------------------- /db/migrate/20200501162244_create_dependencies.rb: -------------------------------------------------------------------------------- 1 | class CreateDependencies < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :dependencies do |t| 4 | t.integer :version_id 5 | t.integer :package_id 6 | t.string :package_name 7 | t.string :platform 8 | t.string :kind 9 | t.boolean :optional, default: false 10 | t.string :requirements 11 | 12 | t.timestamps 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20200825094122_add_file_fields_to_repositories.rb: -------------------------------------------------------------------------------- 1 | class AddFileFieldsToRepositories < ActiveRecord::Migration[6.0] 2 | def change 3 | add_column :repositories, :readme_path, :string 4 | add_column :repositories, :code_of_conduct_path, :string 5 | add_column :repositories, :contributing_path, :string 6 | add_column :repositories, :license_path, :string 7 | add_column :repositories, :changelog_path, :string 8 | end 9 | end 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 | -------------------------------------------------------------------------------- /db/migrate/20200210150021_create_issues.rb: -------------------------------------------------------------------------------- 1 | class CreateIssues < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :issues do |t| 4 | t.string :title 5 | t.text :body 6 | t.string :state 7 | t.integer :number 8 | t.string :html_url 9 | t.integer :comments_count 10 | t.string :user 11 | t.string :repo_full_name 12 | t.datetime :closed_at 13 | 14 | t.timestamps 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /db/migrate/20200707083613_create_search_results.rb: -------------------------------------------------------------------------------- 1 | class CreateSearchResults < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :search_results do |t| 4 | t.integer :search_query_id 5 | t.string :kind 6 | t.string :repository_full_name 7 | t.string :org 8 | t.string :title 9 | t.string :html_url 10 | t.jsonb :text_matches, null: false, default: '{}' 11 | 12 | t.timestamps 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /config/credentials.yml.enc: -------------------------------------------------------------------------------- 1 | f9h8BxtDxsFPPeum+50jiYCPj2FouZAyMMLM9s96IsaUBecT3e6vnML6VfVuwISnGE4L2Y+bq6XuXuOTq72OO2lVoHiP3beHuJXY2ZuouTAZ+wZe2rS2xdGUmQQQVMDGXWxmyMyHz8w1f7phIM6zKmoZYRq+vVRV27CReuYzAgOYUcMWkRYW3LFpoK8KPbnlqEnRpMsSGza46Vl+/DcMIJJZ8B5XgOPG930lh3Uz9knSTHil+p2wq1j01wMRsvkqmHvV5J4v8oftHe9Ih1wV8cwuDHD22jjliDVu/v6m+2KY2PoSdTRcXYJmJXNaadDw24X2tkLNs+8iIog+kJBTCIEho8X/uTYd24LVX29c2/lDGHAEzb7PBnjDfgUrbfNlYEi0+Fm1i87jMEwsPFOb0N99DJxQkNJvH2BS--m0a5wtifa9BO24p6--yMuG6fY1hBw9Os4xI8pGmQ== -------------------------------------------------------------------------------- /db/migrate/20200430152230_create_events.rb: -------------------------------------------------------------------------------- 1 | class CreateEvents < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :events do |t| 4 | t.string :github_id 5 | t.string :actor 6 | t.string :event_type 7 | t.string :action 8 | t.integer :repository_id 9 | t.string :repository_full_name 10 | t.string :org 11 | t.jsonb :payload, null: false, default: '{}' 12 | 13 | t.timestamps 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /test/fixtures/events.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 2 | 3 | one: 4 | actor: MyString 5 | event_type: 6 | action: MyString 7 | repository_id: 1 8 | repository_full_name: MyString 9 | org: MyString 10 | payload: {} 11 | 12 | two: 13 | actor: MyString 14 | event_type: 15 | action: MyString 16 | repository_id: 1 17 | repository_full_name: MyString 18 | org: MyString 19 | payload: {} 20 | -------------------------------------------------------------------------------- /app/views/versions/_version.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | <%= link_to version.number, package_version_path(package_id: version.package.id, id: version.id) %> 5 | <%= version.published_at %> 6 |
7 | 8 | 9 | Runtime dependencies: <%= version.runtime_dependencies_count %> 10 | 11 |
12 |
13 | -------------------------------------------------------------------------------- /test/fixtures/dependencies.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 2 | 3 | one: 4 | version_id: 1 5 | package_id: 1 6 | package_name: MyString 7 | platform: MyString 8 | kind: MyString 9 | optional: false 10 | requirements: MyString 11 | 12 | two: 13 | version_id: 1 14 | package_id: 1 15 | package_name: MyString 16 | platform: MyString 17 | kind: MyString 18 | optional: false 19 | requirements: MyString 20 | -------------------------------------------------------------------------------- /test/fixtures/versions.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 2 | 3 | one: 4 | package_id: 1 5 | number: 1.0.0 6 | published_at: 2020-05-01 17:13:38 7 | runtime_dependencies_count: 1 8 | spdx_expression: MyString 9 | original_license: 10 | 11 | two: 12 | package_id: 1 13 | number: 1.0.1 14 | published_at: 2020-05-01 17:13:38 15 | runtime_dependencies_count: 1 16 | spdx_expression: MyString 17 | original_license: 18 | -------------------------------------------------------------------------------- /app/views/organizations/index.rss.builder: -------------------------------------------------------------------------------- 1 | xml.instruct! :xml, :version => "1.0" 2 | xml.rss :version => "2.0" do 3 | xml.channel do 4 | xml.title @page_title 5 | xml.description @page_description 6 | xml.link request.original_url 7 | 8 | @orgs.each do |org| 9 | xml.item do 10 | xml.title org.name 11 | xml.pubDate org.created_at.to_fs(:rfc822) 12 | xml.link org_url(org) 13 | xml.guid org_url(org) 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/views/events/index.rss.builder: -------------------------------------------------------------------------------- 1 | xml.instruct! :xml, :version => "1.0" 2 | xml.rss :version => "2.0" do 3 | xml.channel do 4 | xml.title @page_title 5 | xml.description @page_description 6 | xml.link request.original_url 7 | 8 | @events.each do |event| 9 | xml.item do 10 | xml.title event.title 11 | xml.pubDate event.created_at.to_fs(:rfc822) 12 | xml.link event.html_url 13 | xml.guid event.html_url 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/views/repositories/_events.html.erb: -------------------------------------------------------------------------------- 1 | <% if @events.any? %> 2 | <% @events.group_by{|e| e.created_at.beginning_of_week }.each do |week, events| %> 3 |

4 | <%= week.strftime('Week %V - %G') %> 5 | <%= pluralize events.length, 'event' %> 6 |

7 | <%= render events %> 8 | <% end %> 9 | 10 | <%== pagy_bootstrap_nav(@events_pagy) if @events_pagy.pages > 1 %> 11 | <% else %> 12 |

No events found

13 | <% end %> 14 | -------------------------------------------------------------------------------- /db/migrate/20200501145133_create_repository_dependencies.rb: -------------------------------------------------------------------------------- 1 | class CreateRepositoryDependencies < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :repository_dependencies do |t| 4 | t.integer :package_id 5 | t.integer :manifest_id 6 | t.integer :repository_id 7 | t.boolean :optional, default: false 8 | t.string :package_name 9 | t.string :platform 10 | t.string :requirements 11 | t.string :kind 12 | 13 | t.timestamps 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /app/models/search_result.rb: -------------------------------------------------------------------------------- 1 | class SearchResult < ApplicationRecord 2 | belongs_to :search_query 3 | 4 | scope :this_period, ->(period) { where('search_results.created_at > ?', period.days.ago) } 5 | scope :last_period, ->(period) { where('search_results.created_at > ?', (period*2).days.ago).where('search_results.created_at < ?', period.days.ago) } 6 | 7 | def repository_url 8 | "https://github.com/#{repository_full_name}" 9 | end 10 | 11 | def icon_url 12 | "https://github.com/#{org}.png" 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/views/issues/all.rss.builder: -------------------------------------------------------------------------------- 1 | xml.instruct! :xml, :version => "1.0" 2 | xml.rss :version => "2.0" do 3 | xml.channel do 4 | xml.title @page_title 5 | xml.description issues_title 6 | xml.link request.original_url 7 | 8 | @issues.each do |issue| 9 | xml.item do 10 | xml.title issue.title 11 | xml.description issue.body 12 | xml.pubDate issue.created_at.to_fs(:rfc822) 13 | xml.link issue.html_url 14 | xml.guid issue.html_url 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/views/versions/index.html.erb: -------------------------------------------------------------------------------- 1 |

2 | <% @page_title = "#{@package.platform.downcase}/#{@package.name} releases" %> 3 | <%= link_to "#{@package.platform.downcase}/#{@package.name}", @package %> 4 | releases 5 |

6 | 7 | <%= render @versions %> 8 | 9 |

10 | 11 | RSS feed for this page: <%= link_to truncate(url_for(request.params.merge(page: nil, format: :rss, only_path: false)), length: 100), url_for(request.params.merge(page: nil, format: :rss, only_path: false)) %> 12 | 13 |

14 | -------------------------------------------------------------------------------- /test/fixtures/repository_dependencies.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 2 | 3 | one: 4 | package_id: 1 5 | manifest_id: 1 6 | repository_id: 1 7 | optional: false 8 | package_name: MyString 9 | platform: MyString 10 | requirements: MyString 11 | kind: MyString 12 | 13 | two: 14 | package_id: 1 15 | manifest_id: 1 16 | repository_id: 1 17 | optional: false 18 | package_name: MyString 19 | platform: MyString 20 | requirements: MyString 21 | kind: MyString 22 | -------------------------------------------------------------------------------- /app/views/search/index.rss.builder: -------------------------------------------------------------------------------- 1 | xml.instruct! :xml, :version => "1.0" 2 | xml.rss :version => "2.0" do 3 | xml.channel do 4 | xml.title @page_title 5 | xml.description @page_description 6 | xml.link request.original_url 7 | 8 | @search_results.each do |search_result| 9 | xml.item do 10 | xml.title search_result.title 11 | xml.pubDate search_result.created_at.to_fs(:rfc822) 12 | xml.link search_result.html_url 13 | xml.guid search_result.html_url 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /test/fixtures/issues.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 2 | 3 | one: 4 | title: MyString 5 | body: MyText 6 | state: MyString 7 | number: 1 8 | html_url: https://github.com/ipfs-shipyard/ecosystem-dashboard/issues/113 9 | comments_count: 1 10 | user: MyString 11 | 12 | two: 13 | title: MyString 14 | body: MyText 15 | state: MyString 16 | number: 1 17 | html_url: https://github.com/ipfs-shipyard/ecosystem-dashboard/issues/120 18 | comments_count: 1 19 | user: MyString 20 | -------------------------------------------------------------------------------- /test/models/repository_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class RepositoryTest < ActiveSupport::TestCase 4 | context 'associations' do 5 | should belong_to(:organization).optional 6 | should have_many(:events) 7 | should have_many(:release_events) 8 | should have_many(:manifests) 9 | should have_many(:repository_dependencies) 10 | should have_many(:dependencies) 11 | should have_many(:tags) 12 | should have_many(:packages) 13 | should have_many(:issues) 14 | should have_many(:search_results) 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /app/views/packages/index.rss.builder: -------------------------------------------------------------------------------- 1 | xml.instruct! :xml, :version => "1.0" 2 | xml.rss :version => "2.0" do 3 | xml.channel do 4 | xml.title @page_title 5 | xml.description @page_description 6 | xml.link request.original_url 7 | 8 | @packages.each do |package| 9 | xml.item do 10 | xml.title package.name 11 | xml.description package.description 12 | xml.pubDate package.created_at.to_fs(:rfc822) 13 | xml.link package_url(package) 14 | xml.guid package_url(package) 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /db/migrate/20200505133355_add_package_indexes.rb: -------------------------------------------------------------------------------- 1 | class AddPackageIndexes < ActiveRecord::Migration[6.0] 2 | def change 3 | add_index :manifests, :repository_id 4 | add_index :packages, [:platform, :name], unique: true 5 | add_index :repository_dependencies, :manifest_id 6 | add_index :repository_dependencies, :repository_id 7 | add_index :repository_dependencies, :package_id 8 | add_index :dependencies, :package_id 9 | add_index :dependencies, :version_id 10 | add_index :versions, [:package_id, :number], :unique => true 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /bin/spring: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # This file loads Spring without using Bundler, in order to be fast. 4 | # It gets overwritten when you run the `spring binstub` command. 5 | 6 | unless defined?(Spring) 7 | require 'rubygems' 8 | require 'bundler' 9 | 10 | lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read) 11 | spring = lockfile.specs.detect { |spec| spec.name == 'spring' } 12 | if spring 13 | Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path 14 | gem 'spring', spring.version 15 | require 'spring/binstub' 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/controllers/versions_controller.rb: -------------------------------------------------------------------------------- 1 | class VersionsController < ApplicationController 2 | def index 3 | @package = Package.find(params[:package_id]) 4 | @versions = @package.versions.sort 5 | 6 | respond_to do |format| 7 | format.html 8 | format.rss do 9 | render 'index', :layout => false 10 | end 11 | format.json do 12 | render json: @versions 13 | end 14 | end 15 | end 16 | 17 | def show 18 | @package = Package.find(params[:package_id]) 19 | @version = @package.versions.find(params[:id]) 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /app/views/search/highlights.html.erb: -------------------------------------------------------------------------------- 1 | 13 | 14 | Nothing here right now -------------------------------------------------------------------------------- /lib/tasks/cleanup.rake: -------------------------------------------------------------------------------- 1 | namespace :cleanup do 2 | desc "Cleanup repositories" 3 | task repos: :environment do 4 | Repository.community.without_internal_deps.find_each(&:destroy) 5 | end 6 | 7 | desc "vacuum" 8 | task vacuum: :environment do 9 | begin 10 | tables = ActiveRecord::Base.connection.tables 11 | tables.each do |table| 12 | ActiveRecord::Base.connection.execute("VACUUM FULL ANALYZE #{table};") 13 | end 14 | rescue Exception => exc 15 | Rails.logger.error("Database VACUUM error: #{exc.message}") 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/models/manifest.rb: -------------------------------------------------------------------------------- 1 | class Manifest < ApplicationRecord 2 | belongs_to :repository 3 | has_many :repository_dependencies, dependent: :delete_all 4 | 5 | scope :latest, -> { order("manifests.filepath, manifests.created_at DESC").select("DISTINCT on (manifests.filepath) *") } 6 | scope :platform, ->(platform) { where('lower(manifests.platform) = ?', platform.try(:downcase)) } 7 | scope :kind, ->(kind) { where(kind: kind) } 8 | 9 | def repository_link 10 | repository.blob_url(branch) + filepath 11 | end 12 | 13 | def lockfile? 14 | kind == 'lockfile' 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/templates/erb/scaffold/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%# frozen_string_literal: true %> 2 | <%%= simple_form_for(@<%= singular_table_name %>) do |f| %> 3 | <%%= f.error_notification %> 4 | <%%= f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present? %> 5 | 6 |
7 | <%- attributes.each do |attribute| -%> 8 | <%%= f.<%= attribute.reference? ? :association : :input %> :<%= attribute.name %> %> 9 | <%- end -%> 10 |
11 | 12 |
13 | <%%= f.button :submit %> 14 |
15 | <%% end %> 16 | -------------------------------------------------------------------------------- /app/views/admin/organizations/_tabs.html.erb: -------------------------------------------------------------------------------- 1 | 18 | -------------------------------------------------------------------------------- /app/views/repositories/index.rss.builder: -------------------------------------------------------------------------------- 1 | xml.instruct! :xml, :version => "1.0" 2 | xml.rss :version => "2.0" do 3 | xml.channel do 4 | xml.title @page_title 5 | xml.description @page_description 6 | xml.link request.original_url 7 | 8 | @repositories.each do |repository| 9 | xml.item do 10 | xml.title repository.full_name 11 | xml.description repository.description 12 | xml.pubDate repository.created_at.to_fs(:rfc822) 13 | xml.link repository_url(repository) 14 | xml.guid repository_url(repository) 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/views/events/_push_event.html.erb: -------------------------------------------------------------------------------- 1 | 11 | 12 |

13 | <%= link_to 'View on GitHub', "https://github.com/#{event.repository_full_name}/commits/#{event.payload['ref'].gsub("refs/heads/", '')}" %> 14 |

15 | -------------------------------------------------------------------------------- /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 | # Run tests in parallel with specified workers 7 | parallelize(workers: :number_of_processors) 8 | 9 | # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. 10 | fixtures :all 11 | 12 | # Add more helper methods to be used by all tests here... 13 | 14 | Shoulda::Matchers.configure do |config| 15 | config.integrate do |with| 16 | with.test_framework :minitest 17 | with.library :rails 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /db/migrate/20200904122106_add_details_to_organizations.rb: -------------------------------------------------------------------------------- 1 | class AddDetailsToOrganizations < ActiveRecord::Migration[6.0] 2 | def change 3 | add_column :organizations, :url, :string 4 | add_column :organizations, :description, :string 5 | add_column :organizations, :email, :string 6 | add_column :organizations, :location, :string 7 | add_column :organizations, :verified, :boolean 8 | add_column :organizations, :display_name, :string 9 | add_column :organizations, :company, :string 10 | add_column :organizations, :twitter, :string 11 | add_column :organizations, :last_synced_at, :datetime 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/controllers/users_controller.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | def show 3 | @username = params[:id] 4 | @events_scope = Event.user(@username) 5 | 6 | sort = params[:sort] || 'events.created_at' 7 | order = params[:order] || 'desc' 8 | 9 | @pagy, @events = pagy(@events_scope.order(sort => order)) 10 | end 11 | 12 | def index 13 | # TODO render users 14 | @users = [] 15 | @pagy, @users = pagy_array(@users) 16 | end 17 | 18 | def hackathons 19 | @scope = Repository.discovered_contributors.order('created_at desc') 20 | @pagy, @contributors = pagy(@scope, limit: 100) 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /app/controllers/forage_controller.rb: -------------------------------------------------------------------------------- 1 | class ForageController < ApplicationController 2 | def index 3 | @internal_or_partner_packages = Package.internal.pluck(:platform, :name) 4 | @dependencies = RepositoryDependency.where(repository_id: Repository.internal.pluck(:id)).pluck(:platform, :package_name).uniq 5 | @packages = (@internal_or_partner_packages + @dependencies).map{|p| [p[0].downcase, p[1]]}.uniq.select{|p| ['npm','go'].include? p[0]} 6 | @json = @packages.sort.map do |package| 7 | { 8 | manager: package[0].downcase, 9 | name: package[1] 10 | } 11 | end 12 | render json: @json.to_json 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/views/organizations/_organization.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | <%= link_to "https://github.com/#{organization.name}", target: :_blank, class: 'float-left mr-3' do %> 5 | 6 | <% end %> 7 |
8 | <%= link_to organization.name, org_path(organization.name) %> 9 |
10 | 11 | 30 day activity: <%= organization.events_count %> 12 | 13 |
14 |
15 |
16 | -------------------------------------------------------------------------------- /app/views/versions/index.rss.builder: -------------------------------------------------------------------------------- 1 | xml.instruct! :xml, :version => "1.0" 2 | xml.rss :version => "2.0" do 3 | xml.channel do 4 | xml.title @page_title 5 | xml.description @page_description 6 | xml.link request.original_url 7 | 8 | @versions.each do |version| 9 | xml.item do 10 | xml.title version.number 11 | xml.description version.number 12 | xml.pubDate version.created_at.to_fs(:rfc822) 13 | xml.link package_version_url(package_id: version.package.id, id: version.id) 14 | xml.guid package_version_url(package_id: version.package.id, id: version.id) 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /db/migrate/20200428083829_create_repositories.rb: -------------------------------------------------------------------------------- 1 | class CreateRepositories < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :repositories do |t| 4 | t.integer :github_id 5 | t.string :full_name 6 | t.string :org 7 | t.string :language 8 | t.boolean :archived 9 | t.boolean :fork 10 | t.string :description 11 | t.datetime :pushed_at 12 | t.integer :size 13 | t.integer :stargazers_count 14 | t.integer :open_issues_count 15 | t.integer :forks_count 16 | t.integer :subscribers_count 17 | t.string :default_branch 18 | t.datetime :last_sync_at 19 | 20 | t.timestamps 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/views/issues/_collabs.html.erb: -------------------------------------------------------------------------------- 1 | <% if @collabs.any? %> 2 |
3 | Top collabs 4 | 5 | <%= number_with_delimiter @collabs.length %> 6 | 7 |
8 | 19 | <% end %> 20 | -------------------------------------------------------------------------------- /app/views/events/_newsletter_releases_markdown.html.erb: -------------------------------------------------------------------------------- 1 | <% if @releases.any? %> 2 | ### New Releases this week 3 | 4 | <% @releases.group_by(&:repository).each do |repo, events| %> 5 | * <%= repo.full_name %><% if events.length == 1 %> [<%= events.first.payload['release']['tag_name'] %>](<%= events.first.payload['release']['html_url'] %>)<% if events.first.breaking? %> _(Breaking Changes)_<% end %> 6 | <% else %> 7 | 8 | <% events.each_with_index do |event, i| %> 9 | * [<%= event.payload['release']['tag_name'] %>](<%= event.payload['release']['html_url'] %>)<% if event.breaking? %> _(Breaking Changes)_<% end %> 10 | <% end %> 11 | <% end %> 12 | <% end %> 13 | <% else %> 14 | No releases this week?! 15 | <% end %> 16 | -------------------------------------------------------------------------------- /app/workers/backfill_worker.rb: -------------------------------------------------------------------------------- 1 | require 'open-uri' 2 | require 'zlib' 3 | 4 | class BackfillWorker 5 | include Sidekiq::Worker 6 | 7 | def perform(year, month, day, hour) 8 | repo_names = Repository.not_internal.with_internal_deps.pluck(:full_name) 9 | 10 | # do something 11 | gz = URI.open("http://data.gharchive.org/#{year}-#{month}-#{day}-#{hour}.json.gz") 12 | json = Zlib::GzipReader.new(gz).read 13 | 14 | Oj.load(json) do |event| 15 | repo_name = event['repo']['name'] 16 | if repo_names.include?(repo_name) 17 | repository = Repository.find_by_full_name(repo_name) 18 | Event.record_event(repository, event)if repository 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | //= require jquery3 2 | //= require popper 3 | //= require bootstrap 4 | //= require chartkick 5 | //= require turbolinks 6 | //= require Chart.bundle 7 | //= require rails-ujs 8 | //= require_tree . 9 | 10 | document.addEventListener("turbolinks:load", function() { 11 | $(".dropdown-item").click(function (e) { 12 | if ($(this).data()["alt"]) { 13 | if(e.ctrlKey || e.metaKey || e.shiftKey){ 14 | e.preventDefault(); 15 | Turbolinks.visit($(this).data().alt) 16 | } 17 | if(e.altKey){ 18 | e.preventDefault(); 19 | Turbolinks.visit($(this).data().add) 20 | } 21 | } 22 | }); 23 | 24 | $('[data-toggle="popover"]').popover() 25 | }) 26 | -------------------------------------------------------------------------------- /db/migrate/20210219150421_create_dependency_events.rb: -------------------------------------------------------------------------------- 1 | class CreateDependencyEvents < ActiveRecord::Migration[6.1] 2 | def change 3 | create_table :dependency_events do |t| 4 | t.references :repository, index: true 5 | t.references :package, index: true 6 | t.string :action 7 | t.string :package_name 8 | t.string :commit_message 9 | t.string :requirement 10 | t.string :kind 11 | t.string :manifest_path 12 | t.string :manifest_kind 13 | t.string :branch 14 | t.string :commit_sha 15 | t.string :platform 16 | t.string :previous_requirement 17 | t.string :previous_kind 18 | t.datetime :committed_at, index: true 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | include Pagy::Backend 3 | after_action { pagy_headers_merge(@pagy) if @pagy } 4 | 5 | def authenticate_user! 6 | return if logged_in? 7 | respond_to do |format| 8 | format.html { redirect_to login_path, flash: {error: 'Unauthorized access, please log in first'} } 9 | format.json { render json: { "error" => "unauthorized" }, status: :unauthorized } 10 | end 11 | end 12 | 13 | helper_method :current_user 14 | def current_user 15 | @current_user ||= cookies.permanent.signed[:username] 16 | end 17 | 18 | helper_method :logged_in? 19 | def logged_in? 20 | !current_user.nil? 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /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/views/issues/_contributors.html.erb: -------------------------------------------------------------------------------- 1 | <% if @users.any? %> 2 |
3 | Top <%= collab_title %> contributors 4 | 5 | <%= number_with_delimiter @users.length %> 6 | 7 |
8 | 20 | <% end %> 21 | -------------------------------------------------------------------------------- /test/fixtures/repositories.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 2 | 3 | one: 4 | full_name: MyString 5 | org: MyString 6 | language: MyString 7 | archived: false 8 | fork: false 9 | description: MyString 10 | pushed_at: 2020-04-28 09:38:29 11 | size: 1 12 | stargazers_count: 1 13 | open_issues_count: 1 14 | forks_count: 1 15 | forks_count: 1 16 | default_branch: MyString 17 | 18 | two: 19 | full_name: MyString 20 | org: MyString 21 | language: MyString 22 | archived: false 23 | fork: false 24 | description: MyString 25 | pushed_at: 2020-04-28 09:38:29 26 | size: 1 27 | stargazers_count: 1 28 | open_issues_count: 1 29 | forks_count: 1 30 | forks_count: 1 31 | default_branch: MyString 32 | -------------------------------------------------------------------------------- /app/views/admin/contributors/index.html.erb: -------------------------------------------------------------------------------- 1 | <%= render 'admin/organizations/tabs' %> 2 | 3 | 18 | 19 | <% @contributors.each do |contributor, count| %> 20 | <%= render partial: 'contributor', locals: {contributor: contributor, count: count} %> 21 | <% end %> 22 | <%== pagy_bootstrap_nav(@pagy) if @pagy.pages > 1 %> 23 | -------------------------------------------------------------------------------- /app/views/admin/organizations/index.html.erb: -------------------------------------------------------------------------------- 1 | <%= render 'admin/organizations/tabs' %> 2 | 3 | 18 | 19 | <% if @orgs.any? %> 20 |
21 | <%= render @orgs %> 22 |
23 | <% else %> 24 |

No organizations found.

25 | <% end %> 26 | <%== pagy_bootstrap_nav(@pagy) if @pagy.pages > 1 %> 27 | -------------------------------------------------------------------------------- /app/views/admin/organizations/_organization.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | <%= link_to "https://github.com/#{organization.name}", target: :_blank, class: 'float-left mr-3' do %> 5 | 6 | <% end %> 7 |
8 | <%= link_to organization.name, edit_admin_organization_path(organization) %> 9 |
10 | 11 | <% if organization.internal? %> 12 | Internal 13 | <% elsif organization.partner? %> 14 | Partner 15 | <% elsif organization.collaborator? %> 16 | Collaborator 17 | <% end %> 18 | 19 |
20 |
21 |
22 | -------------------------------------------------------------------------------- /lib/tasks/contributions.rake: -------------------------------------------------------------------------------- 1 | require 'csv' 2 | 3 | namespace :contributions do 4 | desc "generate output a csv of avg first response time for each contributor" 5 | task research: :environment do 6 | counts = Issue.not_core.group(:user).count 7 | 8 | # CSV.open("data/contributors.csv", "w") do |csv| 9 | 10 | csv_string = CSV.generate do |csv| 11 | csv << [ 12 | 'name', 13 | 'Contributions', 14 | 'First response' 15 | ] 16 | 17 | counts.each do |row| 18 | next if row.first == 'ghost' 19 | resp = Issue.where(user: row.first).order('created_at ASC').first.response_time 20 | if resp 21 | csv << [ 22 | row.first, 23 | row.last, 24 | (resp/3600.0).round(1) 25 | ] 26 | end 27 | end 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /.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 pidfiles, but keep the directory. 17 | /tmp/pids/* 18 | !/tmp/pids/ 19 | !/tmp/pids/.keep 20 | 21 | # Ignore uploaded files in development. 22 | /storage/* 23 | !/storage/.keep 24 | /tmp/storage/* 25 | !/tmp/storage/ 26 | !/tmp/storage/.keep 27 | 28 | /public/assets 29 | 30 | # Ignore master key for decrypting credentials and more. 31 | /config/master.key 32 | .env 33 | *.dump 34 | 35 | *topics* 36 | 37 | data/* 38 | -------------------------------------------------------------------------------- /app/models/concerns/releaseable.rb: -------------------------------------------------------------------------------- 1 | module Releaseable 2 | def to_s 3 | number 4 | end 5 | 6 | def semantic_version 7 | @semantic_version ||= begin 8 | Semantic::Version.new(clean_number) 9 | rescue ArgumentError 10 | nil 11 | end 12 | end 13 | 14 | def greater_than_1? 15 | return nil unless follows_semver? 16 | begin 17 | SemanticRange.gte(clean_number, '1.0.0') 18 | rescue 19 | false 20 | end 21 | end 22 | 23 | def stable? 24 | valid_number? && !prerelease? 25 | end 26 | 27 | def valid_number? 28 | !!semantic_version 29 | end 30 | 31 | def follows_semver? 32 | @follows_semver ||= valid_number? 33 | end 34 | 35 | def parsed_number 36 | @parsed_number ||= semantic_version || number 37 | end 38 | 39 | def clean_number 40 | @clean_number ||= (SemanticRange.clean(number) || number) 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /app/views/admin/contributors/_contributor.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 | <%= link_to "https://github.com/#{contributor}", target: :_blank, class: 'float-left mr-3' do %> 4 | 5 | <% end %> 6 |
7 | <%= link_to contributor, edit_admin_contributor_path(contributor) %> 8 | 9 | <% Contributor.collabs(contributor).each do |collab| %> 10 | <%= link_to collab, url_for(request.params.merge(collab: collab, page: nil)), class: "badge badge-info" %> 11 | <% end %> 12 | 13 |
14 | 15 | 16 | <% if contributor.core? %> 17 | Core 18 | <% elsif contributor.bot? %> 19 | Bot 20 | <% end %> 21 | 22 |
23 |
24 | -------------------------------------------------------------------------------- /app/views/issues/_range_filter.html.erb: -------------------------------------------------------------------------------- 1 | <% ranges = [7, 14, 30, 90, 365] %> 2 | 17 | -------------------------------------------------------------------------------- /app/views/issues/_state_filter.html.erb: -------------------------------------------------------------------------------- 1 | 20 | -------------------------------------------------------------------------------- /app/views/contributors/_contributor.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 | <%= link_to "https://github.com/#{contributor}", target: :_blank, class: 'float-left mr-3' do %> 4 | 5 | <% end %> 6 |
7 | <%= link_to contributor, contributor_path(contributor), target: :_blank %> 8 | 9 | <% Contributor.collabs(contributor).each do |collab| %> 10 | <%= link_to collab, url_for(request.params.merge(collab: collab, page: nil)), class: "badge badge-info" %> 11 | <% end %> 12 | 13 |
14 | 15 | 16 | <%= link_to all_issues_path(user: contributor, range: @range) do %> 17 | <%= pluralize count, 'contribution' %> in the past <%= @range %> days 18 | <% end %> 19 | 20 |
21 |
22 | -------------------------------------------------------------------------------- /app/views/issues/_type_filter.html.erb: -------------------------------------------------------------------------------- 1 | 20 | -------------------------------------------------------------------------------- /app/controllers/admin/contributors_controller.rb: -------------------------------------------------------------------------------- 1 | class Admin::ContributorsController < Admin::ApplicationController 2 | def index 3 | @scope = Contributor.order('created_at DESC') 4 | @pagy, @contributors = pagy(@scope) 5 | end 6 | 7 | def new 8 | @contributor = Contributor.new 9 | end 10 | 11 | def create 12 | @contributor = Contributor.new(contributor_params) 13 | if @contributor.save 14 | redirect_to admin_contributors_path 15 | else 16 | render :new 17 | end 18 | end 19 | 20 | def edit 21 | @contributor = Contributor.find(params[:id]) 22 | end 23 | 24 | def update 25 | @contributor = Contributor.find(params[:id]) 26 | if @contributor.update(contributor_params) 27 | redirect_to admin_contributors_path 28 | else 29 | render :edit 30 | end 31 | end 32 | 33 | private 34 | 35 | def contributor_params 36 | params.require(:contributor).permit(:github_username, :core, :bot) 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /config/locales/simple_form.en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | simple_form: 3 | "yes": 'Yes' 4 | "no": 'No' 5 | required: 6 | text: 'required' 7 | mark: '*' 8 | # You can uncomment the line below if you need to overwrite the whole required html. 9 | # When using html, text and mark won't be used. 10 | # html: '*' 11 | error_notification: 12 | default_message: "Please review the problems below:" 13 | # Examples 14 | # labels: 15 | # defaults: 16 | # password: 'Password' 17 | # user: 18 | # new: 19 | # email: 'E-mail to sign in.' 20 | # edit: 21 | # email: 'E-mail.' 22 | # hints: 23 | # defaults: 24 | # username: 'User name to sign in.' 25 | # password: 'No special characters, please.' 26 | # include_blanks: 27 | # defaults: 28 | # age: 'Rather not say' 29 | # prompts: 30 | # defaults: 31 | # age: 'Select your age' 32 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative 'boot' 2 | 3 | require "rails" 4 | 5 | %w( 6 | active_record/railtie 7 | action_controller/railtie 8 | action_view/railtie 9 | rails/test_unit/railtie 10 | sprockets/railtie 11 | ).each do |railtie| 12 | begin 13 | require railtie 14 | rescue LoadError 15 | end 16 | end 17 | 18 | # Require the gems listed in Gemfile, including any gems 19 | # you've limited to :test, :development, or :production. 20 | Bundler.require(*Rails.groups) 21 | 22 | module Ecosystem 23 | class Application < Rails::Application 24 | # Initialize configuration defaults for originally generated Rails version. 25 | config.load_defaults 7.0 26 | 27 | # Settings in config/environments/* take precedence over those specified here. 28 | # Application configuration can go into files in config/initializers 29 | # -- all .rb files in that directory are automatically loaded after loading 30 | # the framework and any gems in your application. 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /app/views/users/hackathons.html.erb: -------------------------------------------------------------------------------- 1 |

2 | Hackathon Contributors 3 |

4 | 5 | <% @contributors.each do |contributor| %> 6 |
7 |
8 | <%= link_to "https://github.com/#{contributor.github_username}", target: :_blank, class: 'float-left mr-3' do %> 9 | 10 | <% end %> 11 |
12 | <%= link_to contributor, contributor_path(contributor.github_username), target: :_blank %> 13 |
14 | 15 | 16 | <% repo_names = contributor.contributed_repository_names %> 17 | Repos: <%= repo_names.first(4).join(', ') %> 18 | <% if repo_names.length > 4 %> 19 | and <%= repo_names.length %> more 20 | <% end %> 21 | 22 |
23 |
24 | 25 | <% end %> 26 | <%== pagy_bootstrap_nav(@pagy) if @pagy.pages > 1 %> 27 | -------------------------------------------------------------------------------- /app/views/repositories/hackathons.html.erb: -------------------------------------------------------------------------------- 1 |

2 | Hackathon Repositories 3 |

4 | 5 | <% @repositories.group_by { |r| r.created_at.beginning_of_month }.each do |month, repos| %> 6 |

<%= month.strftime("%B %Y") %>

7 | 21 | <% end%> 22 | 23 | <%== pagy_bootstrap_nav(@pagy) if @pagy.pages > 1 %> 24 | 25 |

26 | 27 | <%== pagy_info(@pagy) %> 28 | 29 |

30 | -------------------------------------------------------------------------------- /db/migrate/20200501155652_create_packages.rb: -------------------------------------------------------------------------------- 1 | class CreatePackages < ActiveRecord::Migration[6.0] 2 | def change 3 | create_table :packages do |t| 4 | t.string :name 5 | t.string :platform 6 | t.text :description 7 | t.text :keywords 8 | t.string :homepage 9 | t.string :licenses 10 | t.string :repository_url 11 | t.integer :repository_id 12 | t.string :normalized_licenses, default: [], array: true 13 | t.integer :versions_count, default: 0, null: false 14 | t.datetime :latest_release_published_at 15 | t.string :latest_release_number 16 | t.string :keywords_array, default: [], array: true 17 | t.integer :dependents_count, default: 0, null: false 18 | t.string :language 19 | t.string :status 20 | t.datetime :last_synced_at 21 | t.integer :dependent_repos_count 22 | t.integer :runtime_dependencies_count 23 | t.string :latest_stable_release_number 24 | t.string :latest_stable_release_published_at 25 | 26 | t.timestamps 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/tasks/stewards.rake: -------------------------------------------------------------------------------- 1 | require 'csv' 2 | 3 | namespace :stewards do 4 | desc 'export all internal repositories as csv' 5 | task export_repos: :environment do 6 | exclude_fields = ['topics', 'direct_internal_dependency_package_ids', 'indirect_internal_dependency_package_ids'] 7 | 8 | scope = Repository.internal 9 | csv_string = CSV.generate do |csv| 10 | csv << Repository.attribute_names.excluding(exclude_fields) 11 | scope.find_each(batch_size: 5000) do |repo| 12 | csv << repo.attributes.except(*exclude_fields).values 13 | end 14 | end 15 | 16 | puts csv_string 17 | end 18 | 19 | desc 'export all internal events for the past 6 months as csv' 20 | task export_events: :environment do 21 | scope = Event.internal.this_period(180).humans 22 | csv_string = CSV.generate do |csv| 23 | csv << Event.attribute_names.excluding("payload") 24 | scope.find_each(batch_size: 5000) do |event| 25 | csv << event.attributes.except("payload").values 26 | end 27 | end 28 | 29 | puts csv_string 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /app/controllers/admin/organizations_controller.rb: -------------------------------------------------------------------------------- 1 | class Admin::OrganizationsController < Admin::ApplicationController 2 | def index 3 | @scope = Organization.order('name DESC').where('collaborator IS true or internal IS true') 4 | @pagy, @orgs = pagy(@scope, limit: 100) 5 | end 6 | 7 | def new 8 | @organization = Organization.new 9 | end 10 | 11 | def create 12 | @organization = Organization.new(organization_params) 13 | if @organization.save 14 | redirect_to admin_organizations_path 15 | else 16 | render :new 17 | end 18 | end 19 | 20 | def edit 21 | @organization = Organization.find_by_name(params[:id]) 22 | end 23 | 24 | def update 25 | @organization = Organization.find_by_name(params[:id]) 26 | if @organization.update(organization_params) 27 | redirect_to admin_organizations_path 28 | else 29 | render :edit 30 | end 31 | end 32 | 33 | private 34 | 35 | def organization_params 36 | params.require(:organization).permit(:name, :internal, :collaborator, :docker_hub_org, :partner) 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib/tasks/repositories.rake: -------------------------------------------------------------------------------- 1 | namespace :repositories do 2 | desc "import new repositories discovered searching github " 3 | task discover_from_search_results: :environment do 4 | Repository.discover_from_search_results 5 | end 6 | 7 | desc "update ecosystem score for each repository" 8 | task recalculate_scores: :environment do 9 | Repository.recalculate_scores 10 | end 11 | 12 | desc "update 1200 least recently synced repositories" 13 | task sync: :environment do 14 | Repository.order('last_events_sync_at ASC nulls first').limit(1000).each(&:sync_if_updates) 15 | Repository.order('last_sync_at ASC nulls first').limit(200).each(&:sync) 16 | end 17 | 18 | desc "import 200 new repositories from discovered contributor events to other repos" 19 | task sync_discovered: :environment do 20 | all_names = Repository.discovered_related_repo_names 21 | existing_names = Repository.where(full_name: all_names).pluck(:full_name) 22 | missing_names = all_names - existing_names 23 | missing_names.shuffle.first(200).each do |name| 24 | Repository.download_async(name) 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'fileutils' 3 | 4 | # path to your application root. 5 | APP_ROOT = File.expand_path('..', __dir__) 6 | 7 | def system!(*args) 8 | system(*args) || abort("\n== Command #{args} failed ==") 9 | end 10 | 11 | FileUtils.chdir APP_ROOT do 12 | # This script is a way to setup or update your development environment automatically. 13 | # This script is idempotent, so that you can run it at anytime and get an expectable outcome. 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 | # puts "\n== Copying sample files ==" 21 | # unless File.exist?('config/database.yml') 22 | # FileUtils.cp 'config/database.yml.sample', 'config/database.yml' 23 | # end 24 | 25 | puts "\n== Preparing database ==" 26 | system! 'bin/rails db:prepare' 27 | 28 | puts "\n== Removing old logs and tempfiles ==" 29 | system! 'bin/rails log:clear tmp:clear' 30 | 31 | puts "\n== Restarting application server ==" 32 | system! 'bin/rails restart' 33 | end 34 | -------------------------------------------------------------------------------- /app/views/packages/_usage.html.erb: -------------------------------------------------------------------------------- 1 | <% major_versions = @repository_dependencies.select(&:latest_resolvable_version).group_by{|rd| rd.latest_resolvable_version.semantic_version.major } %> 2 | <% if major_versions.length > 1 %> 3 | <% groups = @repository_dependencies.select(&:latest_resolvable_version).group_by{|rd| rd.latest_resolvable_version.semantic_version.major }.sort_by{|v,rds| v } %> 4 | <% else %> 5 | <% groups = @repository_dependencies.select(&:latest_resolvable_version).group_by{|rd| [rd.latest_resolvable_version.semantic_version.major, rd.latest_resolvable_version.semantic_version.minor] }.sort_by{|v,rds| v } %> 6 | <% end %> 7 | 8 | <% if groups.length > 1 %> 9 | 10 | 11 | 12 | 13 | 14 | <% groups.each do |version, repository_dependencies| %> 15 | 16 | 19 | 20 | 21 | <% end %> 22 |
Version rangeUsage Percentage
17 | <%= Array(version).join('.') %>.x 18 | <%= (repository_dependencies.length.to_f/@repository_dependencies.length*100).round(1) %>%
23 | <% end %> 24 | -------------------------------------------------------------------------------- /app/views/repositories/discover.html.erb: -------------------------------------------------------------------------------- 1 |

Discover Repositories

2 | 3 |

Look up GitHub repositories to see if they are part of the ecosystem

4 | 5 | <%= form_tag discover_path, method: :get, :html => {:class => "form-horizontal center"} do |f| %> 6 |
7 |
8 | <%= text_field_tag :names, params[:names], class: "form-control form-control-lg", placeholder: 'Repository names (example foo/bar, bar/baz)', size: 50 %> 9 |
10 |
11 | 12 |
13 |
14 | <% end %> 15 | 16 | <% if @existing_repositories && @existing_repositories.any? %> 17 |
18 |
Existing Repositories
19 | <%= render @existing_repositories %> 20 |
21 | <% end %> 22 | 23 | <% if @new_repos && @new_repos.any? %> 24 |
25 |
New Repositories
26 | <%= render @new_repos %> 27 |
28 | <% end %> 29 | 30 | <% if @remaining_missing_names && @remaining_missing_names.any? %> 31 |
32 | Couldn't load: <%= @remaining_missing_names.join(', ') %> 33 |
34 | <% end %> 35 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | app: 4 | build: 5 | context: . 6 | dockerfile: Dockerfile 7 | ports: 8 | - "3000:3000" 9 | environment: 10 | - PORT=3000 11 | - PIDFILE=/usr/src/app/tmp/pids/server.pid 12 | - RAILS_ENV=development 13 | - GITHUB_CLIENT_ID=${GITHUB_CLIENT_ID} 14 | - GITHUB_CLIENT_SECRET=${GITHUB_CLIENT_SECRET} 15 | - POSTGRES_USER=postgres 16 | - POSTGRES_PASSWORD=development 17 | - DATABASE_HOST=database.service.ecosystem.internal 18 | - REDIS_URL=redis://redis.service.ecosystem.internal 19 | networks: 20 | - internal 21 | depends_on: 22 | - database.service.ecosystem.internal 23 | - redis.service.ecosystem.internal 24 | 25 | database.service.ecosystem.internal: 26 | image: postgres:9.6-alpine 27 | volumes: 28 | - pg_data:/var/lib/postgresql/data 29 | networks: 30 | - internal 31 | environment: 32 | - POSTGRES_PASSWORD=development 33 | 34 | redis.service.ecosystem.internal: 35 | image: redis:4.0-alpine 36 | networks: 37 | - internal 38 | 39 | volumes: 40 | pg_data: 41 | 42 | networks: 43 | internal: 44 | driver: bridge 45 | -------------------------------------------------------------------------------- /app/views/organizations/collabs.html.erb: -------------------------------------------------------------------------------- 1 | <% content_for :rss do %> 2 | <%= auto_discovery_link_tag :rss, url_for(request.params.merge(page: nil, format: :rss, only_path: false)) %> 3 | <% end %> 4 | 5 | 22 | 23 | <% if @orgs.any? %> 24 |
25 | <%= render @orgs %> 26 |
27 | <% else %> 28 |

No organizations found.

29 | <% end %> 30 | <%== pagy_bootstrap_nav(@pagy) if @pagy.pages > 1 %> 31 | 32 |

33 | 34 | RSS feed for this page: <%= link_to truncate(url_for(request.params.merge(page: nil, format: :rss, only_path: false)), length: 100), url_for(request.params.merge(page: nil, format: :rss, only_path: false)) %> 35 | 36 |

37 | -------------------------------------------------------------------------------- /app/views/events/_sort_filter.html.erb: -------------------------------------------------------------------------------- 1 | 24 | -------------------------------------------------------------------------------- /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 https://guides.rubyonrails.org/i18n.html. 31 | 32 | en: 33 | hello: "Hello world" 34 | number: 35 | human: 36 | decimal_units: 37 | format: "%n%u" 38 | units: 39 | unit: "" 40 | thousand: K 41 | million: M 42 | billion: B 43 | trillion: T 44 | quadrillion: Q 45 | -------------------------------------------------------------------------------- /lib/tasks/issues.rake: -------------------------------------------------------------------------------- 1 | namespace :issues do 2 | desc 'sync recently active repos from each collaborator org' 3 | task sync_collabs: :environment do 4 | Organization.collaborator.each(&:sync_recently_active_repos) 5 | end 6 | 7 | desc 'sync issues from recently active repositories' 8 | task sync_recent: :environment do 9 | Repository.not_internal.triage.each(&:download_issues) 10 | Repository.not_internal.triage.each(&:sync_events) 11 | 12 | Repository.sync_recently_active_internal_repos 13 | Issue.update_collab_labels 14 | Issue.internal.state('open').where('issues.last_synced_at < ? or issues.last_synced_at is null', 1.hour.ago).where('issues.created_at > ?', 1.week.ago).limit(1000).order('issues.last_synced_at asc nulls first, issues.updated_at asc').each(&:sync) 15 | Organization.collaborator.each(&:update_counts) 16 | end 17 | 18 | desc 'sync internal issues that have not been synced in over 24 hours' 19 | task sync_daily: :environment do 20 | ids = Issue.internal.state('open').where('issues.last_synced_at < ? or issues.last_synced_at is null', 1.day.ago).limit(1000).order('issues.last_synced_at asc nulls first, issues.updated_at asc').pluck(:id) 21 | Issue.where(id: ids).find_each(&:sync) 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /app/controllers/sessions_controller.rb: -------------------------------------------------------------------------------- 1 | class SessionsController < ApplicationController 2 | def new 3 | if logged_in? 4 | redirect_to admin_path 5 | else 6 | redirect_to '/auth/github' 7 | end 8 | end 9 | 10 | def create 11 | AuthToken.create_multiple([auth_hash.credentials.token]) 12 | client = Octokit::Client.new(access_token: auth_hash.credentials.token) 13 | username = auth_hash.info.nickname 14 | if organization_member?(client, user: username) 15 | cookies.permanent.signed[:username] = {value: username, httponly: true} 16 | redirect_to request.env['omniauth.origin'] || admin_path 17 | else 18 | flash[:error] = 'Access denied.' 19 | redirect_to root_path 20 | end 21 | end 22 | 23 | def destroy 24 | cookies.delete :user_id 25 | redirect_to root_path 26 | end 27 | 28 | def failure 29 | flash[:error] = 'There was a problem authenticating with GitHub, please try again.' 30 | redirect_to root_path 31 | end 32 | 33 | private 34 | 35 | def auth_hash 36 | @auth_hash ||= request.env['omniauth.auth'] 37 | end 38 | 39 | def organization_member?(client, user:) 40 | client.organization_member?(ENV['DEFAULT_ORG'], user, headers: { 'Cache-Control' => 'no-cache, no-store' }) 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /app/views/search_results/_search_result.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 | <%= octicon search_result_icon(search_result.kind), class: 'mr-1' %> 4 | <%= link_to truncate(search_result.title, length: 100), search_result.html_url, target: :_blank %> 5 |
6 | 7 | <% if search_result.text_matches.any? %> 8 | 9 | <% search_result.text_matches.each do |text_match| %> 10 | <% next if text_match['property'] == 'path' %> 11 |
<%= highlight text_match['fragment'], text_match['matches'].map{|m| m['text']}  %>
12 | <% end %> 13 | 14 | <% end %> 15 | 16 |

17 | 18 | <% unless search_result.kind == 'repositories' %> 19 | <%= link_to search_result.repository_url, target: :_blank, class: 'mr-1' do %> 20 | 21 | <% end %> 22 | <%= link_to search_result.repository_full_name, search_result.repository_url, target: :_blank %> - 23 | <% end %> 24 | <%= link_to search_result.search_query.html_url, target: :_blank do %> 25 | <%= distance_of_time_in_words_to_now search_result.created_at %> ago 26 | <% end %> 27 | 28 |

29 |
30 | -------------------------------------------------------------------------------- /app/views/packages/_external_dependents.html.erb: -------------------------------------------------------------------------------- 1 |
2 | 27 |
-------------------------------------------------------------------------------- /app/views/packages/_package.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 | <% if package.repository %> 4 | <%= link_to "https://github.com/#{package.repository.org}", target: :_blank, class: 'float-right' do %> 5 | 6 | <% end %> 7 | <% end %> 8 |
9 | <%= link_to "#{package.platform.downcase}/#{package.name}", package_path(package.id) %> 10 | <%= package.latest_release_number %> 11 |
12 | 13 | <% if package.description.present? %> 14 | <%= package.description %> 15 |
16 | <% end %> 17 | 18 | <% if package.repository %> 19 | Repository: <%= link_to package.repository.html_url, package.repository.html_url, target: :_blank %>
20 | <% elsif package.repository_url.present? %> 21 | Repository: <%= link_to package.repository_url, package.repository_url, target: :_blank %>
22 | <% end %> 23 | Latest release: <%= time_ago_in_words package.latest_release_published_at %>
24 | Dependent collab repositories: <%= package.collab_dependent_repos_count || 0 %> 25 |
26 |
27 |
28 | -------------------------------------------------------------------------------- /app/views/contributors/show.html.erb: -------------------------------------------------------------------------------- 1 | <% @page_title = @contributor %> 2 | <%= link_to "https://github.com/#{@contributor}", target: :_blank, class: 'float-left mr-3 mt-2' do %> 3 | 4 | <% end %> 5 |

6 | <%= link_to @contributor, "https://github.com/#{@contributor}", target: :_blank %> 7 |

8 |
9 | 10 |

11 | Recent Internal Issues and Pull Requests 12 | 13 | <%= link_to 'See all', all_issues_path(user: @contributor, range: 999) %> 14 | 15 |

16 | 17 | <% if @issues.any? %> 18 | <%= render @issues %> 19 | <% else %> 20 |

No issues or pull requests found.

21 | <% end %> 22 | 23 | <% if @internal_events.any? %> 24 |

25 | Recent Internal Events 26 | 27 | <%= link_to 'See all', events_path(user: @contributor, range: 999) %> 28 | 29 |

30 | 31 | <%= render @internal_events %> 32 | <% end %> 33 | 34 | 35 | <% if @external_events.any? %> 36 |

37 | Recent External Events 38 | 39 | <%= link_to 'See all', all_events_path(user: @contributor, range: 365) %> 40 | 41 |

42 | 43 | <%= render @external_events %> 44 | <% end %> -------------------------------------------------------------------------------- /app/views/packages/_internal_dependents.html.erb: -------------------------------------------------------------------------------- 1 |
2 | 27 |
-------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ruby:3.4.7-alpine 2 | 3 | ENV APP_ROOT=/usr/src/app 4 | ENV DATABASE_PORT=5432 5 | WORKDIR $APP_ROOT 6 | 7 | # ============================================= 8 | # System layer 9 | 10 | # Will invalidate cache as soon as the Gemfile changes 11 | COPY Gemfile Gemfile.lock $APP_ROOT/ 12 | 13 | # * Setup system 14 | # * Install Ruby dependencies 15 | RUN apk add --update \ 16 | build-base \ 17 | netcat-openbsd \ 18 | git \ 19 | nodejs \ 20 | postgresql-dev \ 21 | tzdata \ 22 | curl-dev \ 23 | openssl-dev \ 24 | cmake \ 25 | libffi-dev \ 26 | yaml-dev \ 27 | linux-headers \ 28 | && rm -rf /var/cache/apk/* \ 29 | && gem update --system \ 30 | && gem install bundler foreman \ 31 | && bundle config --global frozen 1 \ 32 | && bundle config set without 'test' \ 33 | && bundle config set force_ruby_platform true \ 34 | && bundle install --jobs 2 35 | 36 | # ======================================================== 37 | # Application layer 38 | 39 | # Copy application code 40 | COPY . $APP_ROOT 41 | 42 | RUN bundle install --jobs 2 43 | 44 | # Precompile assets for a production environment. 45 | # This is done to include assets in production images on Dockerhub. 46 | RUN SECRET_KEY_BASE_DUMMY=1 RAILS_ENV=production bundle exec rake assets:precompile 47 | 48 | # Startup 49 | CMD ["bin/docker-start"] 50 | -------------------------------------------------------------------------------- /app/views/repositories/contributors.html.erb: -------------------------------------------------------------------------------- 1 | <% @page_title = "#{@repository.full_name} contributors" %> 2 |

3 | <%= @page_title%> 4 |

5 | 6 | <% @contributors.each do |contributor, count, core| %> 7 | 8 |
9 |
10 | <%= link_to "https://github.com/#{contributor}", target: :_blank, class: 'float-left mr-3' do %> 11 | 12 | <% end %> 13 |
14 | <%= link_to contributor, contributor_path(contributor), target: :_blank %> 15 | 16 | <% if core %> 17 | Core 18 | <% end %> 19 | <% Contributor.collabs(contributor).each do |collab| %> 20 | <%= link_to collab, url_for(request.params.merge(collab: collab, page: nil)), class: "badge badge-info" %> 21 | <% end %> 22 | 23 |
24 | 25 | 26 | <%= link_to events_path(user: contributor, range: 999, repo_full_name: @repository.full_name) do %> 27 | <%= pluralize number_with_delimiter(count), 'contribution' %> 28 | <% end %> 29 | 30 |
31 |
32 | <% end %> 33 | 34 | <%== pagy_bootstrap_nav(@pagy) if @pagy.pages > 1 %> -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | ruby '3.4.7' 4 | 5 | gem 'rails', '7.2.2' 6 | gem 'pg' 7 | gem 'puma' 8 | gem 'sassc-rails' 9 | gem 'sprockets-rails' 10 | gem 'turbolinks' 11 | gem 'octokit' 12 | gem 'bootsnap', require: false 13 | 14 | gem 'pagy', '~> 9.4.0' 15 | gem 'jquery-rails' 16 | gem 'bootstrap', '4.6.2.1' 17 | gem 'octicons_helper' 18 | gem "chartkick" 19 | gem 'groupdate' 20 | gem 'languages' 21 | 22 | gem 'pghero' 23 | gem 'pg_query' 24 | gem 'pg_search' 25 | 26 | gem 'commonmarker', '0.23.10' 27 | gem 'bibliothecary', git: "https://github.com/ecosyste-ms/bibliothecary" 28 | gem "license-compatibility" 29 | gem "semantic", git: "https://github.com/librariesio/semantic" 30 | gem "semantic_range", '2.3.1' 31 | gem 'oj' 32 | gem 'ox' 33 | gem 'faraday' 34 | gem "faraday_middleware" 35 | gem "counter_culture" 36 | gem "spdx", '2.0.10' 37 | 38 | gem 'bugsnag' 39 | gem 'zlib' 40 | 41 | gem 'omniauth-github' 42 | gem 'simple_form' 43 | 44 | gem 'rack-cors' 45 | gem 'dalli' 46 | 47 | gem 'sidekiq', '<7' 48 | 49 | gem 'google-protobuf' 50 | gem 'ostruct' 51 | gem 'csv' 52 | 53 | group :test do 54 | gem 'shoulda' 55 | gem 'minitest', '< 6.0' 56 | end 57 | 58 | group :development do 59 | gem 'web-console' 60 | gem 'listen' 61 | gem 'dotenv-rails' 62 | gem 'sql_queries_count' 63 | end 64 | 65 | group :production do 66 | gem 'lograge' 67 | gem 'puma_worker_killer' 68 | end 69 | -------------------------------------------------------------------------------- /app/views/users/show.html.erb: -------------------------------------------------------------------------------- 1 |

2 | <%= @username %> 3 | <%= link_to octicon('mark-github', height: 26), "https://github.com/#{@username}", target: :_blank %> 4 |

5 | 6 | 19 | 20 | <% if @events.any? %> 21 |
22 | <%= line_chart @events_scope.group_by_day(:created_at).count, colors: ['#bbb'], height: "80px", library: { scales: { yAxes: [{ display: false }], xAxes: [{ display: false }] }}, points: false %> 23 |
24 | <% @events.group_by{|e| e.created_at.beginning_of_week }.each do |week, events| %> 25 |

26 | <%= week.strftime('Week %V - %G') %> 27 | <%= pluralize events.length, 'event' %> 28 |

29 | <%= render events %> 30 | <% end %> 31 | 32 | <% else %> 33 |

No events found.

34 | <% end %> 35 | <%== pagy_bootstrap_nav(@pagy) if @pagy.pages > 1 %> 36 | -------------------------------------------------------------------------------- /app/views/repositories/_internal_direct.html.erb: -------------------------------------------------------------------------------- 1 | <% direct_internal_dependencies = @repository.direct_internal_dependencies.includes(:manifest, :package) %> 2 | <% if direct_internal_dependencies.present? %> 3 | <% @repository.direct_internal_dependencies.group_by(&:manifest).sort_by{|m,d| m.filepath }.each do |manifest, deps| %> 4 |
5 |
6 | <%= link_to manifest.filepath, manifest.repository_link, target: :_blank %> 7 |
8 | 29 |
30 | <% end %> 31 | <% else %> 32 |

No internal direct dependencies found

33 | <% end %> 34 | -------------------------------------------------------------------------------- /app/views/repositories/_internal_indirect.html.erb: -------------------------------------------------------------------------------- 1 | <% indirect_internal_dependencies = @repository.indirect_internal_dependencies.includes(:manifest) %> 2 | <% if indirect_internal_dependencies.present? %> 3 | <% @repository.indirect_internal_dependencies.group_by(&:manifest).sort_by{|m,d| m.filepath }.each do |manifest, deps| %> 4 |
5 |
6 | <%= link_to manifest.filepath, manifest.repository_link, target: :_blank %> 7 |
8 | 29 |
30 | <% end %> 31 | <% else %> 32 |

No internal indirect dependencies found

33 | <% end %> 34 | -------------------------------------------------------------------------------- /app/models/auth_token.rb: -------------------------------------------------------------------------------- 1 | class AuthToken < ApplicationRecord 2 | validates_presence_of :token 3 | 4 | def self.client(options = {}) 5 | find_token.github_client(options) 6 | end 7 | 8 | def self.token 9 | client.access_token 10 | end 11 | 12 | def self.create_multiple(array_of_tokens) 13 | array_of_tokens.each do |token| 14 | self.find_or_create_by(token: token) 15 | end 16 | end 17 | 18 | def high_rate_limit? 19 | github_client.rate_limit.remaining > 500 20 | rescue Octokit::Unauthorized, Octokit::AccountSuspended 21 | false 22 | end 23 | 24 | def still_authorized? 25 | !!github_client.rate_limit 26 | rescue Octokit::Unauthorized, Octokit::AccountSuspended 27 | false 28 | end 29 | 30 | def github_client(options = {}) 31 | AuthToken.new_client(token, options) 32 | end 33 | 34 | def self.fallback_client(token = nil) 35 | AuthToken.new_client(token) 36 | end 37 | 38 | def self.new_client(token, options = {}) 39 | token ||= AuthToken.token 40 | Octokit::Client.new({access_token: token, auto_paginate: true}.merge(options)) 41 | end 42 | 43 | private 44 | 45 | def self.find_token 46 | return @auth_token if @auth_token && @auth_token.high_rate_limit? 47 | auth_token = all.order(Arel.sql("RANDOM()")).limit(100).sample 48 | if auth_token.high_rate_limit? 49 | @auth_token = auth_token 50 | end 51 | find_token 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /app/views/packages/index.html.erb: -------------------------------------------------------------------------------- 1 | <% content_for :rss do %> 2 | <%= auto_discovery_link_tag :rss, url_for(request.params.merge(page: nil, format: :rss, only_path: false)) %> 3 | <% end %> 4 | 5 | 22 | 23 | <%= render @packages %> 24 | <%== pagy_bootstrap_nav(@pagy) if @pagy.pages > 1 %> 25 | 26 |

27 | 28 | RSS feed for this page: <%= link_to truncate(url_for(request.params.merge(page: nil, format: :rss, only_path: false)), length: 100), url_for(request.params.merge(page: nil, format: :rss, only_path: false)) %> 29 | 30 |

31 | -------------------------------------------------------------------------------- /test/fixtures/packages.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 2 | 3 | one: 4 | name: MyString 5 | platform: MyString 6 | description: MyText 7 | keywords: MyText 8 | homepage: MyString 9 | licenses: MyString 10 | repository_url: MyString 11 | repository_id: 1 12 | normalized_licenses: ['MyString'] 13 | versions_count: 1 14 | latest_release_published_at: 2020-05-01 16:56:52 15 | latest_release_number: MyString 16 | keywords_array: ['MyString'] 17 | dependents_count: 1 18 | language: MyString 19 | status: MyString 20 | last_synced_at: 2020-05-01 16:56:52 21 | dependent_repos_count: 1 22 | runtime_dependencies_count: 1 23 | latest_stable_release_number: MyString 24 | latest_stable_release_published_at: MyString 25 | 26 | two: 27 | name: MyString2 28 | platform: MyString 29 | description: MyText 30 | keywords: MyText 31 | homepage: MyString 32 | licenses: MyString 33 | repository_url: MyString 34 | repository_id: 1 35 | normalized_licenses: ['MyString'] 36 | versions_count: 1 37 | latest_release_published_at: 2020-05-01 16:56:52 38 | latest_release_number: MyString 39 | keywords_array: ['MyString'] 40 | dependents_count: 1 41 | language: MyString 42 | status: MyString 43 | last_synced_at: 2020-05-01 16:56:52 44 | dependent_repos_count: 1 45 | runtime_dependencies_count: 1 46 | latest_stable_release_number: MyString 47 | latest_stable_release_published_at: MyString 48 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Rails 2 | on: push 3 | 4 | jobs: 5 | verify: 6 | name: Build 7 | runs-on: ubuntu-latest 8 | 9 | services: 10 | postgres: 11 | image: postgres:11 12 | env: 13 | POSTGRES_USER: ecosystem 14 | DATABASE_NAME: ecosystem_test 15 | POSTGRES_PASSWORD: postgres 16 | ports: ["5432:5432"] 17 | options: >- 18 | --health-cmd pg_isready 19 | --health-interval 10s 20 | --health-timeout 5s 21 | --health-retries 5 22 | 23 | redis: 24 | image: redis 25 | ports: 26 | - 6379:6379 27 | options: --entrypoint redis-server 28 | 29 | steps: 30 | - uses: actions/checkout@v6 31 | - name: Install dependent libraries 32 | run: sudo apt-get install libpq-dev 33 | - name: Set up Node 34 | uses: actions/setup-node@v6 35 | with: 36 | node-version: 15 37 | - name: Set up Ruby 38 | uses: ruby/setup-ruby@v1 39 | with: 40 | ruby-version: 3.4.7 41 | bundler-cache: true 42 | cache-version: 1 43 | 44 | - name: Run tests 45 | env: 46 | RAILS_ENV: test 47 | DATABASE_NAME: ecosystem_test 48 | POSTGRES_USER: ecosystem 49 | POSTGRES_PASSWORD: postgres 50 | DATABASE_HOST: localhost 51 | DATABASE_PORT: ${{ job.services.postgres.ports[5432] }} 52 | run: bundle exec rake db:create db:migrate test -------------------------------------------------------------------------------- /lib/tasks/research.rake: -------------------------------------------------------------------------------- 1 | namespace :research do 2 | desc "download arweave org, repo and packages and output as csv" 3 | task arweave: :environment do 4 | # download all the repos 5 | o = Organization.find_or_create_by(name: 'ArweaveTeam') 6 | Repository.import_org(o.name) 7 | 8 | # # find all the arweave packages 9 | Repository.archived(false).org(o.name).find_missing_npm_packages 10 | Repository.archived(false).org(o.name).find_missing_cargo_packages 11 | Repository.archived(false).org(o.name).find_missing_go_packages 12 | 13 | # # download dependents of each package 14 | o.packages.each(&:find_dependent_github_repos) 15 | 16 | # output the unique list of total dependents 17 | repos = [] 18 | 19 | o.packages.each do |pkg| 20 | repos << pkg.dependent_repositories 21 | end 22 | 23 | 24 | exclude_fields = ['direct_internal_dependency_package_ids', 'indirect_internal_dependency_package_ids'] 25 | 26 | scope = Repository.internal 27 | csv_string = CSV.generate do |csv| 28 | csv << Repository.attribute_names.excluding(exclude_fields) + ['direct_internal_dependency_package_names', 'indirect_internal_dependency_package_names'] 29 | repos.flatten.uniq(&:full_name).sort_by(&:full_name).each do |repo| 30 | csv << repo.attributes.except(*exclude_fields).values + [repo.direct_internal_dependency_package_names, repo.indirect_internal_dependency_package_names] 31 | end 32 | end 33 | 34 | puts csv_string 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /app/views/repositories/_dependencies.html.erb: -------------------------------------------------------------------------------- 1 | <% if @manifests.present? %> 2 | <% @manifests.each do |manifest| %> 3 |
4 |
5 | <%= link_to manifest.filepath, manifest.repository_link, target: :_blank %> 6 |
7 | 33 |
34 | <% end %> 35 | <% else %> 36 |

No dependencies found

37 | <% end %> 38 | -------------------------------------------------------------------------------- /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 14 | # # If you are using webpack-dev-server then specify webpack-dev-server host 15 | # policy.connect_src :self, :https, "http://localhost:3035", "ws://localhost:3035" if Rails.env.development? 16 | 17 | # # Specify URI for violation reports 18 | # # policy.report_uri "/csp-violation-report-endpoint" 19 | # end 20 | 21 | # If you are using UJS then enable automatic nonce generation 22 | # Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) } 23 | 24 | # Set the nonce only to specific directives 25 | # Rails.application.config.content_security_policy_nonce_directives = %w(script-src) 26 | 27 | # Report CSP violations to a specified URI 28 | # For further information see the following documentation: 29 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only 30 | # Rails.application.config.content_security_policy_report_only = true 31 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | - package-ecosystem: bundler 8 | directory: "/" 9 | schedule: 10 | interval: daily 11 | time: "11:00" 12 | open-pull-requests-limit: 10 13 | ignore: 14 | - dependency-name: spdx 15 | versions: 16 | - "> 2.0.10" 17 | - dependency-name: oj 18 | versions: 19 | - 3.11.4 20 | - dependency-name: chartkick 21 | versions: 22 | - 4.0.0 23 | - 4.0.3 24 | - dependency-name: pg_query 25 | versions: 26 | - 2.0.1 27 | - 2.0.3 28 | - dependency-name: listen 29 | versions: 30 | - 3.5.1 31 | - dependency-name: pghero 32 | versions: 33 | - 2.7.5 34 | - 2.8.0 35 | - 2.8.1 36 | - dependency-name: counter_culture 37 | versions: 38 | - 2.8.0 39 | - dependency-name: sidekiq 40 | versions: 41 | - 6.2.0 42 | - dependency-name: ox 43 | versions: 44 | - 2.14.2 45 | - 2.14.3 46 | - dependency-name: pagy 47 | versions: 48 | - 3.11.0 49 | - 3.12.0 50 | - dependency-name: semantic_range 51 | versions: 52 | - 3.0.0 53 | - dependency-name: octicons_helper 54 | versions: 55 | - 12.0.0 56 | - 12.1.0 57 | - dependency-name: rails 58 | versions: 59 | - 6.1.2.1 60 | - 6.1.3 61 | - dependency-name: commonmarker 62 | versions: 63 | - 0.21.2 64 | - dependency-name: puma 65 | versions: 66 | - 5.2.1 67 | - dependency-name: bootsnap 68 | versions: 69 | - 1.7.0 70 | - 1.7.1 71 | - 1.7.2 72 | -------------------------------------------------------------------------------- /app/views/packages/outdated.html.erb: -------------------------------------------------------------------------------- 1 |

<%= @page_title = 'Outdated packages' %>

2 | 3 | 4 | 5 | 8 | 11 | 14 | 17 | 20 | 23 | 24 | 25 | <% @packages.each do |package| %> 26 | 27 | 34 | 37 | 41 | 44 | 47 | 50 | 51 | <% end %> 52 |
6 | Org 7 | 9 | PM 10 | 12 | Package Name 13 | 15 | Last release 16 | 18 | Collabs Usage 19 | 21 | % outdated 22 |
28 | <% if package.repository %> 29 | <%= link_to "https://github.com/#{package.repository.org}", title: package.repository.org, target: :_blank, class: 'float-left mr-2' do %> 30 | 31 | <% end %> 32 | <% end %> 33 | 35 | <%= package.platform_name %> 36 | 38 | <%= link_to package.name, package_path(id: package.id) %> 39 | <%= package.latest_stable_release_number %> 40 | 42 | <%= distance_of_time_in_words_to_now package.latest_release_published_at %> ago 43 | 45 | <%= package.collab_dependent_repos_count %> 46 | 48 | <%= package.outdated %>% 49 |
53 | -------------------------------------------------------------------------------- /app/views/organizations/_packages.html.erb: -------------------------------------------------------------------------------- 1 | <% @repository_dependencies.group_by{|rd| rd.repository }.sort_by{|r,rd| r }.each do |repo, repository_dependencies| %> 2 |
3 |
4 | <%= link_to "https://github.com/#{repo.full_name}", target: :_blank, class: 'mr-1' do %> 5 | 6 | <% end %> 7 | <%= link_to repo.full_name, repository_path(repo) %> 8 |
9 | 28 |
29 | <% end %> 30 | -------------------------------------------------------------------------------- /app/models/package_manager/clojars.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module PackageManager 4 | class Clojars < Base 5 | HAS_VERSIONS = true 6 | HAS_DEPENDENCIES = false 7 | BIBLIOTHECARY_SUPPORT = true 8 | URL = "https://clojars.org" 9 | COLOR = "#db5855" 10 | 11 | def self.package_link(package, version = nil) 12 | "https://clojars.org/#{package.name}" + (version ? "/versions/#{version}" : "") 13 | end 14 | 15 | def self.package_names 16 | @package_names ||= get("https://clojars.libraries.io/packages.json").keys 17 | end 18 | 19 | def self.recent_names 20 | get_html("https://clojars.org/").css(".recent-jar-title a").map(&:text) 21 | end 22 | 23 | def self.packages 24 | @packages ||= begin 25 | pkgs = {} 26 | get("https://clojars.libraries.io/feed.json").each do |k, v| 27 | v.each do |pkg| 28 | group = pkg["group-id"] 29 | key = (group == k ? k : "#{group}/#{k}") 30 | pkgs[key] = pkg 31 | end 32 | end 33 | pkgs 34 | end 35 | end 36 | 37 | def self.package(name) 38 | packages[name.downcase].merge(name: name) 39 | end 40 | 41 | def self.mapping(package) 42 | { 43 | name: package[:name], 44 | description: package["description"], 45 | repository_url: repo_fallback(package.fetch("scm", {})["url"], ""), 46 | } 47 | end 48 | 49 | def self.versions(package, _name) 50 | package["versions"].map do |v| 51 | { 52 | number: v, 53 | } 54 | end 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /app/views/repositories/_repository.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 | <%= link_to "https://github.com/#{repository.org}", target: :_blank, class: 'float-right' do %> 4 | 5 | <% end %> 6 |
7 | 8 | <%= octicon(repo_icon(repository), height: 18) %> 9 | 10 | 11 | <%= link_to repository.full_name, repository %> 12 | <% if repository.topics %> 13 | <% repository.topics.each do |t| %> <%= t %> <% end %> 14 | <% end %> 15 |
16 | 17 | <%= repository.description %> 18 |
19 |
20 | Pushed: <%= distance_of_time_in_words_to_now repository.pushed_at %> ago 21 |
22 |
23 | Stars: <%= number_to_human repository.stargazers_count %> 24 |
25 |
26 | Forks: <%= number_to_human repository.forks_count %> 27 |
28 |
29 | Ecosystem Score: <%= repository.score %> 30 |
31 |
32 | Internal Deps: <%= repository.internal_package_dependency_ids.length %> 33 |
34 |
35 |
36 |
37 |
38 | -------------------------------------------------------------------------------- /app/views/events/_event.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 | <%= link_to "https://github.com/#{event.actor.gsub('[bot]', '')}", target: :_blank, class: 'float-right' do %> 4 | 5 | <% end %> 6 |
7 | 8 | <%= octicon(event_icon(event), height: 18) %> 9 | 10 | 11 | <%= link_to event.actor, "https://github.com/#{event.actor.gsub('[bot]', '')}", target: :_blank %> 12 | 13 | <%= event.action_text %> 14 | 15 | <% if event.repository_id %> 16 | <%= link_to event.repository_full_name, repository_url(event.repository_id) %> 17 | <% else %> 18 | <%= link_to event.repository_full_name, "https://github.com/#{event.repository_full_name}", target: :_blank %> 19 | <% end %> 20 | 21 | 22 | <%= distance_of_time_in_words_to_now event.created_at %> ago 23 | 24 | 25 | <% if event.contributed? %> 26 | <% Contributor.collabs(event.actor).first(5).each do |collab| %> 27 | <%= link_to collab, url_for(request.params.merge(collab: collab, page: nil)), class: "badge badge-info" %> 28 | <% end %> 29 | <% else %> 30 | Core 31 | <% end %> 32 | 33 |
34 |
35 | <%= render partial: "events/#{event.event_type.underscore}", locals: {event: event} %> 36 |
37 |
38 |
39 | -------------------------------------------------------------------------------- /app/views/search/index.html.erb: -------------------------------------------------------------------------------- 1 | <% content_for :rss do %> 2 | <%= auto_discovery_link_tag :rss, url_for(request.params.merge(page: nil, format: :rss, only_path: false)) %> 3 | <% end %> 4 | 5 | 21 | 22 | <% if @search_results.any? %> 23 |
24 | <%= line_chart @scope.group_by_day(:created_at).count, colors: ['#bbb'], height: "80px", library: { scales: { yAxes: [{ display: false }], xAxes: [{ display: false }] }}, points: false %> 25 |
26 | <%= render @search_results %> 27 | <% else %> 28 |

No results found.

29 | <% end %> 30 | <%== pagy_bootstrap_nav(@pagy) if @pagy.pages > 1 %> 31 | 32 |

33 | 34 | RSS feed for this page: <%= link_to truncate(url_for(request.params.merge(page: nil, format: :rss, only_path: false)), length: 100), url_for(request.params.merge(page: nil, format: :rss, only_path: false)) %> 35 | 36 |

37 | -------------------------------------------------------------------------------- /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 | max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } 8 | min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count } 9 | threads min_threads_count, max_threads_count 10 | 11 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000. 12 | # 13 | port ENV.fetch("PORT") { 3000 } 14 | 15 | # Specifies the `environment` that Puma will run in. 16 | # 17 | environment ENV.fetch("RAILS_ENV") { "development" } 18 | 19 | # Specifies the `pidfile` that Puma will use. 20 | pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" } 21 | 22 | # Specifies the number of `workers` to boot in clustered mode. 23 | # Workers are forked web server processes. If using threads and workers together 24 | # the concurrency of the application would be max `threads` * `workers`. 25 | # Workers do not work on JRuby or Windows (both of which do not support 26 | # processes). 27 | # 28 | # workers ENV.fetch("WEB_CONCURRENCY") { 2 } 29 | 30 | # Use the `preload_app!` method when specifying a `workers` number. 31 | # This directive tells Puma to first boot the application and load code 32 | # before forking the application. This takes advantage of Copy On Write 33 | # process behavior so workers use less memory. 34 | # 35 | # preload_app! 36 | 37 | # Allow puma to be restarted by `rails restart` command. 38 | plugin :tmp_restart 39 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "cron": [ 3 | { 4 | "command": "bundle exec rake issues:sync_recent", 5 | "schedule": "*/10 * * * *" 6 | }, 7 | { 8 | "command": "bundle exec rake issues:sync_collabs", 9 | "schedule": "0 * * * *" 10 | }, 11 | { 12 | "command": "bundle exec rake packages:sync_internal", 13 | "schedule": "0 0 * * *" 14 | }, 15 | { 16 | "command": "bundle exec rake packages:find_missing_npm_packages", 17 | "schedule": "0 1 * * *" 18 | }, 19 | { 20 | "command": "bundle exec rake packages:sync", 21 | "schedule": "0 * * * *" 22 | }, 23 | { 24 | "command": "bundle exec rake issues:sync_daily", 25 | "schedule": "0 7 * * *" 26 | }, 27 | { 28 | "command": "bundle exec rake search:run_all", 29 | "schedule": "*/10 * * * *" 30 | }, 31 | { 32 | "command": "bundle exec rake packages:find_missing_package_repos", 33 | "schedule": "0 6 * * *" 34 | }, 35 | { 36 | "command": "bundle exec rake packages:find_dependent_github_repos", 37 | "schedule": "0 3 * * *" 38 | }, 39 | { 40 | "command": "bundle exec rake repositories:sync", 41 | "schedule": "50 * * * *" 42 | }, 43 | { 44 | "command": "bundle exec rake repositories:discover_from_search_results", 45 | "schedule": "0 4 * * *" 46 | }, 47 | { 48 | "command": "bundle exec rake repositories:recalculate_scores", 49 | "schedule": "0 8 * * *" 50 | }, 51 | { 52 | "command": "bundle exec rake contributors:sync", 53 | "schedule": "40 * * * *" 54 | }, 55 | { 56 | "command": "bundle exec rake repositories:sync_discovered", 57 | "schedule": "30 * * * *" 58 | } 59 | ] 60 | } -------------------------------------------------------------------------------- /app/models/tag.rb: -------------------------------------------------------------------------------- 1 | class Tag < ApplicationRecord 2 | include Releaseable 3 | 4 | belongs_to :repository 5 | validates_presence_of :name, :sha, :repository 6 | validates_uniqueness_of :name, scope: :repository_id 7 | 8 | scope :published, -> { where('published_at IS NOT NULL') } 9 | 10 | after_commit :save_packages 11 | 12 | def save_packages 13 | repository.try(:save_packages) 14 | end 15 | 16 | def has_packages? 17 | repository && repository.packages.without_versions.length > 0 18 | end 19 | 20 | def <=>(other) 21 | if parsed_number.is_a?(String) || other.parsed_number.is_a?(String) 22 | other.number <=> number 23 | else 24 | other.parsed_number <=> parsed_number 25 | end 26 | end 27 | 28 | def prerelease? 29 | !!parsed_number.try(:pre) 30 | end 31 | 32 | def number 33 | name 34 | end 35 | 36 | def repository_url 37 | case repository.host_type 38 | when 'GitHub' 39 | "#{repository.url}/releases/tag/#{name}" 40 | when 'GitLab' 41 | "#{repository.url}/tags/#{name}" 42 | when 'Bitbucket' 43 | "#{repository.url}/commits/tag/#{name}" 44 | end 45 | end 46 | 47 | def related_tags 48 | repository.sorted_tags 49 | end 50 | 51 | def tag_index 52 | related_tags.index(self) 53 | end 54 | 55 | def next_tag 56 | related_tags[tag_index - 1] 57 | end 58 | 59 | def previous_tag 60 | related_tags[tag_index + 1] 61 | end 62 | 63 | alias_method :previous_version, :previous_tag 64 | 65 | def related_tag 66 | true 67 | end 68 | 69 | def diff_url 70 | return nil unless repository && previous_tag && previous_tag 71 | repository.compare_url(previous_tag.number, number) 72 | end 73 | 74 | def runtime_dependencies_count 75 | nil # tags can't have dependencies yet 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /app/models/concerns/dependency_checks.rb: -------------------------------------------------------------------------------- 1 | module DependencyChecks 2 | extend ActiveSupport::Concern 3 | 4 | def incompatible_license? 5 | compatible_license? == false 6 | end 7 | 8 | def outdated? 9 | return nil unless valid_requirements? && package && package.latest_stable_release_number 10 | !(SemanticRange.satisfies(SemanticRange.clean(package.latest_stable_release_number), semantic_requirements, false, platform) || 11 | SemanticRange.satisfies(SemanticRange.clean(package.latest_release_number), semantic_requirements, false, platform) || 12 | SemanticRange.ltr(SemanticRange.clean(package.latest_release_number), semantic_requirements, false, platform)) 13 | rescue 14 | nil 15 | end 16 | 17 | def semantic_requirements 18 | case platform.downcase 19 | when 'elm' 20 | numbers = requirements.split('<= v') 21 | ">=#{numbers[0].strip} #{numbers[1].strip}" 22 | else 23 | requirements 24 | end 25 | end 26 | 27 | def valid_requirements? 28 | !!SemanticRange.valid_range(semantic_requirements) 29 | end 30 | 31 | def latest_resolvable_version(date = nil) 32 | return nil unless package.present? 33 | versions = package.versions 34 | if date 35 | versions = versions.where('versions.published_at < ?', date) 36 | end 37 | version_numbers = versions.map {|v| SemanticRange.clean(v.number) }.compact 38 | number = SemanticRange.max_satisfying(version_numbers, semantic_requirements, false, platform) 39 | return nil unless number.present? 40 | 41 | versions.find{|v| SemanticRange.clean(v.number) == number } 42 | end 43 | 44 | def update_package 45 | return unless package_name.present? && package_manager 46 | package_manager.update(package_name) 47 | end 48 | 49 | def package_manager 50 | PackageManager::Base.find(platform) 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /app/views/contributors/index.html.erb: -------------------------------------------------------------------------------- 1 | 35 | 36 | <% @contributors.each do |contributor, count| %> 37 | <%= render partial: 'contributor', locals: {contributor: contributor, count: count} %> 38 | <% end %> 39 | <%== pagy_bootstrap_nav(@pagy) if @pagy.pages > 1 %> 40 | -------------------------------------------------------------------------------- /app/views/contributors/collabs.html.erb: -------------------------------------------------------------------------------- 1 | 35 | 36 | <% @contributors.each do |contributor, count| %> 37 | <%= render partial: 'contributor', locals: {contributor: contributor, count: count} %> 38 | <% end %> 39 | <%== pagy_bootstrap_nav(@pagy) if @pagy.pages > 1 %> 40 | -------------------------------------------------------------------------------- /app/views/contributors/new.html.erb: -------------------------------------------------------------------------------- 1 | 35 | 36 | <% @contributors.each do |contributor, count| %> 37 | <%= render partial: 'contributor', locals: {contributor: contributor, count: count} %> 38 | <% end %> 39 | <%== pagy_bootstrap_nav(@pagy) if @pagy.pages > 1 %> 40 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/tasks/contributors.rake: -------------------------------------------------------------------------------- 1 | require 'csv' 2 | 3 | namespace :contributors do 4 | 5 | desc "sync least recently updated contributors from discovered repositories" 6 | task sync: :environment do 7 | Repository.discovered_contributors.order('last_events_sync_at ASC nulls first').where(bot: false).limit(200).each(&:sync) 8 | end 9 | 10 | desc "output a csv of top contributor data" 11 | task research: :environment do 12 | contributors = Issue.not_core.group(:user).count.sort_by{|u,c| -c}.select{|u,c| c > 10 }.reject{|u,c| u == 'ghost'} 13 | 14 | collabs = {} 15 | 16 | contributors.each do |username, contributions| 17 | collabs[username] = Issue.where(user: username).first.collabs 18 | end 19 | 20 | csv_string = CSV.generate do |csv| 21 | csv << [ 22 | 'Name', 23 | 'Login', 24 | 'Company', 25 | 'URL', 26 | 'Blog', 27 | 'Location', 28 | 'Bio', 29 | 'Email', 30 | 'Twitter', 31 | 'Hireable', 32 | 'Followers', 33 | 'Contributions', 34 | 'Collabs' 35 | ] 36 | 37 | contributors.each do |username, contributions| 38 | begin 39 | json = Issue.github_client.user(username) 40 | next if ['@protocol', 'Protocol Labs'].include?(json.company) 41 | csv << [ 42 | json.name, 43 | username, 44 | json.company, 45 | "https://github.com/#{username}", 46 | json.blog, 47 | json.location, 48 | json.bio, 49 | json.email, 50 | json.twitter, 51 | json.hireable, 52 | json.followers, 53 | contributions, 54 | collabs[username].join(',') 55 | ] 56 | rescue Octokit::NotFound 57 | puts "#{username} DELETED" 58 | end 59 | end 60 | end 61 | 62 | puts csv_string 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /app/models/package_manager/carthage.rb: -------------------------------------------------------------------------------- 1 | module PackageManager 2 | class Carthage < Base 3 | HAS_VERSIONS = false 4 | HAS_DEPENDENCIES = false 5 | BIBLIOTHECARY_SUPPORT = true 6 | URL = 'https://github.com/Carthage/Carthage' 7 | COLOR = '#ffac45' 8 | 9 | def self.package_names 10 | Manifest.platform('Carthage').includes(:repository_dependencies).map{|m| m.repository_dependencies.map(&:package_name).compact.map(&:downcase)}.flatten.uniq 11 | end 12 | 13 | def self.package(name) 14 | if name.match(/^([-\w]+)\/([-.\w]+)$/) 15 | begin 16 | repo = AuthToken.client.repo(name, accept: 'application/vnd.github.drax-preview+json,application/vnd.github.mercy-preview+json') 17 | return repo.to_hash 18 | rescue 19 | return nil 20 | end 21 | elsif name_with_owner = GitlabUrlParser.parse(name) 22 | begin 23 | repo = AuthToken.client.repo(name_with_owner, accept: 'application/vnd.github.drax-preview+json,application/vnd.github.mercy-preview+json') 24 | return repo.to_hash 25 | rescue 26 | return nil 27 | end 28 | elsif name.match(/^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/) 29 | begin 30 | response = request(name) 31 | if response.status == 200 32 | { 33 | full_name: name.sub(/^https?\:\/\//, ''), 34 | homepage: name 35 | } 36 | end 37 | rescue 38 | nil 39 | end 40 | end 41 | end 42 | 43 | def self.mapping(package) 44 | { 45 | :name => package[:full_name], 46 | :description => package[:description], 47 | :homepage => package[:homepage], 48 | :keywords_array => package[:topics], 49 | :licenses => (package.fetch(:license, {}) || {})[:key], 50 | :repository_url => package[:html_url] 51 | } 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /app/models/dependency.rb: -------------------------------------------------------------------------------- 1 | class Dependency < ApplicationRecord 2 | include DependencyChecks 3 | 4 | belongs_to :version 5 | belongs_to :package, optional: true 6 | 7 | validates_presence_of :package_name, :version_id, :requirements, :platform 8 | 9 | scope :with_package, -> { joins(:package).where('packages.id IS NOT NULL') } 10 | scope :without_package_id, -> { where(package_id: nil) } 11 | scope :with_package_name, -> { where("dependencies.package_name <> ''") } 12 | scope :kind, ->(kind) { where(kind: kind) } 13 | scope :platform, ->(platform) { where('lower(dependencies.platform) = ?', platform.try(:downcase)) } 14 | 15 | before_create :set_package_id 16 | 17 | delegate :latest_stable_release_number, :latest_release_number, :is_deprecated?, :score, to: :package, allow_nil: true 18 | 19 | alias_attribute :name, :package_name 20 | alias_method :latest_stable, :latest_stable_release_number 21 | alias_method :latest, :latest_release_number 22 | alias_method :deprecated, :is_deprecated? 23 | alias_method :outdated, :outdated? 24 | 25 | def filepath 26 | nil 27 | end 28 | 29 | def find_package_id 30 | Package.find_best(platform, package_name.strip)&.id 31 | end 32 | 33 | def compatible_license? 34 | return nil unless package 35 | return nil if package.normalized_licenses.empty? 36 | return nil if version.package.normalized_licenses.empty? 37 | package.normalized_licenses.any? do |license| 38 | version.package.normalized_licenses.any? do |other_license| 39 | begin 40 | License::Compatibility.forward_compatibility(license, other_license) 41 | rescue 42 | true 43 | end 44 | end 45 | end 46 | end 47 | 48 | def set_package_id 49 | self.package_id = find_package_id unless package_id.present? 50 | end 51 | 52 | def update_package_id 53 | pack_id = find_package_id 54 | update_column(:package_id, pack_id) if pack_id.present? 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/models/concerns/releases.rb: -------------------------------------------------------------------------------- 1 | module Releases 2 | def stable_releases 3 | versions.select(&:stable?) 4 | end 5 | 6 | def prereleases 7 | versions.select(&:prerelease?) 8 | end 9 | 10 | def latest_stable_version 11 | @latest_version ||= stable_releases.sort.first 12 | end 13 | 14 | def latest_stable_tag 15 | return nil if repository.nil? 16 | tags.published.select(&:stable?).sort.first 17 | end 18 | 19 | def latest_stable_release 20 | latest_stable_version || latest_stable_tag 21 | end 22 | 23 | def latest_stable_release_number 24 | latest_stable_release.try(:number) 25 | end 26 | 27 | def latest_version 28 | versions.sort.first 29 | end 30 | 31 | def latest_tag 32 | return nil if repository.nil? 33 | tags.published.order('published_at DESC').first 34 | end 35 | 36 | def latest_release 37 | latest_version || latest_tag 38 | end 39 | 40 | def first_version 41 | @first_version ||= versions.sort.last 42 | end 43 | 44 | def first_tag 45 | return nil if repository.nil? 46 | tags.published.order('published_at ASC').first 47 | end 48 | 49 | def first_release 50 | first_version || first_tag 51 | end 52 | 53 | def latest_release_published_at 54 | read_attribute(:latest_release_published_at) || (latest_release.try(:published_at).presence || updated_at) 55 | end 56 | 57 | def set_latest_release_published_at 58 | self.latest_release_published_at = (latest_release.try(:published_at).presence || updated_at) 59 | end 60 | 61 | def set_latest_release_number 62 | self.latest_release_number = latest_release.try(:number) 63 | end 64 | 65 | def set_latest_stable_release_info 66 | latest_stable = latest_stable_release 67 | self.latest_stable_release_number = latest_stable.try(:number) 68 | self.latest_stable_release_published_at = (latest_stable.try(:published_at).presence || latest_stable.try([:updated_at])) 69 | end 70 | 71 | def set_runtime_dependencies_count 72 | self.runtime_dependencies_count = latest_release.try(:runtime_dependencies_count) 73 | end 74 | end 75 | -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | # The test environment is used exclusively to run your application's 2 | # test suite. You never need to work with it otherwise. Remember that 3 | # your test database is "scratch space" for the test suite and is wiped 4 | # and recreated between test runs. Don't rely on the data there! 5 | 6 | Rails.application.configure do 7 | # Settings specified here will take precedence over those in config/application.rb. 8 | 9 | config.cache_classes = false 10 | 11 | # Do not eager load code on boot. This avoids loading your whole application 12 | # just for the purpose of running a single test. If you are using a tool that 13 | # preloads Rails for running tests, you may have to set it to true. 14 | config.eager_load = false 15 | 16 | # Configure public file server for tests with Cache-Control for performance. 17 | config.public_file_server.enabled = true 18 | config.public_file_server.headers = { 19 | 'Cache-Control' => "public, max-age=#{1.hour.to_i}" 20 | } 21 | 22 | # Show full error reports and disable caching. 23 | config.consider_all_requests_local = true 24 | config.action_controller.perform_caching = false 25 | config.cache_store = :null_store 26 | 27 | # Raise exceptions instead of rendering exception templates. 28 | config.action_dispatch.show_exceptions = false 29 | 30 | # Disable request forgery protection in test environment. 31 | config.action_controller.allow_forgery_protection = false 32 | 33 | # Store uploaded files on the local file system in a temporary directory. 34 | # config.active_storage.service = :test 35 | 36 | # config.action_mailer.perform_caching = false 37 | 38 | # Tell Action Mailer not to deliver emails to the real world. 39 | # The :test delivery method accumulates sent emails in the 40 | # ActionMailer::Base.deliveries array. 41 | # config.action_mailer.delivery_method = :test 42 | 43 | # Print deprecation notices to the stderr. 44 | config.active_support.deprecation = :stderr 45 | 46 | # Raises error for missing translations. 47 | # config.action_view.raise_on_missing_translations = true 48 | end 49 | -------------------------------------------------------------------------------- /lib/tasks/upgrades.rake: -------------------------------------------------------------------------------- 1 | require 'csv' 2 | 3 | namespace :upgrades do 4 | desc 'discover and export a csv of issues and pull requests on github related to updating ipfs' 5 | task search: :environment do 6 | search_to_csv(["update ipfs is:issue", "update ipfs is:pr", "upgrade ipfs is:issue", "upgrade ipfs is:pr", "bump ipfs", "ipfs 0.5", "go-ipfs", "ipfs docker"], 'ipfs') 7 | end 8 | end 9 | 10 | def search_to_csv(queries, filename) 11 | CSV.open("data/#{filename}.csv", "w") do |csv| 12 | 13 | csv << [ 14 | 'Kind', 15 | 'Created', 16 | 'Owner', 17 | 'Repository', 18 | 'Number', 19 | 'Author', 20 | 'Comments', 21 | 'URL', 22 | 'Title', 23 | 'Labels', 24 | 'State' 25 | ] 26 | 27 | search_to_rows(queries).each do |row| 28 | csv << row 29 | end 30 | end 31 | end 32 | 33 | def search_to_rows(queries) 34 | queries = Array(queries) 35 | client = Issue.github_client 36 | 37 | rows = [] 38 | 39 | queries.each do |query| 40 | search = client.search_issues("#{query} created:>2020-01-01 in:title", sort: 'created', order: 'asc') 41 | puts "#{query} had #{search[:total_count]} results" if search[:total_count] > 1000 42 | search[:items].each do |i| 43 | url = i[:html_url] 44 | state = i["state"] 45 | kind = 'Issue' 46 | repo = i[:repository_url].gsub('https://api.github.com/repos/', '') 47 | owner = repo.split('/').first 48 | 49 | next if Organization.internal.pluck(:name).include?(owner) 50 | 51 | if i[:pull_request].present? 52 | kind = 'Pull Request' 53 | pr = client.get(i[:pull_request][:url]) 54 | url = pr[:html_url] 55 | state = 'merged' if pr[:merged] 56 | end 57 | 58 | rows << [ 59 | kind, 60 | i[:created_at], 61 | owner, 62 | repo, 63 | i[:number], 64 | i[:user][:login], 65 | i[:comments], 66 | url, 67 | i[:title], 68 | i[:labels].map{|l| l[:name]}.join(','), 69 | state 70 | ] 71 | end 72 | end 73 | 74 | rows.uniq.sort_by{|r| r[1] } 75 | end 76 | -------------------------------------------------------------------------------- /app/views/organizations/dependencies.html.erb: -------------------------------------------------------------------------------- 1 |

2 | <%= params[:id] %> dependencies 3 |

4 | 5 |
6 | <%= pluralize @internal_packages.length, 'internal packages' %> required in <%= pluralize @dependencies.map(&:repository_id).uniq.length, ' active repository' %> 7 |
8 | 9 |
10 | <% @internal_packages.each do |package, repository_dependencies| %> 11 |
12 | 23 | 39 |
40 | <% end %> 41 |
42 | -------------------------------------------------------------------------------- /app/models/package_manager/docker.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module PackageManager 4 | class Docker < Base 5 | HAS_VERSIONS = true 6 | HAS_DEPENDENCIES = false 7 | BIBLIOTHECARY_SUPPORT = true 8 | URL = "https://hub.docker.com/" 9 | COLOR = "#099cec" 10 | 11 | def self.package_link(package, version = nil) 12 | "https://hub.docker.com/r/#{package.name}" 13 | end 14 | 15 | def self.package(name) 16 | get("https://hub.docker.com/v2/repositories/#{name}/") 17 | end 18 | 19 | def self.org_package_names(name) 20 | page = 1 21 | images = [] 22 | while page < 100 23 | r = get("https://hub.docker.com/v2/repositories/#{name}/?page=#{page}") 24 | break if r['results'].nil? || r['results'] == [] 25 | 26 | images += r['results'] 27 | page += 1 28 | end 29 | images.map{|i| "#{i["namespace"]}/#{i["name"]}" } 30 | end 31 | 32 | def self.mapping(package) 33 | package_name = "#{package["namespace"]}/#{package["name"]}" 34 | { 35 | name: package_name, 36 | description: package["description"], 37 | repository_url: load_repository_url(package_name) 38 | } 39 | end 40 | 41 | def self.versions(package, name) 42 | page = 1 43 | tags = [] 44 | while page < 100 45 | r = get("https://hub.docker.com/v2/repositories/#{name}/tags?page=#{page}") 46 | break if r['results'] == [] 47 | 48 | tags += r['results'] 49 | page += 1 50 | end 51 | 52 | tags.map do |version| 53 | next if version["name"].match?(/latest/) 54 | { 55 | number: version["name"], 56 | published_at: version["last_updated"] 57 | } 58 | end.compact 59 | end 60 | 61 | def self.load_repository_url(name) 62 | json = get("https://hub.docker.com/api/build/v1/source/?image=#{name}") 63 | return unless json['objects'] 64 | o = json['objects'].first 65 | return unless o 66 | return unless o['provider'] == 'Github' 67 | "https://github.com/#{o['owner']}/#{o['repository']}" 68 | end 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /app/views/issues/_excludable_filter.html.erb: -------------------------------------------------------------------------------- 1 | 34 | -------------------------------------------------------------------------------- /app/views/issues/_sort_filter.html.erb: -------------------------------------------------------------------------------- 1 | 42 | -------------------------------------------------------------------------------- /lib/tasks/backfill.rake: -------------------------------------------------------------------------------- 1 | require 'open-uri' 2 | require 'zlib' 3 | 4 | namespace :backfill do 5 | desc 'import event data for all non-internal repos with internal dependencies from gharchive' 6 | task all: :environment do 7 | 8 | repo_names = Repository.not_internal.with_internal_deps.pluck(:full_name) 9 | 10 | if ENV['START_DATE'].present? 11 | start_date = Time.parse(ENV['START_DATE']) 12 | else 13 | start_date = (Time.now - 1.year).beginning_of_year 14 | end 15 | 16 | start_year = start_date.year 17 | start_year = 2015 if start_year < 2015 18 | 19 | if ENV['END_DATE'].present? 20 | end_date = Date.parse(ENV['END_DATE']) 21 | else 22 | end_date = Date.today 23 | end 24 | 25 | end_year = end_date.year 26 | 27 | (start_year..end_year).each do |year| 28 | (1..12).each do |month| 29 | max_days = Date.new(year, month, -1).day 30 | month = month.to_s.rjust(2, "0") 31 | 32 | (1..max_days).each do |day| 33 | day = day.to_s.rjust(2, "0") 34 | break if Date.parse("#{day}/#{month}/#{year}") > end_date 35 | 36 | if Date.parse("#{day}/#{month}/#{year}") > start_date 37 | 38 | (0..23).each do |hour| 39 | puts "#{day}/#{month}/#{year}/#{hour}" 40 | 41 | begin 42 | gz = URI.open("http://data.gharchive.org/#{year}-#{month}-#{day}-#{hour}.json.gz") 43 | js = Zlib::GzipReader.new(gz).read 44 | 45 | Oj.load(js) do |event| 46 | repo_name = event['repo']['name'] 47 | if repo_names.include?(repo_name) 48 | 49 | repository = Repository.find_by_full_name(repo_name) 50 | if repository 51 | ret = Event.record_event(repository, event) 52 | if ret 53 | puts "#{repo_name} - #{event['type']}" 54 | end 55 | end 56 | 57 | end 58 | end 59 | rescue 60 | # failed to download 61 | end 62 | end 63 | end 64 | end 65 | end 66 | end 67 | end 68 | end 69 | -------------------------------------------------------------------------------- /lib/tasks/changelog.rake: -------------------------------------------------------------------------------- 1 | namespace :changelog do 2 | desc 'list internal repos with no changelog or history file' 3 | task missing: :environment do 4 | release_events = Event.internal.event_type('ReleaseEvent').includes(:repository) 5 | repos = release_events.map(&:repository).uniq.reject(&:archived?) 6 | repos.each do |repo| 7 | file_list = repo.get_file_list 8 | 9 | changelog = file_list.find{|file| file.match(/^CHANGE|^HISTORY/i) } 10 | if changelog 11 | 12 | else 13 | puts repo.full_name 14 | # pp file_list 15 | end 16 | end 17 | end 18 | 19 | desc "generate a changelog file for a repository based on it's releases" 20 | task generate: :environment do 21 | full_name = 'ipfs-shipyard/ipld-explorer-components' 22 | repo = Repository.find_by_full_name(full_name) 23 | releases = repo.events.event_type('ReleaseEvent') 24 | 25 | 26 | releases.order('created_at desc').each_with_index do |release, index| 27 | if index.zero? 28 | puts "# Changelog\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)." 29 | end 30 | puts "## [#{release.payload['release']['tag_name']}] - #{release.created_at.strftime("%Y-%m-%d")}" 31 | puts release.payload['release']['body'] 32 | end;nil 33 | end 34 | end 35 | 36 | 37 | # This changelog was automatically generated from the releases of this GitHub project and the format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). 38 | 39 | # def notes(full_name) 40 | # repo = Repository.find_by_full_name(full_name) 41 | # releases = repo.events.event_type('ReleaseEvent') 42 | # 43 | # releases.order('created_at desc').each_with_index do |release, index| 44 | # if index.zero? 45 | # puts "# Changelog\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)." 46 | # end 47 | # puts "## [#{release.payload['release']['tag_name']}] - #{release.created_at.strftime("%Y-%m-%d")}" 48 | # puts release.payload['release']['body'] 49 | # end;nil 50 | # end 51 | -------------------------------------------------------------------------------- /app/models/repository_dependency.rb: -------------------------------------------------------------------------------- 1 | class RepositoryDependency < ApplicationRecord 2 | include DependencyChecks 3 | 4 | belongs_to :manifest 5 | belongs_to :package, optional: true 6 | belongs_to :repository 7 | 8 | scope :with_package, -> { joins(:package).where('packages.id IS NOT NULL') } 9 | scope :without_package_id, -> { where(package_id: nil) } 10 | scope :with_package_name, -> { where("repository_dependencies.package_name <> ''") } 11 | scope :platform, ->(platform) { where('lower(repository_dependencies.platform) = ?', platform.try(:downcase)) } 12 | scope :kind, ->(kind) { where(kind: kind) } 13 | scope :active, -> { joins(:repository).where(repositories: {archived: false}) } 14 | scope :source, -> { joins(:repository).where(repositories: {fork: false}) } 15 | scope :direct, -> { where(direct: true) } 16 | scope :transitive, -> { where(direct: false) } 17 | 18 | scope :external, -> { where.not(repository_id: Repository.internal.pluck(:id)) } 19 | 20 | before_create :set_package_id 21 | 22 | delegate :latest_stable_release_number, :latest_release_number, :is_deprecated?, to: :package, allow_nil: true 23 | delegate :filepath, to: :manifest 24 | 25 | alias_method :latest_stable, :latest_stable_release_number 26 | alias_method :latest, :latest_release_number 27 | alias_method :deprecated, :is_deprecated? 28 | alias_method :outdated, :outdated? 29 | 30 | def name 31 | package_name 32 | end 33 | 34 | def self.internal 35 | with_package.where(packages: {repository_id: Repository.internal.pluck(:id)}) 36 | end 37 | 38 | def find_package_id 39 | Package.find_best(platform, package_name&.strip)&.id 40 | end 41 | 42 | def compatible_license? 43 | return nil unless package 44 | return nil if package.normalized_licenses.empty? 45 | return nil if repository.license.blank? 46 | package.normalized_licenses.any? do |license| 47 | begin 48 | License::Compatibility.forward_compatibility(license, repository.license) 49 | rescue 50 | true 51 | end 52 | end 53 | end 54 | 55 | def set_package_id 56 | self.package_id = find_package_id unless package_id.present? 57 | end 58 | 59 | def update_package_id 60 | pkg_id = find_package_id 61 | update_column(:package_id, pkg_id) if pkg_id.present? 62 | end 63 | 64 | def package_name 65 | read_attribute(:package_name).try(:tr, " \n\t\r", '') 66 | end 67 | 68 | def direct? 69 | manifest.kind == 'manifest' 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /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 | config.action_controller.enable_fragment_cache_logging = true 20 | 21 | config.cache_store = :memory_store 22 | config.public_file_server.headers = { 23 | 'Cache-Control' => "public, max-age=#{2.days.to_i}" 24 | } 25 | else 26 | config.action_controller.perform_caching = false 27 | 28 | config.cache_store = :null_store 29 | end 30 | 31 | # Store uploaded files on the local file system (see config/storage.yml for options). 32 | # config.active_storage.service = :local 33 | 34 | # Don't care if the mailer can't send. 35 | # config.action_mailer.raise_delivery_errors = false 36 | 37 | # config.action_mailer.perform_caching = false 38 | 39 | # Print deprecation notices to the Rails logger. 40 | config.active_support.deprecation = :log 41 | 42 | # Raise an error on page load if there are pending migrations. 43 | config.active_record.migration_error = :page_load 44 | 45 | # Highlight code that triggered database queries in logs. 46 | config.active_record.verbose_query_logs = true 47 | 48 | # Debug mode disables concatenation and preprocessing of assets. 49 | # This option may cause significant delays in view rendering with a large 50 | # number of complex assets. 51 | config.assets.debug = true 52 | 53 | # Suppress logger output for asset requests. 54 | config.assets.quiet = true 55 | 56 | # Raises error for missing translations. 57 | # config.action_view.raise_on_missing_translations = true 58 | 59 | # Use an evented file watcher to asynchronously detect changes in source code, 60 | # routes, locales, etc. This feature depends on the listen gem. 61 | config.file_watcher = ActiveSupport::EventedFileUpdateChecker 62 | end 63 | --------------------------------------------------------------------------------