├── .editorconfig ├── .github ├── ISSUE_TEMPLATE │ └── bug_report.yml ├── dependabot.yml └── workflows │ └── main.yml ├── .gitignore ├── .standard.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Gemfile ├── Gemfile.lock ├── LICENSE.txt ├── PULL_REQUEST_TEMPLATE.md ├── Procfile ├── Procfile.concurrency ├── Procfile.development ├── README.md ├── Rakefile ├── SECURITY.md ├── app.json ├── app ├── assets │ ├── config │ │ └── manifest.js │ ├── images │ │ ├── CodeTriage2012.png │ │ ├── favicon.ico │ │ ├── heroku.svg │ │ ├── logo-dark-text.png │ │ ├── logo.svg │ │ ├── private-avatar.svg │ │ ├── schneems.svg │ │ ├── sponsors │ │ │ └── scout.png │ │ ├── thoughtbot.svg │ │ └── warning.svg │ ├── javascripts │ │ ├── application.js.erb │ │ ├── badge.js │ │ ├── bootstrap.min.js │ │ ├── bootstrap_custom.js │ │ ├── coffee_script_utilities.js.coffee │ │ ├── global.js │ │ ├── homepage.js │ │ ├── preview_link_builder.js.coffee │ │ ├── replace_text.js │ │ ├── tab_navigation.js.coffee │ │ └── user_email_time_of_day.js │ ├── raw │ │ ├── codetriage-logo-white-text-screen.png │ │ ├── codetriage-logo-white-text.ai │ │ ├── codetriage-logo-white-text.eps │ │ ├── codetriage-logo-white-text.png │ │ ├── codetriage-logo.ai │ │ ├── codetriage-logo.eps │ │ ├── codetriage-logo.png │ │ └── codetriage-logo.svg │ └── stylesheets │ │ ├── application.scss │ │ ├── base │ │ ├── _base.scss │ │ ├── _buttons.scss │ │ ├── _forms.scss │ │ ├── _grid-settings.scss │ │ ├── _lists.scss │ │ ├── _tables.scss │ │ ├── _typography.scss │ │ └── _variables.scss │ │ ├── components │ │ ├── _accordion-tabs.scss │ │ ├── _all-languages-list.scss │ │ ├── _avatar.scss │ │ ├── _content-section.scss │ │ ├── _flashes.scss │ │ ├── _full-width-action.scss │ │ ├── _hero.scss │ │ ├── _nav-tabs.scss │ │ ├── _pagination.scss │ │ ├── _repo-badge.scss │ │ ├── _repo-list.scss │ │ ├── _repo.scss │ │ ├── _section-break.scss │ │ ├── _slats-list.scss │ │ ├── _staticish-pages.scss │ │ ├── _subpage-content.scss │ │ ├── _top-bar.scss │ │ ├── _types-filter.scss │ │ └── _user-email.scss │ │ ├── github │ │ ├── markdown-body.css │ │ └── readme.css │ │ ├── layout │ │ ├── _footer.scss │ │ ├── _header.scss │ │ └── _main-application.scss │ │ ├── markdown.scss │ │ ├── pagination.css │ │ └── repo_body.scss ├── controllers │ ├── api_info_controller.rb │ ├── application_controller.rb │ ├── badges_controller.rb │ ├── doc_methods_controller.rb │ ├── issue_assignments_controller.rb │ ├── pages_controller.rb │ ├── repo_based_controller.rb │ ├── repo_subscriptions_controller.rb │ ├── repos_controller.rb │ ├── subscribers_controller.rb │ ├── topics_controller.rb │ ├── university_controller.rb │ ├── users │ │ ├── after_signup_controller.rb │ │ └── omniauth_callbacks_controller.rb │ └── users_controller.rb ├── helpers │ ├── application_helper.rb │ ├── repos_helper.rb │ ├── subscribers_helper.rb │ └── users_helper.rb ├── jobs │ ├── application_job.rb │ ├── background_inactive_email_job.rb │ ├── populate_docs_job.rb │ ├── populate_issues_job.rb │ ├── repo_based_job.rb │ ├── send_daily_triage_email_job.rb │ ├── send_single_triage_email_job.rb │ ├── sentry_job.rb │ ├── subscribe_user_to_docs.rb │ ├── update_repo_info_job.rb │ └── user_based_job.rb ├── mailers │ ├── .gitkeep │ └── user_mailer.rb ├── models │ ├── .gitkeep │ ├── doc_assignment.rb │ ├── doc_class.rb │ ├── doc_comment.rb │ ├── doc_mailer_maker.rb │ ├── doc_method.rb │ ├── email_rate_limit.rb │ ├── git_branchname_generator.rb │ ├── github_fetcher.rb │ ├── github_fetcher │ │ ├── commit_sha.rb │ │ ├── email.rb │ │ ├── issue.rb │ │ ├── issue_comments.rb │ │ ├── issues.rb │ │ ├── repo.rb │ │ ├── repos.rb │ │ ├── resource.rb │ │ └── user.rb │ ├── github_url_from_base_path_line.rb │ ├── issue.rb │ ├── issue_assigner.rb │ ├── issue_assignment.rb │ ├── label.rb │ ├── mail_builder │ │ └── grouped_issues_docs.rb │ ├── random │ │ └── cached_id_query.rb │ ├── repo.rb │ ├── repo_label.rb │ ├── repo_subscription.rb │ └── user.rb ├── services │ ├── git_hub_authenticator.rb │ └── repo_label_assigner.rb └── views │ ├── application │ ├── _down.html.slim │ ├── _flashes.html.slim │ ├── _footer.html.slim │ ├── _head.html.slim │ └── _nav.html.slim │ ├── doc_methods │ └── show.html.slim │ ├── layouts │ ├── _app.html.slim │ ├── application.html.slim │ ├── mail_layout.html.erb │ ├── mail_layout.text.erb │ └── markdown_page.html.slim │ ├── pages │ ├── _repos_with_pagination.html.slim │ ├── index.html.slim │ ├── privacy.html.slim │ ├── support.html.slim │ └── what.html.erb │ ├── repo_subscriptions │ └── _list.html.slim │ ├── repos │ ├── _badge.html.slim │ ├── _docs.html.slim │ ├── _issues.html.slim │ ├── _repo.html.slim │ ├── edit.html.slim │ ├── list.html.slim │ ├── new.html.slim │ └── show.html.slim │ ├── shared │ └── _add_repos_list.html.slim │ ├── subscribers │ ├── _avatars.html.slim │ └── show.html.slim │ ├── topics │ └── show.html.slim │ ├── university │ ├── example_app.md.erb │ ├── index.md.erb │ ├── picking_a_repo.md.erb │ ├── rebase.md.erb │ ├── reproduction_code.md.erb │ └── squash.md.erb │ ├── user_mailer │ ├── daily_docs.md.erb │ ├── invalid_token.md.erb │ ├── poke_inactive.md.erb │ ├── send_daily_triage.md.erb │ ├── send_triage.md.erb │ └── spam.md.erb │ └── users │ ├── _form.html.slim │ ├── after_signup │ └── set_privacy.html.slim │ ├── edit.html.slim │ ├── show.html.slim │ └── token_delete.html.slim ├── bin ├── bundle ├── puma ├── pumactl ├── rails ├── rake ├── setup ├── update └── yarn ├── config.ru ├── config ├── application.rb ├── boot.rb ├── cable.yml ├── database.ci.yml ├── database.example.yml ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── initializers │ ├── application_controller_renderer.rb │ ├── assets.rb │ ├── backlog.rb │ ├── backtrace_silencers.rb │ ├── content_security_policy.rb │ ├── cookie_rotator.rb │ ├── cookies_serializer.rb │ ├── cors.rb │ ├── covid.rb │ ├── devise.rb │ ├── feature_policy.rb │ ├── filter_parameter_logging.rb │ ├── git_hub_bub.rb │ ├── inflections.rb │ ├── mail.rb │ ├── mime_types.rb │ ├── mini-profiler.rb │ ├── new_framework_defaults.rb │ ├── new_framework_defaults_5_2.rb │ ├── new_framework_defaults_6_0.rb │ ├── new_framework_defaults_6_1.rb │ ├── new_framework_defaults_7_0.rb │ ├── permissions_policy.rb │ ├── puma_worker_killer.rb │ ├── request_forgery_protection.rb │ ├── sentry.rb │ ├── session_store.rb │ ├── sidekiq.rb │ ├── silence_warnings.rb │ ├── the_lone_dyno.rb │ ├── time_formats.rb │ └── wrap_parameters.rb ├── locales │ ├── devise.en.yml │ └── en.yml ├── newrelic.yml ├── puma.rb ├── routes.rb ├── scout_apm.yml ├── secrets.yml ├── sitemap.rb └── storage.yml ├── db ├── migrate │ ├── 20120222223509_devise_create_users.rb │ ├── 20120222231841_user_data.rb │ ├── 20120518083745_create_issues.rb │ ├── 20120518085406_create_repo_subscriptions.rb │ ├── 20120518090213_create_issue_assignments.rb │ ├── 20120518091140_create_repos.rb │ ├── 20120620000230_add_github_to_users.rb │ ├── 20120622203236_add_repos_to_issues.rb │ ├── 20120622233057_issue_titles.rb │ ├── 20120622235822_add_user_and_repo_to_repo_subscription.rb │ ├── 20120624204958_add_last_sent_at_to_repo_sub.rb │ ├── 20120624212352_add_html_url_to_issues.rb │ ├── 20120624213335_add_status_to_issues.rb │ ├── 20120627213051_add_repo_sub_to_issue_assignment.rb │ ├── 20120707182259_add_admin_to_users.rb │ ├── 20121106072214_repo_issue_counter_cache.rb │ ├── 20121110213717_add_avatar_url_to_users.rb │ ├── 20121112100015_add_name_to_users.rb │ ├── 20121120203919_add_language_and_description_to_repo.rb │ ├── 20121127163516_add_privacy_to_users.rb │ ├── 20121127171308_add_full_name_to_repos.rb │ ├── 20121128162942_add_notes_to_repo.rb │ ├── 20130222201747_add_favorite_languages_to_user.rb │ ├── 20130312023533_add_email_limit_to_repo_subscriptions.rb │ ├── 20130503183402_add_daily_issue_limit_to_user.rb │ ├── 20130803144944_update_private_attribute_of_user.rb │ ├── 20130918055659_add_pr_attached_to_issues.rb │ ├── 20130918060600_add_skip_issues_with_pr_to_users.rb │ ├── 20131107042958_add_delete_token_to_users.rb │ ├── 20140524120051_add_click_to_issue_assignment.rb │ ├── 20140621155109_add_delievered_to_issue_assignment.rb │ ├── 20140710161559_store_github_request_errors.rb │ ├── 20140710164307_remove_user_id_from_issue_assignments.rb │ ├── 20150629002651_remove_repo_name_from_repo_subscriptions.rb │ ├── 20150629010617_remove_user_name_from_repo_subscriptions.rb │ ├── 20151007212330_create_data_dumps.rb │ ├── 20151021160718_add_index_to_issues.rb │ ├── 20151214120117_enable_pg_stat_extension.rb │ ├── 20160321152204_create_docs_doctor_integration.rb │ ├── 20160404140129_add_click_to_doc_assignments.rb │ ├── 20160404173429_remove_user_from_doc_assignments.rb │ ├── 20160606060546_add_email_frequency_to_users.rb │ ├── 20160613220116_change_issues_title_to_text.rb │ ├── 20160827023209_add_stars_count_to_repos.rb │ ├── 20160827073041_update_stars_count_of_repos.rb │ ├── 20160927195642_add_index_to_repo_subscription_id.rb │ ├── 20170203143323_add_email_time_of_day_to_users.rb │ ├── 20170516193426_repos_name_index.rb │ ├── 20170523195422_add_language_index.rb │ ├── 20170604190958_add_old_token_to_users.rb │ ├── 20170619152237_add_index_to_issues_count.rb │ ├── 20170622201545_add_created_at_index_to_issues.rb │ ├── 20170626162701_make_multi_index.rb │ ├── 20170627024417_add_index_to_doc_method.rb │ ├── 20170629151616_add_unique_index_to_repos.rb │ ├── 20170629230855_index_full_name_of_repos.rb │ ├── 20170629233102_add_foreign_key_to_repo_subscriptions.rb │ ├── 20170630002100_add_foreign_key_for_repo_users.rb │ ├── 20170630191148_add_index_to_issue_on_repo_id_and_number.rb │ ├── 20170630192013_add_index_to_doc_method_creation.rb │ ├── 20170801021151_default_value_for_max_issues.rb │ ├── 20171125012313_add_partial_index_to_id.rb │ ├── 20171125013100_remove_partial_index_to_id.rb │ ├── 20171125013319_add_index_for_pagination.rb │ ├── 20171125202301_fix_doc_methods_indexes.rb │ ├── 20171125212307_add_subscribers_count_to_repos.rb │ ├── 20180716144937_add_streak_to_users.rb │ ├── 20180820155654_add_last_email_at_to_user.rb │ ├── 20190322102229_add_foreign_key_constraint_to_active_storage_attachments_for_blob_id.active_storage.rb │ ├── 20190809190610_add_missing_indexes.rb │ ├── 20191010023214_add_docs_subscriber_count_to_repos.rb │ ├── 20191014201819_drop_unused_indexes.rb │ ├── 20191017202913_add_removed_from_github_to_repo.rb │ ├── 20191025030201_add_unique_index_number_repo_id.rb │ ├── 20191025191255_add_updated_at_index_to_issues.rb │ ├── 20191029194525_make_timestamps_not_nullable.rb │ ├── 20191031142027_add_comment_to_doc_method.rb │ ├── 20191031144636_add_unique_indexes_to_doc_methods.rb │ ├── 20201115123024_add_service_name_to_active_storage_blobs.active_storage.rb │ ├── 20201115123025_create_active_storage_variant_records.active_storage.rb │ ├── 20220908011235_add_htos_promo_to_user.rb │ ├── 20221004010742_create_labels_table.rb │ ├── 20221004010749_create_repo_labels_table.rb │ ├── 20221010011048_remove_not_null_on_active_storage_blobs_checksum.active_storage.rb │ ├── 20221013212959_add_archived_column_to_repo.rb │ └── 20221228065556_add_index_subscriber_count.rb ├── schema.rb ├── seeds.rb └── structure.sql ├── doc └── README_FOR_APP ├── heroku.yml ├── lib ├── assets │ └── .gitkeep ├── dependency_parser │ ├── php │ │ └── parse.rb │ └── ruby │ │ └── parse.rb ├── docs_doctor │ ├── doc_obj.rb │ ├── loader.rb │ ├── parser.rb │ ├── parsers │ │ └── ruby │ │ │ ├── require 'atomic' │ │ │ └── yard.rb │ ├── runner.rb │ └── task.rb ├── sorted_repo_collection.rb └── tasks │ ├── .gitkeep │ ├── performance.rake │ ├── schedule.rake │ └── test.rake ├── log └── .gitkeep ├── package.json ├── perf.rake ├── perf_scripts ├── cache_version.rb ├── dirty_tracking.rb ├── partial_cache.rb ├── partial_cache │ └── test │ │ ├── _customer.html.erb │ │ ├── _render_collection.html.erb │ │ └── _render_collection_with_cache.html.erb └── respond_to.rb ├── public ├── 404.html ├── 422.html ├── 500.html └── robots.txt ├── scratch.rb ├── test ├── dependency_parsers │ ├── php_parser_test.rb │ └── ruby_parser_test.rb ├── emails │ └── send_daily_triage_email_test.rb ├── fixtures │ ├── .gitkeep │ ├── doc_methods.yml │ ├── files │ │ └── composer.lock │ ├── issue_assignments.yml │ ├── issues.yml │ ├── repo_subscriptions.yml │ ├── repos.yml │ └── users.yml ├── functional │ ├── .gitkeep │ ├── after_register_controller_test.rb │ ├── doc_methods_controller_test.rb │ ├── issue_assignments_controller_test.rb │ ├── omniauth_callbacks_controller_test.rb │ ├── pages_controller_test.rb │ ├── repo_subscriptions_controller_test.rb │ ├── repos_controller_test.rb │ ├── subscribers_controller_test.rb │ ├── topics_controller_test.rb │ ├── user_mailer_test.rb │ └── users_controller_test.rb ├── helpers │ ├── .gitkeep │ └── application_helper_test.rb ├── integration │ ├── .gitkeep │ ├── adding_repos_test.rb │ ├── filtering_language_test.rb │ ├── maintaining_repo_subscriptions_test.rb │ ├── repos_test.rb │ ├── user_session_test.rb │ ├── user_unsubscribes_test.rb │ └── user_update_test.rb ├── jobs │ ├── background_inactive_email_job_test.rb │ ├── parse_docs_test.rb │ ├── populate_issues_job_test.rb │ ├── send_daily_triage_email_job_test.rb │ ├── send_single_triage_email_job_test.rb │ └── update_repo_info_job_test.rb ├── mailers │ └── previews │ │ └── user_preview.rb ├── test_helper.rb ├── unit │ ├── .gitkeep │ ├── email_rate_limit_test.rb │ ├── git_branchname_generator_test.rb │ ├── git_hub_authenticator_test.rb │ ├── github_fetcher │ │ ├── commit_sha_test.rb │ │ ├── email_test.rb │ │ ├── issue_comments_test.rb │ │ ├── issue_test.rb │ │ ├── issues_test.rb │ │ ├── repo_test.rb │ │ ├── repos_test.rb │ │ ├── resource_test.rb │ │ └── user_test.rb │ ├── helpers │ │ └── after_register_helper_test.rb │ ├── issue_assignment_test.rb │ ├── issue_test.rb │ ├── mail_builder │ │ └── grouped_issues_docs_test.rb │ ├── repo_labels_assigner_test.rb │ ├── repo_subscriptions_test.rb │ ├── repo_test.rb │ ├── sorted_repo_collection_test.rb │ └── user_test.rb └── vcr_cassettes │ ├── bad_fetch_emails.yml │ ├── blank_repo.yml │ ├── commenting_users_issue.yml │ ├── create_duplicate_repo_refinery.yml │ ├── create_repo_refinery.yml │ ├── create_repo_without_issues.yml │ ├── dependency_parser │ └── rubygems.yml │ ├── fetch_emails.yml │ ├── fetch_github_user.yml │ ├── fetch_github_user_invalid_check.yml │ ├── fetch_github_user_valid_check.yml │ ├── fetch_issue.yml │ ├── fetch_issue_commenters.yml │ ├── fetch_issue_comments.yml │ ├── fetch_labels_for_repo.yml │ ├── fetcher_commit_sha.yml │ ├── fetcher_more_starred_repos_for_user.yml │ ├── fetcher_owned_repos_for_user.yml │ ├── fetcher_owned_repos_for_user_first_100.yml │ ├── fetcher_starred_repos_for_user.yml │ ├── fetcher_subscribed_repos_for_user.yml │ ├── issue_triage_sandbox_fake_api_responses.yml │ ├── issue_triage_sandbox_fetch_issues.yml │ ├── my_repos.yml │ ├── rails_rails_fetch_issues.yml │ ├── rails_rails_fetch_issues_page_two.yml │ ├── repo_info.yml │ ├── repo_info_error.yml │ ├── repo_populate_docs.yml │ └── update_repo_info_schneems │ └── sextant.yml ├── vendor ├── assets │ ├── javascripts │ │ ├── .gitkeep │ │ └── raven.js │ └── stylesheets │ │ └── .gitkeep └── plugins │ └── .gitkeep └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent coding styles between different editors and IDEs 2 | # @see http://editorconfig.org 3 | root = true 4 | 5 | [*] 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | indent_style = spaces 11 | tab_width = 2 12 | 13 | [{*.{rb,erb,js,coffee,json,yml,css,scss,sh,markdown,md,html}] 14 | indent_size = 2 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve. 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Describe the bug 11 | 12 | 13 | ## To reproduce 14 | Steps to reproduce the behavior: 15 | 16 | 1. Go to '...' 17 | 2. Click on '....' 18 | 3. Scroll down to '....' 19 | 4. See error 20 | 21 | ## Expected behavior 22 | 23 | 24 | ## Actual behavior 25 | 26 | 27 | ## Screenshots 28 | 29 | 30 | ## Additional context 31 | 32 | 33 | ## Your Environment 34 | 35 | * Version used: 36 | * Browser Name and version: 37 | * Operating System and version (desktop or mobile): 38 | * Link to your project: 39 | 40 | ## Possible Solution 41 | 42 | 43 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: bundler 4 | directory: "/" 5 | schedule: 6 | interval: monthly 7 | open-pull-requests-limit: 10 8 | ignore: 9 | - dependency-name: neat 10 | versions: 11 | - "< 4, >= 3.a" 12 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Main 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | base: 6 | runs-on: ubuntu-latest 7 | strategy: 8 | fail-fast: false 9 | matrix: 10 | ruby: ["3.1"] 11 | name: Running tests with Ruby ${{ matrix.ruby }} 12 | services: 13 | postgres: 14 | image: postgres:12 15 | env: 16 | POSTGRES_DB: triage_test 17 | POSTGRES_PASSWORD: password 18 | POSTGRES_USER: postgres 19 | ports: ["5432:5432"] 20 | env: 21 | BUNDLE_GEMFILE: Gemfile 22 | 23 | steps: 24 | - name: Checkout code 25 | uses: actions/checkout@v4 26 | 27 | - name: Setup Ruby 28 | uses: ruby/setup-ruby@v1 29 | with: 30 | ruby-version: ${{ matrix.ruby }} 31 | bundler-cache: true 32 | - name: Linting 33 | run: | 34 | bundle exec standardrb 35 | - name: Setup database 36 | run: | 37 | cp config/database.ci.yml config/database.yml 38 | RAILS_ENV=test bundle exec rake db:create db:schema:load 39 | - name: Run tests 40 | run: bin/rake test 41 | - name: Ensure seed task still works 42 | run: RAILS_ENV=test bin/rails db:seed 43 | 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-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 the default SQLite database. 11 | /db/*.sqlite3 12 | 13 | # Ignore all logfiles and tempfiles. 14 | /log/*.log 15 | /tmp 16 | 17 | .DS_Store 18 | .rvmrc 19 | .ruby-version 20 | .ruby-gemset 21 | 22 | 23 | .env 24 | dump.rdb 25 | 26 | # Code test coverage reports 27 | /coverage 28 | 29 | # ignore personal settings 30 | /config/database.yml 31 | 32 | public/assets 33 | 34 | # ignore vendor/bundle 35 | vendor/bundle 36 | tags 37 | 38 | config/secrets.yml.key -------------------------------------------------------------------------------- /.standard.yml: -------------------------------------------------------------------------------- 1 | ruby_version: 3.1 2 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2012 Schneems 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: jemalloc.sh bin/puma -C config/puma.rb 2 | worker: jemalloc.sh bundle exec sidekiq -q default -q mailers -c ${SIDEKIQ_CONCURRENCY:-5} 3 | release: RUBYOPT=--jit bundle exec rake db:migrate 4 | -------------------------------------------------------------------------------- /Procfile.concurrency: -------------------------------------------------------------------------------- 1 | console: rails c 2 | 3 | -------------------------------------------------------------------------------- /Procfile.development: -------------------------------------------------------------------------------- 1 | web: bin/puma -C config/puma.rb 2 | worker: bundle exec sidekiq -q default -q mailers -c ${SIDEKIQ_CONCURRENCY:-5} 3 | release: bundle exec rake db:migrate 4 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Use this section to tell people about which versions of your project are 6 | currently being supported with security updates. 7 | 8 | | Version | Supported | 9 | | ------- | ------------------ | 10 | | main branch | :white_check_mark: | 11 | 12 | 13 | ## Reporting a Vulnerability 14 | 15 | Contact using the email address on https://github.com/schneems/ profile. 16 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Code Triage ", 3 | "description": "Helping you contribute to open source since 2013", 4 | "website": "https://www.codetriage.com/", 5 | "repository": "https://github.com/codetriage/codetriage", 6 | "scripts": { 7 | "postdeploy": "rake db:schema:load db:migrate db:seed", 8 | "test": "bundle exec standardrb && bin/rails test && rails db:seed" 9 | }, 10 | "env": { 11 | "DEVISE_SECRET_KEY": "foo", 12 | "DEVISE_SECRET_TOKEN": "bar", 13 | "WEB_CONCURRENCY": "1" 14 | }, 15 | "addons": [ 16 | "heroku-postgresql:in-dyno", 17 | "heroku-redis:in-dyno", 18 | "memcachier" 19 | ], 20 | "buildpacks": [ 21 | { 22 | "url": "https://github.com/mojodna/heroku-buildpack-jemalloc.git" 23 | }, 24 | { 25 | "url": "heroku/nodejs" 26 | }, 27 | { 28 | "url": "heroku/ruby" 29 | } 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link application.js 2 | //= link application.css 3 | //= link markdown.css 4 | //= link_tree ../images 5 | -------------------------------------------------------------------------------- /app/assets/images/CodeTriage2012.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codetriage/CodeTriage/2f193418ca0b8757ca0a1159a2e7827ff5a02926/app/assets/images/CodeTriage2012.png -------------------------------------------------------------------------------- /app/assets/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codetriage/CodeTriage/2f193418ca0b8757ca0a1159a2e7827ff5a02926/app/assets/images/favicon.ico -------------------------------------------------------------------------------- /app/assets/images/heroku.svg: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /app/assets/images/logo-dark-text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codetriage/CodeTriage/2f193418ca0b8757ca0a1159a2e7827ff5a02926/app/assets/images/logo-dark-text.png -------------------------------------------------------------------------------- /app/assets/images/private-avatar.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/assets/images/sponsors/scout.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codetriage/CodeTriage/2f193418ca0b8757ca0a1159a2e7827ff5a02926/app/assets/images/sponsors/scout.png -------------------------------------------------------------------------------- /app/assets/images/thoughtbot.svg: -------------------------------------------------------------------------------- 1 | 2 | 5 | -------------------------------------------------------------------------------- /app/assets/images/warning.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/assets/javascripts/application.js.erb: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into application.js, which will include all the files 2 | // listed below. 3 | // 4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, 5 | // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path. 6 | // 7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 8 | // the compiled file. 9 | // 10 | // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD 11 | // GO AFTER THE REQUIRES BELOW. 12 | // 13 | //= require jquery 14 | //= require jquery_ujs 15 | //= require local-time 16 | //= require bootstrap.min 17 | //= require bootstrap_custom 18 | //= require coffee_script_utilities 19 | //= require preview_link_builder 20 | //= require tab_navigation 21 | //= require global 22 | //= require badge.js 23 | //= require homepage.js 24 | //= require user_email_time_of_day.js 25 | //= require raven.js 26 | 27 | Raven.config("<%= ENV['SENTRY_DSN_PUBLIC'] %>", { 28 | release: "<%= Raven.configuration.release %>", 29 | environment: "<%= Rails.env %>", 30 | ignoreErrors: [ 31 | // Error generated by a bug in auto-fill library from browser 32 | // https://github.com/getsentry/sentry/issues/5267 33 | /Blocked a frame with origin/, 34 | ] 35 | }).install(); 36 | -------------------------------------------------------------------------------- /app/assets/javascripts/badge.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | $('.accordion-tabs').on('click', 'li > a.tab-link', function(event) { 3 | if (!$(this).hasClass('is-active')) { 4 | event.preventDefault(); 5 | var accordionTabs = $(this).closest('.accordion-tabs'); 6 | accordionTabs.find('.is-open').removeClass('is-open').hide(); 7 | 8 | $(this).next().toggleClass('is-open').show(); 9 | accordionTabs.find('.is-active').removeClass('is-active'); 10 | $(this).addClass('is-active'); 11 | } else { 12 | event.preventDefault(); 13 | } 14 | }); 15 | }) 16 | -------------------------------------------------------------------------------- /app/assets/javascripts/bootstrap_custom.js: -------------------------------------------------------------------------------- 1 | $('.nav-tabs li a').click(function(e){ 2 | e.preventDefault(); 3 | e.stopImmediatePropagation(); 4 | $(this).tab('show'); 5 | }); 6 | -------------------------------------------------------------------------------- /app/assets/javascripts/coffee_script_utilities.js.coffee: -------------------------------------------------------------------------------- 1 | # Creates a namespaced class or method 2 | # 3 | # Example usages: 4 | # namespace 'Foo.Bar', (exports) -> 5 | # class export.Baz 6 | # ... 7 | # namespace 'Foo.Baz', (exports) -> 8 | # exports.alertFoo = -> 9 | # alert 'foo' 10 | 11 | window.namespace = (target, name, block) -> 12 | [target, name, block] = [(if typeof exports isnt 'undefined' then exports else window), arguments...] if arguments.length < 3 13 | top = target 14 | target = target[item] or= {} for item in name.split '.' 15 | block target, top 16 | -------------------------------------------------------------------------------- /app/assets/javascripts/global.js: -------------------------------------------------------------------------------- 1 | jQuery(document).ready(function(){ 2 | new Codetriage.TabNavigation(); 3 | new Codetriage.previewLinkBuilder(); 4 | }); 5 | -------------------------------------------------------------------------------- /app/assets/javascripts/preview_link_builder.js.coffee: -------------------------------------------------------------------------------- 1 | namespace 'Codetriage', (exports) -> 2 | class exports.previewLinkBuilder 3 | constructor: (customOptions = {}) -> 4 | defaultOptions = { 5 | "userNameInput" : ".preview_user_name", 6 | "userRepoNameInput" : ".preview_repo_name", 7 | "previewLink" : "#repo_preview" 8 | } 9 | 10 | @options = $.extend({}, defaultOptions, customOptions) 11 | @repoPreviewAnchorTag = $("#{@options.previewLink} > a") 12 | @updatePreviewLink() 13 | @updatePreviewLinkOnKeyup() 14 | 15 | createGithubUrl: -> 16 | userName = $(@options.userNameInput).val()?.trim() 17 | repoName = $(@options.userRepoNameInput).val()?.trim() 18 | "https://github.com/#{userName}/#{repoName}" 19 | 20 | checkPreviewUrl: -> 21 | if @repoPreviewAnchorTag.attr('href') == "https://github.com//" 22 | $(@options.previewLink).fadeOut(300) 23 | else 24 | $(@options.previewLink).fadeIn(300) 25 | 26 | updatePreviewLink: -> 27 | @repoPreviewAnchorTag.html(@createGithubUrl()).attr('href',@createGithubUrl()) 28 | @checkPreviewUrl() 29 | 30 | updatePreviewLinkOnKeyup: -> 31 | $(document).delegate @options.userNameInput, "keyup", => 32 | @updatePreviewLink() 33 | 34 | $(document).delegate @options.userRepoNameInput, "keyup", => 35 | @updatePreviewLink() -------------------------------------------------------------------------------- /app/assets/javascripts/replace_text.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery replaceText - v1.1 - 11/21/2009 3 | * http://benalman.com/projects/jquery-replacetext-plugin/ 4 | * 5 | * Copyright (c) 2009 "Cowboy" Ben Alman 6 | * Dual licensed under the MIT and GPL licenses. 7 | * http://benalman.com/about/license/ 8 | */ 9 | (function($){$.fn.replaceText=function(b,a,c){return this.each(function(){var f=this.firstChild,g,e,d=[];if(f){do{if(f.nodeType===3){g=f.nodeValue;e=g.replace(b,a);if(e!==g){if(!c&&/ 2 | class exports.TabNavigation 3 | constructor: (options = {}) -> 4 | @tabItem = $("a[data-toggle]") 5 | @locationHash = location.hash 6 | @defaultLocation() unless @locationHash 7 | @watchHistory() 8 | @bindDataToggleTabEvent() 9 | @showTabLocation() if @locationHash 10 | 11 | defaultLocation: -> 12 | $("#repo-tabs a:first").tab 'show' 13 | 14 | watchHistory: -> 15 | $(window).on "popstate", => 16 | @locationHash = location.hash or @tabItem.first().attr("href") 17 | @showTabLocation() 18 | 19 | bindDataToggleTabEvent: -> 20 | @tabItem.on "click", (event) => 21 | event.preventDefault() 22 | @locationHash = $(event.target).attr('href') 23 | location.hash = @locationHash 24 | @showTabLocation() 25 | 26 | showTabLocation: -> 27 | $("a[href='#{@locationHash}']").tab "show" -------------------------------------------------------------------------------- /app/assets/javascripts/user_email_time_of_day.js: -------------------------------------------------------------------------------- 1 | 2 | // Display options in select box in local time 3 | $(document).ready(function(){ 4 | 5 | $("#user_email_time_of_day option") 6 | .toArray() 7 | .forEach(function(element) { 8 | var time = new Date(element.value); 9 | element.text = LocalTime.strftime(time, "%H:%M %Z"); 10 | }) 11 | 12 | $("#user_email_time_of_day").html($("#user_email_time_of_day option").sort(function (a, b) { 13 | return a.text == b.text ? 0 : a.text < b.text ? -1 : 1 14 | })) 15 | 16 | }); 17 | -------------------------------------------------------------------------------- /app/assets/raw/codetriage-logo-white-text-screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codetriage/CodeTriage/2f193418ca0b8757ca0a1159a2e7827ff5a02926/app/assets/raw/codetriage-logo-white-text-screen.png -------------------------------------------------------------------------------- /app/assets/raw/codetriage-logo-white-text.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codetriage/CodeTriage/2f193418ca0b8757ca0a1159a2e7827ff5a02926/app/assets/raw/codetriage-logo-white-text.ai -------------------------------------------------------------------------------- /app/assets/raw/codetriage-logo-white-text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codetriage/CodeTriage/2f193418ca0b8757ca0a1159a2e7827ff5a02926/app/assets/raw/codetriage-logo-white-text.png -------------------------------------------------------------------------------- /app/assets/raw/codetriage-logo.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codetriage/CodeTriage/2f193418ca0b8757ca0a1159a2e7827ff5a02926/app/assets/raw/codetriage-logo.ai -------------------------------------------------------------------------------- /app/assets/raw/codetriage-logo.eps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codetriage/CodeTriage/2f193418ca0b8757ca0a1159a2e7827ff5a02926/app/assets/raw/codetriage-logo.eps -------------------------------------------------------------------------------- /app/assets/raw/codetriage-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codetriage/CodeTriage/2f193418ca0b8757ca0a1159a2e7827ff5a02926/app/assets/raw/codetriage-logo.png -------------------------------------------------------------------------------- /app/assets/stylesheets/application.scss: -------------------------------------------------------------------------------- 1 | @import "normalize-rails"; 2 | @import "bourbon"; 3 | @import "neat"; 4 | @import "base/base"; 5 | 6 | @import "layout/*"; 7 | @import "components/*"; 8 | @import "markdown"; 9 | @import "repo_body"; 10 | -------------------------------------------------------------------------------- /app/assets/stylesheets/base/_base.scss: -------------------------------------------------------------------------------- 1 | // Bitters 1.1.0 2 | // http://bitters.bourbon.io 3 | // Copyright 2013-2015 thoughtbot, inc. 4 | // MIT License 5 | 6 | @import "variables"; 7 | 8 | // Neat Settings -- uncomment if using Neat -- must be imported before Neat 9 | // @import "grid-settings"; 10 | 11 | @import "buttons"; 12 | @import "forms"; 13 | @import "lists"; 14 | @import "tables"; 15 | @import "typography"; 16 | -------------------------------------------------------------------------------- /app/assets/stylesheets/base/_buttons.scss: -------------------------------------------------------------------------------- 1 | input[type="submit"].btn-danger { 2 | background-color: $codetriage-red; 3 | 4 | &:hover { 5 | background-color: shade($codetriage-red, 20%); 6 | } 7 | } 8 | 9 | .button, 10 | #{$all-buttons} { 11 | appearance: none; 12 | background-color: $action-color; 13 | border-radius: $base-border-radius; 14 | box-shadow: 0 1px 2px darken($base-background-color, 5%); 15 | border: 0; 16 | color: #fff; 17 | cursor: pointer; 18 | display: inline-block; 19 | font-family: $base-font-family; 20 | font-size: 0.875rem; 21 | font-weight: 600; 22 | letter-spacing: 1px; 23 | line-height: 1; 24 | padding: ($small-spacing * 1.5) $base-spacing; 25 | text-decoration: none; 26 | text-transform: uppercase; 27 | transition: background-color $base-duration $base-timing; 28 | user-select: none; 29 | vertical-align: middle; 30 | white-space: nowrap; 31 | 32 | &:hover, 33 | &:focus { 34 | background-color: shade($action-color, 20%); 35 | color: #fff; 36 | } 37 | 38 | &:disabled { 39 | cursor: not-allowed; 40 | opacity: 0.5; 41 | 42 | &:hover { 43 | background-color: $action-color; 44 | } 45 | } 46 | 47 | &.inactive { 48 | cursor: not-allowed; 49 | opacity: 0.5; 50 | } 51 | } 52 | 53 | -------------------------------------------------------------------------------- /app/assets/stylesheets/base/_grid-settings.scss: -------------------------------------------------------------------------------- 1 | @import "neat-helpers"; // or "../neat/neat-helpers" when not in Rails 2 | 3 | // Neat Overrides 4 | // $column: 90px; 5 | // $gutter: 30px; 6 | // $grid-columns: 12; 7 | // $max-width: 1200px; 8 | 9 | // Neat Breakpoints 10 | $medium-screen: 600px; 11 | $large-screen: 900px; 12 | 13 | $medium-screen-up: new-breakpoint(min-width $medium-screen 4); 14 | $large-screen-up: new-breakpoint(min-width $large-screen 8); 15 | -------------------------------------------------------------------------------- /app/assets/stylesheets/base/_lists.scss: -------------------------------------------------------------------------------- 1 | ul { 2 | list-style-type: none; 3 | } 4 | 5 | ul, 6 | ol { 7 | margin: 0; 8 | padding: 0; 9 | 10 | &%default-ul { 11 | list-style-type: disc; 12 | margin-bottom: $small-spacing; 13 | padding-left: $base-spacing; 14 | } 15 | 16 | &%default-ol { 17 | list-style-type: decimal; 18 | margin-bottom: $small-spacing; 19 | padding-left: $base-spacing; 20 | } 21 | } 22 | 23 | dl { 24 | margin-bottom: $small-spacing; 25 | 26 | dt { 27 | font-weight: bold; 28 | margin-top: $small-spacing; 29 | } 30 | 31 | dd { 32 | margin: 0; 33 | } 34 | } 35 | 36 | 37 | .bullets { 38 | list-style-type: disc; 39 | padding: 1em 0 0 2em; 40 | } 41 | 42 | ol.normal-ol { 43 | padding: 0 0 inherit; 44 | } 45 | -------------------------------------------------------------------------------- /app/assets/stylesheets/base/_tables.scss: -------------------------------------------------------------------------------- 1 | table { 2 | border-collapse: collapse; 3 | font-feature-settings: "kern", "liga", "tnum"; 4 | margin: $small-spacing 0; 5 | table-layout: fixed; 6 | // width: 100%; // Breaks rack-mini-profiler 7 | } 8 | 9 | th { 10 | border-bottom: 1px solid shade($base-border-color, 25%); 11 | font-weight: 600; 12 | padding: $small-spacing 0; 13 | text-align: left; 14 | } 15 | 16 | td { 17 | border-bottom: $base-border; 18 | padding: $small-spacing 0; 19 | } 20 | 21 | tr, 22 | td, 23 | th { 24 | vertical-align: middle; 25 | } 26 | -------------------------------------------------------------------------------- /app/assets/stylesheets/components/_accordion-tabs.scss: -------------------------------------------------------------------------------- 1 | .accordion-tabs { 2 | $large-screen: 54em !default; 3 | 4 | @include clearfix; 5 | padding: 0; 6 | 7 | .tab-header-and-content { 8 | list-style: none; 9 | 10 | @include media($large-screen) { 11 | display: inline; 12 | } 13 | } 14 | 15 | .tab-link { 16 | display: block; 17 | padding-right: 1em; 18 | 19 | @include media($large-screen) { 20 | display: inline-block; 21 | } 22 | } 23 | 24 | .is-active { 25 | color: $codetriage-red; 26 | } 27 | 28 | .tab-content { 29 | display: none; 30 | width: 100%; 31 | margin-top: $small-spacing; 32 | float: left; 33 | 34 | &.is-open { 35 | display: block; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/assets/stylesheets/components/_all-languages-list.scss: -------------------------------------------------------------------------------- 1 | .all-languages-list { 2 | @include clearfix; 3 | } 4 | 5 | .language-item { 6 | position: relative; 7 | 8 | @include media(max-width 699px) { 9 | @include omega(2n); 10 | @include span-columns(6); 11 | } 12 | 13 | @include media(700px) { 14 | @include omega(4n); 15 | @include span-columns(3); 16 | } 17 | 18 | input[type='checkbox'] { 19 | width: 1rem; 20 | cursor: pointer; 21 | 22 | + label { 23 | cursor: pointer; 24 | margin-bottom: $small-spacing; 25 | 26 | &::before { 27 | @include position(absolute, 3px null null -2px); 28 | @include size(1.1rem, 1rem); 29 | background-color: $codetriage-blue; 30 | border-radius: $base-border-radius; 31 | color: $base-background-color; 32 | content: ""; 33 | display: inline-block; 34 | line-height: 1rem; 35 | pointer-events: none; 36 | text-align: center; 37 | } 38 | } 39 | 40 | &:checked + label { 41 | &::before { 42 | background-color: $white; 43 | content: "×"; 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/assets/stylesheets/components/_avatar.scss: -------------------------------------------------------------------------------- 1 | .avatars-list { 2 | @include row; 3 | 4 | li { 5 | margin-bottom: $small-spacing; 6 | 7 | @include media(max-width 24.9375rem) { 8 | @include omega(2n); 9 | @include span-columns(6); 10 | } 11 | 12 | @include media(min-width 25rem max-width 42.9375rem) { 13 | @include omega(3n); 14 | @include span-columns(4); 15 | } 16 | 17 | @include media(43rem) { 18 | @include omega(6n); 19 | @include span-columns(2); 20 | } 21 | } 22 | } 23 | 24 | .avatar-image { 25 | border-radius: $base-border-radius; 26 | width: 100%; 27 | } 28 | -------------------------------------------------------------------------------- /app/assets/stylesheets/components/_content-section.scss: -------------------------------------------------------------------------------- 1 | .content-section { 2 | @include clearfix; 3 | border-bottom: $content-border; 4 | padding: $base-spacing 0; 5 | 6 | &:last-of-type { 7 | border-bottom: 0; 8 | margin-bottom: 0; 9 | padding-bottom: 0; 10 | } 11 | } 12 | 13 | .content-section-title { 14 | font-size: 1.375rem; 15 | margin-bottom: 1rem; 16 | color: $base-font-color; 17 | } 18 | 19 | .selectable p { 20 | margin: 0; 21 | 22 | &::before { 23 | content: "$ " 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/assets/stylesheets/components/_flashes.scss: -------------------------------------------------------------------------------- 1 | @mixin flash($color) { 2 | background-color: $color; 3 | color: darken($color, 60%); 4 | display: block; 5 | padding: $base-spacing $small-spacing; 6 | text-align: center; 7 | 8 | p { 9 | margin-bottom: 0; 10 | } 11 | 12 | a { 13 | color: darken($color, 70%); 14 | text-decoration: underline; 15 | 16 | &:focus, 17 | &:hover { 18 | color: darken($color, 90%); 19 | } 20 | } 21 | } 22 | 23 | .flash-alert { 24 | @include flash($alert-color); 25 | } 26 | 27 | .flash-error { 28 | @include flash($error-color); 29 | } 30 | 31 | .flash-notice { 32 | @include flash($notice-color); 33 | } 34 | 35 | .flash-success { 36 | @include flash($success-color); 37 | } 38 | -------------------------------------------------------------------------------- /app/assets/stylesheets/components/_full-width-action.scss: -------------------------------------------------------------------------------- 1 | .full-width-action { 2 | clear: both; 3 | display: block; 4 | margin-top: $base-spacing; 5 | text-align: center; 6 | width: 100%; 7 | } 8 | -------------------------------------------------------------------------------- /app/assets/stylesheets/components/_hero.scss: -------------------------------------------------------------------------------- 1 | .hero { 2 | max-width: 55em; 3 | margin: 0 auto; 4 | padding-top: 4rem; 5 | text-align: center; 6 | 7 | .button { 8 | margin-top: 3rem; 9 | } 10 | 11 | .github-icon { 12 | @include position(relative, 2px null null null); 13 | @include size(1rem); 14 | fill: $base-font-color; 15 | margin-right: 1rem; 16 | } 17 | } 18 | 19 | .hero-title-primary { 20 | color: $base-font-color; 21 | font-size: 2.5rem; 22 | margin-bottom: $base-spacing; 23 | } 24 | 25 | .hero-title-secondary { 26 | font-size: 1.25rem; 27 | line-height: 1.5; 28 | font-weight: 100; 29 | margin: 0 auto; 30 | max-width: 50rem; 31 | } 32 | 33 | .hero-repo-list-container { 34 | text-align: center; 35 | 36 | .repo-list { 37 | justify-content: center; 38 | text-align: left; 39 | } 40 | 41 | .repo-item { 42 | &:last-of-type { 43 | margin-right: 0; 44 | } 45 | } 46 | 47 | .button { 48 | &:not(:last-of-type) { 49 | margin-right: $base-spacing; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /app/assets/stylesheets/components/_nav-tabs.scss: -------------------------------------------------------------------------------- 1 | .nav-tabs { 2 | @include row; 3 | border-left: $content-border; 4 | border-right: $content-border; 5 | border-top: $content-border; 6 | } 7 | 8 | .nav-tabs-item { 9 | padding: $small-spacing; 10 | text-align: center; 11 | 12 | @include media(31rem) { 13 | float: left; 14 | width: 33.3333%; 15 | } 16 | 17 | a { 18 | display: block; 19 | } 20 | 21 | &:not(:last-child) { 22 | border-right: $content-border; 23 | } 24 | 25 | &.active { 26 | background-color: $action-color; 27 | 28 | a { 29 | color: $base-background-color; 30 | } 31 | } 32 | } 33 | 34 | .tab-pane { 35 | display: none; 36 | 37 | &.active { 38 | display: block; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/assets/stylesheets/components/_pagination.scss: -------------------------------------------------------------------------------- 1 | .pagination { 2 | border-color: $base-background-color; 3 | border-style: solid; 4 | border-width: 2px; 5 | display: block; 6 | text-align: center; 7 | 8 | .slats-list & { 9 | border-width: 0 2px 2px 2px; 10 | } 11 | 12 | span, 13 | em, 14 | a { 15 | display: inline-block; 16 | padding: $small-spacing; 17 | 18 | &:not(:last-child) { 19 | border-right: $content-border; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/assets/stylesheets/components/_repo-badge.scss: -------------------------------------------------------------------------------- 1 | .repo-badge { 2 | float: right; 3 | } 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/components/_repo.scss: -------------------------------------------------------------------------------- 1 | .repo-language { 2 | color: $light-font-color; 3 | font-size: $small-font-size; 4 | font-weight: normal; 5 | letter-spacing: 1px; 6 | margin-top: 4px; 7 | text-transform: uppercase; 8 | 9 | @include media(38rem) { 10 | float: right; 11 | } 12 | } 13 | 14 | .repo-badge { 15 | @include media(38rem) { 16 | float: right; 17 | } 18 | } 19 | 20 | .repo-description { 21 | clear: both; 22 | color: $light-font-color; 23 | margin-bottom: 0; 24 | margin-top: $small-spacing / 2; 25 | } 26 | 27 | .repo-instructions { 28 | @include media(43em) { 29 | @include span-columns(6); 30 | } 31 | } 32 | 33 | .repo-action { 34 | text-align: center; 35 | 36 | @include media(43em) { 37 | @include span-columns(6); 38 | margin-right: 0; 39 | } 40 | } 41 | 42 | .stop-triaging { 43 | background-color: $codetriage-red; 44 | 45 | &:hover { 46 | background-color: darken($codetriage-red, 5%); 47 | } 48 | } 49 | 50 | .email-amount { 51 | label { 52 | font-weight: normal; 53 | } 54 | 55 | select { 56 | display: inline-block; 57 | } 58 | 59 | .save-amount { 60 | background: none; 61 | color: $action-color; 62 | display: inline-block; 63 | margin-left: $small-spacing; 64 | padding: 0; 65 | 66 | &:hover { 67 | background: none; 68 | color: $base-font-color; 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /app/assets/stylesheets/components/_section-break.scss: -------------------------------------------------------------------------------- 1 | .section-break { 2 | border-bottom: 5px solid lighten($base-background-color, 5%); 3 | margin: 6rem auto; 4 | max-width: 25rem; 5 | } 6 | -------------------------------------------------------------------------------- /app/assets/stylesheets/components/_staticish-pages.scss: -------------------------------------------------------------------------------- 1 | .static-page { 2 | font-size: $larger-font-size; 3 | 4 | color: $off-white-font-color; 5 | h2 { 6 | color: $base-font-color; 7 | font-size: 2rem; 8 | 9 | padding: 0.5em 0; 10 | margin: 1em 0; 11 | } 12 | .bullets { 13 | padding: 1em 1em 1em 2em; 14 | } 15 | 16 | @import "github/markdown-body"; 17 | } 18 | -------------------------------------------------------------------------------- /app/assets/stylesheets/components/_top-bar.scss: -------------------------------------------------------------------------------- 1 | %top-bar { 2 | float: left; 3 | width: 25%; 4 | height: 10px; 5 | display: block; 6 | } 7 | 8 | .top-bar-red { 9 | @extend %top-bar; 10 | background-color: $codetriage-red; 11 | } 12 | 13 | .top-bar-yellow { 14 | @extend %top-bar; 15 | background-color: $codetriage-yellow; 16 | } 17 | 18 | .top-bar-blue { 19 | @extend %top-bar; 20 | background-color: $codetriage-blue; 21 | } 22 | 23 | .top-bar-green { 24 | @extend %top-bar; 25 | background-color: $codetriage-green; 26 | } 27 | 28 | -------------------------------------------------------------------------------- /app/assets/stylesheets/components/_user-email.scss: -------------------------------------------------------------------------------- 1 | .user-email { 2 | margin-top: 7px; 3 | font-weight: normal; 4 | } 5 | -------------------------------------------------------------------------------- /app/assets/stylesheets/github/readme.css: -------------------------------------------------------------------------------- 1 | .readme.contributing>div{ 2 | max-height:250px; 3 | overflow:auto 4 | } 5 | .readme .markdown-body,.readme .plain{ 6 | padding:45px; 7 | word-wrap:break-word; 8 | background-color:#fff; 9 | border:1px solid #ddd; 10 | border-bottom-right-radius:3px; 11 | border-bottom-left-radius:3px 12 | } 13 | .readme .plain pre{ 14 | font-size:15px; 15 | white-space:pre-wrap 16 | } 17 | .file .readme .markdown-body{ 18 | padding:45px; 19 | border:0; 20 | border-radius:0 21 | } 22 | .file .readme table[data-table-type="yaml-metadata"]{ 23 | font-size:12px; 24 | line-height:1 25 | } 26 | .file .readme table[data-table-type="yaml-metadata"] table{ 27 | margin:0 28 | } 29 | -------------------------------------------------------------------------------- /app/assets/stylesheets/layout/_footer.scss: -------------------------------------------------------------------------------- 1 | 2 | .application-footer { 3 | margin-bottom: 4rem; 4 | text-align: center; 5 | } 6 | 7 | .footer-action { 8 | margin-bottom: 2rem; 9 | } 10 | 11 | .housekeeping { 12 | margin-bottom: 1rem; 13 | a { 14 | margin: 0 0.5em; 15 | } 16 | } 17 | 18 | .credits-link { 19 | color: $base-font-color; 20 | display: block; 21 | margin-bottom: 1rem; 22 | padding: 1rem; 23 | 24 | &:hover { 25 | color: $codetriage-red; 26 | 27 | transform: translateY(-4px); 28 | transition: all 0.25s ease-out; 29 | -webkit-transform: scale(1.1); 30 | -ms-transform: scale(1.1); 31 | transform: scale(1.1); 32 | 33 | .credits-logo { 34 | fill: $codetriage-red; 35 | } 36 | } 37 | } 38 | 39 | .credits-logo { 40 | @include size(11rem); 41 | display: block; 42 | fill: $base-font-color; 43 | margin: 0 auto; 44 | } 45 | 46 | .credits { 47 | display: flex; 48 | flex-wrap: wrap; 49 | justify-content: center; 50 | } 51 | 52 | .credit-title { 53 | color: $lightest-font-color; 54 | font-size: 1.75rem; 55 | text-align: center; 56 | vertical-align: top; 57 | } 58 | 59 | .heroku-logo { 60 | padding: 1rem; 61 | } 62 | -------------------------------------------------------------------------------- /app/assets/stylesheets/layout/_header.scss: -------------------------------------------------------------------------------- 1 | .application-navigation { 2 | @include clearfix; 3 | } 4 | 5 | .application-logo { 6 | padding-top: 3rem; 7 | text-align: center; 8 | } 9 | 10 | .application-logo-image { 11 | background: asset-data-url("logo.svg"); 12 | background-repeat: no-repeat; 13 | width: 200px; 14 | height: 56px; 15 | margin: 0 auto; 16 | display: block; 17 | } 18 | 19 | .application-navigation-list { 20 | float: right; 21 | } 22 | 23 | .application-navigation-list-item { 24 | float: left; 25 | 26 | a { 27 | background-color: darken($base-background-color, 10%); 28 | color: $lightest-font-color; 29 | display: block; 30 | padding: $small-spacing $base-spacing; 31 | 32 | &:hover { 33 | color: $base-font-color; 34 | background-color: darken($base-background-color, 15%); 35 | } 36 | } 37 | } 38 | 39 | .user-profile-image { 40 | line-height: 1; 41 | max-height: 3rem; 42 | } 43 | -------------------------------------------------------------------------------- /app/assets/stylesheets/layout/_main-application.scss: -------------------------------------------------------------------------------- 1 | .main-application { 2 | padding: $small-spacing; 3 | 4 | @include media(26rem) { 5 | padding: $base-spacing; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /app/assets/stylesheets/markdown.scss: -------------------------------------------------------------------------------- 1 | @import "github/markdown-body"; 2 | @import "github/readme"; 3 | 4 | .footer { 5 | margin: 0 auto; 6 | 7 | .logo-wrapper { 8 | padding: 3em 3em 1em 3em; 9 | margin: 0 auto; 10 | width: 200px; 11 | height: 56px; 12 | } 13 | 14 | .logo-wrapper img { 15 | width: 100%; 16 | height: 100%; 17 | } 18 | } 19 | 20 | .footer-links { 21 | padding: 1em; 22 | padding-bottom: 2em; 23 | text-align: center; 24 | margin: 0 auto; 25 | font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"; 26 | font-size: 16px; 27 | line-height: 1.5; 28 | } 29 | 30 | -------------------------------------------------------------------------------- /app/assets/stylesheets/pagination.css: -------------------------------------------------------------------------------- 1 | .digg_pagination { 2 | background: white; 3 | cursor: default; 4 | /* self-clearing method: */ 5 | } 6 | 7 | .digg_pagination a, .digg_pagination span, .digg_pagination em { 8 | padding: 0.2em 0.5em; 9 | display: block; 10 | float: left; 11 | margin-right: 1px; 12 | } 13 | 14 | .digg_pagination .disabled { 15 | color: #999999; 16 | border: 1px solid #dddddd; 17 | } 18 | 19 | .digg_pagination .current { 20 | font-style: normal; 21 | font-weight: bold; 22 | background: #2e6ab1; 23 | color: white; 24 | border: 1px solid #2e6ab1; 25 | } 26 | 27 | .digg_pagination a { 28 | text-decoration: none; 29 | color: #105cb6; 30 | border: 1px solid #9aafe5; 31 | } 32 | 33 | .digg_pagination a:hover, .digg_pagination a:focus { 34 | color: #000033; 35 | border-color: #000033; 36 | } 37 | 38 | .digg_pagination .page_info { 39 | background: #2e6ab1; 40 | color: white; 41 | padding: 0.4em 0.6em; 42 | width: 22em; 43 | margin-bottom: 0.3em; 44 | text-align: center; 45 | } 46 | 47 | .digg_pagination .page_info b { 48 | color: #000033; 49 | background: #6aa6ed; 50 | padding: 0.1em 0.25em; 51 | } 52 | 53 | .digg_pagination:after { 54 | content: "."; 55 | display: block; 56 | height: 0; 57 | clear: both; 58 | visibility: hidden; 59 | } 60 | 61 | * html .digg_pagination { 62 | height: 1%; 63 | } 64 | 65 | *:first-child + html .digg_pagination { 66 | overflow: hidden; 67 | } -------------------------------------------------------------------------------- /app/assets/stylesheets/repo_body.scss: -------------------------------------------------------------------------------- 1 | .repo-body strong { 2 | color: #fff; 3 | } 4 | -------------------------------------------------------------------------------- /app/controllers/api_info_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require File.expand_path("../../../lib/sorted_repo_collection", __FILE__) 4 | 5 | class ApiInfoController < RepoBasedController 6 | include ActionView::Helpers::DateHelper 7 | 8 | def show 9 | @repo = find_repo(params) 10 | added_by = @repo.subscribers.where(private: false).order(:created_at).first&.github 11 | count = @repo.subscribers_count 12 | count = 0 if added_by.nil? 13 | 14 | respond_to do |format| 15 | format.json do 16 | render json: { 17 | stars_count: @repo.stars_count, 18 | subscriber_count: count, 19 | added_by_username: added_by, 20 | time_ago: time_ago_in_words(@repo.created_at), 21 | message: "This is an unstable interface, do not use" 22 | }.to_json 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class ApplicationController < ActionController::Base 4 | include ActionView::Helpers::NumberHelper 5 | protect_from_forgery 6 | 7 | before_action do 8 | if current_user&.admin? 9 | Rack::MiniProfiler.authorize_request 10 | end 11 | end 12 | 13 | before_action do 14 | if current_user 15 | Raven.user_context email: current_user.email, id: current_user.id, username: current_user.github 16 | end 17 | end 18 | 19 | def set_description(val) 20 | @meta_description = val 21 | end 22 | 23 | def set_title(val) 24 | @meta_title = val 25 | end 26 | 27 | def authenticate_user! 28 | redirect_to user_github_omniauth_authorize_path(origin: request.fullpath) unless user_signed_in? 29 | end 30 | 31 | def after_sign_in_path_for(resource) 32 | request.env["omniauth.origin"] || stored_location_for(resource) || root_path 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /app/controllers/issue_assignments_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class IssueAssignmentsController < ApplicationController 4 | def create 5 | repo_sub = current_user.repo_subscriptions.find(params[:id]) 6 | SendSingleTriageEmailJob.perform_later(repo_sub.id) 7 | redirect_to repo_path(repo_sub.repo), notice: "You will receive an email with your new issue shortly" 8 | end 9 | 10 | # get "/issue_assignments/:id/users/:user_id/click/:created_at", to: "issue_assignments#click" 11 | def click_issue_redirect 12 | assignment = IssueAssignment.find(params[:id]) 13 | if assignment&.user&.id.to_s == params[:user_id] 14 | assignment.user.record_click! 15 | assignment.update(clicked: true) 16 | assignment.user.update(last_clicked_at: Time.now) 17 | redirect_to assignment.issue.html_url, allow_other_host: true 18 | else 19 | redirect_to :root, message: "Bad url, if this problem persists please open an issue github.com/codetriage/codetriage" 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /app/controllers/repo_based_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class RepoBasedController < ApplicationController 4 | protected 5 | 6 | def name_from_params(options) 7 | [options[:name], options[:format]].compact.join(".") 8 | end 9 | 10 | def find_repo(options, only_active: true) 11 | repo = Repo 12 | repo = repo.active if only_active 13 | repo.find_by_full_name(options[:full_name].downcase) 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/controllers/repo_subscriptions_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class RepoSubscriptionsController < ApplicationController 4 | before_action :authenticate_user! 5 | 6 | def create 7 | @repo_subscription = create_or_update_subscription 8 | if @repo_subscription.save 9 | SendSingleTriageEmailJob.perform_later(@repo_subscription.id) 10 | redirect_to @repo_subscription.repo, notice: I18n.t("repo_subscriptions.subscribed") 11 | else 12 | flash[:error] = "Something went wrong" 13 | redirect_to repo_path(@repo_subscription.try(:repo) || Repo.find(repo_subscription_params[:repo_id])) 14 | end 15 | end 16 | 17 | def destroy 18 | @repo_sub = current_user.repo_subscriptions.find params[:id] 19 | repo = @repo_sub.repo 20 | @repo_sub.destroy 21 | redirect_to repo_path(repo) 22 | end 23 | 24 | def update 25 | @repo_sub = create_or_update_subscription 26 | if @repo_sub.save 27 | flash[:success] = "Preferences updated!" 28 | else 29 | flash[:error] = "Something went wrong" 30 | end 31 | redirect_to repo_path(@repo_sub.repo) 32 | end 33 | 34 | def create_or_update_subscription 35 | repo_sub = current_user.repo_subscriptions.find(params[:id]) if params[:id] 36 | repo_sub ||= current_user.repo_subscriptions.new(repo: Repo.find(repo_subscription_params[:repo_id])) 37 | repo_sub.update(repo_subscription_params) 38 | repo_sub 39 | end 40 | 41 | private 42 | 43 | def repo_subscription_params 44 | params.require(:repo_subscription).permit( 45 | :repo_id, 46 | :email_limit, 47 | :write, 48 | :write_limit, 49 | :read, 50 | :read_limit 51 | ) 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /app/controllers/subscribers_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class SubscribersController < RepoBasedController 4 | def show 5 | @repo = find_repo(params) 6 | @subscribers = @repo.users.page(params[:page]).per_page(params[:per_page] || 50) 7 | set_title("Code helpers for #{@repo.full_name} - #{@repo.language}") 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/controllers/topics_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class TopicsController < ApplicationController 4 | TOPICS = ["hacktoberfest".html_safe].freeze 5 | before_action :set_topic 6 | 7 | def show 8 | set_title("Open Source Project Topic: #{@topic}") 9 | set_description("Open source repos labeled #{@topic}") 10 | 11 | label = Label.find_by(name: @topic) 12 | @repos = Repo.with_some_issues 13 | .joins(:repo_labels) 14 | .where(repo_labels: {label_id: label.id}) 15 | .select(:id, :issues_count, :language, :full_name, :name, :description) 16 | 17 | @repos = @repos.order_by_issue_count.page(valid_params[:page]).per_page(valid_params[:per_page] || 50) 18 | end 19 | 20 | private def set_topic 21 | topic_name = valid_params["id"].downcase 22 | @topic = TOPICS.detect { |t| t == topic_name } 23 | 24 | if @topic.blank? 25 | redirect_back fallback_location: root_path, notice: "Invalid topic #{topic_name.inspect} not found in #{TOPICS.map(&:inspect).join(", ")}" 26 | end 27 | end 28 | 29 | private def valid_params 30 | params.permit(:id, :per_page, :page) 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /app/controllers/users/after_signup_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Users::AfterSignupController < ApplicationController 4 | include Wicked::Wizard 5 | 6 | before_action :authenticate_user! 7 | 8 | steps :set_privacy 9 | 10 | def show 11 | @user = current_user 12 | render_wizard 13 | end 14 | 15 | def update 16 | @user = current_user 17 | @user.update(user_params) 18 | render_wizard @user 19 | end 20 | 21 | private 22 | 23 | def user_params 24 | params.require(:user).permit( 25 | :private, 26 | :email, 27 | :password, 28 | :password_confirmation, 29 | :remember_me, 30 | :zip, 31 | :phone_number, 32 | :twitter, 33 | :github, 34 | :github_access_token, 35 | :avatar_url, 36 | :name, 37 | :favorite_languages, 38 | :daily_issue_limit 39 | ) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /app/controllers/users/omniauth_callbacks_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController 4 | def github 5 | @user = GitHubAuthenticator.authenticate(request.env["omniauth.auth"], current_user) 6 | if @user.persisted? 7 | flash[:notice] = if @user.valid_email? 8 | I18n.t "devise.omniauth_callbacks.success", kind: "GitHub" 9 | else 10 | I18n.t "devise.omniauth_callbacks.bad_email_success", kind: "GitHub" 11 | end 12 | 13 | sign_in_and_redirect @user, event: :authentication 14 | else 15 | session["devise.github_data"] = request.env["omniauth.auth"].delete("extra") 16 | flash[:error] = no_email_error if request.env["omniauth.auth"].info.email.blank? 17 | if flash[:error].blank? 18 | flash[:notice] = I18n.t "devise.omniauth_callbacks.failure", 19 | kind: "GitHub", reason: "Invalid credentials" 20 | end 21 | redirect_to root_path 22 | end 23 | end 24 | 25 | private 26 | 27 | def no_email_error 28 | msg = "You need a public email address on GitHub to sign up you can add" 29 | msg << " an email, sign up for triage, then remove it from GitHub:
" 30 | msg << "GitHub Profile" 31 | msg.html_safe 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module ApplicationHelper 4 | WARNING_SVG = %( 5 | 6 | 7 | 8 | 9 | 10 | 11 | ).html_safe 12 | 13 | STAR_SVG = %( 14 | 15 | 16 | 17 | ).html_safe 18 | 19 | def flash_class(level) 20 | case level 21 | when :notice then "info" 22 | when :error then "error" 23 | when :alert then "warning" 24 | end 25 | end 26 | 27 | def warning_svg 28 | WARNING_SVG 29 | end 30 | 31 | def star_svg 32 | STAR_SVG 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /app/helpers/repos_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module ReposHelper 4 | def link_to_or_log_in(options = {}) 5 | path = user_signed_in? ? options[:path] : user_github_omniauth_authorize_path(origin: request.fullpath) 6 | button_to options[:text], path, class: "button #{options[:html_class]}", method: :post 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /app/helpers/subscribers_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module SubscribersHelper 4 | end 5 | -------------------------------------------------------------------------------- /app/helpers/users_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module UsersHelper 4 | def available_times_of_day 5 | (0...24).map { |hour| Time.utc(2000, 1, 1, hour, 0o0, 0) } 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class ApplicationJob < ActiveJob::Base 4 | queue_as :default 5 | end 6 | -------------------------------------------------------------------------------- /app/jobs/background_inactive_email_job.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class BackgroundInactiveEmailJob < UserBasedJob 4 | def perform(user, repos_by_need_ids:) 5 | return false if user.repo_subscriptions.present? 6 | 7 | UserMailer.poke_inactive(user: user, repos_by_need_ids: repos_by_need_ids).deliver_now 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/jobs/populate_docs_job.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class PopulateDocsJob < RepoBasedJob 4 | around_perform do |_job, block| 5 | ActiveRecord::Base.uncached do 6 | block.call 7 | end 8 | end 9 | 10 | def perform(repo) 11 | repo.populate_docs! 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/jobs/repo_based_job.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # The purpose of this class is to provide 4 | # a base class that any job that needs to accept 5 | # a repo can use. 6 | # 7 | # This class allows a job to either take an 8 | # ActiveRecord instance or a repo integer 9 | # 10 | # The context of which repo the job is working 11 | # on will be automatically added to scout 12 | # 13 | # Example: 14 | # 15 | # class PrintRepoFullNameJob < RepoBasedJob 16 | # def perform(repo) 17 | # puts repo.full_name 18 | # end 19 | # end 20 | # 21 | # repo = Repo.where(full_name: "rails/rails").first 22 | # PrintRepoFullNameJob.perform_now(repo) 23 | # # => "rails/rails" 24 | # 25 | # repo_id = repo.id 26 | # PrintRepoFullNameJob.perform_now(repo_id) 27 | # # => "rails/rails" 28 | class RepoBasedJob < ApplicationJob 29 | around_perform do |job, block| 30 | repo_or_id = job.arguments[0] 31 | repo = if repo_or_id.is_a?(Integer) 32 | Repo.find(repo_or_id) 33 | else 34 | repo_or_id 35 | end 36 | job.arguments[0] = repo 37 | ScoutApm::Context.add(repo_id: repo.id) 38 | 39 | block.call 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /app/jobs/send_single_triage_email_job.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class SendSingleTriageEmailJob < ApplicationJob 4 | def perform(id, create: false) 5 | repo_sub = RepoSubscription.includes(:user, :repo).find(id) 6 | return unless repo_sub 7 | 8 | IssueAssigner.new(repo_sub.user, [repo_sub]).assign! 9 | if assignment(repo_sub) 10 | assignment.update!(delivered: true) 11 | UserMailer.send_triage( 12 | repo: repo_sub.repo, 13 | user: repo_sub.user, 14 | assignment: assignment, 15 | create: create 16 | ).deliver_later 17 | end 18 | end 19 | 20 | private 21 | 22 | def assignment(repo_sub = nil) 23 | raise ArgumentError if repo_sub.nil? && @assignment.nil? # provide repo_sub first time 24 | @assignment ||= repo_sub.user.issue_assignments.order(:created_at).eager_load(:repo_subscription) 25 | .where(repo_subscriptions: {repo_id: repo_sub.repo_id}).last 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /app/jobs/sentry_job.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class SentryJob < ApplicationJob 4 | def perform(event) 5 | Raven.send_event(event) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/jobs/subscribe_user_to_docs.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class SubscribeUserToDocs < UserBasedJob 4 | def perform(user) 5 | user.subscribe_docs! 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/jobs/update_repo_info_job.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class UpdateRepoInfoJob < ApplicationJob 4 | def perform(repo) 5 | repo.update_from_github 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/jobs/user_based_job.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # The purpose of this class is to provide 4 | # a base class that any job that needs to accept 5 | # a user can use. 6 | # 7 | # This class allows a job to either take an 8 | # ActiveRecord instance or a user integer 9 | # 10 | # The context of which user the job is working 11 | # on will be automatically added to scout 12 | # 13 | # Example: 14 | # 15 | # class PrintUserGithubJob < UserBasedJob 16 | # def perform(user) 17 | # puts user.github 18 | # end 19 | # end 20 | # 21 | # user = User.where(github: "schneems").first 22 | # PrintUserGithubJob.perform_now(user) 23 | # # => "schneems" 24 | # 25 | # user_id = user.id 26 | # PrintUserGithubJob.perform_now(user_id) 27 | # # => "schneems" 28 | class UserBasedJob < ApplicationJob 29 | around_perform do |job, block| 30 | user_or_id = job.arguments[0] 31 | user = if user_or_id.is_a?(Integer) 32 | User.find(user_or_id) 33 | else 34 | user_or_id 35 | end 36 | job.arguments[0] = user 37 | ScoutApm::Context.add_user(github: user.github) 38 | 39 | block.call 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /app/mailers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codetriage/CodeTriage/2f193418ca0b8757ca0a1159a2e7827ff5a02926/app/mailers/.gitkeep -------------------------------------------------------------------------------- /app/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codetriage/CodeTriage/2f193418ca0b8757ca0a1159a2e7827ff5a02926/app/models/.gitkeep -------------------------------------------------------------------------------- /app/models/doc_assignment.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class DocAssignment < ActiveRecord::Base 4 | belongs_to :repo_subscription 5 | belongs_to :doc_method, optional: true 6 | belongs_to :doc_class, optional: true 7 | 8 | delegate :user, to: :repo_subscription 9 | end 10 | -------------------------------------------------------------------------------- /app/models/doc_class.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class DocClass < ActiveRecord::Base 4 | belongs_to :repo 5 | has_many :doc_comments, dependent: :destroy 6 | end 7 | -------------------------------------------------------------------------------- /app/models/doc_comment.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class DocComment < ActiveRecord::Base 4 | belongs_to :doc_method, counter_cache: true 5 | belongs_to :doc_class, counter_cache: true 6 | 7 | validates :comment, uniqueness: {scope: [:doc_method_id, :doc_class_id]} 8 | validates :comment, presence: true 9 | 10 | def doc_method? 11 | doc_method_id.present? 12 | end 13 | 14 | def md_safe_comment_block 15 | md = comment 16 | md = DocMethod::NEEDS_DOCS if md.blank? 17 | md 18 | end 19 | 20 | def doc_class? 21 | doc_class_id.present? 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /app/models/doc_method.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class DocMethod < ActiveRecord::Base 4 | NEEDS_DOCS = "⚠ Needs Docs ⚠" 5 | 6 | belongs_to :repo 7 | has_many :doc_comments, dependent: :destroy, counter_cache: :doc_comments_count 8 | 9 | validates :raw_file, :name, :path, presence: true 10 | 11 | include ActiveRecord::CounterCache 12 | 13 | def self.missing_docs 14 | where(doc_methods: {doc_comments_count: 0}) 15 | end 16 | 17 | def self.active 18 | where(active: true) 19 | end 20 | 21 | def self.with_docs 22 | where("doc_comments_count > 0") 23 | end 24 | 25 | def missing_docs? 26 | doc_comments_count.zero? 27 | end 28 | 29 | def raw_file 30 | read_attribute(:file) 31 | end 32 | 33 | def file 34 | return nil if raw_file.blank? 35 | _absolute, _match, relative = raw_file.partition(/(\/|^)#{repo.name}\//i) 36 | relative 37 | end 38 | 39 | # converts a doc method to a github path 40 | def to_github 41 | GithubUrlFromBasePathLine.new(repo.github_url, repo.commit_sha, file, line).to_github 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /app/models/git_branchname_generator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # http://stackoverflow.com/questions/3651860/which-characters-are-illegal-within-a-branch-name 4 | class GitBranchnameGenerator 5 | def initialize(username:, doc_path:) 6 | @username = username 7 | @doc_path = doc_path 8 | end 9 | 10 | def branchname 11 | "#{@username}-update-docs-#{@doc_path}-for-pr".gsub(/[^a-zA-Z0-9\_]/, "-") 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/models/github_fetcher.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module GithubFetcher 4 | # See app/models/github_fetcher/*.rb 5 | end 6 | -------------------------------------------------------------------------------- /app/models/github_fetcher/commit_sha.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module GithubFetcher 4 | class CommitSha < Resource 5 | def initialize(user_name:, name:, default_branch:) 6 | @api_path = File.join( 7 | "repos", 8 | user_name, 9 | name, 10 | "commits", 11 | default_branch || "master" 12 | ) 13 | super({}) 14 | end 15 | 16 | def commit_sha 17 | @commit_sha ||= as_json["sha"] 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /app/models/github_fetcher/email.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module GithubFetcher 4 | class Email < Resource 5 | def initialize(options) 6 | super 7 | @api_path = "/user/emails" 8 | end 9 | 10 | private 11 | 12 | def null_response_body 13 | [{}] 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /app/models/github_fetcher/issue.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module GithubFetcher 4 | class Issue < Resource 5 | def initialize(owner_name:, repo_name:, number:) 6 | @api_path = File.join( 7 | "repos", 8 | owner_name, 9 | repo_name, 10 | "issues", 11 | number.to_s 12 | ) 13 | super({}) 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /app/models/github_fetcher/issue_comments.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module GithubFetcher 4 | class IssueComments < Resource 5 | def initialize(owner_name:, repo_name:, number:) 6 | @api_path = File.join( 7 | "repos", 8 | owner_name, 9 | repo_name, 10 | "issues", 11 | number.to_s, 12 | "comments" 13 | ) 14 | super({}) 15 | end 16 | 17 | def commenters 18 | as_json.collect { |comment| comment["user"]["login"] }.uniq 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /app/models/github_fetcher/issues.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module GithubFetcher 4 | class Issues < Resource 5 | def initialize(options) 6 | @api_path = File.join( 7 | "repos", 8 | options.delete(:user_name), 9 | options.delete(:name), 10 | "issues" 11 | ) 12 | 13 | options[:sort] ||= "comments" 14 | options[:direction] ||= "desc" 15 | options[:state] ||= "open" 16 | options[:page] ||= 1 17 | 18 | super 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /app/models/github_fetcher/repo.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module GithubFetcher 4 | class Repo < Resource 5 | def initialize(options) 6 | @api_path = File.join( 7 | "repos", 8 | options.delete(:user_name), 9 | options.delete(:name) 10 | ) 11 | super 12 | end 13 | 14 | def default_branch 15 | as_json["default_branch"] 16 | end 17 | 18 | # TODO - does this really belong here? Seems like it (and Repo#populate_docs!) 19 | # should move into the PopulateDocs job 20 | def clone 21 | out = `cd #{dir} && git clone #{clone_url} 2>&1` 22 | raise "Error executing git clone #{clone_url.inspect}: #{out.inspect}" unless $?.success? 23 | dir 24 | end 25 | 26 | private 27 | 28 | # TODO - moves w/ clone 29 | def dir 30 | @dir ||= Dir.mktmpdir 31 | end 32 | 33 | # TODO - moves w/ clone 34 | def clone_url 35 | as_json["clone_url"] 36 | end 37 | 38 | # TODO - this appears to be uncalled... remove? or do we need it and should use it? 39 | def cleanup 40 | FileUtils.remove_entry(dir) 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /app/models/github_fetcher/repos.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module GithubFetcher 4 | class Repos < Resource 5 | KINDS = [ 6 | OWNED = "repos", 7 | STARRED = "starred", 8 | SUBSCRIBED = "subscriptions" 9 | ] 10 | 11 | def initialize(options) 12 | unless options[:kind].in? KINDS 13 | raise TypeError.new("kind must be one of #{KINDS.join(", ")} (#{options[:kind]} invalid)") 14 | end 15 | 16 | options[:type] = "owner" if options[:kind] == OWNED 17 | 18 | @api_path = File.join( 19 | "user", 20 | options.delete(:kind) 21 | ) 22 | super 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /app/models/github_fetcher/user.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module GithubFetcher 4 | class User < Resource 5 | def initialize(options) 6 | @api_path = "/user" 7 | super 8 | end 9 | 10 | def valid? 11 | GitHubBub.valid_token?(@options[:token]) 12 | rescue GitHubBub::RequestError 13 | false 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /app/models/github_url_from_base_path_line.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class GithubUrlFromBasePathLine 4 | def initialize(base, commit_sha, path, line) 5 | @base = base 6 | @commit_sha = commit_sha 7 | @line = line 8 | @path = path 9 | end 10 | 11 | # https://github.com/codetriage/docs_doctor/blob/f5dd91595d5cdad0a2348515c6f715ef19c51070/app/models/github_url_from_base_path_line.rb#L10 12 | def to_github 13 | File.join(@base, "blob", @commit_sha, @path, "#L#{@line}") 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/models/issue_assignment.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class IssueAssignment < ActiveRecord::Base 4 | belongs_to :repo_subscription 5 | has_one :user, through: :repo_subscription 6 | has_one :repo, through: :repo_subscription 7 | belongs_to :issue 8 | validates :repo_subscription_id, presence: true 9 | validates :issue_id, uniqueness: {scope: :repo_subscription_id}, presence: true 10 | end 11 | -------------------------------------------------------------------------------- /app/models/label.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class Label < ActiveRecord::Base 4 | has_many :repo_labels 5 | has_many :repos, through: :repo_lables 6 | end 7 | -------------------------------------------------------------------------------- /app/models/random/cached_id_query.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Provides an actual random result exactly as expected 4 | # but it's cached for a period of time. 5 | # 6 | # Cache keys expire after the set time but 7 | # are unbounded. Take care to not pass in arbitrary data 8 | # from users into the query. 9 | # 10 | # The raw SQL is used for a cache key so take care 11 | # to pass in values in sorted order etc. 12 | # 13 | # The first run of this is VERY expensive on a large 14 | # model and can still cause significant database load 15 | class Random::CachedIdQuery 16 | private attr_reader :limit, :query, :key, :expires_in 17 | 18 | def initialize(query:, limit:, expires_in:) 19 | @query = query.order("RANDOM()") 20 | @key = "random_cached_query:#{@query.to_sql.hash}" 21 | @expires_in = expires_in 22 | @limit = limit 23 | end 24 | 25 | def call 26 | ids = Rails.cache.fetch(key, expires_in: expires_in) do 27 | query.select(:id).first(limit).map(&:id) 28 | end 29 | 30 | query.klass.where(id: ids) 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /app/models/repo_label.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class RepoLabel < ActiveRecord::Base 4 | belongs_to :repo 5 | belongs_to :label 6 | end 7 | -------------------------------------------------------------------------------- /app/services/git_hub_authenticator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class GitHubAuthenticator 4 | def initialize(auth, current_user = nil) 5 | @auth = auth 6 | @current_user = current_user 7 | end 8 | 9 | def self.authenticate(auth, current_user = nil) 10 | new(auth, current_user).authenticate 11 | end 12 | 13 | def authenticate 14 | if (user = find_user) 15 | user.update!(user_params) 16 | user 17 | else 18 | User.create(user_params.merge!(password: Devise.friendly_token[0, 20])) 19 | end 20 | end 21 | 22 | private 23 | 24 | attr_reader :auth, :current_user 25 | 26 | def find_user 27 | current_user || 28 | User.find_by(github: auth.info.nickname) || 29 | User.find_by(email: auth_email) 30 | end 31 | 32 | def auth_email 33 | @auth_email ||= auth.info.email 34 | end 35 | 36 | def token 37 | @token ||= auth.credentials.token 38 | end 39 | 40 | def github_email 41 | return auth_email if auth_email.present? 42 | GithubFetcher::Email.new(token: token).as_json.first 43 | end 44 | 45 | def github_params 46 | @github_params ||= { 47 | github: auth.info.nickname, 48 | github_access_token: token, 49 | avatar_url: auth.extra.raw_info.avatar_url 50 | } 51 | end 52 | 53 | def user_params 54 | github_params.merge( 55 | name: auth.extra.raw_info.name, 56 | email: github_email 57 | ) 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /app/services/repo_label_assigner.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # PORO 4 | # This class takes in a repo and fetch all labels for that repo. It then 5 | # creates labels and associates them with the passed in repo 6 | # 7 | # Example: 8 | # 9 | # repo = Repo.first 10 | # assigner = RepoLabelAssigner.new(repo: repo) 11 | # assigner.create_and_associate_labels! 12 | # 13 | class RepoLabelAssigner 14 | def initialize(repo:) 15 | @repo = repo 16 | url = ["repos", repo.user_name, repo.name, "labels"].join("/") 17 | @github_bub_response = GitHubBub.get(url) 18 | end 19 | 20 | def create_and_associate_labels! 21 | return unless github_bub_response.success? 22 | 23 | remote_labels.each do |label_hash| 24 | label_name = label_hash["name"].downcase 25 | label = Label.where(name: label_name).first_or_create! 26 | repo.repo_labels.where(label: label).first_or_create 27 | end 28 | end 29 | 30 | private 31 | 32 | attr_reader :github_bub_response, :repo 33 | 34 | def remote_labels 35 | Array(github_bub_response.json_body) 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /app/views/application/_down.html.slim: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/views/application/_flashes.html.slim: -------------------------------------------------------------------------------- 1 | - [:notice, :error, :alert, :success].each do |level| 2 | - unless flash[level].blank? 3 | div class="alert flash-#{level}" 4 | = content_tag :p, flash[level] 5 | -------------------------------------------------------------------------------- /app/views/application/_footer.html.slim: -------------------------------------------------------------------------------- 1 | footer.application-footer 2 | hr.section-break 3 | = link_to "Submit a Repo", new_repo_path, class: 'button footer-action' 4 | 5 | h2.credit-title Sponsors 6 | section.credits 7 | a href="https://ter.li/pi16r5" class="credits-link" 8 | = image_tag "sponsors/scout.png", class: "credits-logo", alt: 'Scout logo (sponsors CodeTriage)' 9 | 10 | h2.credit-title Credits 11 | section.credits 12 | a href="https://www.schneems.com" class="credits-link" 13 | = image_tag "schneems.svg", class: "credits-logo", alt: 'Schneems logo (created CodeTriage)' 14 | | Created by Schneems 15 | 16 | a href="https://thoughtbot.com" class="credits-link" 17 | = image_tag "thoughtbot.svg", class: "credits-logo", alt: 'Thoughtbot logo (donated some visual designer time)' 18 | | Designed by thoughtbot 19 | 20 | a href="https://www.heroku.com" class="credits-link" 21 | = image_tag "heroku.svg", class: "credits-logo heroku-logo", alt: 'Heroku logo (app runs on the Heroku platform)' 22 | | Powered by Heroku 23 | 24 | p.housekeeping 25 | = link_to "What is CodeTriage?", what_path 26 | = link_to "Privacy Policy", privacy_path 27 | = link_to "Support", support_path 28 | = link_to "Fork us on GitHub", "https://github.com/codetriage/codetriage" 29 | 30 | p 31 | | © CodeTriage   32 | = Time.now.year 33 | -------------------------------------------------------------------------------- /app/views/application/_head.html.slim: -------------------------------------------------------------------------------- 1 | head 2 | meta charset="utf-8" 3 | title 4 | = @meta_title ? "#{@meta_title} | CodeTriage" : "CodeTriage" 5 | 6 | meta name="viewport" content="width=device-width, initial-scale=1.0" 7 | = csrf_meta_tags 8 | 9 | = stylesheet_link_tag "application", media: "all" 10 | 11 | = favicon_link_tag 'favicon.ico', rel: 'shortcut icon', type: 'image/x-icon' 12 | = favicon_link_tag 'favicon.ico', rel: 'icon', type: 'image/x-icon' 13 | 14 | 15 | 16 | 17 | meta name="description" content="#{ @meta_description || 'The easiest way to get started contributing to open source projects.' }" 18 | 19 | = javascript_include_tag "application", async: true 20 | -------------------------------------------------------------------------------- /app/views/layouts/_app.html.slim: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | = render "head" 4 | 5 | body 6 | = render "flashes" 7 | = render "nav" 8 | 9 | main.main-application 10 | = yield 11 | 12 | = render "footer" 13 | 14 | = content_for :render_async 15 | -------------------------------------------------------------------------------- /app/views/layouts/application.html.slim: -------------------------------------------------------------------------------- 1 | = render "layouts/app" do 2 | = yield 3 | 4 | -------------------------------------------------------------------------------- /app/views/layouts/mail_layout.html.erb: -------------------------------------------------------------------------------- 1 | <%= stylesheet_link_tag "markdown.css" %> 2 | 3 |
4 |
5 | <%= yield %> 6 |
7 | 30 |
31 | -------------------------------------------------------------------------------- /app/views/layouts/mail_layout.text.erb: -------------------------------------------------------------------------------- 1 | <%= yield %> 2 | 3 | ____________________ 4 | | ___ _ _____ _ | 5 | | / __\___ __| | __/__ \_ __(_) __ _ __ _ ___ | 6 | | / / / _ \ / _` |/ _ \/ /\/ '__| |/ _` |/ _` |/ _ \ | 7 | | / /__| (_) | (_| | __/ / | | | | (_| | (_| | __/ | 8 | | \____/\___/ \__,_|\___\/ |_| |_|\__,_|\__, |\___| | 9 | | |___/ | 10 | |________________________________________________ 11 | 12 | <% if @user %> 13 | <% if @htos %> 14 | Don't want to receive free content from my upcoming book "How to Open Source"? 15 | [Click here to "mute" these emails.](<%= edit_user_url(@user) %>%). 16 | You'll still receive CodeTriage emails. 17 | 18 | <% end %> 19 | [Remove Account](<%= token_delete_user_url(@user.account_delete_token) %>) 20 | <% end %> 21 | -------------------------------------------------------------------------------- /app/views/layouts/markdown_page.html.slim: -------------------------------------------------------------------------------- 1 | = render "layouts/app" do 2 | section.subpage-content-wrapper.static-page 3 | header.subpage-content-header 4 | h1.subpage-primary-title= @page_title or raise "Must provide @page_title" 5 | 6 | .repo-body.markdown-body 7 | = yield 8 | -------------------------------------------------------------------------------- /app/views/pages/_repos_with_pagination.html.slim: -------------------------------------------------------------------------------- 1 | ul.repo-list = render partial: "repos/repo", collection: repos, cached: false 2 | .pagination= will_paginate repos, container: false 3 | -------------------------------------------------------------------------------- /app/views/pages/support.html.slim: -------------------------------------------------------------------------------- 1 | .subpage-content-wrapper. 2 | .subpage-content-header 3 | h1.subpage-primary-title Support 4 | p All support is provided via GitHub issues: 5 | p= link_to "CodeTriage/CodeTriage issues", "https://github.com/codetriage/codetriage/issues" 6 | p If you have a private question or comment, open an issue noting you want to be contacted, and support can be provided by email. 7 | 8 | -------------------------------------------------------------------------------- /app/views/repo_subscriptions/_list.html.slim: -------------------------------------------------------------------------------- 1 | ul.repo-list 2 | = render partial: "repos/repo", collection: @repos_subs.map(&:repo), cached: true 3 | -------------------------------------------------------------------------------- /app/views/repos/_badge.html.slim: -------------------------------------------------------------------------------- 1 | - code_triagers_badge_url = File.join(repo_url(@repo), "badges/users.svg") 2 | 3 | ul.accordion-tabs 4 | li.tab-header-and-content 5 | a.is-active.tab-link href="javascript:void(0)" Image URL 6 | .tab-content.is-open 7 | textarea.single-line 8 | = code_triagers_badge_url 9 | li.tab-header-and-content 10 | a.tab-link href="javascript:void(0)" Markdown 11 | .tab-content 12 | textarea.single-line 13 | | [![Open Source Helpers](#{code_triagers_badge_url})](#{repo_url(@repo)}) 14 | li.tab-header-and-content 15 | a.tab-link href="javascript:void(0)" Textile 16 | .tab-content 17 | textarea.single-line 18 | | !#{code_triagers_badge_url}!:#{repo_url(@repo)} 19 | li.tab-header-and-content 20 | a.tab-link href="javascript:void(0)" Rdoc 21 | .tab-content 22 | textarea.single-line 23 | | {Open Source Helpers}[#{repo_url(@repo)}] 24 | -------------------------------------------------------------------------------- /app/views/repos/_docs.html.slim: -------------------------------------------------------------------------------- 1 | - @docs.each # Load into memory so we don't hit multiple queries via empty? 2 | 3 | - if @repo.docs_subscriber_count.zero? && @repo.can_doctor_docs? 4 | li.slats-item Subscribe to help with docs for this repo and come back later 5 | 6 | - elsif @docs.empty? 7 | ul.slats-list 8 | - if params[:docs_before] || params[:docs_after] 9 | li.slats-item Oops, no more docs in that direction, try going back. 10 | - else 11 | - if @repo.can_doctor_docs? 12 | li.slats-item No docs 13 | - else 14 | li.slats-item = "#{@repo.language} not yet supported" 15 | 16 | - else 17 | ul.slats-list 18 | - @docs.each do |doc| 19 | li.slats-item 20 | - if doc.missing_docs? 21 | span.half-opacity.warning-svg.issue-icon 22 | - else 23 | = "🚀".html_safe 24 | = link_to doc.path, doc 25 | .pagination 26 | a.previous_page href=repo_path(full_name: @repo.full_name, docs_before: @docs.first.id)+"#help-out" 27 | | ← Previous 28 | a.previous_page href=repo_path(full_name: @repo.full_name, docs_after: @docs.last.id)+"#help-out" 29 | | Next → 30 | -------------------------------------------------------------------------------- /app/views/repos/_issues.html.slim: -------------------------------------------------------------------------------- 1 | - if @issues.each && @issues.empty? 2 | ul.slats-list 3 | - if params[:issues_before] || params[:issues_after] 4 | li.slats-item Oops, no more open issues in that direction, try going back. 5 | - else 6 | li.slats-item No open issues 7 | - else 8 | ul.slats-list 9 | - @issues.each do |issue| 10 | li.slats-item 11 | span.half-opacity.warning-svg.issue-icon 12 | = link_to issue.title, issue.html_url 13 | .pagination 14 | a.previous_page href=repo_path(full_name: @repo.full_name, issues_before: @issues.first.id)+"#help-out" 15 | | ← Previous 16 | a.previous_page href=repo_path(full_name: @repo.full_name, issues_after: @issues.last.id)+"#help-out" 17 | | Next → 18 | -------------------------------------------------------------------------------- /app/views/repos/_repo.html.slim: -------------------------------------------------------------------------------- 1 | li class="repo-item #{ repo.weight }" data-language="#{ repo.language if repo.language }" 2 | = link_to repo 3 | header.repo-item-header 4 | h3.repo-item-title = repo.name 5 | span.repo-item-issues 6 | span.warning-svg.issue-icon 7 | = repo.issues_count 8 | | Issues 9 | 10 | p.repo-item-description = repo.description 11 | 12 | - if repo.full_name.present? 13 | span.repo-item-full-name 14 | | (#{repo.full_name}) 15 | -------------------------------------------------------------------------------- /app/views/repos/edit.html.slim: -------------------------------------------------------------------------------- 1 | h2 Editing repo #{@repo.full_name} 2 | 3 | = form_for @repo do |f| 4 | 5 | p 6 | | As a repo owner you are able to add some text to your repos page to encourage users to subscribe to your project and help out. 7 | You may also want to add additional information on the best ways to help out and any common things that people might want to know in advance. 8 | p Markdown is supported here 9 | 10 | p= f.text_area :notes, class: 'span6', rows: 5 11 | 12 | div 13 | => f.submit "Update repo", class: 'btn btn-primary' 14 | = link_to "Cancel", repo_path(@repo), class: 'btn' 15 | -------------------------------------------------------------------------------- /app/views/repos/list.html.slim: -------------------------------------------------------------------------------- 1 | = render 'shared/add_repos_list', repos: @repos 2 | -------------------------------------------------------------------------------- /app/views/shared/_add_repos_list.html.slim: -------------------------------------------------------------------------------- 1 | - if repos.blank? || repos.size == 0 2 | h3 No repos 3 | - else 4 | ul.slats-list 5 | - repos.each do |repo| 6 | - if repo["has_issues"] && !repo["private"] 7 | li.slats-item 8 | = form_for @repo do |f| 9 | = f.hidden_field :user_name, value: repo["full_name"].split("/").first 10 | = f.hidden_field :name, value: repo["full_name"].split("/").last 11 | = f.hidden_field :stars_count, value: repo["stargazers_count"] 12 | .slats-hgroup 13 | h3= repo["full_name"] 14 | 15 | h4 #{repo["stargazers_count"]} stars, 16 | h4 #{repo["open_issues_count"]} issues 17 | 18 | .slats-action 19 | - if Repo.exists_with_name?(repo["full_name"]) 20 | p Added! 21 | - else 22 | = f.submit "Add" 23 | -------------------------------------------------------------------------------- /app/views/subscribers/_avatars.html.slim: -------------------------------------------------------------------------------- 1 | - subscribers.each do |subscriber| 2 | li.user 3 | - if subscriber.public? 4 | => link_to subscriber.github_url, title: "@#{subscriber.github}" do 5 | = image_tag subscriber.avatar_url, alt: subscriber.github, class: 'avatar-image' 6 | - else 7 | = image_tag 'private-avatar.svg', alt: 'private avatar', class: 'avatar-image' 8 | -------------------------------------------------------------------------------- /app/views/subscribers/show.html.slim: -------------------------------------------------------------------------------- 1 | div class="subpage-content-wrapper #{ @repo.weight }" 2 | header.subpage-content-header 3 | h1.subpage-primary-title #{ @repo.subscribers_count } #{ @repo.name } Subscribers 4 | .subpage-header-actions 5 | = link_to @repo, class: "header-primary-action" 6 | | Back to #{@repo.name} 7 | 8 | ul.avatars-list = render 'avatars', subscribers: @subscribers, show_names: true 9 | = will_paginate @subscribers 10 | -------------------------------------------------------------------------------- /app/views/topics/show.html.slim: -------------------------------------------------------------------------------- 1 | .hero 2 | h1.hero-title-primary Repos with label: #{@topic} 3 | 4 | - unless user_signed_in? 5 | = link_to user_github_omniauth_authorize_path, class: 'button', method: :post 6 | | Sign up with GitHub 7 | 8 | hr.section-break 9 | 10 | .repos-list-wrapper 11 | h2.repos-list-title All repos with label #{@topic}. 12 | 13 | section.repo-list-with-pagination 14 | = render "pages/repos_with_pagination", repos: @repos 15 | -------------------------------------------------------------------------------- /app/views/user_mailer/daily_docs.md.erb: -------------------------------------------------------------------------------- 1 | Hi @<%= @user.github %>, 2 | 3 | <% if @write_docs.present? %> 4 | ## Write Docs 5 | 6 | <% @write_docs.sort_by {|d| d.repo.full_name }.each do |doc| %> 7 | **<%= doc.repo.full_name %>** ([source](<%= doc.to_github %>)): [<%= doc.path %>](<%= doc_method_url doc %>) 8 | 9 | <% end %> 10 | <% end %> 11 | 12 | 13 | <% if @read_docs.present? %> 14 | 15 | ## Read Docs 16 | 17 | <% @read_docs.sort_by {|d| d.repo.full_name }.each do |doc| %> 18 | **<%= doc.repo.full_name %>** ([source](<%= doc_source_click_url(doc.id, @user.id) %>)): [<%= doc.path %>](<%= doc_method_click_url(doc.id, @user.id) %>) 19 | 20 |
21 |     <%= doc.doc_comments.first.try(:comment) %>
22 |   
23 | 24 | <% end %> 25 | <% end %> 26 | 27 | -- 28 | 29 | Go forth and make the world a better place 30 | 31 | [@HowToOpenSource](https://twitter.com/HowToOpenSource) 32 | [Help doctor more docs at codetriage.com](<%= root_url %>) 33 | -------------------------------------------------------------------------------- /app/views/user_mailer/spam.md.erb: -------------------------------------------------------------------------------- 1 | Hi <%= @user.github %>, 2 | 3 | <%= @message.html_safe %> 4 | 5 | -- 6 | [Richard Schneeman](https://www.schneems.com), creator of CodeTriage 7 | -------------------------------------------------------------------------------- /app/views/users/after_signup/set_privacy.html.slim: -------------------------------------------------------------------------------- 1 | h2 Set Privacy 2 | p By default when you subscribe to a repo to triage, that information will be public knowledge. We think this is a good thing, you're helping out the open source community so why not share the fun? We won't share your email, or anything else, just your GitHub username and the repo you are subscribing to. It might look something like this: 3 | div style='height:50px; border: solid 1px black; display:inline-block; padding: 0 100px 0 0; border-radius: 5px' 4 | => image_tag current_user.avatar_url, class: 'img-rounded', style: "height: 40px; margin: 5px" 5 | | #{current_user.github} has subscribed to: #{link_to "codetriage/codetriage", "https://github.com/codetriage/codetriage"} 6 | p While we think this might be an awesome show of your commitment to quality code, you might not want this info public, if that's the case, we understand. You can make your account private by selecting the appropriate button below: 7 | = link_to "Make Private", next_wizard_path(user: {private: true}), method: :patch, class: "btn btn-primary" 8 | =<> link_to "Stay Public", next_wizard_path(user: {private: false}), method: :patch, class: "btn btn-success" 9 | = link_to "skip →", next_wizard_path, class: "btn btn-primary" 10 | p You can change your mind any time, and set your account to private on your settings page. 11 | -------------------------------------------------------------------------------- /app/views/users/edit.html.slim: -------------------------------------------------------------------------------- 1 | .subpage-content-wrapper 2 | section.subpage-content-header 3 | h1.subpage-primary-title 4 | | Edit 5 | = @user.github 6 | 7 | section.user-settings.content-section 8 | = form_for @user, url: user_path(@user), method: :patch, html: { class: "edit_user", id: "edit_user" } do |f| 9 | = render partial: "form", locals: {f: f} 10 | -------------------------------------------------------------------------------- /app/views/users/token_delete.html.slim: -------------------------------------------------------------------------------- 1 | section.subpage-content-wrapper.high 2 | header.subpage-content-header 3 | h1.subpage-primary-title Delete Account? 4 | p.subpage-secondary-title This cannot be undone. 5 | 6 | .repo-body 7 | - if @lonely_repos.present? 8 | section.help-triage.content-section 9 | | If you delete your account these CodeTriage repos will have no subscribers and will be removed as well. 10 | ul.bullets 11 | - @lonely_repos.each do |repo| 12 | li= link_to repo.full_name, repo_path(repo) 13 | 14 | section.help-triage.content-section 15 | | The only thing this app does is send emails. The only way to unsubscribe is account deletion. If you have feedback for the project, please 16 | = link_to " open up an issue on GitHub", "https://github.com/codetriage/codetriage/issues" 17 | | . 18 | 19 | section.help-triage.content-section 20 | = form_for @user, url: token_delete_user_path(@user.account_delete_token), method: :delete, html_options: { class: "form form-horizontal danger" } do |f| 21 | = link_to "Cancel", root_url, class: "button" 22 | |   23 | |   24 | = f.submit "💀 Delete My Account", class: "btn btn-danger", id: "destroy_user", data: { confirm: 'This cannot be undone, are you sure?' } 25 | -------------------------------------------------------------------------------- /bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /bin/puma: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'puma' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require 'pathname' 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require 'rubygems' 14 | require 'bundler/setup' 15 | 16 | load Gem.bin_path('puma', 'puma') 17 | -------------------------------------------------------------------------------- /bin/pumactl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'pumactl' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require 'pathname' 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require 'rubygems' 14 | require 'bundler/setup' 15 | 16 | load Gem.bin_path('puma', 'pumactl') 17 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path("../config/application", __dir__) 3 | require_relative "../config/boot" 4 | require "rails/commands" 5 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative "../config/boot" 3 | require "rake" 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /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 set up or update your development environment automatically. 13 | # This script is idempotent, so that you can run it at any time 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 | # Install JavaScript dependencies 21 | # system('bin/yarn') 22 | 23 | # puts "\n== Copying sample files ==" 24 | # unless File.exist?('config/database.yml') 25 | # FileUtils.cp 'config/database.yml.sample', 'config/database.yml' 26 | # end 27 | 28 | puts "\n== Preparing database ==" 29 | system! 'bin/rails db:setup' 30 | 31 | puts "\n== Removing old logs and tempfiles ==" 32 | system! 'bin/rails log:clear tmp:clear' 33 | 34 | puts "\n== Restarting application server ==" 35 | system! 'bin/rails restart' 36 | end 37 | -------------------------------------------------------------------------------- /bin/update: -------------------------------------------------------------------------------- 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 update your development environment automatically. 13 | # Add necessary update steps to this file. 14 | 15 | puts '== Installing dependencies ==' 16 | system! 'gem install bundler --conservative' 17 | system('bundle check') || system!('bundle install') 18 | 19 | # Install JavaScript dependencies 20 | # system('bin/yarn') 21 | 22 | puts "\n== Updating database ==" 23 | system! 'rails db:migrate' 24 | 25 | puts "\n== Removing old logs and tempfiles ==" 26 | system! 'rails log:clear tmp:clear' 27 | 28 | puts "\n== Restarting application server ==" 29 | system! 'rails restart' 30 | end 31 | -------------------------------------------------------------------------------- /bin/yarn: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | 4 | APP_ROOT = File.expand_path('..', __dir__) 5 | Dir.chdir(APP_ROOT) do 6 | executable_path = ENV["PATH"].split(File::PATH_SEPARATOR).find do |path| 7 | normalized_path = File.expand_path(path) 8 | 9 | normalized_path != __dir__ && File.executable?(Pathname.new(normalized_path).join('yarn')) 10 | end 11 | 12 | if executable_path 13 | exec File.expand_path(Pathname.new(executable_path).join('yarn')), *ARGV 14 | else 15 | $stderr.puts "Yarn executable was not detected in the system." 16 | $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install" 17 | exit 1 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # This file is used by Rack-based servers to start the application. 4 | 5 | require "rbtrace" 6 | require_relative "config/environment" 7 | 8 | run Rails.application 9 | Rails.application.load_server 10 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative "boot" 4 | 5 | require "rails/all" 6 | 7 | # Require the gems listed in Gemfile, including any gems 8 | # you've limited to :test, :development, or :production. 9 | Bundler.require(*Rails.groups) 10 | 11 | module CodeTriage 12 | class Application < Rails::Application 13 | # Initialize configuration defaults for originally generated Rails version. 14 | config.load_defaults 7.0 15 | 16 | # Settings in config/environments/* take precedence over those specified here. 17 | # Application configuration should go into files in config/initializers 18 | # -- all .rb files in that directory are automatically loaded. 19 | config.active_job.queue_adapter = :sidekiq 20 | config.encoding = "utf-8" 21 | # Enable the asset pipeline 22 | config.assets.enabled = true 23 | config.assets.initialize_on_precompile = false 24 | # Set i18n.enforce_available_locales to true 25 | config.i18n.enforce_available_locales = true 26 | 27 | config.force_ssl = ENV["APPLICATION_HOST"] 28 | config.middleware.insert_after ActionDispatch::SSL, Rack::CanonicalHost, ENV["APPLICATION_HOST"] if ENV["APPLICATION_HOST"] 29 | # Application configuration can go into files in config/initializers 30 | # -- all .rb files in that directory are automatically loaded after loading 31 | # the framework and any gems in your application. 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 4 | 5 | require "bundler/setup" # Set up gems listed in the Gemfile. 6 | require "bootsnap/setup" # Speed up boot time by caching expensive operations. 7 | -------------------------------------------------------------------------------- /config/cable.yml: -------------------------------------------------------------------------------- 1 | development: 2 | adapter: async 3 | 4 | test: 5 | adapter: test 6 | 7 | production: 8 | adapter: redis 9 | url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %> 10 | channel_prefix: code_triage_production 11 | -------------------------------------------------------------------------------- /config/database.ci.yml: -------------------------------------------------------------------------------- 1 | defaults: &defaults 2 | adapter: postgresql 3 | encoding: utf8 4 | pool: 5 5 | host: localhost 6 | password: password 7 | username: postgres 8 | 9 | development: 10 | <<: *defaults 11 | database: triage_development 12 | 13 | test: 14 | <<: *defaults 15 | database: triage_test 16 | -------------------------------------------------------------------------------- /config/database.example.yml: -------------------------------------------------------------------------------- 1 | defaults: &defaults 2 | adapter: postgresql 3 | encoding: utf8 4 | pool: 5 5 | host: localhost 6 | password: 7 | 8 | development: 9 | <<: *defaults 10 | database: triage_development 11 | 12 | test: 13 | <<: *defaults 14 | database: triage_test 15 | 16 | production: 17 | <<: *defaults 18 | database: triage_production 19 | 20 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Load the Rails application. 4 | require_relative "application" 5 | 6 | # Initialize the Rails application. 7 | Rails.application.initialize! 8 | -------------------------------------------------------------------------------- /config/initializers/application_controller_renderer.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # ActiveSupport::Reloader.to_prepare do 6 | # ApplicationController.renderer.defaults.merge!( 7 | # http_host: 'example.org', 8 | # https: false 9 | # ) 10 | # end 11 | -------------------------------------------------------------------------------- /config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # Version of your assets, change this if you want to expire all your assets. 6 | Rails.application.config.assets.version = "1.0" 7 | 8 | # Add additional assets to the asset load path. 9 | # Rails.application.config.assets.paths << Emoji.images_path 10 | # Add Yarn node_modules folder to the asset load path. 11 | Rails.application.config.assets.paths << Rails.root.join("node_modules") 12 | 13 | # Precompile additional assets. 14 | # application.js, application.css, and all non-JS/CSS in the app/assets 15 | # folder are already added. 16 | # Rails.application.config.assets.precompile += %w( admin.js admin.css ) 17 | -------------------------------------------------------------------------------- /config/initializers/backlog.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Thread.new do 4 | # while 1 do 5 | # sleep 5 6 | 7 | # puts "backlog: " + `netstat -nap tcp | grep $PORT| wc -l` if ENV['PORT'] 8 | # end 9 | # end 10 | -------------------------------------------------------------------------------- /config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 6 | # Rails.backtrace_cleaner.add_silencer { |line| /my_noisy_library/.match?(line) } 7 | 8 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code 9 | # by setting BACKTRACE=1 before calling your invocation, like "BACKTRACE=1 ./bin/rails runner 'MyClass.perform'". 10 | Rails.backtrace_cleaner.remove_silencers! if ENV["BACKTRACE"] 11 | -------------------------------------------------------------------------------- /config/initializers/content_security_policy.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # Define an application-wide content security policy 6 | # For further information see the following documentation 7 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy 8 | 9 | # Rails.application.config.content_security_policy do |policy| 10 | # policy.default_src :self, :https 11 | # policy.font_src :self, :https, :data 12 | # policy.img_src :self, :https, :data 13 | # policy.object_src :none 14 | # policy.script_src :self, :https 15 | # policy.style_src :self, :https 16 | # # If you are using webpack-dev-server then specify webpack-dev-server host 17 | # policy.connect_src :self, :https, "http://localhost:3035", "ws://localhost:3035" if Rails.env.development? 18 | 19 | # # Specify URI for violation reports 20 | # # policy.report_uri "/csp-violation-report-endpoint" 21 | # end 22 | 23 | # If you are using UJS then enable automatic nonce generation 24 | # Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) } 25 | 26 | # Set the nonce only to specific directives 27 | # Rails.application.config.content_security_policy_nonce_directives = %w(script-src) 28 | 29 | # Report CSP violations to a specified URI 30 | # For further information see the following documentation: 31 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only 32 | # Rails.application.config.content_security_policy_report_only = true 33 | -------------------------------------------------------------------------------- /config/initializers/cookie_rotator.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | Rails.application.config.after_initialize do 4 | Rails.application.config.action_dispatch.cookies_rotations.tap do |cookies| 5 | salt = Rails.application.config.action_dispatch.authenticated_encrypted_cookie_salt 6 | secret_key_base = Rails.application.secret_key_base 7 | 8 | key_generator = ActiveSupport::KeyGenerator.new( 9 | secret_key_base, iterations: 1000, hash_digest_class: OpenSSL::Digest::SHA1 10 | ) 11 | key_len = ActiveSupport::MessageEncryptor.key_len 12 | secret = key_generator.generate_key(salt, key_len) 13 | 14 | cookies.rotate :encrypted, secret 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # Specify a serializer for the signed and encrypted cookie jars. 6 | # Valid options are :json, :marshal, and :hybrid. 7 | Rails.application.config.action_dispatch.cookies_serializer = :hybrid 8 | -------------------------------------------------------------------------------- /config/initializers/cors.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # Avoid CORS issues when API is called from the frontend app. 6 | # Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests. 7 | 8 | # Read more: https://github.com/cyu/rack-cors 9 | 10 | # Rails.application.config.middleware.insert_before 0, Rack::Cors do 11 | # allow do 12 | # origins 'example.com' 13 | # 14 | # resource '*', 15 | # headers: :any, 16 | # methods: [:get, :post, :put, :patch, :delete, :options, :head] 17 | # end 18 | # end 19 | -------------------------------------------------------------------------------- /config/initializers/covid.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Here is how to add a "COVID-19" related resource to the project 4 | # 5 | # 1) Find the GitHub url of the project such as `https://github.com/nextstrain/ncov` 6 | # 2) Ensure the project is added to CodeTriage, visit https://www.codetriage.com/repos/new and add the repo 7 | # 3) Come back to this file on GitHub and in the COVID_REPOS array (inside the %W{}) add the username and name of the repo 8 | # 4) Save the file in the UI and submit a PR 9 | # 5) Repeat 10 | # 11 | COVID_REPOS = %W[ 12 | nextstrain/ncov 13 | jcl5m1/ventilator 14 | CSSEGISandData/COVID-19 15 | nextstrain/auspice 16 | nextstrain/augur 17 | nextstrain/cli 18 | nextstrain/fauna 19 | nextstrain/nextstrain.org 20 | neherlab/covid19_scenarios 21 | neherlab/covid19_scenarios 22 | mhdhejazi/CoronaTracker 23 | ahmadawais/corona-cli 24 | ushahidi/platform 25 | ushahidi/ushahidi_web 26 | HospitalRun/hospitalrun 27 | HospitalRun/hospitalrun-frontend 28 | HospitalRun/hospitalrun-server 29 | learningequality/kolibri 30 | svs/covid19-supply-chain 31 | nytimes/covid-19-data 32 | Leo1690/BtPedalClient 33 | Leo1690/BtPedalServer 34 | covid19india/covid19india-react 35 | ] 36 | -------------------------------------------------------------------------------- /config/initializers/feature_policy.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | # Define an application-wide HTTP feature policy. For further 3 | # information see https://developers.google.com/web/updates/2018/06/feature-policy 4 | # 5 | # Rails.application.config.feature_policy do |f| 6 | # f.camera :none 7 | # f.gyroscope :none 8 | # f.microphone :none 9 | # f.usb :none 10 | # f.fullscreen :self 11 | # f.payment :self, "https://secure.example.com" 12 | # end 13 | -------------------------------------------------------------------------------- /config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # Configure sensitive parameters which will be filtered from the log file. 6 | Rails.application.config.filter_parameters += [:password] 7 | -------------------------------------------------------------------------------- /config/initializers/git_hub_bub.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # I know it's awful but I don't have time to fix the GitHubBub API and this makes `rails c` not noisey 4 | GitHubBub::Request.send(:remove_const, :GITHUB_VERSION) 5 | GitHubBub::Request.send(:remove_const, :USER_AGENT) 6 | GitHubBub::Request.send(:remove_const, :RETRIES) 7 | 8 | GitHubBub::Request::GITHUB_VERSION = "vnd.github.v3.full+json" 9 | GitHubBub::Request::USER_AGENT = "codetriage" 10 | GitHubBub::Request::RETRIES = 3 11 | 12 | class CodeTraigeRandomApiKeyStore 13 | def initialize 14 | @keys = [] 15 | @mutex = Mutex.new 16 | end 17 | 18 | def call 19 | return ENV["GITHUB_API_KEY"] if ENV["GITHUB_API_KEY"] 20 | 21 | until (key = @keys.pop) 22 | populate_keys 23 | end 24 | 25 | key 26 | end 27 | 28 | private def get_key 29 | @keys.pop 30 | end 31 | 32 | private def populate_keys 33 | @mutex.synchronize do 34 | return if @keys.any? 35 | 36 | @keys = User.fast_rand.where.not(token: nil).limit(5000).pluck(:token) 37 | 38 | if @keys.empty? && User.where.not(token: nil).empty? 39 | raise "No users with tokens in the database" 40 | end 41 | end 42 | end 43 | end 44 | 45 | code_triage_random_api_key_store = CodeTraigeRandomApiKeyStore.new 46 | 47 | # Auth all non authed requests (due to github request limits) 48 | GitHubBub::Request.set_before_callback do |request| 49 | if request.token? 50 | # Request is authorized, do nothing 51 | else 52 | request.token = code_triage_random_api_key_store.call 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # Add new inflection rules using the following format. Inflections 6 | # are locale specific, and you may define rules for as many different 7 | # locales as you wish. All of these examples are active by default: 8 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 9 | # inflect.plural /^(ox)$/i, '\1en' 10 | # inflect.singular /^(ox)en/i, '\1' 11 | # inflect.irregular 'person', 'people' 12 | # inflect.uncountable %w( fish sheep ) 13 | # end 14 | 15 | # These inflection rules are supported but not enabled by default: 16 | ActiveSupport::Inflector.inflections(:en) do |inflect| 17 | inflect.acronym "PHP" 18 | end 19 | -------------------------------------------------------------------------------- /config/initializers/mail.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | ActionMailer::Base.smtp_settings = { 4 | address: "smtp.sendgrid.net", 5 | port: "587", 6 | authentication: :plain, 7 | user_name: ENV["SENDGRID_USERNAME"], 8 | password: ENV["SENDGRID_PASSWORD"], 9 | domain: "codetriage.com" 10 | } 11 | ActionMailer::Base.delivery_method ||= :smtp 12 | 13 | Maildown.allow_indentation = true 14 | Premailer::Rails.config[:generate_text_part] = false 15 | -------------------------------------------------------------------------------- /config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # Add new mime types for use in respond_to blocks: 6 | # Mime::Type.register "text/richtext", :rtf 7 | 8 | Mime::Type.register "image/svg+xml", :svg 9 | -------------------------------------------------------------------------------- /config/initializers/mini-profiler.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # set RedisStore so Rack Mini-Profiler works with multiple servers 4 | if Rails.env.production? && ENV["REDIS_URL"] 5 | uri = URI.parse(ENV["REDIS_URL"]) 6 | Rack::MiniProfiler.config.storage = Rack::MiniProfiler::RedisStore 7 | Rack::MiniProfiler.config.storage_options = {host: uri.host, port: uri.port, password: uri.password} 8 | end 9 | -------------------------------------------------------------------------------- /config/initializers/new_framework_defaults.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | # 5 | # This file contains migration options to ease your Rails 5.0 upgrade. 6 | # 7 | # Once upgraded flip defaults one by one to migrate to the new default. 8 | # 9 | # Read the Guide for Upgrading Ruby on Rails for more info on each option. 10 | 11 | # Enable per-form CSRF tokens. Next major version defaults to true. 12 | Rails.application.config.action_controller.per_form_csrf_tokens = true 13 | 14 | # Enable origin-checking CSRF mitigation. Next major version defaults to true. 15 | Rails.application.config.action_controller.forgery_protection_origin_check = true 16 | 17 | # Make Ruby 2.4 preserve the timezone of the receiver when calling `to_time`. 18 | ActiveSupport.to_time_preserves_timezone = true 19 | -------------------------------------------------------------------------------- /config/initializers/permissions_policy.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Define an application-wide HTTP permissions policy. For further 4 | # information see https://developers.google.com/web/updates/2018/06/feature-policy 5 | # 6 | # Rails.application.config.permissions_policy do |f| 7 | # f.camera :none 8 | # f.gyroscope :none 9 | # f.microphone :none 10 | # f.usb :none 11 | # f.fullscreen :self 12 | # f.payment :self, "https://secure.example.com" 13 | # end 14 | -------------------------------------------------------------------------------- /config/initializers/puma_worker_killer.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # PumaAutoTune.start 4 | -------------------------------------------------------------------------------- /config/initializers/request_forgery_protection.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # Enable origin-checking CSRF mitigation. 6 | Rails.application.config.action_controller.forgery_protection_origin_check = true 7 | -------------------------------------------------------------------------------- /config/initializers/sentry.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | Raven.configure do |config| 4 | config.excluded_exceptions += ["Sidekiq::Shutdown"] 5 | 6 | config.async = lambda do |event| 7 | Thread.new do 8 | Raven.send_event(event) 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | Rails.application.config.session_store :cookie_store, key: "_code_triage_session" 6 | -------------------------------------------------------------------------------- /config/initializers/sidekiq.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | Sidekiq.default_configuration.redis = { 4 | url: ENV["REDIS_URL"], 5 | ssl_params: { 6 | verify_mode: OpenSSL::SSL::VERIFY_NONE 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /config/initializers/silence_warnings.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codetriage/CodeTriage/2f193418ca0b8757ca0a1159a2e7827ff5a02926/config/initializers/silence_warnings.rb -------------------------------------------------------------------------------- /config/initializers/the_lone_dyno.rb: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /config/initializers/time_formats.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | Time::DATE_FORMATS[:time_only_with_zone] = "%H:%M %Z" 4 | -------------------------------------------------------------------------------- /config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Be sure to restart your server when you modify this file. 4 | 5 | # This file contains settings for ActionController::ParamsWrapper which 6 | # is enabled by default. 7 | 8 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 9 | ActiveSupport.on_load(:action_controller) do 10 | wrap_parameters format: [:json] 11 | end 12 | 13 | # To enable root element in JSON for ActiveRecord objects. 14 | # ActiveSupport.on_load(:active_record) do 15 | # self.include_root_in_json = true 16 | # end 17 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # The following keys must be escaped otherwise they will not be retrieved by 20 | # the default I18n backend: 21 | # 22 | # true, false, on, off, yes, no 23 | # 24 | # Instead, surround them with single quotes. 25 | # 26 | # en: 27 | # 'true': 'foo' 28 | # 29 | # To learn more, please read the Rails Internationalization guide 30 | # available at http://guides.rubyonrails.org/i18n.html. 31 | 32 | en: 33 | repo_subscriptions: 34 | subscribed: "Awesome! You'll receive daily triage e-mails for this repository." 35 | -------------------------------------------------------------------------------- /config/puma.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Puma can serve each request in a thread from an internal thread pool. 4 | # The `threads` method setting takes two numbers: a minimum and maximum. 5 | # Any libraries that use thread pools should be configured to match 6 | # the maximum value specified for Puma. Default is set to 5 threads for minimum 7 | # and maximum; this matches the default thread size of Active Record. 8 | # 9 | threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } 10 | threads threads_count, threads_count 11 | 12 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000. 13 | # 14 | port ENV.fetch("PORT") { 3000 } 15 | 16 | # Allow puma to be restarted by `rails restart` command. 17 | plugin :tmp_restart 18 | 19 | before_fork do 20 | require "puma_worker_killer" 21 | 22 | PumaWorkerKiller.enable_rolling_restart # Default is every 6 hours 23 | end 24 | 25 | # on_worker_boot do 26 | # Barnes.start 27 | # end 28 | -------------------------------------------------------------------------------- /config/scout_apm.yml: -------------------------------------------------------------------------------- 1 | defaults: &defaults 2 | name: codetriage 3 | key: <%= ENV['SCOUT_KEY'] %> 4 | monitor: <%= true if ENV['SCOUT_MONITOR'] %> 5 | 6 | development: 7 | <<: *defaults 8 | test: 9 | <<: *defaults 10 | production: 11 | <<: *defaults 12 | -------------------------------------------------------------------------------- /config/secrets.yml: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key is used for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | 6 | # Make sure the secret is at least 30 characters and all random, 7 | # no regular words or you'll be exposed to dictionary attacks. 8 | # You can use `rails secret` to generate a secure secret key. 9 | 10 | # Make sure the secrets in this file are kept private 11 | # if you're sharing your code publicly. 12 | 13 | development: 14 | secret_key_base: 8a251689fe2c9dbbc9bae32031162df989752e2e178b745d1a2f90c1979272e5fbb38e5b43fb5629a40ede0b3fd8958b3bc8a13a38b332356b0974a3a8c843d2 15 | 16 | test: 17 | secret_key_base: 7ec058f05fe649308e5e566086efc18f11130f8196da8a64567347aa812d28a62be55b4dedd140aa7e086619dd7e40a85eb3b0ebc5c26e1483f17d8fd931e7a5 18 | 19 | # Do not keep production secrets in the repository, 20 | # instead read values from the environment. 21 | production: 22 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> 23 | -------------------------------------------------------------------------------- /config/storage.yml: -------------------------------------------------------------------------------- 1 | test: 2 | service: Disk 3 | root: <%= Rails.root.join("tmp/storage") %> 4 | 5 | local: 6 | service: Disk 7 | root: <%= Rails.root.join("storage") %> 8 | 9 | # Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) 10 | # amazon: 11 | # service: S3 12 | # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> 13 | # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> 14 | # region: us-east-1 15 | # bucket: your_own_bucket 16 | 17 | # Remember not to checkin your GCS keyfile to a repository 18 | # google: 19 | # service: GCS 20 | # project: your_project 21 | # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %> 22 | # bucket: your_own_bucket 23 | 24 | # Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key) 25 | # microsoft: 26 | # service: AzureStorage 27 | # storage_account_name: your_account_name 28 | # storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %> 29 | # container: your_container_name 30 | 31 | # mirror: 32 | # service: Mirror 33 | # primary: local 34 | # mirrors: [ amazon, google, microsoft ] 35 | -------------------------------------------------------------------------------- /db/migrate/20120222223509_devise_create_users.rb: -------------------------------------------------------------------------------- 1 | class DeviseCreateUsers < ActiveRecord::Migration[4.2] 2 | def change 3 | create_table(:users) do |t| 4 | ## Database authenticatable 5 | t.string :email, null: false, default: "" 6 | t.string :encrypted_password, null: false, default: "" 7 | 8 | ## Recoverable 9 | t.string :reset_password_token 10 | t.datetime :reset_password_sent_at 11 | 12 | ## Rememberable 13 | t.datetime :remember_created_at 14 | 15 | ## Trackable 16 | t.integer :sign_in_count, default: 0 17 | t.datetime :current_sign_in_at 18 | t.datetime :last_sign_in_at 19 | t.string :current_sign_in_ip 20 | t.string :last_sign_in_ip 21 | 22 | ## Encryptable 23 | # t.string :password_salt 24 | 25 | ## Confirmable 26 | # t.string :confirmation_token 27 | # t.datetime :confirmed_at 28 | # t.datetime :confirmation_sent_at 29 | # t.string :unconfirmed_email # Only if using reconfirmable 30 | 31 | ## Lockable 32 | # t.integer :failed_attempts, default: 0 # Only if lock strategy is :failed_attempts 33 | # t.string :unlock_token # Only if unlock strategy is :email or :both 34 | # t.datetime :locked_at 35 | 36 | ## Token authenticatable 37 | # t.string :authentication_token 38 | 39 | t.timestamps 40 | end 41 | 42 | add_index :users, :email, unique: true 43 | add_index :users, :reset_password_token, unique: true 44 | # add_index :users, :confirmation_token, unique: true 45 | # add_index :users, :unlock_token, unique: true 46 | # add_index :users, :authentication_token, unique: true 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /db/migrate/20120222231841_user_data.rb: -------------------------------------------------------------------------------- 1 | class UserData < ActiveRecord::Migration[4.2] 2 | def up 3 | add_column :users, :zip, :string 4 | add_column :users, :phone_number, :string 5 | add_column :users, :twitter, :boolean 6 | end 7 | 8 | def down 9 | remove_column :users, :phone_number 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20120518083745_create_issues.rb: -------------------------------------------------------------------------------- 1 | class CreateIssues < ActiveRecord::Migration[4.2] 2 | def change 3 | create_table :issues do |t| 4 | t.integer :comment_count 5 | t.string :url, :repo_name, :user_name 6 | t.datetime :last_touched_at 7 | t.integer :number 8 | 9 | t.timestamps 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20120518085406_create_repo_subscriptions.rb: -------------------------------------------------------------------------------- 1 | class CreateRepoSubscriptions < ActiveRecord::Migration[4.2] 2 | def change 3 | create_table :repo_subscriptions do |t| 4 | t.string :user_name 5 | t.string :repo_name 6 | t.timestamps 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20120518090213_create_issue_assignments.rb: -------------------------------------------------------------------------------- 1 | class CreateIssueAssignments < ActiveRecord::Migration[4.2] 2 | def change 3 | create_table :issue_assignments do |t| 4 | t.integer :user_id 5 | t.integer :issue_id 6 | 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20120518091140_create_repos.rb: -------------------------------------------------------------------------------- 1 | class CreateRepos < ActiveRecord::Migration[4.2] 2 | def change 3 | create_table :repos do |t| 4 | t.string "name" 5 | t.string "user_name" 6 | t.integer "issues_count", default: 0, null: false 7 | t.string "language" 8 | t.string "description" 9 | t.string "full_name" 10 | t.text "notes" 11 | 12 | t.timestamps 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /db/migrate/20120620000230_add_github_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddGithubToUsers < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :users, :github, :string 4 | add_column :users, :github_access_token, :string 5 | add_index :users, :github, unique: true 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20120622203236_add_repos_to_issues.rb: -------------------------------------------------------------------------------- 1 | class AddReposToIssues < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :issues, :repo_id, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20120622233057_issue_titles.rb: -------------------------------------------------------------------------------- 1 | class IssueTitles < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :issues, :title, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20120622235822_add_user_and_repo_to_repo_subscription.rb: -------------------------------------------------------------------------------- 1 | class AddUserAndRepoToRepoSubscription < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :repo_subscriptions, :user_id, :integer 4 | add_column :repo_subscriptions, :repo_id, :integer 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20120624204958_add_last_sent_at_to_repo_sub.rb: -------------------------------------------------------------------------------- 1 | class AddLastSentAtToRepoSub < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :repo_subscriptions, :last_sent_at, :datetime 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20120624212352_add_html_url_to_issues.rb: -------------------------------------------------------------------------------- 1 | class AddHtmlUrlToIssues < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :issues, :html_url, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20120624213335_add_status_to_issues.rb: -------------------------------------------------------------------------------- 1 | class AddStatusToIssues < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :issues, :state, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20120627213051_add_repo_sub_to_issue_assignment.rb: -------------------------------------------------------------------------------- 1 | class AddRepoSubToIssueAssignment < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :issue_assignments, :repo_subscription_id, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20120707182259_add_admin_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddAdminToUsers < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :users, :admin, :boolean 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20121106072214_repo_issue_counter_cache.rb: -------------------------------------------------------------------------------- 1 | class RepoIssueCounterCache < ActiveRecord::Migration[4.2] 2 | def up 3 | unless column_exists?(:repos, :issues_count) 4 | add_column :repos, :issues_count, :integer, default: 0, null: false 5 | end 6 | 7 | Repo.reset_column_information 8 | Repo.find_each do |repo| 9 | Repo.update_counters(repo.id, issues_count: repo.issues.where(state: "open").count) 10 | end 11 | end 12 | 13 | def down 14 | remove_column :repos, :issues_count 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /db/migrate/20121110213717_add_avatar_url_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddAvatarUrlToUsers < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :users, :avatar_url, :string, default: "http://gravatar.com/avatar/default" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20121112100015_add_name_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddNameToUsers < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :users, :name, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20121120203919_add_language_and_description_to_repo.rb: -------------------------------------------------------------------------------- 1 | class AddLanguageAndDescriptionToRepo < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :repos, :language, :string unless column_exists?(:repos, :language) 4 | add_column :repos, :description, :string unless column_exists?(:repos, :description) 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20121127163516_add_privacy_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddPrivacyToUsers < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :users, :private, :boolean 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20121127171308_add_full_name_to_repos.rb: -------------------------------------------------------------------------------- 1 | class AddFullNameToRepos < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :repos, :full_name, :string unless column_exists?(:repos, :full_name) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20121128162942_add_notes_to_repo.rb: -------------------------------------------------------------------------------- 1 | class AddNotesToRepo < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :repos, :notes, :text unless column_exists?(:repos, :notes) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20130222201747_add_favorite_languages_to_user.rb: -------------------------------------------------------------------------------- 1 | class AddFavoriteLanguagesToUser < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :users, :favorite_languages, :string, array: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20130312023533_add_email_limit_to_repo_subscriptions.rb: -------------------------------------------------------------------------------- 1 | class AddEmailLimitToRepoSubscriptions < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :repo_subscriptions, :email_limit, :integer, default: 1 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20130503183402_add_daily_issue_limit_to_user.rb: -------------------------------------------------------------------------------- 1 | class AddDailyIssueLimitToUser < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :users, :daily_issue_limit, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20130803144944_update_private_attribute_of_user.rb: -------------------------------------------------------------------------------- 1 | class UpdatePrivateAttributeOfUser < ActiveRecord::Migration[4.2] 2 | def change 3 | change_column :users, :private, :boolean, default: false 4 | # Update existing users. Set :private to false if it is nil 5 | User.where(private: nil).find_each do |u| 6 | u.private = false 7 | u.save! 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20130918055659_add_pr_attached_to_issues.rb: -------------------------------------------------------------------------------- 1 | class AddPrAttachedToIssues < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :issues, :pr_attached, :boolean, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20130918060600_add_skip_issues_with_pr_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddSkipIssuesWithPrToUsers < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :users, :skip_issues_with_pr, :boolean, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20131107042958_add_delete_token_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddDeleteTokenToUsers < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :users, :account_delete_token, :string 4 | add_index :users, :account_delete_token 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20140524120051_add_click_to_issue_assignment.rb: -------------------------------------------------------------------------------- 1 | class AddClickToIssueAssignment < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :issue_assignments, :clicked, :boolean, default: false 4 | add_column :users, :last_clicked_at, :timestamp 5 | User.find_each(&:save) 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20140621155109_add_delievered_to_issue_assignment.rb: -------------------------------------------------------------------------------- 1 | class AddDelieveredToIssueAssignment < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :issue_assignments, :delivered, :boolean, {default: false} 4 | # add_index :users, :github, unique: true 5 | add_index :issue_assignments, :delivered 6 | 7 | ::IssueAssignment.find_each do |assignment| 8 | assignment.delivered = true if assignment.respond_to?(:delivered) 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /db/migrate/20140710161559_store_github_request_errors.rb: -------------------------------------------------------------------------------- 1 | class StoreGithubRequestErrors < ActiveRecord::Migration[4.2] 2 | def change 3 | add_column :repos, :github_error_msg, :text 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20140710164307_remove_user_id_from_issue_assignments.rb: -------------------------------------------------------------------------------- 1 | class RemoveUserIdFromIssueAssignments < ActiveRecord::Migration[4.2] 2 | def change 3 | remove_column :issue_assignments, :user_id 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20150629002651_remove_repo_name_from_repo_subscriptions.rb: -------------------------------------------------------------------------------- 1 | class RemoveRepoNameFromRepoSubscriptions < ActiveRecord::Migration[4.2] 2 | def change 3 | remove_column :repo_subscriptions, :repo_name 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20150629010617_remove_user_name_from_repo_subscriptions.rb: -------------------------------------------------------------------------------- 1 | class RemoveUserNameFromRepoSubscriptions < ActiveRecord::Migration[4.2] 2 | def change 3 | remove_column :repo_subscriptions, :user_name 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20151007212330_create_data_dumps.rb: -------------------------------------------------------------------------------- 1 | class CreateDataDumps < ActiveRecord::Migration[4.2] 2 | def change 3 | create_table :data_dumps do |t| 4 | t.text :data 5 | t.timestamps 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20151021160718_add_index_to_issues.rb: -------------------------------------------------------------------------------- 1 | class AddIndexToIssues < ActiveRecord::Migration[4.2] 2 | def change 3 | add_index :issues, :state 4 | add_index :issues, :number 5 | add_index :issues, :repo_id 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20151214120117_enable_pg_stat_extension.rb: -------------------------------------------------------------------------------- 1 | class EnablePgStatExtension < ActiveRecord::Migration[4.2] 2 | def change 3 | execute <<-SQL 4 | CREATE extension IF NOT EXISTS "pg_stat_statements" 5 | SQL 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20160404140129_add_click_to_doc_assignments.rb: -------------------------------------------------------------------------------- 1 | class AddClickToDocAssignments < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :doc_assignments, :clicked, :boolean, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160404173429_remove_user_from_doc_assignments.rb: -------------------------------------------------------------------------------- 1 | class RemoveUserFromDocAssignments < ActiveRecord::Migration[5.0] 2 | def change 3 | remove_column :doc_assignments, :user_id 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160606060546_add_email_frequency_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddEmailFrequencyToUsers < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :users, :email_frequency, :string, default: "daily" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160613220116_change_issues_title_to_text.rb: -------------------------------------------------------------------------------- 1 | class ChangeIssuesTitleToText < ActiveRecord::Migration[5.0] 2 | def up 3 | change_column :issues, :title, :text 4 | end 5 | 6 | def down 7 | change_column :issues, :title, :string, limit: 255 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20160827023209_add_stars_count_to_repos.rb: -------------------------------------------------------------------------------- 1 | class AddStarsCountToRepos < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :repos, :stars_count, :integer, default: 0 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160827073041_update_stars_count_of_repos.rb: -------------------------------------------------------------------------------- 1 | class UpdateStarsCountOfRepos < ActiveRecord::Migration[5.0] 2 | def change 3 | Repo.where(stars_count: nil).find_each do |repo| 4 | repo_path = File.join "repos", "#{repo.user_name}/#{repo.name}" 5 | resp = GitHubBub.get(repo_path).json_body 6 | repo.stars_count = resp["stargazers_count"] 7 | repo.save! 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20160927195642_add_index_to_repo_subscription_id.rb: -------------------------------------------------------------------------------- 1 | class AddIndexToRepoSubscriptionId < ActiveRecord::Migration[5.0] 2 | def change 3 | add_index :issue_assignments, :repo_subscription_id 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20170203143323_add_email_time_of_day_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddEmailTimeOfDayToUsers < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :users, :email_time_of_day, :time 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20170516193426_repos_name_index.rb: -------------------------------------------------------------------------------- 1 | class ReposNameIndex < ActiveRecord::Migration[5.0] 2 | def change 3 | add_index :repos, :user_name 4 | add_index :repos, :name 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20170523195422_add_language_index.rb: -------------------------------------------------------------------------------- 1 | class AddLanguageIndex < ActiveRecord::Migration[5.0] 2 | def change 3 | add_index :repos, :language 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20170604190958_add_old_token_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddOldTokenToUsers < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :users, :old_token, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20170619152237_add_index_to_issues_count.rb: -------------------------------------------------------------------------------- 1 | class AddIndexToIssuesCount < ActiveRecord::Migration[5.1] 2 | def change 3 | add_index :repos, :issues_count 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20170622201545_add_created_at_index_to_issues.rb: -------------------------------------------------------------------------------- 1 | class AddCreatedAtIndexToIssues < ActiveRecord::Migration[5.1] 2 | def change 3 | add_index :issues, :created_at, where: "state = '#{Issue::OPEN}'" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20170626162701_make_multi_index.rb: -------------------------------------------------------------------------------- 1 | class MakeMultiIndex < ActiveRecord::Migration[5.1] 2 | def change 3 | remove_index(:issues, :created_at) if index_exists?(:issues, :created_at) 4 | add_index(:issues, [:repo_id, :created_at], where: "state = '#{Issue::OPEN}'") 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20170627024417_add_index_to_doc_method.rb: -------------------------------------------------------------------------------- 1 | class AddIndexToDocMethod < ActiveRecord::Migration[5.1] 2 | def change 3 | add_index(:doc_methods, [:repo_id, :created_at]) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20170629151616_add_unique_index_to_repos.rb: -------------------------------------------------------------------------------- 1 | class AddUniqueIndexToRepos < ActiveRecord::Migration[5.1] 2 | def change 3 | add_index :repos, [:name, :user_name], unique: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20170629230855_index_full_name_of_repos.rb: -------------------------------------------------------------------------------- 1 | class IndexFullNameOfRepos < ActiveRecord::Migration[5.1] 2 | def change 3 | add_index(:repos, [:full_name]) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20170629233102_add_foreign_key_to_repo_subscriptions.rb: -------------------------------------------------------------------------------- 1 | class AddForeignKeyToRepoSubscriptions < ActiveRecord::Migration[5.1] 2 | def change 3 | add_foreign_key :repo_subscriptions, :repos 4 | add_index(:repo_subscriptions, :repo_id) 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20170630002100_add_foreign_key_for_repo_users.rb: -------------------------------------------------------------------------------- 1 | class AddForeignKeyForRepoUsers < ActiveRecord::Migration[5.1] 2 | def change 3 | add_foreign_key :repo_subscriptions, :users 4 | add_index(:repo_subscriptions, :user_id) 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20170630191148_add_index_to_issue_on_repo_id_and_number.rb: -------------------------------------------------------------------------------- 1 | class AddIndexToIssueOnRepoIdAndNumber < ActiveRecord::Migration[5.1] 2 | def change 3 | add_index(:issues, [:repo_id, :number]) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20170630192013_add_index_to_doc_method_creation.rb: -------------------------------------------------------------------------------- 1 | class AddIndexToDocMethodCreation < ActiveRecord::Migration[5.1] 2 | def change 3 | add_index(:doc_methods, [:repo_id, :name, :path]) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20170801021151_default_value_for_max_issues.rb: -------------------------------------------------------------------------------- 1 | class DefaultValueForMaxIssues < ActiveRecord::Migration[5.1] 2 | def change 3 | change_column_default :users, :daily_issue_limit, 50 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20171125012313_add_partial_index_to_id.rb: -------------------------------------------------------------------------------- 1 | class AddPartialIndexToId < ActiveRecord::Migration[5.1] 2 | def change 3 | add_index(:issues, :id, where: "state = '#{Issue::OPEN}'") 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20171125013100_remove_partial_index_to_id.rb: -------------------------------------------------------------------------------- 1 | class RemovePartialIndexToId < ActiveRecord::Migration[5.1] 2 | def change 3 | remove_index(:issues, name: "index_issues_on_repo_id_and_created_at") 4 | remove_index(:issues, name: "index_issues_on_id") 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20171125013319_add_index_for_pagination.rb: -------------------------------------------------------------------------------- 1 | class AddIndexForPagination < ActiveRecord::Migration[5.1] 2 | def change 3 | add_index(:issues, [:repo_id, :id], where: "state = '#{Issue::OPEN}'") 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20171125202301_fix_doc_methods_indexes.rb: -------------------------------------------------------------------------------- 1 | class FixDocMethodsIndexes < ActiveRecord::Migration[5.1] 2 | def change 3 | remove_index(:doc_methods, name: "index_doc_methods_on_repo_id_and_created_at") 4 | add_index(:doc_methods, [:repo_id, :id]) 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20171125212307_add_subscribers_count_to_repos.rb: -------------------------------------------------------------------------------- 1 | class AddSubscribersCountToRepos < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :repos, :subscribers_count, :integer, default: 0 4 | Repo.reset_column_information 5 | Repo.find_each do |p| 6 | Repo.reset_counters p.id, :subscribers_count 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20180716144937_add_streak_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddStreakToUsers < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :users, :raw_streak_count, :integer, default: 0 4 | add_column :users, :raw_emails_since_click, :integer, default: 0 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20180820155654_add_last_email_at_to_user.rb: -------------------------------------------------------------------------------- 1 | class AddLastEmailAtToUser < ActiveRecord::Migration[5.2] 2 | def change 3 | add_column :users, :last_email_at, :datetime 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20190322102229_add_foreign_key_constraint_to_active_storage_attachments_for_blob_id.active_storage.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from active_storage (originally 20180723000244) 2 | class AddForeignKeyConstraintToActiveStorageAttachmentsForBlobId < ActiveRecord::Migration[6.0] 3 | def up 4 | return if foreign_key_exists?(:active_storage_attachments, column: :blob_id) 5 | 6 | if table_exists?(:active_storage_blobs) 7 | add_foreign_key :active_storage_attachments, :active_storage_blobs, column: :blob_id 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20191010023214_add_docs_subscriber_count_to_repos.rb: -------------------------------------------------------------------------------- 1 | class AddDocsSubscriberCountToRepos < ActiveRecord::Migration[6.0] 2 | def change 3 | add_column :repos, :docs_subscriber_count, :integer, default: 0 4 | add_index :repo_subscriptions, :read 5 | add_index :repo_subscriptions, :write 6 | add_index :repo_subscriptions, :repo_id 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20191014201819_drop_unused_indexes.rb: -------------------------------------------------------------------------------- 1 | class DropUnusedIndexes < ActiveRecord::Migration[6.0] 2 | def change 3 | # public.issues | index_issues_on_number | 198 MB | 0 4 | # public.issues | index_issues_on_repo_id | 1536 MB | 0 5 | # public.issue_assignments | index_issue_assignments_on_repo_subscription_id_and_created_at | 370 MB | 0 6 | remove_index(:issues, name: "index_issues_on_number") 7 | remove_index(:issues, name: "index_issues_on_repo_id") 8 | remove_index(:issue_assignments, name: "index_issue_assignments_on_repo_subscription_id_and_created_at") 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /db/migrate/20191017202913_add_removed_from_github_to_repo.rb: -------------------------------------------------------------------------------- 1 | class AddRemovedFromGithubToRepo < ActiveRecord::Migration[6.0] 2 | def change 3 | add_column :repos, :removed_from_github, :boolean, default: false 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20191025030201_add_unique_index_number_repo_id.rb: -------------------------------------------------------------------------------- 1 | class AddUniqueIndexNumberRepoId < ActiveRecord::Migration[6.0] 2 | def change 3 | add_index :issues, [:number, :repo_id], unique: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20191025191255_add_updated_at_index_to_issues.rb: -------------------------------------------------------------------------------- 1 | class AddUpdatedAtIndexToIssues < ActiveRecord::Migration[6.0] 2 | def change 3 | add_index :issues, :updated_at, where: "state = '#{Issue::OPEN}'" 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20191029194525_make_timestamps_not_nullable.rb: -------------------------------------------------------------------------------- 1 | class MakeTimestampsNotNullable < ActiveRecord::Migration[6.0] 2 | def change 3 | %W[issues repos users doc_methods doc_classes repos repo_subscriptions issue_assignments].each do |table_name| 4 | change_column_null table_name, :created_at, false, "NOW()" 5 | change_column_null table_name, :updated_at, false, "NOW()" 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20191031142027_add_comment_to_doc_method.rb: -------------------------------------------------------------------------------- 1 | class AddCommentToDocMethod < ActiveRecord::Migration[6.0] 2 | def change 3 | add_column :doc_methods, :has_comment, :boolean, index: true 4 | add_column :doc_methods, :comment, :text 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20191031144636_add_unique_indexes_to_doc_methods.rb: -------------------------------------------------------------------------------- 1 | class AddUniqueIndexesToDocMethods < ActiveRecord::Migration[6.0] 2 | disable_ddl_transaction! 3 | 4 | def change 5 | remove_index :doc_methods, [:repo_id, :name, :path] 6 | add_index :doc_methods, [:repo_id, :name, :path], unique: true, algorithm: :concurrently 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20201115123024_add_service_name_to_active_storage_blobs.active_storage.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from active_storage (originally 20190112182829) 2 | class AddServiceNameToActiveStorageBlobs < ActiveRecord::Migration[6.0] 3 | def up 4 | return unless table_exists?(:active_storage_blobs) 5 | 6 | unless column_exists?(:active_storage_blobs, :service_name) 7 | add_column :active_storage_blobs, :service_name, :string 8 | 9 | configured_service = ActiveStorage::Blob.service.name 10 | if configured_service 11 | ActiveStorage::Blob.unscoped.update_all(service_name: configured_service) 12 | end 13 | 14 | change_column :active_storage_blobs, :service_name, :string, null: false 15 | end 16 | end 17 | 18 | def down 19 | remove_column :active_storage_blobs, :service_name 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /db/migrate/20201115123025_create_active_storage_variant_records.active_storage.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from active_storage (originally 20191206030411) 2 | class CreateActiveStorageVariantRecords < ActiveRecord::Migration[6.0] 3 | def change 4 | return unless table_exists?(:active_storage_blobs) 5 | 6 | create_table :active_storage_variant_records do |t| 7 | t.belongs_to :blob, null: false, index: false 8 | t.string :variation_digest, null: false 9 | 10 | t.index %i[blob_id variation_digest], name: "index_active_storage_variant_records_uniqueness", unique: true 11 | t.foreign_key :active_storage_blobs, column: :blob_id 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /db/migrate/20220908011235_add_htos_promo_to_user.rb: -------------------------------------------------------------------------------- 1 | class AddHtosPromoToUser < ActiveRecord::Migration[6.1] 2 | def change 3 | add_column :users, :htos_contributor_unsubscribe, :boolean, default: false, null: false 4 | add_column :users, :htos_contributor_bought, :boolean, default: false, null: false 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20221004010742_create_labels_table.rb: -------------------------------------------------------------------------------- 1 | class CreateLabelsTable < ActiveRecord::Migration[6.1] 2 | def change 3 | create_table :labels do |t| 4 | t.string :name, null: false 5 | 6 | t.timestamps 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20221004010749_create_repo_labels_table.rb: -------------------------------------------------------------------------------- 1 | class CreateRepoLabelsTable < ActiveRecord::Migration[6.1] 2 | def change 3 | create_table :repo_labels do |t| 4 | t.references :repo, foreign_key: true, null: false 5 | t.references :label, foreign_key: true, null: false 6 | 7 | t.timestamps 8 | end 9 | 10 | add_index :repo_labels, [:repo_id, :label_id], unique: true 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20221010011048_remove_not_null_on_active_storage_blobs_checksum.active_storage.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from active_storage (originally 20211119233751) 2 | class RemoveNotNullOnActiveStorageBlobsChecksum < ActiveRecord::Migration[6.0] 3 | def change 4 | return unless table_exists?(:active_storage_blobs) 5 | 6 | change_column_null(:active_storage_blobs, :checksum, true) 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /db/migrate/20221013212959_add_archived_column_to_repo.rb: -------------------------------------------------------------------------------- 1 | class AddArchivedColumnToRepo < ActiveRecord::Migration[7.0] 2 | def change 3 | add_column :repos, :archived, :boolean, default: false 4 | add_index :repos, :archived 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20221228065556_add_index_subscriber_count.rb: -------------------------------------------------------------------------------- 1 | class AddIndexSubscriberCount < ActiveRecord::Migration[7.0] 2 | def change 3 | add_index :repos, :subscribers_count 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /doc/README_FOR_APP: -------------------------------------------------------------------------------- 1 | Use this README file to introduce your application and point to useful places in the API for learning more. 2 | Run "rake doc:app" to generate API documentation for your models, controllers, helpers, and libraries. 3 | -------------------------------------------------------------------------------- /heroku.yml: -------------------------------------------------------------------------------- 1 | build: 2 | buildpacks: 3 | - heroku/metrics 4 | - https://github.com/mojodna/heroku-buildpack-jemalloc.git#v3.6.0 5 | - heroku/nodejs 6 | - heroku/ruby 7 | -------------------------------------------------------------------------------- /lib/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codetriage/CodeTriage/2f193418ca0b8757ca0a1159a2e7827ff5a02926/lib/assets/.gitkeep -------------------------------------------------------------------------------- /lib/dependency_parser/php/parse.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "json" 4 | 5 | module DependencyParser 6 | module PHP 7 | class Parse 8 | attr_reader :errors 9 | 10 | def initialize(content) 11 | @content = content 12 | @errors = [] 13 | @direct = [] 14 | end 15 | 16 | def call 17 | deps = JSON.parse(content, symbolize_names: true) 18 | @direct = deps[:packages].map do |package| 19 | {name: package[:name], url: package.dig(:source, :url), description: package[:description]} 20 | end 21 | @direct += deps[:"packages-dev"].map do |package| 22 | {name: package[:name], url: package.dig(:source, :url), description: package[:description]} 23 | end 24 | rescue 25 | @errors << "Cannot parse lock file" 26 | end 27 | 28 | def direct 29 | {repos: @direct, language: "php"} 30 | end 31 | 32 | def success? 33 | errors.empty? 34 | end 35 | 36 | def error? 37 | errors.any? 38 | end 39 | 40 | private 41 | 42 | attr_reader :content 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /lib/docs_doctor/doc_obj.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codetriage/CodeTriage/2f193418ca0b8757ca0a1159a2e7827ff5a02926/lib/docs_doctor/doc_obj.rb -------------------------------------------------------------------------------- /lib/docs_doctor/loader.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # loads parsers for different languages and tools 4 | module DocsDoctor 5 | class Loader 6 | attr_accessor :name, :version, :lang 7 | 8 | def initialize(parser_lang_name, version) 9 | @lang, @name = parser_lang_name.to_s.downcase.split("/") 10 | @version = Gem::Version.new version 11 | load_parser 12 | end 13 | 14 | def parser 15 | require path 16 | constant.new 17 | end 18 | 19 | def constant 20 | Kernel.const_get "DocsDoctor::Parsers::#{lang.capitalize}::#{name.capitalize}_#{pretty_version}" 21 | end 22 | 23 | # parsers/ruby/rdoc.rb 24 | def path 25 | [__dir__, "parsers", lang, name_extension].join("/") 26 | end 27 | 28 | def load_parser 29 | case lang 30 | when "ruby" 31 | Ruby.new(name, version).load 32 | else 33 | raise "looks like parser for language #{lang} is not yet implemented" 34 | end 35 | end 36 | 37 | private 38 | 39 | def name_extension 40 | name + dot_extension 41 | end 42 | 43 | def dot_extension 44 | @extension = extension 45 | @extension.prepend(".rb") unless @extension.include?(".") 46 | @extension 47 | end 48 | 49 | def pretty_version 50 | version.to_s.tr(".", "_") 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /lib/docs_doctor/parser.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module DocsDoctor 4 | class Parser 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /lib/docs_doctor/parsers/ruby/require 'atomic': -------------------------------------------------------------------------------- 1 | require 'atomic' 2 | 3 | array = Atomic.new([]) 4 | 5 | threads = [] 6 | def insert_into_array(array, value, threads) 7 | threads << Thread.new do 8 | array.update{|v| 9 | puts "== Running Update" 10 | v << value 11 | } 12 | end 13 | end 14 | 15 | 16 | insert_into_array(array, 1, threads) 17 | insert_into_array(array, 2, threads) 18 | insert_into_array(array, 3, threads) 19 | 20 | threads.map(&:join) 21 | 22 | puts array.value -------------------------------------------------------------------------------- /lib/docs_doctor/runner.rb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codetriage/CodeTriage/2f193418ca0b8757ca0a1159a2e7827ff5a02926/lib/docs_doctor/runner.rb -------------------------------------------------------------------------------- /lib/docs_doctor/task.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | namespace :docs do 4 | task :play do 5 | parser = DocsDoctor::Runner.new("ruby/rdoc", "4.0.0").parser 6 | puts parser.inspect 7 | parser.add_files("test/fixtures/ruby/string/strip.rb") 8 | parser.go! 9 | end 10 | end 11 | 12 | # parser = DocsDoctor::Parser.new('ruby/rdoc', '4.0.0') 13 | # parser.add_files("/Users/schneems/Documents/projects/rails/activesupport/lib/active_support/core_ext/string/strip.rb") 14 | # parser.go! 15 | 16 | # parser.files.each do |file| 17 | # file.classes.each do |klass| 18 | # klass.methods.each do |method| 19 | 20 | # end 21 | # end 22 | # end 23 | -------------------------------------------------------------------------------- /lib/sorted_repo_collection.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class SortedRepoCollection 4 | include Enumerable 5 | 6 | def initialize(repos) 7 | @repos = repos.sort_by { |r| r.fetch("full_name").downcase } 8 | end 9 | 10 | def each 11 | @repos.each { |r| yield r } 12 | end 13 | 14 | def size 15 | @repos.length 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/tasks/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codetriage/CodeTriage/2f193418ca0b8757ca0a1159a2e7827ff5a02926/lib/tasks/.gitkeep -------------------------------------------------------------------------------- /lib/tasks/performance.rake: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codetriage/CodeTriage/2f193418ca0b8757ca0a1159a2e7827ff5a02926/lib/tasks/performance.rake -------------------------------------------------------------------------------- /lib/tasks/test.rake: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # namespace 'test' do 4 | # desc "Run the javascript test" 5 | # task javascript: :environment do 6 | # require "teaspoon/console" 7 | # puts "\n\n===== Starting Javascript Test =====\n\n" 8 | # fail if Teaspoon::Console.new({suite: ENV["suite"]}).execute 9 | # puts "===== Javascript Test Complete =====\n\n\n" 10 | # end 11 | # end 12 | 13 | # Rake::Task[:test].enhance(['test:javascript']) 14 | -------------------------------------------------------------------------------- /log/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codetriage/CodeTriage/2f193418ca0b8757ca0a1159a2e7827ff5a02926/log/.gitkeep -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /perf.rake: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "bundler" 4 | Bundler.setup 5 | 6 | require "derailed_benchmarks" 7 | require "derailed_benchmarks/tasks" 8 | 9 | ENV["RAILS_LOG_TO_STDOUT"] ||= "1" 10 | ENV["RAILS_SERVE_STATIC_FILES"] ||= "1" 11 | ENV["SECRET_KEY_BASE"] ||= "lol" 12 | ENV["DEVISE_SECRET_KEY"] ||= "lol" 13 | -------------------------------------------------------------------------------- /perf_scripts/cache_version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "active_record" 4 | 5 | ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:") 6 | ActiveRecord::Base.cache_versioning = true 7 | 8 | ActiveRecord::Schema.define do 9 | create_table :users, force: true do |t| 10 | t.string :name 11 | t.timestamps null: false 12 | end 13 | 14 | create_table :topics, force: true do |t| 15 | t.string :title 16 | t.integer :user_id 17 | t.timestamps null: false 18 | end 19 | end 20 | 21 | class Topic < ActiveRecord::Base 22 | belongs_to :user 23 | end 24 | 25 | class User < ActiveRecord::Base 26 | has_many :topics 27 | end 28 | 29 | mass_user_create = 1000.times.each.map do 30 | {name: "user"} 31 | end 32 | 33 | User.create(mass_user_create) 34 | 35 | ALL_USERS = User.all 36 | 37 | puts ALL_USERS.first.method(:cache_version).source_location 38 | 39 | Benchmark.bm { |x| 40 | x.report("title_was") { 41 | ALL_USERS.each do |user| 42 | user.cache_version 43 | end 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /perf_scripts/dirty_tracking.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "active_record" 4 | 5 | ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:") 6 | 7 | ActiveRecord::Schema.define do 8 | create_table :users, force: true do |t| 9 | t.string :name 10 | t.timestamps null: false 11 | end 12 | 13 | create_table :topics, force: true do |t| 14 | t.string :title 15 | t.integer :user_id 16 | t.timestamps null: false 17 | end 18 | end 19 | 20 | class Topic < ActiveRecord::Base 21 | belongs_to :user 22 | end 23 | 24 | class User < ActiveRecord::Base 25 | has_many :topics 26 | end 27 | 28 | user = User.create!(name: "user name") 29 | topic = user.topics.build(title: "topic title") 30 | 31 | # Benchmark.ips do |x| 32 | # x.report("changed?") { topic.changed? } 33 | # x.report("changed") { topic.changed } 34 | # x.report("changes") { topic.changes } 35 | # x.report("changed_attributes") { topic.changed_attributes } 36 | # x.report("title_change") { topic.title_change } 37 | # x.report("title_was") { topic.title_was } 38 | # end 39 | 40 | Benchmark.bm { |x| 41 | x.report("title_was") { 42 | 1_000_000.times { 43 | topic.title_was 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /perf_scripts/partial_cache.rb: -------------------------------------------------------------------------------- 1 | require "benchmark/ips" 2 | require "action_view" 3 | require "action_pack" 4 | require "action_controller" 5 | require "active_record" 6 | require "stackprof" 7 | 8 | ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:") 9 | 10 | ActiveRecord::Schema.define do 11 | create_table :customers, force: true do |t| 12 | t.string :name 13 | end 14 | end 15 | 16 | class Customer < ActiveRecord::Base; end 17 | 18 | class TestController < ActionController::Base 19 | end 20 | 21 | ActionView::PartialRenderer.collection_cache = ActiveSupport::Cache::MemoryStore.new 22 | TestController.view_paths = [File.expand_path("perf_scripts/partial_cache")] 23 | 24 | puts TestController.view_paths 25 | 26 | # Create a bunch of data 27 | 28 | letters = ("a".."z").to_a * 5 29 | 1000.times { Customer.create!(name: letters.sample(10).join) } 30 | 31 | customers = Customer.all.to_a 32 | 33 | controller_view = TestController.new.view_context 34 | 35 | # Heat compilation cache and collection cache 36 | controller_view.render("render_collection", customers: customers) 37 | controller_view.render("render_collection_with_cache", customers: customers) 38 | 39 | p ActionView::PartialRenderer.collection_cache # 1000 entries 40 | 41 | Benchmark.ips do |x| 42 | x.report("collection render: no cache") do 43 | controller_view.render("render_collection", customers: customers) 44 | end 45 | 46 | x.report("collection render: with cache") do 47 | controller_view.render("render_collection_with_cache", customers: customers) 48 | end 49 | 50 | x.compare! 51 | end 52 | -------------------------------------------------------------------------------- /perf_scripts/partial_cache/test/_customer.html.erb: -------------------------------------------------------------------------------- 1 | _customer.html.erb 2 |
  • Hello: <%= customer.name %>
  • 3 | -------------------------------------------------------------------------------- /perf_scripts/partial_cache/test/_render_collection.html.erb: -------------------------------------------------------------------------------- 1 | _render_collection.html.erb 2 | 5 | -------------------------------------------------------------------------------- /perf_scripts/partial_cache/test/_render_collection_with_cache.html.erb: -------------------------------------------------------------------------------- 1 | _render_collection_with_cache.html.erb 2 | 5 | -------------------------------------------------------------------------------- /perf_scripts/respond_to.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "active_record" 4 | 5 | ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:") 6 | 7 | ActiveRecord::Schema.define do 8 | create_table :users, force: true do |t| 9 | t.string :name, :email, :username, :password, :birthday, :foo, :bar, :baz, :dog_name 10 | t.timestamps null: false 11 | end 12 | 13 | create_table :topics, force: true do |t| 14 | t.string :title 15 | t.integer :user_id 16 | t.timestamps null: false 17 | end 18 | end 19 | 20 | class Topic < ActiveRecord::Base 21 | belongs_to :user 22 | end 23 | 24 | class User < ActiveRecord::Base 25 | has_many :topics 26 | end 27 | 28 | user = User.create!(name: "user name", dog_name: "cinco") 29 | user.respond_to?(:name) 30 | user.topics.build(title: "topic title") 31 | 32 | # Benchmark.ips do |x| 33 | # x.report("changed?") { topic.changed? } 34 | # x.report("changed") { topic.changed } 35 | # x.report("changes") { topic.changes } 36 | # x.report("changed_attributes") { topic.changed_attributes } 37 | # x.report("title_change") { topic.title_change } 38 | # x.report("title_was") { topic.title_was } 39 | # end 40 | 41 | Benchmark.bm { |x| 42 | x.report("respond_to") { 43 | 1_000_000.times { 44 | user.respond_to?(:dog_name) 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 404 Not Found 5 | 46 | 47 | 48 | 49 | 50 | 51 | 52 |
    53 |

    CodeTriage

    54 |

    404 Not Found

    55 |

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

    56 | 57 | Home | 58 | Repos 59 | 60 |
    61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 17 | 18 | 19 | 20 | 21 |
    22 |

    The change you wanted was rejected.

    23 |

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

    24 |
    25 | 26 | 27 | -------------------------------------------------------------------------------- /public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 17 | 18 | 19 | 20 | 21 |
    22 |

    We're sorry, but something went wrong.

    23 |
    24 | 25 | 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/wc/norobots.html for documentation on how to use the robots.txt file 2 | # 3 | # To ban all spiders from the entire site uncomment the next two lines: 4 | # User-Agent: * 5 | # Disallow: / 6 | -------------------------------------------------------------------------------- /test/dependency_parsers/php_parser_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | require_relative "../../lib/dependency_parser/php/parse" 5 | 6 | class PHPParserTest < ActiveSupport::TestCase 7 | test "returns nothing on invalid composer.lock" do 8 | parser = DependencyParser::PHP::Parse.new("invalid") 9 | parser.call 10 | refute parser.success? 11 | assert_equal({repos: [], language: "php"}, parser.direct) 12 | end 13 | 14 | test "returns the list of dependencies (with dev dependencies) for a small composer.lock file" do 15 | gemfile_lock = file_fixture("composer.lock").read 16 | 17 | parser = DependencyParser::PHP::Parse.new(gemfile_lock) 18 | parser.call 19 | 20 | composer_data = { 21 | repos: [ 22 | { 23 | name: "doctrine/annotations", 24 | url: "https://github.com/doctrine/annotations.git", 25 | description: "Docblock Annotations Parser" 26 | }, 27 | { 28 | name: "doctrine/cache", 29 | url: "https://github.com/doctrine/cache.git", 30 | description: "PHP Doctrine Cache library is a popular cache implementation that supports many different drivers such as redis, memcache, apc, mongodb and others." 31 | }, 32 | { 33 | name: "phpunit/php-code-coverage", 34 | url: "https://github.com/sebastianbergmann/php-code-coverage.git", 35 | description: "Library that provides collection, processing, and rendering functionality for PHP code coverage information." 36 | } 37 | ], 38 | language: "php" 39 | } 40 | 41 | assert parser.success? 42 | assert_equal composer_data, parser.direct 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /test/dependency_parsers/ruby_parser_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | require_relative "../../lib/dependency_parser/ruby/parse" 5 | 6 | class RubyParserTest < ActiveSupport::TestCase 7 | test "returns nothing on invalid Gemfile.lock" do 8 | parser = DependencyParser::Ruby::Parse.new("invalid") 9 | parser.call 10 | refute parser.success? 11 | assert_equal({repos: [], language: "ruby"}, parser.direct) 12 | end 13 | 14 | test "returns the list of gems for a small Gemfle" do 15 | gemfile_lock = <<~LOCKFILE 16 | GEM 17 | remote: https://rubygems.org/ 18 | specs: 19 | solid_use_case (2.2.0) 20 | actionpack (= 6.1.4.1) 21 | activesupport (= 6.1.4.1) 22 | nio4r (~> 2.0) 23 | websocket-driver (>= 0.6.1) 24 | 25 | PLATFORMS 26 | ruby 27 | 28 | DEPENDENCIES 29 | actioncable 30 | 31 | RUBY VERSION 32 | ruby 3.0.4p208 33 | 34 | BUNDLED WITH 35 | 2.3.21 36 | 37 | LOCKFILE 38 | 39 | parser = DependencyParser::Ruby::Parse.new(gemfile_lock) 40 | VCR.use_cassette("dependency_parser/rubygems") do 41 | parser.call 42 | end 43 | 44 | gem_data = { 45 | repos: 46 | [{ 47 | name: "solid_use_case", 48 | url: "https://github.com/mindeavor/solid_use_case", 49 | description: "Create use cases the way they were meant to be. Easily verify inputs at each step and seamlessly fail with custom error data and convenient pattern matching." 50 | }], 51 | language: "ruby" 52 | } 53 | 54 | assert parser.success? 55 | assert_equal gem_data, parser.direct 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /test/fixtures/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codetriage/CodeTriage/2f193418ca0b8757ca0a1159a2e7827ff5a02926/test/fixtures/.gitkeep -------------------------------------------------------------------------------- /test/fixtures/doc_methods.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html 2 | 3 | issue_triage_doc: 4 | repo: issue_triage_sandbox 5 | name: "Foo#baz" 6 | line: 9 7 | path: "/path/here/issue_triage" 8 | file: "baz.rb" 9 | created_at: 2013-10-29 21:09:48.351554000 Z 10 | updated_at: 2013-10-29 21:09:48.351554000 Z 11 | -------------------------------------------------------------------------------- /test/fixtures/issue_assignments.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html 2 | 3 | one: 4 | repo_subscription: schneems_to_triage 5 | issue: issue_one 6 | 7 | schneems_triage_issue_assignment: 8 | repo_subscription: schneems_to_triage 9 | issue: issue_one 10 | 11 | schneems_sinatra_issue_assignment: 12 | repo_subscription: schneems_to_sinatra 13 | issue: issue_two 14 | 15 | two: 16 | repo_subscription: jroes_to_rails 17 | issue: issue_two 18 | 19 | -------------------------------------------------------------------------------- /test/fixtures/repo_subscriptions.yml: -------------------------------------------------------------------------------- 1 | schneems_to_triage: 2 | repo: issue_triage_sandbox 3 | user: schneems 4 | created_at: 2013-10-29 21:09:48.351554000 Z 5 | updated_at: 2013-10-29 21:09:48.351554000 Z 6 | last_sent_at: 7 | email_limit: 3 8 | write_limit: 1 9 | 10 | schneems_to_sinatra: 11 | repo: sinatra_sinatra 12 | user: schneems 13 | created_at: 2013-10-29 21:09:48.351554000 Z 14 | updated_at: 2013-10-29 21:09:48.351554000 Z 15 | last_sent_at: 16 | email_limit: 3 17 | write_limit: 18 | 19 | jroes_to_rails: 20 | repo: rails_rails 21 | user: jroes 22 | #user_name: rails 23 | #repo_name: rails 24 | created_at: 2013-10-29 21:09:48.351554000 Z 25 | updated_at: 2013-10-29 21:09:48.351554000 Z 26 | last_sent_at: 27 | email_limit: 5 28 | 29 | empty_to_archived: 30 | repo: archived_repo 31 | user: empty 32 | created_at: 2013-10-29 21:09:48.351554000 Z 33 | updated_at: 2013-10-29 21:09:48.351554000 Z 34 | last_sent_at: 35 | email_limit: 5 36 | 37 | read_doc_only: 38 | repo: issue_triage_sandbox 39 | user: foo_user 40 | read_limit: 1 41 | created_at: 2013-10-29 21:09:48.351554000 Z 42 | updated_at: 2013-10-29 21:09:48.351554000 Z 43 | last_sent_at: 44 | email_limit: 0 45 | 46 | write_doc_only: 47 | repo: issue_triage_sandbox 48 | user: bar_user 49 | write_limit: 1 50 | write: true 51 | created_at: 2013-10-29 21:09:48.351554000 Z 52 | updated_at: 2013-10-29 21:09:48.351554000 Z 53 | last_sent_at: 54 | email_limit: 0 55 | -------------------------------------------------------------------------------- /test/functional/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codetriage/CodeTriage/2f193418ca0b8757ca0a1159a2e7827ff5a02926/test/functional/.gitkeep -------------------------------------------------------------------------------- /test/functional/after_register_controller_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | 5 | class AfterRegisterControllerTest < ActionController::TestCase 6 | # test "the truth" do 7 | # assert true 8 | # end 9 | end 10 | -------------------------------------------------------------------------------- /test/functional/doc_methods_controller_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | 5 | class DocMethodsControllerTest < ActionController::TestCase 6 | include Devise::Test::ControllerHelpers 7 | 8 | setup do 9 | sign_in users(:schneems) 10 | @user = users(:schneems) 11 | @triage_doc = doc_methods(:issue_triage_doc) 12 | @repo_sub = RepoSubscription.where(user_id: @user.id, repo: @triage_doc.repo).first 13 | end 14 | 15 | test "click_method_redirect with a DocAssignment redirects to the doc_method url" do 16 | DocAssignment.create(doc_method_id: @triage_doc.id, repo_subscription_id: @repo_sub.id) 17 | 18 | get :click_method_redirect, params: {id: @triage_doc.id, user_id: @user.id} 19 | 20 | assert_redirected_to doc_method_url(@triage_doc) 21 | end 22 | 23 | test "click_method_redirect without any DocAssignment returns to root an displays an error" do 24 | get :click_method_redirect, params: {id: @triage_doc.id, user_id: @user.id} 25 | 26 | assert flash[:notice].eql? "Bad url, if this problem persists please open an issue github.com/codetriage/codetriage" 27 | assert_redirected_to :root 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /test/functional/issue_assignments_controller_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | 5 | class IssueAssignmentsControllerTest < ActionController::TestCase 6 | include Devise::Test::ControllerHelpers 7 | 8 | setup do 9 | sign_in users(:schneems) 10 | end 11 | 12 | test "#create redirects when the user request a new issue" do 13 | repo_sub = repo_subscriptions(:schneems_to_triage) 14 | 15 | post :create, params: {id: repo_sub.id} 16 | 17 | assert_redirected_to repo_path(repo_sub.repo.full_name) 18 | end 19 | 20 | test "#click_issue_redirect redirects user when assignment matches the user id" do 21 | user = users(:schneems) 22 | assignment = issue_assignments(:schneems_triage_issue_assignment) 23 | 24 | get :click_issue_redirect, params: {id: assignment.id, user_id: user.id} 25 | 26 | assert_redirected_to assignment.issue.html_url 27 | end 28 | 29 | test "#click_issue_redirect redirects to error when there is a bad url" do 30 | assignment = issue_assignments(:schneems_triage_issue_assignment) 31 | 32 | get :click_issue_redirect, params: {id: assignment.id, user_id: 42} 33 | 34 | assert_redirected_to :root 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /test/functional/omniauth_callbacks_controller_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | 5 | class Users::OmniauthCallbacksControllerTest < ActionController::TestCase 6 | include Devise::Test::ControllerHelpers 7 | 8 | setup do 9 | request.env["devise.mapping"] = Devise.mappings[:user] 10 | request.env["omniauth.auth"] = OmniAuth.config.mock_auth[:github] 11 | end 12 | 13 | test "make user feel good when legit email address" do 14 | stub_oauth_user("legit@legit.com") 15 | get :github 16 | assert flash[:notice] == I18n.t("devise.omniauth_callbacks.success", 17 | kind: "GitHub") 18 | end 19 | 20 | test "redirect to user page and inform when bad e-mail address" do 21 | stub_oauth_user("awful e-mail address") 22 | get :github 23 | assert flash[:notice] == I18n.t("devise.omniauth_callbacks.bad_email_success", 24 | kind: "GitHub") 25 | assert_redirected_to root_path 26 | end 27 | 28 | def stub_oauth_user(email) 29 | user = User.new(email: email) 30 | user.stubs(:persisted?).returns(true) 31 | GitHubAuthenticator.stubs(:authenticate).returns(user) 32 | user 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /test/functional/pages_controller_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | 5 | class PagesControllerTest < ActionController::TestCase 6 | include Devise::Test::ControllerHelpers 7 | 8 | test "siged in user" do 9 | sign_in users(:mockstar) 10 | get :index 11 | 12 | assert_response :success, "successfully renders index" 13 | assert_not_nil assigns(:repos), "assigns to repos" 14 | assert_not_nil assigns(:repos_subs), "assigns to repos_subs" 15 | assert_template :index, "render index template" 16 | end 17 | 18 | test "not signed in user" do 19 | get :index 20 | assert_nil assigns(:repos_subs), "not assigns to repos_subs" 21 | end 22 | 23 | test "cache control for headers" do 24 | get :index 25 | assert_equal("no-store", response.headers["Cache-Control"]) 26 | assert_equal("no-cache", response.headers["Pragma"]) 27 | assert_equal("Fri, 01 Jan 1990 00:00:00 GMT", response.headers["Expires"]) 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /test/functional/repos_controller_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | 5 | class ReposControllerTest < ActionController::TestCase 6 | include Devise::Test::ControllerHelpers 7 | include ActiveJob::TestHelper 8 | 9 | test "responds with 404 if repo does not exist" do 10 | get :show, params: {full_name: "foo/bar"} 11 | 12 | assert_redirected_to new_repo_url(user_name: "foo", name: "bar") 13 | end 14 | 15 | test "trying to create repo without logged in will redirect to login page" do 16 | assert_no_difference -> { Repo.count } do 17 | post :create, params: {repo: {name: "codetriage", user_name: "codetriage"}} 18 | end 19 | 20 | assert_redirected_to user_github_omniauth_authorize_path(origin: "/repos") 21 | end 22 | 23 | test "do not send email for repo without issues" do 24 | sign_in users(:mockstar) 25 | 26 | assert_enqueued_jobs 2, only: SendSingleTriageEmailJob do 27 | VCR.use_cassette "create_repo_refinery", record: :once do 28 | post :create, params: {repo: {name: "refinerycms", user_name: "refinery"}} 29 | end 30 | VCR.use_cassette "create_repo_without_issues", record: :once do 31 | post :create, params: {repo: {name: "scene-hub-v2", user_name: "chrisccerami"}} 32 | end 33 | end 34 | end 35 | 36 | test "logged in user can create repo" do 37 | sign_in users(:mockstar) 38 | 39 | VCR.use_cassette "create_repo_refinery", record: :once do 40 | assert_difference -> { Repo.count } do 41 | post :create, params: {repo: {name: "refinerycms", user_name: "refinery"}} 42 | end 43 | end 44 | 45 | assert_redirected_to repo_path("refinery/refinerycms") 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /test/functional/subscribers_controller_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | 5 | class SubscribersControllerTest < ActionController::TestCase 6 | include Devise::Test::ControllerHelpers 7 | 8 | test "list subscribers for a repo" do 9 | repo = repos(:issue_triage_sandbox) 10 | repo.users << users(:mockstar) 11 | get :show, params: {full_name: "#{repo.user_name}/#{repo.name}"} 12 | assert_response :success 13 | assert_not_nil assigns(:subscribers) 14 | assert_not_nil assigns(:repo) 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /test/functional/topics_controller_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | 5 | class TopicsControllerTest < ActionController::TestCase 6 | include Devise::Test::ControllerHelpers 7 | 8 | test "redirect invalid topic" do 9 | get :show, params: {id: "test"} 10 | assert_redirected_to root_url 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /test/functional/user_mailer_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | 5 | class UserMailerTest < ActionMailer::TestCase 6 | test "send_triage works" do 7 | repo_sub = repo_subscriptions(:schneems_to_triage) 8 | assignment = issue_assignments(:one) 9 | email = UserMailer.send_triage( 10 | repo: repo_sub.repo, 11 | user: repo_sub.user, 12 | assignment: assignment 13 | ) 14 | 15 | assert_emails 1 do 16 | email.deliver_now 17 | end 18 | end 19 | 20 | test "poke_inactive works" do 21 | user = users(:schneems) 22 | email = UserMailer.poke_inactive( 23 | user: user, 24 | min_issue_count: 0, 25 | min_subscriber_count: 0 26 | ) 27 | 28 | assert_emails 1 do 29 | email.deliver_now 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /test/functional/users_controller_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | 5 | class UsersControllerTest < ActionController::TestCase 6 | include Devise::Test::ControllerHelpers 7 | 8 | test "#show" do 9 | sign_in users(:mockstar) 10 | user = users(:jroes) 11 | 12 | get :show, params: {id: user.id} 13 | 14 | assert_redirected_to root_path 15 | end 16 | 17 | test "#update should update an user" do 18 | user = users(:mockstar) 19 | sign_in user 20 | 21 | put :update, params: {user: {name: "New Name"}} 22 | user.reload 23 | 24 | assert_redirected_to user 25 | assert_equal "New Name", user.name 26 | end 27 | 28 | test "#token_delete" do 29 | sign_in users(:schneems) 30 | 31 | get :token_delete, params: {account_delete_token: users(:schneems).account_delete_token} 32 | 33 | assert(assigns(:lonely_repos).any?) 34 | end 35 | 36 | test "should destroy a user with the correct account_delete_token" do 37 | sign_in users(:mockstar) 38 | 39 | assert_difference "User.count", -1 do 40 | delete :token_destroy, params: {account_delete_token: users(:mockstar).account_delete_token} 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /test/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codetriage/CodeTriage/2f193418ca0b8757ca0a1159a2e7827ff5a02926/test/helpers/.gitkeep -------------------------------------------------------------------------------- /test/helpers/application_helper_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | 5 | class ApplicationHelperTest < ActionView::TestCase 6 | test "warning_svg" do 7 | expected_warning_svg = %( 8 | 9 | 10 | 11 | 12 | 13 | 14 | ).html_safe 15 | assert_equal expected_warning_svg, warning_svg 16 | end 17 | 18 | test "star_svg" do 19 | expected_star_svg = %( 20 | 21 | 22 | 23 | ).html_safe 24 | assert_equal expected_star_svg, star_svg 25 | end 26 | 27 | test "flash_class levels" do 28 | assert_equal "info", flash_class(:notice) 29 | assert_equal "error", flash_class(:error) 30 | assert_equal "warning", flash_class(:alert) 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /test/integration/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codetriage/CodeTriage/2f193418ca0b8757ca0a1159a2e7827ff5a02926/test/integration/.gitkeep -------------------------------------------------------------------------------- /test/integration/filtering_language_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | 5 | class FilteringLanguageTest < ActionDispatch::IntegrationTest 6 | teardown do 7 | Rails.cache.clear 8 | end 9 | 10 | test "filtering language in normal projects" do 11 | visit "/?language=Ruby" 12 | 13 | assert_equal page.all(".types-filter-button")[0].text, "Ruby" 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /test/integration/maintaining_repo_subscriptions_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | 5 | class MaintainingRepoSubscriptionsTest < ActionDispatch::IntegrationTest 6 | include ActiveJob::TestHelper 7 | 8 | def triage_the_sandbox 9 | login_via_github 10 | visit "/" 11 | click_link "issue_triage_sandbox" 12 | 13 | first(:button, "Triage Issues").click 14 | 15 | assert page.has_content?("You'll receive daily triage e-mails for this repository.") 16 | end 17 | 18 | test "subscribing to a repo" do 19 | assert_enqueued_jobs 1, only: SendSingleTriageEmailJob do 20 | triage_the_sandbox 21 | assert page.has_content?("issue_triage_sandbox") 22 | end 23 | end 24 | 25 | test "send an issue! button" do 26 | triage_the_sandbox 27 | assert_enqueued_jobs 1, only: SendSingleTriageEmailJob do 28 | click_link "Send me a new issue!" 29 | assert page.has_content?("You will receive an email with your new issue shortly") 30 | end 31 | end 32 | 33 | test "listing subscribers" do 34 | triage_the_sandbox 35 | assert page.has_content?("mockstar") 36 | end 37 | 38 | # test "list only favorite languages" do 39 | # login_via_github 40 | # visit "/" 41 | # assert !page.has_content?("javascript") 42 | # end 43 | end 44 | -------------------------------------------------------------------------------- /test/integration/user_session_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | 5 | class UserSessionTest < ActionDispatch::IntegrationTest 6 | test "sign_in screen renders" do 7 | visit new_user_session_path 8 | assert page.has_content?("Sign in") 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /test/integration/user_unsubscribes_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | 5 | class UserUnsubscribeTest < ActionDispatch::IntegrationTest 6 | test "unsubscribing successfully" do 7 | @user = users(:mockstar) 8 | token = @user.account_delete_token 9 | login_as(@user, scope: :user) 10 | 11 | visit token_delete_user_path(token) 12 | click_on "destroy_user" 13 | 14 | assert page.has_content?("Successfully removed your user account") 15 | end 16 | 17 | test "unsubscribing more than once" do 18 | @user = users(:mockstar) 19 | token = @user.account_delete_token 20 | login_as(@user, scope: :user) 21 | 22 | visit token_delete_user_path(token) 23 | click_on "destroy_user" 24 | 25 | visit token_delete_user_path(token) 26 | 27 | assert page.has_content?("Account could not be found. You may have already deleted it, or your GitHub username may have changed.") 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /test/jobs/background_inactive_email_job_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | 5 | class BackgroundInactiveEmailJobTest < ActiveJob::TestCase 6 | # test "the truth" do 7 | # assert true 8 | # end 9 | end 10 | -------------------------------------------------------------------------------- /test/jobs/parse_docs_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | 5 | class ParseDocsTest < ActiveJob::TestCase 6 | test "Can parse yard docs" do 7 | repo = repos(:get_process_mem) 8 | assert_equal 0, repo.doc_methods.count 9 | 10 | parser = DocsDoctor::Parsers::Ruby::Yard.new( 11 | get_process_mem_disk_location 12 | ) 13 | parser.process 14 | parser.store(repo) 15 | 16 | refute_equal 0, repo.doc_methods.count 17 | 18 | assert DocMethod.where(repo_id: repo).where(path: "GetProcessMem#initialize").any? 19 | end 20 | 21 | test "Does not error" do 22 | repos(:get_process_mem) 23 | .populate_docs!( 24 | location: get_process_mem_disk_location, 25 | commit_sha: repos(:get_process_mem).commit_sha, 26 | has_subscribers: true 27 | ) 28 | end 29 | 30 | test "Errors are caught in fork" do 31 | assert_raise do 32 | DocsDoctor::Parsers::Ruby::Yard.new( 33 | get_process_mem_disk_location 34 | ).in_fork { raise "foo" } 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /test/jobs/send_single_triage_email_job_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | 5 | class SendSingleTriageEmailJobTest < ActiveJob::TestCase 6 | # test "the truth" do 7 | # assert true 8 | # end 9 | end 10 | -------------------------------------------------------------------------------- /test/unit/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codetriage/CodeTriage/2f193418ca0b8757ca0a1159a2e7827ff5a02926/test/unit/.gitkeep -------------------------------------------------------------------------------- /test/unit/git_branchname_generator_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | 5 | class GitBranchnameGeneratorTest < ActiveSupport::TestCase 6 | test "replaces non alpha-numerics and underscores with dashes" do 7 | doc_path = "Pay-)(*&^%$#@!~ment.charge?" 8 | assert_equal "username-update-docs-Pay------------ment-charge--for-pr", 9 | GitBranchnameGenerator.new(username: "username", doc_path: doc_path).branchname 10 | end 11 | 12 | test "does not allow pounds (#)" do 13 | doc_path = "Payment#charge?" 14 | assert_equal "username-update-docs-Payment-charge--for-pr", 15 | GitBranchnameGenerator.new(username: "username", doc_path: doc_path).branchname 16 | end 17 | 18 | test "does not allow periods" do 19 | doc_path = "Payment.charge?" 20 | assert_equal "username-update-docs-Payment-charge--for-pr", 21 | GitBranchnameGenerator.new(username: "username", doc_path: doc_path).branchname 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /test/unit/github_fetcher/commit_sha_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | 5 | class GithubFetcher::CommitShaTest < ActiveSupport::TestCase 6 | def fetcher(repo) 7 | GithubFetcher::CommitSha.new( 8 | user_name: repo.user_name, 9 | name: repo.name, 10 | default_branch: "master" 11 | ) 12 | end 13 | 14 | test "quacks like a GithubFetcher::Resource" do 15 | assert_kind_of GithubFetcher::Resource, GithubFetcher::User.new(token: "asdf") 16 | end 17 | 18 | test "#as_json returns json" do 19 | fetcher = fetcher(repos(:scene_hub_v2)) 20 | commit_message = "Fixed geojson method in band model" 21 | 22 | VCR.use_cassette "fetcher_commit_sha" do 23 | assert_equal fetcher.as_json["commit"]["message"], commit_message, fetcher.as_json 24 | end 25 | end 26 | 27 | test "#as_json returns {} when error" do 28 | GitHubBub.stub(:get, ->(_, _) { raise GitHubBub::RequestError }) do 29 | fetcher = fetcher(repos(:scene_hub_v2)) 30 | assert_equal fetcher.as_json, {} 31 | end 32 | end 33 | 34 | test "#commit_sha returns the sha branch" do 35 | fetcher = fetcher(repos(:scene_hub_v2)) 36 | expected_sha = "12dd603a7ea29fe2cb4ec7394d48c6efd4cad653" 37 | 38 | VCR.use_cassette "fetcher_commit_sha" do 39 | assert_equal fetcher.commit_sha, expected_sha 40 | end 41 | end 42 | 43 | test "#commit_sha returns nil when error" do 44 | GitHubBub.stub(:get, ->(_, _) { raise GitHubBub::RequestError }) do 45 | fetcher = fetcher(repos(:scene_hub_v2)) 46 | assert_nil fetcher.commit_sha 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /test/unit/github_fetcher/email_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | 5 | class GithubFetcher::EmailTest < ActiveSupport::TestCase 6 | test "quacks like a GithubFetcher::Resource" do 7 | assert_kind_of GithubFetcher::Resource, GithubFetcher::User.new(token: "asdf") 8 | end 9 | 10 | test "#as_json includes list of emails for given user" do 11 | VCR.use_cassette "fetch_emails" do 12 | email_fetcher = GithubFetcher::Email.new( 13 | token: OmniAuth.config.mock_auth[:github][:credentials][:token] 14 | ) 15 | 16 | assert_equal email_fetcher.as_json, [ 17 | {"email" => "dmragone@gmail.com", "primary" => true, "verified" => true, "visibility" => "public"}, 18 | {"email" => "david.ragone@hired.com", "primary" => false, "verified" => true, "visibility" => nil} 19 | ], "Failed: Got #{email_fetcher.as_json}" 20 | end 21 | end 22 | 23 | test "#as_json returns null response when bad request" do 24 | VCR.use_cassette "bad_fetch_emails" do 25 | email_fetcher = GithubFetcher::Email.new(token: "asdf") 26 | 27 | assert_equal email_fetcher.as_json, {"message" => "Bad credentials", "documentation_url" => "https://developer.github.com/v3"} 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /test/unit/github_fetcher/issue_comments_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | 5 | class GithubFetcher::IssueCommentsTest < ActiveSupport::TestCase 6 | def comments_fetcher 7 | issue = issues(:issue_three) 8 | issue.repo = repos(:sinatra_sinatra) # For some reason, this isn't set :/ 9 | 10 | GithubFetcher::IssueComments.new( 11 | owner_name: issue.owner_name, 12 | repo_name: issue.repo_name, 13 | number: issue.number 14 | ) 15 | end 16 | 17 | test "quacks like a GithubFetcher::Resource" do 18 | assert_kind_of GithubFetcher::Resource, GithubFetcher::User.new(token: "asdf") 19 | end 20 | 21 | test "#as_json returns list of comments" do 22 | first_comment_url = "https://api.github.com/repos/sinatra/sinatra/issues/comments/5" 23 | 24 | VCR.use_cassette "fetch_issue_comments" do 25 | assert_equal comments_fetcher.as_json.first["url"], first_comment_url 26 | end 27 | end 28 | 29 | test "#as_json returns [] when error" do 30 | GitHubBub.stub(:get, ->(_, _) { raise GitHubBub::RequestError }) do 31 | assert_equal comments_fetcher.as_json, {} 32 | end 33 | end 34 | 35 | test "#commenters returns list of unique commenters" do 36 | fetcher = comments_fetcher 37 | fetcher.stub(:as_json, -> { 38 | [ 39 | {"id" => 5, "user" => {"login" => "rtomayko", "id" => 404}}, 40 | {"id" => 6, "user" => {"login" => "rtomayko", "id" => 404}}, 41 | {"id" => 7, "user" => {"login" => "DavidRagone", "id" => 22345}} 42 | ] 43 | }) do 44 | assert_equal fetcher.commenters, ["rtomayko", "DavidRagone"] 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /test/unit/github_fetcher/issue_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | 5 | class GithubFetcher::IssueTest < ActiveSupport::TestCase 6 | def issue_fetcher(issue) 7 | GithubFetcher::Issue.new( 8 | owner_name: issue.owner_name, 9 | repo_name: issue.repo_name, 10 | number: issue.number 11 | ) 12 | end 13 | 14 | test "quacks like a GithubFetcher::Resource" do 15 | assert_kind_of GithubFetcher::Resource, GithubFetcher::User.new(token: "asdf") 16 | end 17 | 18 | test "#as_json includes issue details" do 19 | fetcher = issue_fetcher(issues(:issue_one)) 20 | 21 | VCR.use_cassette "fetch_issue" do 22 | assert_equal fetcher.as_json["title"], "first test issue", fetcher.as_json 23 | end 24 | end 25 | 26 | test "#as_json returns {} when error" do 27 | GitHubBub.stub(:get, ->(_, _) { raise GitHubBub::RequestError }) do 28 | fetcher = issue_fetcher(issues(:issue_one)) 29 | assert_nil fetcher.as_json["title"], fetcher.as_json 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /test/unit/github_fetcher/repo_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | 5 | class GithubFetcher::RepoTest < ActiveSupport::TestCase 6 | def fetcher(repo) 7 | GithubFetcher::Repo.new(user_name: repo.user_name, name: repo.name) 8 | end 9 | 10 | test "quacks like a GithubFetcher::Resource" do 11 | assert_kind_of GithubFetcher::Resource, GithubFetcher::User.new(token: "asdf") 12 | end 13 | 14 | test "#as_json returns json" do 15 | fetcher = fetcher(repos(:scene_hub_v2)) 16 | expected_clone_url = "https://github.com/chrisccerami/scene-hub-v2.git" 17 | 18 | VCR.use_cassette "create_repo_without_issues" do 19 | assert_equal fetcher.as_json["clone_url"], expected_clone_url, fetcher.as_json 20 | end 21 | end 22 | 23 | test "#as_json returns {} when error" do 24 | GitHubBub.stub(:get, ->(_, _) { raise GitHubBub::RequestError }) do 25 | fetcher = fetcher(repos(:scene_hub_v2)) 26 | assert_equal fetcher.as_json, {} 27 | assert_nil fetcher.as_json["clone_url"], fetcher.as_json 28 | end 29 | end 30 | 31 | test "#default_branch returns the default branch" do 32 | fetcher = fetcher(repos(:scene_hub_v2)) 33 | expected_default_branch = "master" 34 | 35 | VCR.use_cassette "create_repo_without_issues" do 36 | assert_equal fetcher.default_branch, expected_default_branch 37 | end 38 | end 39 | 40 | test "default_branch returns nil when error" do 41 | GitHubBub.stub(:get, ->(_, _) { raise GitHubBub::RequestError }) do 42 | fetcher = fetcher(repos(:scene_hub_v2)) 43 | assert_nil fetcher.default_branch 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /test/unit/github_fetcher/resource_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | 5 | class GithubFetcher::ResourceTest < ActiveSupport::TestCase 6 | def status_validation(mocked_status, method, expected_response) 7 | GithubFetcher::Resource.any_instance.stubs(:status).returns(mocked_status) 8 | resource = GithubFetcher::Resource.new({}) 9 | assert_equal expected_response, resource.send(method) 10 | end 11 | 12 | test "#not_found? is true on 404 status responses" do 13 | status_validation(404, :not_found?, true) 14 | end 15 | 16 | test "#not_found? is false on non 404 status responses" do 17 | [200, 201, 204, 400, 403, 500, 502].each { |status| status_validation(status, :not_found?, false) } 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /test/unit/helpers/after_register_helper_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | 5 | class AfterRegisterHelperTest < ActionView::TestCase 6 | end 7 | -------------------------------------------------------------------------------- /test/unit/repo_labels_assigner_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | 5 | class RepoLabelsAssignerTest < ActiveSupport::TestCase 6 | test "#create_and_associate_labels! creates labels" do 7 | repo = repos(:rails_rails) 8 | VCR.use_cassette("fetch_labels_for_repo", record: :once) do 9 | assigner = RepoLabelAssigner.new(repo: repo) 10 | assert_difference("Label.count", 30) do 11 | assigner.create_and_associate_labels! 12 | end 13 | end 14 | end 15 | 16 | test "#create_and_associate_labels! it does not create existing labels" do 17 | repo = repos(:rails_rails) 18 | Label.create!(name: :autoloading) 19 | VCR.use_cassette("fetch_labels_for_repo", record: :once) do 20 | assigner = RepoLabelAssigner.new(repo: repo) 21 | assigner.create_and_associate_labels! 22 | assert_equal Label.where(name: :autoloading).count, 1 23 | end 24 | end 25 | 26 | test "#create_and_associate_labels! it does not care about label case" do 27 | repo = repos(:rails_rails) 28 | VCR.use_cassette("fetch_labels_for_repo", record: :once) do 29 | assigner = RepoLabelAssigner.new(repo: repo) 30 | assigner.create_and_associate_labels! 31 | assert_equal Label.where(name: :ActionMailer).count, 0 32 | end 33 | end 34 | 35 | test "#create_and_associate_labels! associates labels with the repo" do 36 | repo = repos(:rails_rails) 37 | VCR.use_cassette("fetch_labels_for_repo", record: :once) do 38 | assigner = RepoLabelAssigner.new(repo: repo) 39 | assigner.create_and_associate_labels! 40 | end 41 | 42 | assert_equal Label.count, RepoLabel.where(repo: repo).count 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /test/unit/sorted_repo_collection_test.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require "test_helper" 4 | require_relative "../../lib/sorted_repo_collection" 5 | 6 | class SortedRepoCollectionTest < ActiveSupport::TestCase 7 | test "yields repos sorted by full_name, case-insensitive" do 8 | collection = SortedRepoCollection.new([ 9 | {"full_name" => "alice/coolness"}, 10 | {"full_name" => "Bob/bravado"}, 11 | {"full_name" => "Bob/awesomeness"}, 12 | {"full_name" => "alice/bravado"}, 13 | {"full_name" => "charlie/bravado"} 14 | ]) 15 | 16 | full_names_in_sort_order = [] 17 | collection.each do |repo| 18 | full_names_in_sort_order << repo.fetch("full_name") 19 | end 20 | 21 | expected_order = [ 22 | "alice/bravado", 23 | "alice/coolness", 24 | "Bob/awesomeness", 25 | "Bob/bravado", 26 | "charlie/bravado" 27 | ] 28 | assert_equal expected_order, full_names_in_sort_order 29 | end 30 | 31 | test "yields the correct length" do 32 | collection = SortedRepoCollection.new([ 33 | {"full_name" => "alice/coolness"}, 34 | {"full_name" => "Bob/bravado"}, 35 | {"full_name" => "Bob/awesomeness"}, 36 | {"full_name" => "alice/bravado"}, 37 | {"full_name" => "charlie/bravado"} 38 | ]) 39 | assert_equal collection.size, 5 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /test/vcr_cassettes/repo_info_error.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://api.github.com/repos/codetriage/codetriage 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | Accept: 11 | - application/vnd.github.3.raw+json 12 | response: 13 | status: 14 | code: 403 15 | message: Forbidden 16 | headers: 17 | Server: 18 | - GitHub.com 19 | Date: 20 | - Sat, 09 Feb 2013 08:02:19 GMT 21 | Content-Type: 22 | - application/json; charset=utf-8 23 | Connection: 24 | - keep-alive 25 | Status: 26 | - 403 Forbidden 27 | X-Content-Type-Options: 28 | - nosniff 29 | X-Github-Media-Type: 30 | - github.beta; param=raw.3; format=json 31 | Cache-Control: 32 | - '' 33 | X-Ratelimit-Limit: 34 | - '60' 35 | Content-Length: 36 | - '54' 37 | X-Ratelimit-Remaining: 38 | - '0' 39 | body: 40 | encoding: US-ASCII 41 | string: ! '{"message":"API Rate Limit Exceeded for 5.63.147.100"}' 42 | http_version: 43 | recorded_at: Sat, 09 Feb 2013 08:02:19 GMT -------------------------------------------------------------------------------- /vendor/assets/javascripts/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codetriage/CodeTriage/2f193418ca0b8757ca0a1159a2e7827ff5a02926/vendor/assets/javascripts/.gitkeep -------------------------------------------------------------------------------- /vendor/assets/stylesheets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codetriage/CodeTriage/2f193418ca0b8757ca0a1159a2e7827ff5a02926/vendor/assets/stylesheets/.gitkeep -------------------------------------------------------------------------------- /vendor/plugins/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codetriage/CodeTriage/2f193418ca0b8757ca0a1159a2e7827ff5a02926/vendor/plugins/.gitkeep -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | --------------------------------------------------------------------------------