├── .browserslistrc ├── .env ├── .env.test ├── .eslintrc ├── .gitignore ├── .hound.yml ├── .rspec ├── .rubocop.yml ├── .ruby-version ├── .stylelintrc.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── Procfile ├── README.md ├── Rakefile ├── app ├── assets │ ├── config │ │ └── manifest.js │ ├── images │ │ ├── companies │ │ │ ├── 18f.png │ │ │ ├── airbrake.png │ │ │ ├── bbc-news.png │ │ │ ├── blendtec.png │ │ │ ├── codeship.png │ │ │ ├── dropbox.png │ │ │ ├── elastic.png │ │ │ ├── envoy.png │ │ │ ├── envy-labs.png │ │ │ ├── gitlab.png │ │ │ ├── heroku.png │ │ │ ├── hp.png │ │ │ ├── intuit.png │ │ │ ├── joomla.png │ │ │ ├── lumosity.png │ │ │ ├── meundies.png │ │ │ ├── middleman.jpg │ │ │ ├── mixpanel.png │ │ │ ├── mozilla.png │ │ │ ├── namely.png │ │ │ ├── netguru.png │ │ │ ├── percona.png │ │ │ ├── rackspace.png │ │ │ ├── rails-girls.png │ │ │ ├── salesforce.png │ │ │ ├── shipio.png │ │ │ ├── shubox.jpg │ │ │ ├── suse.png │ │ │ ├── thoughtbot.png │ │ │ ├── treehouse.png │ │ │ ├── turing-school.png │ │ │ ├── untuit.png │ │ │ ├── wealthsimple.png │ │ │ ├── wildbit.png │ │ │ ├── yahoo.png │ │ │ └── zendesk.png │ │ ├── github-logomark.svg │ │ ├── hatch-gray.svg │ │ ├── hatch-purple.svg │ │ ├── home │ │ │ ├── hound_flow.png │ │ │ ├── hound_flow.webp │ │ │ ├── review_example.png │ │ │ └── review_example.webp │ │ ├── hound-logo.svg │ │ ├── icon.svg │ │ ├── languages │ │ │ ├── coffeescript.svg │ │ │ ├── elixir.svg │ │ │ ├── go.svg │ │ │ ├── haml.svg │ │ │ ├── javascript.svg │ │ │ ├── php.svg │ │ │ ├── python.svg │ │ │ ├── ruby.svg │ │ │ ├── sass.svg │ │ │ ├── shell.svg │ │ │ ├── swift.svg │ │ │ └── typescript.svg │ │ ├── madi.jpg │ │ ├── pricing_pattern.svg │ │ ├── pricing_pattern_os.svg │ │ └── select-arrow.svg │ ├── javascripts │ │ └── application.js │ └── stylesheets │ │ ├── application.scss │ │ ├── base │ │ ├── _animations.scss │ │ ├── _base.scss │ │ ├── _buttons.scss │ │ ├── _extends.scss │ │ ├── _fonts.scss │ │ ├── _forms.scss │ │ ├── _helpers.scss │ │ ├── _media.scss │ │ ├── _mixins.scss │ │ ├── _tables.scss │ │ ├── _typography.scss │ │ └── _variables.scss │ │ ├── components │ │ ├── _allowance.scss │ │ ├── _app-footer.scss │ │ ├── _app-nav.scss │ │ ├── _avatar.scss │ │ ├── _badge.scss │ │ ├── _button.scss │ │ ├── _code.scss │ │ ├── _components.scss │ │ ├── _icon.scss │ │ ├── _inline-flash.scss │ │ ├── _logo.scss │ │ ├── _plan-markers.scss │ │ ├── _pricing.scss │ │ └── _toggle-switch.scss │ │ ├── marketing.scss │ │ ├── marketing │ │ ├── components │ │ │ ├── _app-footer.scss │ │ │ ├── _app-nav.scss │ │ │ ├── _button.scss │ │ │ ├── _page-hero.scss │ │ │ ├── _page-section.scss │ │ │ ├── _plan.scss │ │ │ └── _text-partition.scss │ │ ├── elements │ │ │ ├── _layout.scss │ │ │ └── _typography.scss │ │ ├── generic │ │ │ ├── _fonts.scss │ │ │ └── _reset.scss │ │ ├── objects │ │ │ ├── _container.scss │ │ │ ├── _grid.scss │ │ │ └── _list-bare.scss │ │ ├── settings │ │ │ ├── _breakpoints.scss │ │ │ ├── _color.scss │ │ │ ├── _controls.scss │ │ │ ├── _transitions.scss │ │ │ └── _typography.scss │ │ └── utilities │ │ │ ├── _font-weight.scss │ │ │ ├── _line-height.scss │ │ │ ├── _margin.scss │ │ │ └── _text-align.scss │ │ ├── pages │ │ ├── _builds.scss │ │ ├── _pages.scss │ │ ├── _tier_change.scss │ │ ├── accounts │ │ │ ├── _accounts.scss │ │ │ └── _show.scss │ │ └── repos │ │ │ ├── _index.scss │ │ │ ├── _onboarding.scss │ │ │ ├── _organization.scss │ │ │ ├── _repos.scss │ │ │ ├── _syncing.scss │ │ │ └── _tools.scss │ │ └── utilities │ │ ├── _clearfix.scss │ │ ├── _line-height.scss │ │ ├── _margin.scss │ │ └── _utilities.scss ├── controllers │ ├── accounts_controller.rb │ ├── activations_controller.rb │ ├── admin │ │ ├── application_controller.rb │ │ ├── blacklisted_pull_requests_controller.rb │ │ ├── job_failures_controller.rb │ │ ├── masquerades_controller.rb │ │ └── owners_controller.rb │ ├── application_controller.rb │ ├── builds_controller.rb │ ├── credit_cards_controller.rb │ ├── deactivations_controller.rb │ ├── deleted_subscriptions_controller.rb │ ├── errors_controller.rb │ ├── github_events_controller.rb │ ├── home_controller.rb │ ├── owners_controller.rb │ ├── pages_controller.rb │ ├── plans_controller.rb │ ├── rebuilds_controller.rb │ ├── repo_syncs_controller.rb │ ├── repos_controller.rb │ ├── sessions_controller.rb │ ├── setups_controller.rb │ ├── subscriptions_controller.rb │ └── users_controller.rb ├── dashboards │ ├── blacklisted_pull_request_dashboard.rb │ ├── dashboard_manifest.rb │ ├── job_failure_dashboard.rb │ └── owner_dashboard.rb ├── helpers │ ├── admin │ │ └── job_failures_helper.rb │ ├── analytics_helper.rb │ ├── application_helper.rb │ ├── flashes_helper.rb │ └── repos_helper.rb ├── javascript │ ├── __tests__ │ │ ├── RepoToolsAdd.test.jsx │ │ ├── __snapshots__ │ │ │ ├── RepoToolsAdd.test.jsx.snap │ │ │ ├── empty_repo_list-test.jsx.snap │ │ │ ├── notify_tier_change-test.jsx.snap │ │ │ ├── organization-test.jsx.snap │ │ │ ├── organization_list-test.jsx.snap │ │ │ ├── populated_repo_list-test.jsx.snap │ │ │ ├── repo-test.jsx.snap │ │ │ ├── repo_activation_button-test.jsx.snap │ │ │ ├── repo_allowance-test.jsx.snap │ │ │ ├── repo_deactivation_button-test.jsx.snap │ │ │ ├── repo_list-test.jsx.snap │ │ │ ├── repo_tools-test.jsx.snap │ │ │ ├── repo_tools_refresh-test.jsx.snap │ │ │ ├── repo_tools_search-test.jsx.snap │ │ │ ├── repos_container-test.jsx.snap │ │ │ ├── repos_sync_spinner-test.jsx.snap │ │ │ ├── repos_view-test.jsx.snap │ │ │ ├── tier_plan-test.jsx.snap │ │ │ ├── update_account_credit_card-test.jsx.snap │ │ │ ├── update_account_email-test.jsx.snap │ │ │ ├── update_account_email_message-test.jsx.snap │ │ │ └── upgrade_subscription_link-test.jsx.snap │ │ ├── empty_repo_list-test.jsx │ │ ├── notify_tier_change-test.jsx │ │ ├── organization-test.jsx │ │ ├── organization_list-test.jsx │ │ ├── populated_repo_list-test.jsx │ │ ├── repo-test.jsx │ │ ├── repo_activation_button-test.jsx │ │ ├── repo_allowance-test.jsx │ │ ├── repo_deactivation_button-test.jsx │ │ ├── repo_list-test.jsx │ │ ├── repo_tools-test.jsx │ │ ├── repo_tools_refresh-test.jsx │ │ ├── repo_tools_search-test.jsx │ │ ├── repos_container-test.jsx │ │ ├── repos_sync_spinner-test.jsx │ │ ├── repos_view-test.jsx │ │ ├── setup.js │ │ ├── tier_plan-test.jsx │ │ ├── update_account_credit_card-test.jsx │ │ ├── update_account_email-test.jsx │ │ ├── update_account_email_message-test.jsx │ │ └── upgrade_subscription_link-test.jsx │ ├── components │ │ ├── NotifyTierChange.jsx │ │ ├── NotifyTierChange │ │ │ └── components │ │ │ │ ├── App.jsx │ │ │ │ ├── TierPlan.jsx │ │ │ │ └── UpgradeSubscriptionLink.jsx │ │ ├── ReposContainer.jsx │ │ ├── ReposContainer │ │ │ └── components │ │ │ │ ├── App.jsx │ │ │ │ ├── Organization.jsx │ │ │ │ ├── Organization │ │ │ │ ├── OrganizationConfiguration.jsx │ │ │ │ ├── RepoList.jsx │ │ │ │ └── RepoList │ │ │ │ │ ├── EmptyRepoList.jsx │ │ │ │ │ └── PopulatedRepoList.jsx │ │ │ │ ├── Repo.jsx │ │ │ │ ├── Repo │ │ │ │ ├── RepoActivationButton.jsx │ │ │ │ └── RepoDeactivationButton.jsx │ │ │ │ ├── RepoAllowance.jsx │ │ │ │ ├── RepoTools.jsx │ │ │ │ ├── RepoTools │ │ │ │ ├── RepoToolsAdd.jsx │ │ │ │ ├── RepoToolsRefresh.jsx │ │ │ │ └── RepoToolsSearch.jsx │ │ │ │ ├── ReposView.jsx │ │ │ │ └── ReposView │ │ │ │ ├── NoReposMessage.jsx │ │ │ │ ├── OrganizationsList.jsx │ │ │ │ └── ReposSyncSpinner.jsx │ │ ├── UpdateAccountCreditCard.jsx │ │ ├── UpdateAccountCreditCard │ │ │ └── components │ │ │ │ └── App.jsx │ │ ├── UpdateAccountEmail.jsx │ │ └── UpdateAccountEmail │ │ │ └── components │ │ │ ├── App.jsx │ │ │ └── UpdateAccountEmailMessage.jsx │ ├── modules │ │ ├── Ajax.js │ │ └── Utils.js │ └── packs │ │ ├── notify_tier_change.js │ │ ├── repos_container.js │ │ ├── update_account_credit_card.js │ │ └── update_account_email.js ├── jobs │ ├── application_job.rb │ ├── buildable.rb │ ├── completed_file_review_job.rb │ ├── large_build_job.rb │ ├── linters_job.rb │ ├── repo_synchronization_job.rb │ └── small_build_job.rb ├── mailers │ └── .gitkeep ├── models │ ├── .gitkeep │ ├── analytics.rb │ ├── app_token.rb │ ├── application_record.rb │ ├── blacklisted_pull_request.rb │ ├── build.rb │ ├── commit.rb │ ├── commit_file.rb │ ├── commit_status.rb │ ├── config │ │ ├── base.rb │ │ ├── coffeelint.rb │ │ ├── credo.rb │ │ ├── erb_lint.rb │ │ ├── eslint.rb │ │ ├── flake8.rb │ │ ├── flog.rb │ │ ├── golint.rb │ │ ├── haml_lint.rb │ │ ├── jshint.rb │ │ ├── json_with_comments.rb │ │ ├── parser.rb │ │ ├── parser_error.rb │ │ ├── phpcs.rb │ │ ├── reek.rb │ │ ├── remark.rb │ │ ├── rubocop.rb │ │ ├── sass_lint.rb │ │ ├── scss_lint.rb │ │ ├── serializer.rb │ │ ├── shellcheck.rb │ │ ├── slim_lint.rb │ │ ├── stylelint.rb │ │ ├── swiftlint.rb │ │ ├── tslint.rb │ │ └── unsupported.rb │ ├── config_content.rb │ ├── config_content │ │ └── remote.rb │ ├── empty_commit.rb │ ├── file_review.rb │ ├── github_auth.rb │ ├── github_event.rb │ ├── github_plan.rb │ ├── home.rb │ ├── hound_config.rb │ ├── ignore_file.rb │ ├── job_failure.rb │ ├── line.rb │ ├── linter │ │ ├── base.rb │ │ ├── coffeelint.rb │ │ ├── credo.rb │ │ ├── erb_lint.rb │ │ ├── eslint.rb │ │ ├── flake8.rb │ │ ├── flog.rb │ │ ├── golint.rb │ │ ├── haml_lint.rb │ │ ├── jshint.rb │ │ ├── phpcs.rb │ │ ├── reek.rb │ │ ├── remark.rb │ │ ├── rubocop.rb │ │ ├── sass_lint.rb │ │ ├── scss_lint.rb │ │ ├── shellcheck.rb │ │ ├── slim_lint.rb │ │ ├── stylelint.rb │ │ ├── swiftlint.rb │ │ └── tslint.rb │ ├── membership.rb │ ├── metered_stripe_plan.rb │ ├── missing_owner.rb │ ├── owner.rb │ ├── patch.rb │ ├── payload.rb │ ├── payment_gateway_customer.rb │ ├── payment_gateway_subscription.rb │ ├── plan.rb │ ├── plan_selector.rb │ ├── pull_request.rb │ ├── repo.rb │ ├── review_body.rb │ ├── stripe_plan.rb │ ├── subscription.rb │ ├── unchanged_line.rb │ ├── user.rb │ └── violation.rb ├── policies │ └── commenting_policy.rb ├── presenters │ ├── account_page.rb │ ├── monthly_line_item.rb │ └── plan_presenter.rb ├── queries │ ├── recent_builds_by_repo_query.rb │ └── repos_with_membership_or_subscription_query.rb ├── serializers │ ├── plan_serializer.rb │ ├── repo_serializer.rb │ └── user_serializer.rb ├── services │ ├── build_config.rb │ ├── build_owner_hound_config.rb │ ├── complete_build.rb │ ├── complete_file_review.rb │ ├── create_repo.rb │ ├── deactivate_repo.rb │ ├── delete_subscriptions.rb │ ├── normalize_config.rb │ ├── rebuild_pull_request.rb │ ├── repo_subscriber.rb │ ├── repo_synchronization.rb │ ├── report_invalid_config.rb │ ├── resolve_config_aliases.rb │ ├── resolve_config_conflicts.rb │ ├── review_files.rb │ ├── sanitize_ini_file.rb │ ├── start_build.rb │ ├── submit_review.rb │ ├── update_github_plans.rb │ └── update_repo_status.rb └── views │ ├── accounts │ └── show.haml │ ├── admin │ ├── application │ │ └── _navigation.html.haml │ └── job_failures │ │ └── index.haml │ ├── application │ ├── _analytics.haml │ ├── _app_footer_marketing.html.erb │ ├── _app_nav.haml │ ├── _app_nav_marketing.html.erb │ ├── _footer.haml │ ├── _settings.haml │ └── update_billing.html.erb │ ├── builds │ └── index.html.haml │ ├── errors │ ├── 404.haml │ ├── 422.haml │ └── 500.haml │ ├── home │ ├── _code_quality.html.erb │ ├── _companies.html.erb │ ├── _company.html.erb │ ├── _guides.html.erb │ ├── _hero.html.erb │ ├── _how_it_works.html.erb │ ├── _language.html.erb │ ├── _languages.html.erb │ ├── _pricing.html.erb │ ├── _review.html.erb │ ├── _security.html.erb │ └── index.html.erb │ ├── layouts │ ├── application.html.erb │ └── marketing.html.erb │ ├── plans │ ├── _open_source.html.haml │ ├── _private.html.haml │ └── index.html.haml │ ├── repos │ ├── _github_app.haml │ ├── _onboarding.haml │ └── index.haml │ └── shared │ └── _plan_vertical.html.haml ├── babel.config.js ├── bin ├── bundle ├── bundler ├── deploy ├── production-status ├── rails ├── rake ├── rspec ├── setup ├── update ├── webpack └── webpack-dev-server ├── circle.yml ├── config.ru ├── config ├── application.rb ├── autoprefixer.yml ├── boot.rb ├── cable.yml ├── companies.yml ├── database.yml ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ ├── staging.rb │ └── test.rb ├── initializers │ ├── analytics_ruby.rb │ ├── application_controller_renderer.rb │ ├── assets.rb │ ├── backtrace_silencers.rb │ ├── constants.rb │ ├── content_security_policy.rb │ ├── cookies_serializer.rb │ ├── filter_parameter_logging.rb │ ├── high_voltage.rb │ ├── inflections.rb │ ├── inline_svg.rb │ ├── mime_types.rb │ ├── new_framework_defaults.rb │ ├── new_framework_defaults_6_0.rb │ ├── omni_auth.rb │ ├── sentry.rb │ ├── session_store.rb │ ├── sidekiq.rb │ ├── stripe.rb │ └── wrap_parameters.rb ├── languages.yml ├── locales │ └── en.yml ├── puma.rb ├── routes.rb ├── secrets.yml ├── sidekiq.yml ├── spring.rb ├── storage.yml ├── webpack │ ├── development.js │ ├── environment.js │ ├── production.js │ └── test.js └── webpacker.yml ├── db ├── migrate │ ├── 20121229003013_create_diesel_clearance_users.rb │ ├── 20121229180151_add_github_username_to_users.rb │ ├── 20130104231409_remove_clearance_columns_from_user.rb │ ├── 20130105203331_add_remember_token_to_users.rb │ ├── 20130105203459_change_github_username_null_constraint.rb │ ├── 20130121230036_add_repos.rb │ ├── 20130123030036_add_user_id_to_repos.rb │ ├── 20130123030453_add_github_token_to_users.rb │ ├── 20130228235811_add_hook_id_to_repos.rb │ ├── 20130322200146_add_name_to_repos.rb │ ├── 20130322220351_add_full_github_name_to_repos.rb │ ├── 20130524154558_create_delayed_jobs.rb │ ├── 20130614194012_add_uniqueness_constraint_to_repos.rb │ ├── 20130621203400_create_builds.rb │ ├── 20131212190854_create_memberships.rb │ ├── 20131212192202_remove_user_id_from_repos.rb │ ├── 20140120165453_add_uuid_to_builds.rb │ ├── 20140120234919_add_unique_index_for_uuid.rb │ ├── 20140327142505_remove_github_token_from_users.rb │ ├── 20140417231853_add_timestamps_to_repo.rb │ ├── 20140417232711_add_timestamps_to_memberships.rb │ ├── 20140418142740_add_index_for_remember_token_to_users.rb │ ├── 20140418144357_add_index_for_github_id_to_repos.rb │ ├── 20140419160756_add_refreshing_repos_to_users.rb │ ├── 20140419235609_add_index_for_active_to_repos.rb │ ├── 20140421183905_add_email_address_to_users.rb │ ├── 20140421191318_remove_name_from_repos.rb │ ├── 20140425212732_add_private_to_repos.rb │ ├── 20140425235458_add_in_organization_to_repos.rb │ ├── 20140711223828_create_subscriptions.rb │ ├── 20140711223913_add_stripe_customer_id_to_users.rb │ ├── 20140711223957_add_stripe_subscription_id_to_subscriptions.rb │ ├── 20140808163844_add_null_constraint_to_subscriptions_for_timestamps.rb │ ├── 20140808164104_add_index_for_user_id.rb │ ├── 20140808195409_add_deleted_at_and_price_to_subscriptions.rb │ ├── 20140808202140_remove_unique_index_from_subscriptions_for_repo_id.rb │ ├── 20140926163029_add_pull_request_info_to_builds.rb │ ├── 20141107174021_create_violations.rb │ ├── 20141210070831_drop_delayed_jobs_table.rb │ ├── 20141211224528_create_owners.rb │ ├── 20141219224840_inactivate_repos_without_privacy_or_org_info.rb │ ├── 20150108003045_add_uniqueness_on_full_github_name_to_repos.rb │ ├── 20150123224104_add_owner_to_repos.rb │ ├── 20150130051749_add_index_to_memberships_for_user_id.rb │ ├── 20150213132248_backfill_timestamps_add_null_constraints.rb │ ├── 20150217090319_add_foreign_key_memberships.rb │ ├── 20150220174603_create_style_config.rb │ ├── 20150225001118_add_unique_index_on_subscription_repo_id.rb │ ├── 20150303212157_add_unique_constraint_to_memberships.rb │ ├── 20150306184045_add_token_to_users.rb │ ├── 20150425204143_create_file_reviews.rb │ ├── 20150526214750_associate_violations_with_file_reviews.rb │ ├── 20150612231322_add_utm_source_to_users.rb │ ├── 20150619222741_add_payload_to_builds.rb │ ├── 20150710220040_associate_user_to_build.rb │ ├── 20150725013530_create_bulk_customers.rb │ ├── 20150728154011_add_token_scopes_to_users.rb │ ├── 20150731212842_remove_repo_limit_constraint_on_bulk_customers.rb │ ├── 20151019123908_remove_style_config.rb │ ├── 20151204174817_add_admin_to_memberships.rb │ ├── 20151216235118_add_violations_count_to_builds.rb │ ├── 20160314122828_add_linter_name_to_file_reviews.rb │ ├── 20160430171015_backfill_linter_names_add_null_constraint.rb │ ├── 20160513002940_add_github_id_uniqueness_on_repos.rb │ ├── 20160513205551_add_index_for_builds_pull_request_number_and_commit_sha.rb │ ├── 20160608183021_add_config_to_owner.rb │ ├── 20160624165658_update_thoughtbot_owner_configs.rb │ ├── 20160712203443_create_blacklisted_pull_requests.rb │ ├── 20161021212135_add_default_to_violations_count_on_builds.rb │ ├── 20161021231021_rename_columns_on_repos_and_users.rb │ ├── 20170505220736_add_error_to_file_reviews.rb │ ├── 20170911205516_add_whitelisted_to_owners.rb │ ├── 20180510051449_add_marketplace_plan_id_to_owners.rb │ ├── 20180613230247_add_stripe_subscription_id_to_owners.rb │ ├── 20180620003423_add_installation_id_to_users_and_repos.rb │ └── 20191212043041_add_index_for_created_at_to_builds.rb ├── schema.rb └── seeds.rb ├── doc ├── PRIVACY.md ├── SECURITY.md └── TERMS.md ├── jest.config.js ├── lib ├── assets │ └── .gitkeep ├── github_api.rb ├── migrate_stripe_subscription.rb ├── redirect_to_configuration.rb └── tasks │ ├── billing.rake │ ├── bundler_audit.rake │ ├── js.rake │ ├── plan.rake │ ├── repo.rake │ ├── user.rake │ └── webpack.rake ├── log └── .gitkeep ├── package.json ├── postcss.config.js ├── public ├── apple-touch-icon-precomposed.png ├── favicon.ico └── robots.txt ├── spec ├── controllers │ ├── accounts_controller_spec.rb │ ├── activations_controller_spec.rb │ ├── builds_controller_spec.rb │ ├── credit_cards_controller_spec.rb │ ├── deactivations_controller_spec.rb │ ├── home_controller_spec.rb │ ├── repo_syncs_controller_spec.rb │ ├── repos_controller_spec.rb │ ├── sessions_controller_spec.rb │ ├── subscriptions_controller_spec.rb │ └── users_controller_spec.rb ├── factories.rb ├── features │ ├── account_spec.rb │ ├── admin_authorization_spec.rb │ ├── admin_masquerades_spec.rb │ ├── error_pages_spec.rb │ ├── job_failures_spec.rb │ ├── plans_spec.rb │ ├── repo_list_spec.rb │ ├── user_activates_a_repo_spec.rb │ ├── user_authentication_spec.rb │ ├── user_deactivates_a_repo_spec.rb │ └── user_rebuilds_a_pull_request_spec.rb ├── helpers │ └── analytics_helper_spec.rb ├── jobs │ ├── buildable_spec.rb │ ├── completed_file_review_job_spec.rb │ ├── large_build_job_spec.rb │ ├── repo_synchronization_job_spec.rb │ └── small_build_job_spec.rb ├── lib │ ├── github_api_spec.rb │ └── tasks │ │ └── repo_spec.rb ├── models │ ├── build_spec.rb │ ├── commit_file_spec.rb │ ├── commit_spec.rb │ ├── commit_status_spec.rb │ ├── config │ │ ├── base_spec.rb │ │ ├── coffeelint_spec.rb │ │ ├── credo_spec.rb │ │ ├── erb_lint_spec.rb │ │ ├── eslint_spec.rb │ │ ├── flake8_spec.rb │ │ ├── golint_spec.rb │ │ ├── haml_lint_spec.rb │ │ ├── jshint_spec.rb │ │ ├── json_with_comments_spec.rb │ │ ├── phpcs_spec.rb │ │ ├── remark_spec.rb │ │ ├── rubocop_spec.rb │ │ ├── sass_lint_spec.rb │ │ ├── scss_lint_spec.rb │ │ ├── swiftlint_spec.rb │ │ └── tslint_spec.rb │ ├── config_content │ │ └── remote_spec.rb │ ├── config_content_spec.rb │ ├── file_review_spec.rb │ ├── github_auth_spec.rb │ ├── github_event_spec.rb │ ├── home_spec.rb │ ├── hound_config_spec.rb │ ├── ignore_file_spec.rb │ ├── job_failure_spec.rb │ ├── linter │ │ ├── base_spec.rb │ │ ├── coffeelint_spec.rb │ │ ├── credo_spec.rb │ │ ├── erb_lint_spec.rb │ │ ├── eslint_spec.rb │ │ ├── flake8_spec.rb │ │ ├── flog_spec.rb │ │ ├── go_spec.rb │ │ ├── haml_lint_spec.rb │ │ ├── jshint_spec.rb │ │ ├── phpcs_spec.rb │ │ ├── reek_spec.rb │ │ ├── remark_spec.rb │ │ ├── rubocop_spec.rb │ │ ├── sass_lint_spec.rb │ │ ├── scss_lint_spec.rb │ │ ├── shellcheck_spec.rb │ │ ├── slim_lint_spec.rb │ │ ├── stylelint_spec.rb │ │ ├── swiftlint_spec.rb │ │ └── tslint_spec.rb │ ├── membership_spec.rb │ ├── missing_owner_spec.rb │ ├── owner_spec.rb │ ├── patch_spec.rb │ ├── payload_spec.rb │ ├── payment_gateway_customer_spec.rb │ ├── payment_gateway_subscription_spec.rb │ ├── plan_selector_spec.rb │ ├── pull_request_spec.rb │ ├── repo_spec.rb │ ├── review_body_spec.rb │ ├── stripe_plan_spec.rb │ ├── subscription_spec.rb │ ├── user_spec.rb │ └── violation_spec.rb ├── policies │ └── commenting_policy_spec.rb ├── presenters │ ├── account_page_spec.rb │ ├── monthly_line_item_spec.rb │ └── plan_presenter_spec.rb ├── queries │ ├── recent_builds_by_repo_query_spec.rb │ └── repos_with_membership_or_subscription_query_spec.rb ├── rails_helper.rb ├── requests │ ├── admin │ │ └── masquerades_spec.rb │ ├── builds_spec.rb │ ├── github_events_spec.rb │ └── subscriptions_spec.rb ├── serializers │ ├── plan_serializer_spec.rb │ ├── repo_serializer_spec.rb │ └── user_serializer_spec.rb ├── services │ ├── build_config_spec.rb │ ├── build_owner_hound_config_spec.rb │ ├── complete_build_spec.rb │ ├── complete_file_review_spec.rb │ ├── delete_subscriptions_spec.rb │ ├── normalize_config_spec.rb │ ├── rebuild_pull_request_spec.rb │ ├── repo_deactivator_spec.rb │ ├── repo_subscriber_spec.rb │ ├── repo_synchronization_spec.rb │ ├── report_invalid_config_spec.rb │ ├── resolve_config_aliases_spec.rb │ ├── resolve_config_conflicts_spec.rb │ ├── review_files_spec.rb │ ├── sanitize_ini_file_spec.rb │ ├── start_build_spec.rb │ ├── submit_review_spec.rb │ ├── update_github_plans_spec.rb │ └── update_repo_status_spec.rb ├── spec_helper.rb ├── support │ ├── background_jobs.rb │ ├── fake_github.rb │ ├── features.rb │ ├── fixtures │ │ ├── config_contents.json │ │ ├── contents_with_violations.json │ │ ├── failed_hook.json │ │ ├── github_app_uninstall.json │ │ ├── github_hook_creation_response.json │ │ ├── github_installation_repositories_added.json │ │ ├── github_installation_repositories_removed.json │ │ ├── github_marketplace_purchase_cancelled.json │ │ ├── github_patch.diff │ │ ├── github_repos_response_for_jimtom.json │ │ ├── github_repos_response_for_jimtom_page2.json │ │ ├── github_status_response.json │ │ ├── legacy_coffeescript.json │ │ ├── legacy_rubocop_config.yml │ │ ├── patch.diff │ │ ├── pull_request_comments.json │ │ ├── pull_request_event_with_many_files.json │ │ ├── pull_request_files.json │ │ ├── pull_request_opened_event.json │ │ ├── pull_request_synchronize_event.json │ │ ├── push_event.json │ │ ├── stripe_customer_create.json │ │ ├── stripe_customer_find.json │ │ ├── stripe_customer_find_with_subscriptions.json │ │ ├── stripe_customer_find_with_tier2_subscription.json │ │ ├── stripe_customer_find_with_tier3_subscription.json │ │ ├── stripe_customer_update.json │ │ ├── stripe_discounted_amount_subscription.json │ │ ├── stripe_discounted_percent_subscription.json │ │ ├── stripe_individual_subscription.json │ │ ├── stripe_org_subscription.json │ │ ├── stripe_private_subscription.json │ │ ├── stripe_subscription_create.json │ │ ├── stripe_subscription_delete.json │ │ ├── stripe_subscription_find.json │ │ ├── stripe_subscription_update.json │ │ ├── thoughtbot_coffeescript.json │ │ ├── thoughtbot_rubocop_config.yml │ │ └── zen_payload.json │ ├── helpers │ │ ├── analytics_helper.rb │ │ ├── authentication_helper.rb │ │ ├── commit_file_helper.rb │ │ ├── config_helper.rb │ │ ├── fake_analytics_ruby.rb │ │ ├── github_api_helper.rb │ │ ├── https_helper.rb │ │ ├── linter_helper.rb │ │ ├── oauth_helper.rb │ │ └── stripe_api_helper.rb │ ├── linters.rb │ ├── matchers │ │ ├── have_tracked_matcher.rb │ │ └── violate_matcher.rb │ ├── shared_examples.rb │ ├── user_on_page.rb │ └── wait_for_ajax.rb └── views │ └── application │ └── _analytics.haml_spec.rb ├── vendor ├── assets │ ├── javascripts │ │ ├── .gitkeep │ │ └── namespaced.js │ └── stylesheets │ │ ├── .gitkeep │ │ └── _reset.scss └── plugins │ └── .gitkeep └── yarn.lock /.browserslistrc: -------------------------------------------------------------------------------- 1 | defaults 2 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "rules": {}, 3 | "env": { 4 | "es6": true, 5 | "node": true, 6 | "browser": true 7 | }, 8 | "extends": "eslint:recommended" 9 | } 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env.local 2 | .foreman 3 | /.bundle 4 | /db/*.sqlite3 5 | /log/*.log 6 | /tmp 7 | 8 | node_modules/ 9 | yarn-debug.log* 10 | yarn-error.log* 11 | public/packs 12 | public/packs-test 13 | /public/packs 14 | /public/packs-test 15 | /node_modules 16 | -------------------------------------------------------------------------------- /.hound.yml: -------------------------------------------------------------------------------- 1 | fail_on_violations: true 2 | 3 | rubocop: 4 | config_file: .rubocop.yml 5 | version: 0.75.0 6 | 7 | scss: 8 | enabled: false 9 | 10 | stylelint: 11 | config_file: .stylelintrc.json 12 | enabled: true 13 | 14 | eslint: 15 | enabled: true 16 | version: 5.7.0 17 | config_file: .eslintrc 18 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --require spec_helper 3 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.7.1 2 | -------------------------------------------------------------------------------- /.stylelintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@thoughtbot/stylelint-config" 3 | } 4 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: bundle exec puma -C config/puma.rb 2 | webpack_dev_server: ./bin/webpack-dev-server 3 | worker: bundle exec sidekiq 4 | release: bundle exec rake db:migrate 5 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | # Add your own tasks in files placed in lib/tasks ending in .rake, 3 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 4 | 5 | require File.expand_path('../config/application', __FILE__) 6 | 7 | Houndapp::Application.load_tasks 8 | 9 | task(:default).clear 10 | 11 | if defined? RSpec 12 | task(:spec).clear 13 | RSpec::Core::RakeTask.new(:spec) do |t| 14 | t.verbose = false 15 | end 16 | end 17 | 18 | task default: ["assets:clobber", "spec", "js:spec", "bundler:audit"] 19 | -------------------------------------------------------------------------------- /app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_tree ../images 2 | //= link_directory ../javascripts .js 3 | //= link_directory ../stylesheets .css 4 | 5 | //= link administrate/application.css 6 | //= link administrate/application.js 7 | -------------------------------------------------------------------------------- /app/assets/images/companies/18f.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/companies/18f.png -------------------------------------------------------------------------------- /app/assets/images/companies/airbrake.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/companies/airbrake.png -------------------------------------------------------------------------------- /app/assets/images/companies/bbc-news.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/companies/bbc-news.png -------------------------------------------------------------------------------- /app/assets/images/companies/blendtec.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/companies/blendtec.png -------------------------------------------------------------------------------- /app/assets/images/companies/codeship.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/companies/codeship.png -------------------------------------------------------------------------------- /app/assets/images/companies/dropbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/companies/dropbox.png -------------------------------------------------------------------------------- /app/assets/images/companies/elastic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/companies/elastic.png -------------------------------------------------------------------------------- /app/assets/images/companies/envoy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/companies/envoy.png -------------------------------------------------------------------------------- /app/assets/images/companies/envy-labs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/companies/envy-labs.png -------------------------------------------------------------------------------- /app/assets/images/companies/gitlab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/companies/gitlab.png -------------------------------------------------------------------------------- /app/assets/images/companies/heroku.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/companies/heroku.png -------------------------------------------------------------------------------- /app/assets/images/companies/hp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/companies/hp.png -------------------------------------------------------------------------------- /app/assets/images/companies/intuit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/companies/intuit.png -------------------------------------------------------------------------------- /app/assets/images/companies/joomla.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/companies/joomla.png -------------------------------------------------------------------------------- /app/assets/images/companies/lumosity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/companies/lumosity.png -------------------------------------------------------------------------------- /app/assets/images/companies/meundies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/companies/meundies.png -------------------------------------------------------------------------------- /app/assets/images/companies/middleman.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/companies/middleman.jpg -------------------------------------------------------------------------------- /app/assets/images/companies/mixpanel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/companies/mixpanel.png -------------------------------------------------------------------------------- /app/assets/images/companies/mozilla.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/companies/mozilla.png -------------------------------------------------------------------------------- /app/assets/images/companies/namely.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/companies/namely.png -------------------------------------------------------------------------------- /app/assets/images/companies/netguru.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/companies/netguru.png -------------------------------------------------------------------------------- /app/assets/images/companies/percona.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/companies/percona.png -------------------------------------------------------------------------------- /app/assets/images/companies/rackspace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/companies/rackspace.png -------------------------------------------------------------------------------- /app/assets/images/companies/rails-girls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/companies/rails-girls.png -------------------------------------------------------------------------------- /app/assets/images/companies/salesforce.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/companies/salesforce.png -------------------------------------------------------------------------------- /app/assets/images/companies/shipio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/companies/shipio.png -------------------------------------------------------------------------------- /app/assets/images/companies/shubox.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/companies/shubox.jpg -------------------------------------------------------------------------------- /app/assets/images/companies/suse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/companies/suse.png -------------------------------------------------------------------------------- /app/assets/images/companies/thoughtbot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/companies/thoughtbot.png -------------------------------------------------------------------------------- /app/assets/images/companies/treehouse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/companies/treehouse.png -------------------------------------------------------------------------------- /app/assets/images/companies/turing-school.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/companies/turing-school.png -------------------------------------------------------------------------------- /app/assets/images/companies/untuit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/companies/untuit.png -------------------------------------------------------------------------------- /app/assets/images/companies/wealthsimple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/companies/wealthsimple.png -------------------------------------------------------------------------------- /app/assets/images/companies/wildbit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/companies/wildbit.png -------------------------------------------------------------------------------- /app/assets/images/companies/yahoo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/companies/yahoo.png -------------------------------------------------------------------------------- /app/assets/images/companies/zendesk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/companies/zendesk.png -------------------------------------------------------------------------------- /app/assets/images/hatch-gray.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/assets/images/hatch-purple.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/assets/images/home/hound_flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/home/hound_flow.png -------------------------------------------------------------------------------- /app/assets/images/home/hound_flow.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/home/hound_flow.webp -------------------------------------------------------------------------------- /app/assets/images/home/review_example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/home/review_example.png -------------------------------------------------------------------------------- /app/assets/images/home/review_example.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/home/review_example.webp -------------------------------------------------------------------------------- /app/assets/images/icon.svg: -------------------------------------------------------------------------------- 1 | Hound icon 2 | -------------------------------------------------------------------------------- /app/assets/images/languages/elixir.svg: -------------------------------------------------------------------------------- 1 | Group 2 2 | -------------------------------------------------------------------------------- /app/assets/images/languages/php.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/assets/images/languages/ruby.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | Ruby 4 4 | 5 | 6 | Created with Sketch. 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/assets/images/languages/shell.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/assets/images/languages/typescript.svg: -------------------------------------------------------------------------------- 1 | Group 2 Copy 2 | -------------------------------------------------------------------------------- /app/assets/images/madi.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houndci/hound/a50bf519bcdd42817ebb35bb19c268893e7a9909/app/assets/images/madi.jpg -------------------------------------------------------------------------------- /app/assets/images/select-arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | //= require jquery3 2 | //= require jquery_ujs 3 | //= require namespaced 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/application.scss: -------------------------------------------------------------------------------- 1 | @import "bourbon"; 2 | @import "neat"; 3 | 4 | @import "normalize.css/normalize"; 5 | 6 | @import "base/base"; 7 | @import "components/components"; 8 | @import "pages/pages"; 9 | @import "utilities/utilities"; 10 | -------------------------------------------------------------------------------- /app/assets/stylesheets/base/_animations.scss: -------------------------------------------------------------------------------- 1 | @keyframes loading { 2 | 0% { 3 | opacity: 0.15; 4 | transform: scale(0.95); 5 | } 6 | 7 | 50% { 8 | opacity: 1; 9 | transform: scale(1); 10 | } 11 | 12 | 100% { 13 | opacity: 0.15; 14 | transform: scale(0.95); 15 | } 16 | } 17 | 18 | @keyframes pulse-in { 19 | 0% { 20 | opacity: 0; 21 | transform: scale(0.95); 22 | } 23 | 24 | 50% { 25 | transform: scale(1.02); 26 | } 27 | 28 | 100% { 29 | opacity: 1; 30 | transform: scale(1); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/assets/stylesheets/base/_base.scss: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | 3 | @import "animations"; 4 | @import "buttons"; 5 | @import "extends"; 6 | @import "mixins"; 7 | @import "fonts"; 8 | @import "forms"; 9 | @import "helpers"; 10 | @import "media"; 11 | @import "tables"; 12 | @import "typography"; 13 | -------------------------------------------------------------------------------- /app/assets/stylesheets/base/_buttons.scss: -------------------------------------------------------------------------------- 1 | #{$all-buttons} { 2 | font-family: $font-family-default; 3 | } 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/base/_extends.scss: -------------------------------------------------------------------------------- 1 | %content-aside { 2 | @include span-columns(3 of 12, table); 3 | @include omega; 4 | padding: 0; 5 | padding-right: 2em; 6 | vertical-align: top; 7 | } 8 | 9 | %content-main { 10 | @include span-columns(9 of 12, table); 11 | border-left: 1px solid $base-border-color; 12 | padding: 0; 13 | padding-left: 2em; 14 | } 15 | -------------------------------------------------------------------------------- /app/assets/stylesheets/base/_forms.scss: -------------------------------------------------------------------------------- 1 | #{$all-text-inputs}, 2 | select { 3 | font-family: $font-family-default; 4 | } 5 | -------------------------------------------------------------------------------- /app/assets/stylesheets/base/_helpers.scss: -------------------------------------------------------------------------------- 1 | .content { 2 | @include outer-container; 3 | } 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/base/_media.scss: -------------------------------------------------------------------------------- 1 | figure { 2 | margin: 0; 3 | } 4 | 5 | img, 6 | picture { 7 | margin: 0; 8 | max-width: 100%; 9 | } 10 | -------------------------------------------------------------------------------- /app/assets/stylesheets/base/_mixins.scss: -------------------------------------------------------------------------------- 1 | @mixin content-frame { 2 | @include clearfix; 3 | border: 1px solid $base-border-color; 4 | border-radius: 6px; 5 | padding: 2em; 6 | } 7 | 8 | -------------------------------------------------------------------------------- /app/assets/stylesheets/base/_tables.scss: -------------------------------------------------------------------------------- 1 | $_table-heading-background-color: rgb(245, 245, 245); 2 | 3 | table { 4 | border-collapse: collapse; 5 | width: 100%; 6 | 7 | tr { 8 | border: 1px solid $base-border-color; 9 | } 10 | 11 | th, 12 | td { 13 | text-align: left; 14 | } 15 | 16 | td { 17 | padding: 1em 1.1em; 18 | } 19 | 20 | th { 21 | background-color: $_table-heading-background-color; 22 | font-size: 0.8em; 23 | font-weight: $font-weight-medium; 24 | padding: 1em 1.4em; 25 | text-transform: uppercase; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/assets/stylesheets/components/_allowance.scss: -------------------------------------------------------------------------------- 1 | .allowance { 2 | border: 2px solid $base-accent-color; 3 | border-radius: 100px; 4 | color: $base-accent-color; 5 | line-height: 40px; 6 | margin-right: $base-spacing; 7 | padding: 0 $small-spacing; 8 | 9 | strong { 10 | border-left: 1px dotted $base-accent-color; 11 | margin-left: $small-spacing; 12 | padding-left: $small-spacing; 13 | } 14 | 15 | &.stressed { 16 | color: $red; 17 | } 18 | 19 | &.large { 20 | display: inline-block; 21 | font-size: 1.3em; 22 | line-height: 60px; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/assets/stylesheets/components/_app-footer.scss: -------------------------------------------------------------------------------- 1 | .app-footer { 2 | padding: $large-spacing $base-spacing; 3 | text-align: center; 4 | } 5 | 6 | .app-footer__nav { 7 | margin-bottom: $base-spacing; 8 | } 9 | 10 | .app-footer__nav-link:not(:first-of-type) { 11 | margin-left: $base-spacing; 12 | } 13 | -------------------------------------------------------------------------------- /app/assets/stylesheets/components/_avatar.scss: -------------------------------------------------------------------------------- 1 | $_avatar-size: 2rem; 2 | 3 | .avatar { 4 | @include size($_avatar-size); 5 | background-color: $light-gray; 6 | border-radius: 50%; 7 | display: inline-block; 8 | overflow: hidden; 9 | position: relative; 10 | vertical-align: middle; 11 | 12 | &::after { 13 | @include position(absolute, 0); 14 | border-radius: 50%; 15 | box-shadow: inset 0 0 0 1px rgba($black, 0.1); 16 | content: ""; 17 | pointer-events: none; 18 | } 19 | 20 | img { 21 | @include size($_avatar-size); 22 | object-fit: cover; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/assets/stylesheets/components/_badge.scss: -------------------------------------------------------------------------------- 1 | .badge { 2 | border: 1px solid $light-gray; 3 | border-radius: 3px; 4 | color: $base-font-color; 5 | display: inline-block; 6 | font-size: $font-size-small; 7 | line-height: 1; 8 | margin: 0; 9 | padding: 0.125em 0.25em; 10 | } 11 | -------------------------------------------------------------------------------- /app/assets/stylesheets/components/_code.scss: -------------------------------------------------------------------------------- 1 | .code, 2 | .code-block { 3 | background-color: $code-background-color; 4 | display: inline-block; 5 | font-family: $font-family-code; 6 | font-style: normal; 7 | } 8 | 9 | .code-block { 10 | border: 1px solid $code-background-color; 11 | border-radius: 3px; 12 | display: block; 13 | margin: 1em 0 2em; 14 | padding: 1em; 15 | white-space: pre-wrap; 16 | } 17 | 18 | .code-inline { 19 | border-radius: 2px; 20 | padding: 0.1em; 21 | } 22 | -------------------------------------------------------------------------------- /app/assets/stylesheets/components/_components.scss: -------------------------------------------------------------------------------- 1 | @import "allowance"; 2 | @import "app-footer"; 3 | @import "app-nav"; 4 | @import "avatar"; 5 | @import "badge"; 6 | @import "button"; 7 | @import "code"; 8 | @import "icon"; 9 | @import "inline-flash"; 10 | @import "logo"; 11 | @import "plan-markers"; 12 | @import "pricing"; 13 | @import "toggle-switch"; 14 | -------------------------------------------------------------------------------- /app/assets/stylesheets/components/_icon.scss: -------------------------------------------------------------------------------- 1 | .icon { 2 | @include size(1em); 3 | fill: currentcolor; 4 | } 5 | -------------------------------------------------------------------------------- /app/assets/stylesheets/components/_inline-flash.scss: -------------------------------------------------------------------------------- 1 | .inline-flash { 2 | margin: 0; 3 | } 4 | 5 | .inline-flash--visible { 6 | display: block; 7 | } 8 | 9 | .inline-flash--success { 10 | color: darken($green, 20%); 11 | } 12 | 13 | .inline-flash--error { 14 | color: $red; 15 | } 16 | -------------------------------------------------------------------------------- /app/assets/stylesheets/components/_logo.scss: -------------------------------------------------------------------------------- 1 | $_logo-aspect-ratio: 217 / 50; 2 | $_logo-height: 2.5rem; 3 | $_logo-color: $purple; 4 | 5 | .logo { 6 | color: $_logo-color; 7 | height: $_logo-height; 8 | transition: color $base-transition-timing $base-easing; 9 | width: $_logo-height * $_logo-aspect-ratio; 10 | 11 | &:hover { 12 | color: shade($_logo-color, 15%); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /app/assets/stylesheets/components/_plan-markers.scss: -------------------------------------------------------------------------------- 1 | .plan-marker { 2 | border-radius: 100px; 3 | display: inline-block; 4 | font-size: 0.8em; 5 | margin-bottom: $small-spacing; 6 | padding: 0 1em; 7 | text-transform: uppercase; 8 | } 9 | 10 | .plan-marker--current { 11 | background-color: lighten($gold, 50%); 12 | border: 1px solid lighten($gold, 3%); 13 | color: darken($gold, 10%); 14 | } 15 | 16 | .plan-marker--new { 17 | background-color: lighten($green, 10%); 18 | border: 1px solid darken($green, 20%); 19 | color: darken($green, 40%); 20 | } 21 | 22 | .plan-marker__wrapper { 23 | display: block; 24 | } 25 | -------------------------------------------------------------------------------- /app/assets/stylesheets/marketing/components/_app-footer.scss: -------------------------------------------------------------------------------- 1 | .c-app-footer { 2 | padding: 5rem 2rem; 3 | text-align: center; 4 | } 5 | 6 | .c-app-footer__list { 7 | --link-text-decoration: none; 8 | --link-text-decoration-hover: underline; 9 | 10 | align-items: center; 11 | display: flex; 12 | gap: 2rem; 13 | justify-content: center; 14 | margin-bottom: 1rem; 15 | } 16 | -------------------------------------------------------------------------------- /app/assets/stylesheets/marketing/components/_page-hero.scss: -------------------------------------------------------------------------------- 1 | .c-page-hero { 2 | padding-bottom: 6rem; 3 | padding-top: 6rem; 4 | text-align: center; 5 | } 6 | 7 | .c-page-hero__heading { 8 | font-size: 3.5rem; 9 | } 10 | 11 | .c-page-hero__lede { 12 | font-size: 1.25rem; 13 | margin-top: 2rem; 14 | } 15 | 16 | .c-page-hero__cta { 17 | margin-top: 2rem; 18 | } 19 | -------------------------------------------------------------------------------- /app/assets/stylesheets/marketing/components/_plan.scss: -------------------------------------------------------------------------------- 1 | .c-plan { 2 | background-image: image-url("hatch-gray.svg"); 3 | background-repeat: repeat-y; 4 | border: 2px solid currentColor; 5 | border-radius: 5px; 6 | display: flex; 7 | flex-direction: column; 8 | padding: 1.25rem 1.25rem 1.25rem calc(1.25rem + 10px); 9 | } 10 | 11 | .c-plan--private { 12 | background-image: image-url("hatch-purple.svg"); 13 | color: $purple; 14 | } 15 | 16 | .c-plan__title { 17 | font-size: 1.5rem; 18 | font-weight: $font-weight-medium; 19 | line-height: var(--line-height-heading); 20 | margin-bottom: 0.25rem; 21 | } 22 | 23 | .c-plan__allowance { 24 | font-size: 0.875rem; 25 | margin-bottom: 1rem; 26 | } 27 | 28 | .c-plan__price { 29 | margin-top: auto; 30 | } 31 | 32 | .c-plan__dollar-amount { 33 | font-size: 1.5rem; 34 | } 35 | -------------------------------------------------------------------------------- /app/assets/stylesheets/marketing/components/_text-partition.scss: -------------------------------------------------------------------------------- 1 | $_text-partition-stroke-height: 0.125em; 2 | 3 | .c-text-partition { 4 | align-items: center; 5 | column-gap: 1em; 6 | display: grid; 7 | grid-template-columns: 23% auto 23%; 8 | justify-content: center; 9 | letter-spacing: 0.125em; 10 | text-transform: uppercase; 11 | width: 100%; 12 | 13 | &::before, 14 | &::after { 15 | background-color: var(--color-border); 16 | content: ""; 17 | height: $_text-partition-stroke-height; 18 | position: relative; 19 | top: -($_text-partition-stroke-height / 2); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/assets/stylesheets/marketing/elements/_layout.scss: -------------------------------------------------------------------------------- 1 | html { 2 | background-color: var(--color-background); 3 | 4 | @media (prefers-reduced-motion: no-preference) { 5 | scroll-behavior: smooth; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /app/assets/stylesheets/marketing/generic/_reset.scss: -------------------------------------------------------------------------------- 1 | html { 2 | box-sizing: border-box; 3 | } 4 | 5 | *, 6 | *::before, 7 | *::after { 8 | box-sizing: inherit; 9 | } 10 | 11 | body { 12 | line-height: inherit; 13 | } 14 | 15 | img { 16 | height: auto; 17 | max-width: 100%; 18 | } 19 | 20 | [hidden] { 21 | // stylelint-disable-next-line declaration-no-important 22 | display: none !important; 23 | } 24 | -------------------------------------------------------------------------------- /app/assets/stylesheets/marketing/objects/_container.scss: -------------------------------------------------------------------------------- 1 | .o-container { 2 | margin-left: auto; 3 | margin-right: auto; 4 | max-width: 70rem; 5 | padding-left: 1.25rem; 6 | padding-right: 1.25rem; 7 | } 8 | 9 | .o-container--small { 10 | max-width: 48rem; 11 | } 12 | -------------------------------------------------------------------------------- /app/assets/stylesheets/marketing/objects/_list-bare.scss: -------------------------------------------------------------------------------- 1 | .o-list-bare { 2 | list-style-type: none; 3 | margin: 0; 4 | padding: 0; 5 | } 6 | -------------------------------------------------------------------------------- /app/assets/stylesheets/marketing/settings/_breakpoints.scss: -------------------------------------------------------------------------------- 1 | $breakpoints: ( 2 | "small": 30em, 3 | "medium": 48em, 4 | "large": 60em, 5 | ); 6 | 7 | $breakpoint-small: map-get($breakpoints, "small"); 8 | $breakpoint-medium: map-get($breakpoints, "medium"); 9 | $breakpoint-large: map-get($breakpoints, "large"); 10 | -------------------------------------------------------------------------------- /app/assets/stylesheets/marketing/settings/_color.scss: -------------------------------------------------------------------------------- 1 | $black: #000; 2 | $white: #fff; 3 | 4 | $gray: #6a737d; 5 | $gray-dark: #586069; 6 | 7 | $purple: #a873d1; 8 | $purple-light: #fbf8fd; 9 | $purple-dark: #8f62b2; 10 | 11 | html { 12 | --color-background: #{$white}; 13 | --color-border: #dcdcdc; 14 | 15 | --color-text: #{$gray}; 16 | --color-text-heading: #{$gray-dark}; 17 | --color-text-link: var(--color-text); 18 | --color-text-link-hover: #{$purple}; 19 | } 20 | -------------------------------------------------------------------------------- /app/assets/stylesheets/marketing/settings/_controls.scss: -------------------------------------------------------------------------------- 1 | $control-block-size: 3rem; 2 | $control-block-size-large: 4rem; 3 | -------------------------------------------------------------------------------- /app/assets/stylesheets/marketing/settings/_transitions.scss: -------------------------------------------------------------------------------- 1 | html { 2 | @media (prefers-reduced-motion: no-preference) { 3 | --transition-duration: 200ms; 4 | --transition-timing: cubic-bezier(0.39, 0.575, 0.565, 1); 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /app/assets/stylesheets/marketing/settings/_typography.scss: -------------------------------------------------------------------------------- 1 | $font-family-body: "Tofino", ui-sans-serif, system-ui, -apple-system, sans-serif; 2 | $font-family-heading: $font-family-body; 3 | $font-family-code: "Menlo", "Monaco", ui-monospace, monospace; 4 | 5 | $font-weights: ( 6 | "light": 300, 7 | "normal": 400, 8 | "medium": 500, 9 | ); 10 | 11 | $font-weight-light: map-get($font-weights, "light"); 12 | $font-weight-normal: map-get($font-weights, "normal"); 13 | $font-weight-medium: map-get($font-weights, "medium"); 14 | 15 | html { 16 | --line-height-body: 1.5; 17 | --line-height-heading: 1.2; 18 | 19 | --link-text-decoration: underline; 20 | --link-text-decoration-hover: none; 21 | } 22 | -------------------------------------------------------------------------------- /app/assets/stylesheets/marketing/utilities/_font-weight.scss: -------------------------------------------------------------------------------- 1 | @each $name, $weight in $font-weights { 2 | .u-font-weight-#{$name} { 3 | font-weight: $weight; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /app/assets/stylesheets/marketing/utilities/_line-height.scss: -------------------------------------------------------------------------------- 1 | .u-line-height-0 { 2 | line-height: 0; 3 | } 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/marketing/utilities/_margin.scss: -------------------------------------------------------------------------------- 1 | .u-margin-bottom-2 { 2 | margin-bottom: 0.5rem; 3 | } 4 | 5 | .u-margin-top-2 { 6 | margin-top: 1.5rem; 7 | } 8 | 9 | .u-margin-top-4 { 10 | margin-top: 3rem; 11 | } 12 | -------------------------------------------------------------------------------- /app/assets/stylesheets/marketing/utilities/_text-align.scss: -------------------------------------------------------------------------------- 1 | .u-text-align-start { 2 | text-align: left; 3 | 4 | @supports (text-align: start) { 5 | text-align: start; 6 | } 7 | } 8 | 9 | .u-text-align-center { 10 | text-align: center; 11 | } 12 | 13 | .u-text-align-end { 14 | text-align: left; 15 | 16 | @supports (text-align: end) { 17 | text-align: end; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/assets/stylesheets/pages/_pages.scss: -------------------------------------------------------------------------------- 1 | @import "accounts/accounts"; 2 | @import "repos/repos"; 3 | @import "builds"; 4 | @import "tier_change"; 5 | -------------------------------------------------------------------------------- /app/assets/stylesheets/pages/accounts/_accounts.scss: -------------------------------------------------------------------------------- 1 | @import "show"; 2 | -------------------------------------------------------------------------------- /app/assets/stylesheets/pages/repos/_repos.scss: -------------------------------------------------------------------------------- 1 | @import "index"; 2 | @import "onboarding"; 3 | @import "organization"; 4 | @import "syncing"; 5 | @import "tools"; 6 | -------------------------------------------------------------------------------- /app/assets/stylesheets/pages/repos/_syncing.scss: -------------------------------------------------------------------------------- 1 | .repos-syncing { 2 | background-color: $white; 3 | padding: 4em 1em; 4 | text-align: center; 5 | 6 | .dot { 7 | @include size(24px); 8 | animation: loading 1.3s ease-out; 9 | animation-iteration-count: infinite; 10 | background-color: rgba($green, 0.1); 11 | border: 1px solid $green; 12 | border-radius: 100%; 13 | display: inline-block; 14 | margin: 0 1.2em; 15 | opacity: 0.1; 16 | 17 | &:nth-child(2) { 18 | animation-delay: 0.2s; 19 | } 20 | 21 | &:nth-child(3) { 22 | animation-delay: 0.4s; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/assets/stylesheets/utilities/_clearfix.scss: -------------------------------------------------------------------------------- 1 | .clearfix { 2 | @include clearfix; 3 | } 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/utilities/_line-height.scss: -------------------------------------------------------------------------------- 1 | .line-height-zero { 2 | line-height: 0; 3 | } 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/utilities/_margin.scss: -------------------------------------------------------------------------------- 1 | .margin-right-x-small { 2 | margin-right: $xsmall-spacing !important; 3 | } 4 | 5 | .margin-left-small { 6 | margin-left: $small-spacing !important; 7 | } 8 | 9 | .margin-top-large { 10 | margin-top: $large-spacing; 11 | } 12 | -------------------------------------------------------------------------------- /app/assets/stylesheets/utilities/_utilities.scss: -------------------------------------------------------------------------------- 1 | @import "clearfix"; 2 | @import "line-height"; 3 | @import "margin"; 4 | -------------------------------------------------------------------------------- /app/controllers/activations_controller.rb: -------------------------------------------------------------------------------- 1 | class ActivationsController < ApplicationController 2 | class CannotActivatePaidRepo < StandardError; end 3 | 4 | before_action :ensure_repo_allowed 5 | 6 | def create 7 | repo.activate 8 | 9 | render json: repo, status: :created 10 | end 11 | 12 | private 13 | 14 | def ensure_repo_allowed 15 | if repo.private? 16 | raise CannotActivatePaidRepo 17 | end 18 | end 19 | 20 | def repo 21 | @repo ||= current_user.repos.find(params[:repo_id]) 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /app/controllers/admin/application_controller.rb: -------------------------------------------------------------------------------- 1 | module Admin 2 | class ApplicationController < Administrate::ApplicationController 3 | before_action :authenticate_admin 4 | 5 | helper_method :navigation_resources 6 | 7 | private 8 | 9 | def navigation_resources 10 | Administrate::Namespace.new(namespace).resources.select do |resource| 11 | DashboardManifest::DASHBOARDS.include?(resource.name) 12 | end 13 | end 14 | 15 | def authenticate_admin 16 | unless github_admin? 17 | redirect_to :root 18 | end 19 | end 20 | 21 | def github_admin? 22 | current_user && 23 | Hound::ADMIN_GITHUB_USERNAMES.include?(current_user.username) 24 | end 25 | 26 | def current_user 27 | @current_user ||= User.find_by(remember_token: session[:remember_token]) 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /app/controllers/admin/blacklisted_pull_requests_controller.rb: -------------------------------------------------------------------------------- 1 | module Admin 2 | class BlacklistedPullRequestsController < Admin::ApplicationController 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/controllers/admin/job_failures_controller.rb: -------------------------------------------------------------------------------- 1 | module Admin 2 | class JobFailuresController < Admin::ApplicationController 3 | def index 4 | render locals: { resources: JobFailure.grouped } 5 | end 6 | 7 | def destroy 8 | JobFailure.remove(params[:ids]) 9 | 10 | redirect_to action: :index 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/controllers/admin/masquerades_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | module Admin 3 | class MasqueradesController < Admin::ApplicationController 4 | def show 5 | requested_user = User.find_by(username: params[:username]) 6 | session[:masqueraded_user_id] = requested_user.id 7 | 8 | redirect_to repos_path 9 | end 10 | 11 | def destroy 12 | session.delete(:masqueraded_user_id) 13 | 14 | redirect_to repos_path 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/controllers/admin/owners_controller.rb: -------------------------------------------------------------------------------- 1 | module Admin 2 | class OwnersController < Admin::ApplicationController 3 | before_action :default_params 4 | 5 | private 6 | 7 | def default_params 8 | params[:order] ||= :whitelisted 9 | params[:direction] ||= :desc 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/controllers/credit_cards_controller.rb: -------------------------------------------------------------------------------- 1 | class CreditCardsController < ApplicationController 2 | class CreditCardUpdateFailed < StandardError; end 3 | 4 | def update 5 | customer = PaymentGatewayCustomer.new(current_user) 6 | 7 | if customer.update_card(params[:card_token]) 8 | head 200 9 | else 10 | report_error 11 | head 422 12 | end 13 | end 14 | 15 | private 16 | 17 | def report_error 18 | exception = CreditCardUpdateFailed.new( 19 | "Credit card failed to update for user #{current_user.id}" 20 | ) 21 | Raven.capture_exception(exception) 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /app/controllers/deleted_subscriptions_controller.rb: -------------------------------------------------------------------------------- 1 | class DeletedSubscriptionsController < ApplicationController 2 | skip_before_action :authenticate, only: :create 3 | 4 | def create 5 | DeleteSubscriptions.call(params) 6 | 7 | head :ok 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/controllers/errors_controller.rb: -------------------------------------------------------------------------------- 1 | class ErrorsController < ApplicationController 2 | skip_before_action :authenticate 3 | 4 | def show 5 | render view, status: status_code 6 | end 7 | 8 | private 9 | 10 | def view 11 | status_code.to_s 12 | end 13 | 14 | def status_code 15 | params[:code] || 500 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/controllers/home_controller.rb: -------------------------------------------------------------------------------- 1 | class HomeController < ApplicationController 2 | skip_before_action :authenticate, only: [:index] 3 | 4 | before_action :redirect_to_repos, if: :signed_in? 5 | 6 | layout "marketing" 7 | 8 | def index 9 | @home = Home.new(current_user || guest) 10 | @companies = YAML.safe_load(File.read("config/companies.yml")) 11 | @languages = YAML.safe_load(File.read("config/languages.yml")) 12 | end 13 | 14 | private 15 | 16 | def redirect_to_repos 17 | redirect_to repos_path 18 | end 19 | 20 | def guest 21 | User.new 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /app/controllers/owners_controller.rb: -------------------------------------------------------------------------------- 1 | class OwnersController < ApplicationController 2 | def update 3 | if owner.update(owner_params) 4 | render json: owner 5 | end 6 | end 7 | 8 | private 9 | 10 | def owner 11 | @_owner ||= Owner.find(params[:id]) 12 | end 13 | 14 | def owner_params 15 | params.require(:owner).permit(:config_enabled, :config_repo) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/controllers/pages_controller.rb: -------------------------------------------------------------------------------- 1 | class PagesController < ApplicationController 2 | include HighVoltage::StaticPage 3 | 4 | skip_before_action :authenticate 5 | end 6 | -------------------------------------------------------------------------------- /app/controllers/rebuilds_controller.rb: -------------------------------------------------------------------------------- 1 | class RebuildsController < ApplicationController 2 | def create 3 | RebuildPullRequest.call( 4 | repo: repo, 5 | pull_request_number: pull_request_number, 6 | ) 7 | flash[:notice] = t(".success") 8 | 9 | redirect_to builds_path 10 | end 11 | 12 | private 13 | 14 | def repo 15 | current_user.repos.find(params[:repo_id]) 16 | end 17 | 18 | def pull_request_number 19 | params.require(:rebuild)[:pull_request_number] 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /app/controllers/repo_syncs_controller.rb: -------------------------------------------------------------------------------- 1 | class RepoSyncsController < ApplicationController 2 | def create 3 | unless current_user.refreshing_repos? 4 | current_user.update(refreshing_repos: true) 5 | 6 | RepoSynchronizationJob.perform_async(current_user.id) 7 | end 8 | 9 | head 201 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/controllers/repos_controller.rb: -------------------------------------------------------------------------------- 1 | class ReposController < ApplicationController 2 | def index 3 | respond_to do |format| 4 | format.html 5 | format.json do 6 | repos = ReposWithMembershipOrSubscriptionQuery.call(current_user) 7 | 8 | render json: repos 9 | end 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/controllers/setups_controller.rb: -------------------------------------------------------------------------------- 1 | class SetupsController < ApplicationController 2 | prepend_before_action :save_installation_id 3 | 4 | def show 5 | if session[:installation_id] 6 | ids = current_user.installation_ids | [session[:installation_id].to_i] 7 | current_user.update!(installation_ids: ids, repos: []) 8 | end 9 | 10 | redirect_to repos_path 11 | end 12 | 13 | private 14 | 15 | def save_installation_id 16 | if params[:installation_id] 17 | session[:installation_id] = params[:installation_id] 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /app/controllers/users_controller.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | def show 3 | render json: current_user 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/dashboards/blacklisted_pull_request_dashboard.rb: -------------------------------------------------------------------------------- 1 | require "administrate/base_dashboard" 2 | 3 | class BlacklistedPullRequestDashboard < Administrate::BaseDashboard 4 | ATTRIBUTE_TYPES = { 5 | full_repo_name: Field::String, 6 | pull_request_number: Field::Number, 7 | }.freeze 8 | 9 | COLLECTION_ATTRIBUTES = [ 10 | :full_repo_name, 11 | :pull_request_number, 12 | ].freeze 13 | 14 | SHOW_PAGE_ATTRIBUTES = [ 15 | :full_repo_name, 16 | :pull_request_number, 17 | ].freeze 18 | 19 | FORM_ATTRIBUTES = [ 20 | :full_repo_name, 21 | :pull_request_number, 22 | ].freeze 23 | end 24 | -------------------------------------------------------------------------------- /app/dashboards/dashboard_manifest.rb: -------------------------------------------------------------------------------- 1 | class DashboardManifest 2 | DASHBOARDS = [ 3 | :blacklisted_pull_requests, 4 | :job_failures, 5 | :owners, 6 | ] 7 | ROOT_DASHBOARD = :owners 8 | end 9 | -------------------------------------------------------------------------------- /app/dashboards/job_failure_dashboard.rb: -------------------------------------------------------------------------------- 1 | require "administrate/custom_dashboard" 2 | 3 | class JobFailureDashboard < Administrate::CustomDashboard 4 | resource "JobFailure" 5 | end 6 | -------------------------------------------------------------------------------- /app/helpers/admin/job_failures_helper.rb: -------------------------------------------------------------------------------- 1 | module Admin 2 | module JobFailuresHelper 3 | def latest_failed_at(job_failures) 4 | latest_failed = job_failures.max_by(&:failed_at).failed_at 5 | 6 | Time.zone.at(latest_failed).strftime("%l:%M%P, %b %e") 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/helpers/analytics_helper.rb: -------------------------------------------------------------------------------- 1 | module AnalyticsHelper 2 | def analytics? 3 | Hound::SEGMENT_KEY.present? 4 | end 5 | 6 | def identify_hash(user = current_user) 7 | { 8 | created: user.created_at, 9 | email: user.email, 10 | username: user.username, 11 | user_id: user.id, 12 | active_repo_ids: user.active_repos.ids, 13 | } 14 | end 15 | 16 | def intercom_hash(user = current_user) 17 | { 18 | "Intercom" => { 19 | userHash: OpenSSL::HMAC.hexdigest( 20 | "sha256", 21 | Hound::INTERCOM_API_SECRET, 22 | user.id.to_s 23 | ) 24 | } 25 | } 26 | end 27 | 28 | def campaign_hash 29 | { 30 | context: { 31 | campaign: session[:campaign_params] 32 | } 33 | } 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | def avatar_url(user) 3 | "https://github.com/#{user.username}.png?size=64" 4 | end 5 | 6 | def display_onboarding? 7 | !current_user.has_active_repos? || current_user.builds.none? 8 | end 9 | 10 | def new_window_options(options = {}) 11 | options.merge(target: "_blank", rel: "noopener noreferrer") 12 | end 13 | 14 | def svg(file_name, options = {}) 15 | if options[:title].present? 16 | options[:aria] = true 17 | else 18 | options[:aria_hidden] = true 19 | end 20 | 21 | inline_svg_tag(file_name, options) 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /app/helpers/flashes_helper.rb: -------------------------------------------------------------------------------- 1 | module FlashesHelper 2 | def user_facing_flashes 3 | flash.to_hash.slice("notice", "error") 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/helpers/repos_helper.rb: -------------------------------------------------------------------------------- 1 | module ReposHelper 2 | def switch(repo) 3 | current_state = repo.active ? 'off' : 'on' 4 | link_to(current_state, '#', { 'data-github-id' => repo.github_id }) 5 | end 6 | 7 | def has_active_repos?(user) 8 | user.repos.any? && user.has_active_repos? 9 | end 10 | 11 | def has_no_builds?(user) 12 | user.has_active_repos? && user.builds.none? 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /app/javascript/__tests__/RepoToolsAdd.test.jsx: -------------------------------------------------------------------------------- 1 | import RepoToolsAdd from '../components/ReposContainer/components/RepoTools/RepoToolsAdd'; 2 | 3 | describe('RepoToolsAdd component', () => { 4 | it('renders the button to add repos', () => { 5 | const wrapper = shallow(); 6 | 7 | expect(wrapper).toMatchSnapshot(); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /app/javascript/__tests__/__snapshots__/RepoToolsAdd.test.jsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`RepoToolsAdd component renders the button to add repos 1`] = ` 4 |
7 | 11 | Add GitHub repos 12 | 13 |
14 | `; 15 | -------------------------------------------------------------------------------- /app/javascript/__tests__/__snapshots__/empty_repo_list-test.jsx.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`renders an empty unordered list 1`] = ` 4 |