From bf9526739b5c90790907c1d8b9410dd339e3d395 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Mon, 4 Apr 2016 17:23:43 +0200 Subject: Rebase repo check MR --- app/controllers/admin/projects_controller.rb | 24 ++++++++++- app/mailers/repo_check_mailer.rb | 16 ++++++++ app/views/admin/logs/show.html.haml | 3 +- app/views/admin/projects/index.html.haml | 15 ++++++- app/views/admin/projects/show.html.haml | 32 +++++++++++++++ app/views/repo_check_mailer/notify.html.haml | 5 +++ app/views/repo_check_mailer/notify.text.haml | 3 ++ app/workers/admin_email_worker.rb | 12 ++++++ app/workers/repo_check_worker.rb | 46 ++++++++++++++++++++++ app/workers/single_repo_check_worker.rb | 34 ++++++++++++++++ config/gitlab.yml.example | 7 ++++ config/initializers/1_settings.rb | 7 +++- config/routes.rb | 5 +++ .../20160315135439_project_add_repo_check.rb | 6 +++ db/schema.rb | 2 + doc/administration/repo_checks.md | 39 ++++++++++++++++++ lib/gitlab/repo_check_logger.rb | 7 ++++ spec/features/admin/admin_projects_spec.rb | 37 ++++++++++++++++- spec/mailers/repo_check_mailer_spec.rb | 21 ++++++++++ spec/workers/repo_check_worker_spec.rb | 31 +++++++++++++++ 20 files changed, 346 insertions(+), 6 deletions(-) create mode 100644 app/mailers/repo_check_mailer.rb create mode 100644 app/views/repo_check_mailer/notify.html.haml create mode 100644 app/views/repo_check_mailer/notify.text.haml create mode 100644 app/workers/admin_email_worker.rb create mode 100644 app/workers/repo_check_worker.rb create mode 100644 app/workers/single_repo_check_worker.rb create mode 100644 db/migrate/20160315135439_project_add_repo_check.rb create mode 100644 doc/administration/repo_checks.md create mode 100644 lib/gitlab/repo_check_logger.rb create mode 100644 spec/mailers/repo_check_mailer_spec.rb create mode 100644 spec/workers/repo_check_worker_spec.rb diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb index 4089091d569..b8c276fb1bb 100644 --- a/app/controllers/admin/projects_controller.rb +++ b/app/controllers/admin/projects_controller.rb @@ -1,5 +1,5 @@ class Admin::ProjectsController < Admin::ApplicationController - before_action :project, only: [:show, :transfer] + before_action :project, only: [:show, :transfer, :repo_check] before_action :group, only: [:show, :transfer] def index @@ -8,6 +8,7 @@ class Admin::ProjectsController < Admin::ApplicationController @projects = @projects.where("visibility_level IN (?)", params[:visibility_levels]) if params[:visibility_levels].present? @projects = @projects.with_push if params[:with_push].present? @projects = @projects.abandoned if params[:abandoned].present? + @projects = @projects.where(last_repo_check_failed: true) if params[:last_repo_check_failed].present? @projects = @projects.non_archived unless params[:with_archived].present? @projects = @projects.search(params[:name]) if params[:name].present? @projects = @projects.sort(@sort = params[:sort]) @@ -30,6 +31,27 @@ class Admin::ProjectsController < Admin::ApplicationController redirect_to admin_namespace_project_path(@project.namespace, @project) end + def repo_check + SingleRepoCheckWorker.perform_async(@project.id) + + redirect_to( + admin_namespace_project_path(@project.namespace, @project), + notice: 'Repo check was triggered' + ) + end + + def clear_repo_check_states + Project.update_all( + last_repo_check_failed: false, + last_repo_check_at: nil + ) + + redirect_to( + admin_namespaces_projects_path, + notice: 'All project repo check states were cleared' + ) + end + protected def project diff --git a/app/mailers/repo_check_mailer.rb b/app/mailers/repo_check_mailer.rb new file mode 100644 index 00000000000..d98533f120d --- /dev/null +++ b/app/mailers/repo_check_mailer.rb @@ -0,0 +1,16 @@ +class RepoCheckMailer < BaseMailer + include ActionView::Helpers::TextHelper + + def notify(failed_count) + if failed_count == 1 + @message = "One project failed its last repository check" + else + @message = "#{failed_count} projects failed their last repository check" + end + + mail( + to: User.admins.pluck(:email), + subject: @message + ) + end +end diff --git a/app/views/admin/logs/show.html.haml b/app/views/admin/logs/show.html.haml index af9fdeb0734..abcc93f4046 100644 --- a/app/views/admin/logs/show.html.haml +++ b/app/views/admin/logs/show.html.haml @@ -1,6 +1,7 @@ - page_title "Logs" - loggers = [Gitlab::GitLogger, Gitlab::AppLogger, - Gitlab::ProductionLogger, Gitlab::SidekiqLogger] + Gitlab::ProductionLogger, Gitlab::SidekiqLogger, + Gitlab::RepoCheckLogger] %ul.nav-links.log-tabs - loggers.each do |klass| %li{ class: (klass == Gitlab::GitLogger ? 'active' : '') } diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml index d39c0f44031..ed360f2f012 100644 --- a/app/views/admin/projects/index.html.haml +++ b/app/views/admin/projects/index.html.haml @@ -3,7 +3,7 @@ .row.prepend-top-default %aside.col-md-3 - .admin-filter + .panel.admin-filter = form_tag admin_namespaces_projects_path, method: :get, class: '' do .form-group = label_tag :name, 'Name:' @@ -38,11 +38,22 @@ %span.descr = visibility_level_icon(level) = label - %hr + %fieldset + %strong Problems + .checkbox + = label_tag :last_repo_check_failed do + = check_box_tag :last_repo_check_failed, 1, params[:last_repo_check_failed] + %span Last repo check failed + = hidden_field_tag :sort, params[:sort] = button_tag "Search", class: "btn submit btn-primary" = link_to "Reset", admin_namespaces_projects_path, class: "btn btn-cancel" + .panel.panel-default.repo-check-states + .panel-heading + Repo check states + .panel-body + = link_to 'Clear all', clear_repo_check_states_admin_namespace_projects_path(0), data: { confirm: 'This will clear repo check states for ALL projects in the database. This cannot be undone. Are you sure?' }, method: :put, class: "btn btn-sm btn-remove" %section.col-md-9 .panel.panel-default .panel-heading diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml index c638c32a654..a7a3f6349ef 100644 --- a/app/views/admin/projects/show.html.haml +++ b/app/views/admin/projects/show.html.haml @@ -5,6 +5,14 @@ %i.fa.fa-pencil-square-o Edit %hr +- if @project.last_repo_check_failed? + .row + .col-md-12 + .panel + .panel-heading.alert.alert-danger + Last repo check failed. See + = link_to 'repocheck.log', admin_logs_path + for error messages. .row .col-md-6 .panel.panel-default @@ -95,6 +103,30 @@ .col-sm-offset-2.col-sm-10 = f.submit 'Transfer', class: 'btn btn-primary' + .panel.panel-default.repo-check + .panel-heading + Repo check + .panel-body + = form_for @project, url: repo_check_admin_namespace_project_path(@project.namespace, @project), method: :post do |f| + .form-group + - if @project.last_repo_check_at.nil? + This repository has never been checked. + - else + This repository was last checked + = @project.last_repo_check_at.to_s(:medium) + '.' + The check + - if @project.last_repo_check_failed? + = succeed '.' do + %strong.cred failed + See + = link_to 'repocheck.log', admin_logs_path + for error messages. + - else + passed. + + .form-group + = f.submit 'Trigger repo check', class: 'btn btn-primary' + .col-md-6 - if @group .panel.panel-default diff --git a/app/views/repo_check_mailer/notify.html.haml b/app/views/repo_check_mailer/notify.html.haml new file mode 100644 index 00000000000..ef0016f9d3e --- /dev/null +++ b/app/views/repo_check_mailer/notify.html.haml @@ -0,0 +1,5 @@ +%p + #{@message}. + +%p + = link_to "See the affected projects in the GitLab admin panel", admin_namespaces_projects_url(last_repo_check_failed: 1) diff --git a/app/views/repo_check_mailer/notify.text.haml b/app/views/repo_check_mailer/notify.text.haml new file mode 100644 index 00000000000..bdf8c2ad675 --- /dev/null +++ b/app/views/repo_check_mailer/notify.text.haml @@ -0,0 +1,3 @@ +#{@message}. +\ +View details: #{admin_namespaces_projects_url(last_repo_check_failed: 1)} diff --git a/app/workers/admin_email_worker.rb b/app/workers/admin_email_worker.rb new file mode 100644 index 00000000000..fcccb9ea669 --- /dev/null +++ b/app/workers/admin_email_worker.rb @@ -0,0 +1,12 @@ +class AdminEmailWorker + include Sidekiq::Worker + + sidekiq_options retry: false # this job auto-repeats via sidekiq-cron + + def perform + repo_check_failed_count = Project.where(last_repo_check_failed: true).count + return if repo_check_failed_count.zero? + + RepoCheckMailer.notify(repo_check_failed_count).deliver_now + end +end diff --git a/app/workers/repo_check_worker.rb b/app/workers/repo_check_worker.rb new file mode 100644 index 00000000000..9be795309e0 --- /dev/null +++ b/app/workers/repo_check_worker.rb @@ -0,0 +1,46 @@ +class RepoCheckWorker + include Sidekiq::Worker + + RUN_TIME = 3600 + + sidekiq_options retry: false + + def perform + start = Time.now + + # This loop will break after a little more than one hour ('a little + # more' because `git fsck` may take a few minutes), or if it runs out of + # projects to check. By default sidekiq-cron will start a new + # RepoCheckWorker each hour so that as long as there are repositories to + # check, only one (or two) will be checked at a time. + project_ids.each do |project_id| + break if Time.now - start >= RUN_TIME + + next if !try_obtain_lease(project_id) + + SingleRepoCheckWorker.new.perform(project_id) + end + end + + private + + # In an ideal world we would use Project.where(...).find_each. + # Unfortunately, calling 'find_each' drops the 'where', so we must build + # an array of IDs instead. + def project_ids + limit = 10_000 + never_checked_projects = Project.where('last_repo_check_at IS NULL').limit(limit). + pluck(:id) + old_check_projects = Project.where('last_repo_check_at < ?', 1.week.ago). + reorder('last_repo_check_at ASC').limit(limit).pluck(:id) + never_checked_projects + old_check_projects + end + + def try_obtain_lease(id) + lease = Gitlab::ExclusiveLease.new( + "project_repo_check:#{id}", + timeout: RUN_TIME + ) + lease.try_obtain + end +end diff --git a/app/workers/single_repo_check_worker.rb b/app/workers/single_repo_check_worker.rb new file mode 100644 index 00000000000..f8b245247c5 --- /dev/null +++ b/app/workers/single_repo_check_worker.rb @@ -0,0 +1,34 @@ +class SingleRepoCheckWorker + include Sidekiq::Worker + + sidekiq_options retry: false + + def perform(project_id) + project = Project.find(project_id) + update(project, success: check(project)) + end + + private + + def check(project) + [project.repository.path_to_repo, project.wiki.wiki.path].all? do |path| + git_fsck(path) + end + end + + def git_fsck(path) + cmd = %W(nice git --git-dir=#{path} fsck) + output, status = Gitlab::Popen.popen(cmd) + return true if status.zero? + + Gitlab::RepoCheckLogger.error("command failed: #{cmd.join(' ')}\n#{output}") + false + end + + def update(project, success:) + project.update_columns( + last_repo_check_failed: !success, + last_repo_check_at: Time.now, + ) + end +end diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index fb1c3476f65..cf20fc9c63a 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -155,6 +155,13 @@ production: &base # Flag stuck CI builds as failed stuck_ci_builds_worker: cron: "0 0 * * *" + # Periodically run 'git fsck' on all repositories. If started more than + # once per hour you will have concurrent 'git fsck' jobs. + repo_check_worker: + cron: "20 * * * *" + # Send admin emails once a day + admin_email_worker: + cron: "0 0 * * *" # diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 2b989015279..8240978ef06 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -239,7 +239,12 @@ Settings['cron_jobs'] ||= Settingslogic.new({}) Settings.cron_jobs['stuck_ci_builds_worker'] ||= Settingslogic.new({}) Settings.cron_jobs['stuck_ci_builds_worker']['cron'] ||= '0 0 * * *' Settings.cron_jobs['stuck_ci_builds_worker']['job_class'] = 'StuckCiBuildsWorker' - +Settings.cron_jobs['repo_check_worker'] ||= Settingslogic.new({}) +Settings.cron_jobs['repo_check_worker']['cron'] ||= '20 * * * *' +Settings.cron_jobs['repo_check_worker']['job_class'] = 'RepoCheckWorker' +Settings.cron_jobs['admin_email_worker'] ||= Settingslogic.new({}) +Settings.cron_jobs['admin_email_worker']['cron'] ||= '0 0 * * *' +Settings.cron_jobs['admin_email_worker']['job_class'] = 'AdminEmailWorker' # # GitLab Shell diff --git a/config/routes.rb b/config/routes.rb index 6bf22fb4456..fad8600b77d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -264,6 +264,11 @@ Rails.application.routes.draw do member do put :transfer + post :repo_check + end + + collection do + put :clear_repo_check_states end resources :runner_projects diff --git a/db/migrate/20160315135439_project_add_repo_check.rb b/db/migrate/20160315135439_project_add_repo_check.rb new file mode 100644 index 00000000000..ba7ddc9ecb4 --- /dev/null +++ b/db/migrate/20160315135439_project_add_repo_check.rb @@ -0,0 +1,6 @@ +class ProjectAddRepoCheck < ActiveRecord::Migration + def change + add_column :projects, :last_repo_check_failed, :boolean, default: false + add_column :projects, :last_repo_check_at, :datetime + end +end diff --git a/db/schema.rb b/db/schema.rb index e63e22ce864..d2c183f968b 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -732,6 +732,8 @@ ActiveRecord::Schema.define(version: 20160331133914) do t.boolean "public_builds", default: true, null: false t.string "main_language" t.integer "pushes_since_gc", default: 0 + t.boolean "last_repo_check_failed", default: false + t.datetime "last_repo_check_at" end add_index "projects", ["builds_enabled", "shared_runners_enabled"], name: "index_projects_on_builds_enabled_and_shared_runners_enabled", using: :btree diff --git a/doc/administration/repo_checks.md b/doc/administration/repo_checks.md new file mode 100644 index 00000000000..9c2c01594e8 --- /dev/null +++ b/doc/administration/repo_checks.md @@ -0,0 +1,39 @@ +# Repo checks + +_**Note:** This feature was [introduced][ce-3232] in GitLab 8.7_ + +--- + +Git has a built-in mechanism [git fsck][git-fsck] to verify the +integrity of all data commited to a repository. GitLab administrators can +trigger such a check for a project via the admin panel. The checks run +asynchronously so it may take a few minutes before the check result is +visible on the project admin page. If the checks failed you can see their +output on the admin log page under 'repocheck.log'. + +## Periodical checks + +GitLab periodically runs a repo check on all project repositories and +wiki repositories in order to detect data corruption problems. A +project will be checked no more than once per week. If any projects +fail their repo checks all GitLab administrators will receive an email +notification of the situation. This notification is sent out no more +than once a day. + + +## What to do if a check failed + +If the repo check fails for some repository you should look up the error +in repocheck.log (in the admin panel or on disk; see +`/var/log/gitlab/gitlab-rails` for Omnibus installations or +`/home/git/gitlab/log` for installations from source). Once you have +resolved the issue use the admin panel to trigger a new repo check on +the project. This will clear the 'check failed' state. + +If for some reason the periodical repo check caused a lot of false +alarms you can choose to clear ALL repo check states from the admin +project index page. + +--- +[ce-3232]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3232 "Auto git fsck" +[git-fsck]: https://www.kernel.org/pub/software/scm/git/docs/git-fsck.html "git fsck documentation" \ No newline at end of file diff --git a/lib/gitlab/repo_check_logger.rb b/lib/gitlab/repo_check_logger.rb new file mode 100644 index 00000000000..9409f68722c --- /dev/null +++ b/lib/gitlab/repo_check_logger.rb @@ -0,0 +1,7 @@ +module Gitlab + class RepoCheckLogger < Gitlab::Logger + def self.file_name_noext + 'repocheck' + end + end +end diff --git a/spec/features/admin/admin_projects_spec.rb b/spec/features/admin/admin_projects_spec.rb index 101d955d693..e3991d48ed6 100644 --- a/spec/features/admin/admin_projects_spec.rb +++ b/spec/features/admin/admin_projects_spec.rb @@ -1,6 +1,7 @@ require 'spec_helper' +require 'rails_helper' -describe "Admin::Projects", feature: true do +describe "Admin Projects", feature: true do before do @project = create(:project) login_as :admin @@ -31,4 +32,38 @@ describe "Admin::Projects", feature: true do expect(page).to have_content(@project.name) end end + + feature 'repo checks' do + scenario 'trigger repo check' do + visit_admin_project_page + + page.within('.repo-check') do + click_button 'Trigger repo check' + end + + expect(page).to have_content('Repo check was triggered') + end + + scenario 'see failed repo check' do + @project.update_column(:last_repo_check_failed, true) + visit_admin_project_page + + expect(page).to have_content('Last repo check failed') + end + + scenario 'clear repo checks', js: true do + @project.update_column(:last_repo_check_failed, true) + visit admin_namespaces_projects_path + + page.within('.repo-check-states') do + click_link 'Clear all' # pop-up should be auto confirmed + end + + expect(@project.reload.last_repo_check_failed).to eq(false) + end + end + + def visit_admin_project_page + visit admin_namespace_project_path(@project.namespace, @project) + end end diff --git a/spec/mailers/repo_check_mailer_spec.rb b/spec/mailers/repo_check_mailer_spec.rb new file mode 100644 index 00000000000..d49a6ae0c05 --- /dev/null +++ b/spec/mailers/repo_check_mailer_spec.rb @@ -0,0 +1,21 @@ +require 'rails_helper' + +describe RepoCheckMailer do + include EmailSpec::Matchers + + describe '.notify' do + it 'emails all admins' do + admins = 3.times.map { create(:admin) } + + mail = described_class.notify(1) + + expect(mail).to deliver_to admins.map(&:email) + end + + it 'mentions the number of failed checks' do + mail = described_class.notify(3) + + expect(mail).to have_subject '3 projects failed their last repository check' + end + end +end diff --git a/spec/workers/repo_check_worker_spec.rb b/spec/workers/repo_check_worker_spec.rb new file mode 100644 index 00000000000..7ef3eba9ac5 --- /dev/null +++ b/spec/workers/repo_check_worker_spec.rb @@ -0,0 +1,31 @@ +require 'spec_helper' + +describe RepoCheckWorker do + subject { RepoCheckWorker.new } + + it 'prefers projects that have never been checked' do + projects = 3.times.map { create(:project) } + projects[0].update_column(:last_repo_check_at, 1.month.ago) + projects[2].update_column(:last_repo_check_at, 3.weeks.ago) + + expect(subject.perform).to eq(projects.values_at(1, 0, 2).map(&:id)) + end + + it 'sorts projects by last_repo_check_at' do + projects = 3.times.map { create(:project) } + projects[0].update_column(:last_repo_check_at, 2.weeks.ago) + projects[1].update_column(:last_repo_check_at, 1.month.ago) + projects[2].update_column(:last_repo_check_at, 3.weeks.ago) + + expect(subject.perform).to eq(projects.values_at(1, 2, 0).map(&:id)) + end + + it 'excludes projects that were checked recently' do + projects = 3.times.map { create(:project) } + projects[0].update_column(:last_repo_check_at, 2.days.ago) + projects[1].update_column(:last_repo_check_at, 1.month.ago) + projects[2].update_column(:last_repo_check_at, 3.days.ago) + + expect(subject.perform).to eq([projects[1].id]) + end +end -- cgit v1.2.1 From e3558ed67e7e829fe5148c3fb2fe80ed045fe1b4 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Wed, 6 Apr 2016 12:26:29 +0200 Subject: Document how to disable repo checks --- doc/administration/repo_checks.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/doc/administration/repo_checks.md b/doc/administration/repo_checks.md index 9c2c01594e8..81087f3ac1c 100644 --- a/doc/administration/repo_checks.md +++ b/doc/administration/repo_checks.md @@ -20,6 +20,22 @@ fail their repo checks all GitLab administrators will receive an email notification of the situation. This notification is sent out no more than once a day. +## Disabling periodic checks + +You can disable the periodic checks by giving them an empty cron +schedule in gitlab.yml. + +``` +# For omnibus installations, in /etc/gitlab/gitlab.rb: +gitlab_rails['cron_jobs_repo_check_worker_cron'] = '' +``` + +``` +# For installations from source, in config/gitlab.yml: + cron_jobs: + repo_check_worker: + cron: "" +``` ## What to do if a check failed -- cgit v1.2.1 From 5cf56e56470e695b10db02dff70d0f0b50060518 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Wed, 6 Apr 2016 13:47:05 +0200 Subject: Rename almost all the things --- app/controllers/admin/projects_controller.rb | 18 +++---- app/mailers/repo_check_mailer.rb | 16 ------- app/mailers/repository_check_mailer.rb | 16 +++++++ app/views/admin/logs/show.html.haml | 2 +- app/views/admin/projects/index.html.haml | 12 ++--- app/views/admin/projects/show.html.haml | 18 +++---- app/views/repo_check_mailer/notify.html.haml | 5 -- app/views/repo_check_mailer/notify.text.haml | 3 -- app/views/repository_check_mailer/notify.html.haml | 5 ++ app/views/repository_check_mailer/notify.text.haml | 3 ++ app/workers/admin_email_worker.rb | 6 +-- app/workers/repo_check_worker.rb | 46 ------------------ app/workers/repository_check_worker.rb | 46 ++++++++++++++++++ app/workers/single_repo_check_worker.rb | 34 ------------- app/workers/single_repository_check_worker.rb | 34 +++++++++++++ config/gitlab.yml.example | 2 +- config/initializers/1_settings.rb | 6 +-- config/routes.rb | 4 +- .../20160315135439_project_add_repo_check.rb | 6 --- .../20160315135439_project_add_repository_check.rb | 6 +++ db/schema.rb | 4 +- doc/administration/repo_checks.md | 55 ---------------------- doc/administration/repository_checks.md | 55 ++++++++++++++++++++++ lib/gitlab/repo_check_logger.rb | 7 --- lib/gitlab/repository_check_logger.rb | 7 +++ spec/features/admin/admin_projects_spec.rb | 24 +++++----- spec/mailers/repo_check_mailer_spec.rb | 21 --------- spec/mailers/repository_check_mailer_spec.rb | 21 +++++++++ spec/workers/repo_check_worker_spec.rb | 31 ------------ spec/workers/repository_check_worker_spec.rb | 31 ++++++++++++ 30 files changed, 272 insertions(+), 272 deletions(-) delete mode 100644 app/mailers/repo_check_mailer.rb create mode 100644 app/mailers/repository_check_mailer.rb delete mode 100644 app/views/repo_check_mailer/notify.html.haml delete mode 100644 app/views/repo_check_mailer/notify.text.haml create mode 100644 app/views/repository_check_mailer/notify.html.haml create mode 100644 app/views/repository_check_mailer/notify.text.haml delete mode 100644 app/workers/repo_check_worker.rb create mode 100644 app/workers/repository_check_worker.rb delete mode 100644 app/workers/single_repo_check_worker.rb create mode 100644 app/workers/single_repository_check_worker.rb delete mode 100644 db/migrate/20160315135439_project_add_repo_check.rb create mode 100644 db/migrate/20160315135439_project_add_repository_check.rb delete mode 100644 doc/administration/repo_checks.md create mode 100644 doc/administration/repository_checks.md delete mode 100644 lib/gitlab/repo_check_logger.rb create mode 100644 lib/gitlab/repository_check_logger.rb delete mode 100644 spec/mailers/repo_check_mailer_spec.rb create mode 100644 spec/mailers/repository_check_mailer_spec.rb delete mode 100644 spec/workers/repo_check_worker_spec.rb create mode 100644 spec/workers/repository_check_worker_spec.rb diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb index b8c276fb1bb..01257a68616 100644 --- a/app/controllers/admin/projects_controller.rb +++ b/app/controllers/admin/projects_controller.rb @@ -1,5 +1,5 @@ class Admin::ProjectsController < Admin::ApplicationController - before_action :project, only: [:show, :transfer, :repo_check] + before_action :project, only: [:show, :transfer, :repository_check] before_action :group, only: [:show, :transfer] def index @@ -8,7 +8,7 @@ class Admin::ProjectsController < Admin::ApplicationController @projects = @projects.where("visibility_level IN (?)", params[:visibility_levels]) if params[:visibility_levels].present? @projects = @projects.with_push if params[:with_push].present? @projects = @projects.abandoned if params[:abandoned].present? - @projects = @projects.where(last_repo_check_failed: true) if params[:last_repo_check_failed].present? + @projects = @projects.where(last_repository_check_failed: true) if params[:last_repository_check_failed].present? @projects = @projects.non_archived unless params[:with_archived].present? @projects = @projects.search(params[:name]) if params[:name].present? @projects = @projects.sort(@sort = params[:sort]) @@ -31,24 +31,24 @@ class Admin::ProjectsController < Admin::ApplicationController redirect_to admin_namespace_project_path(@project.namespace, @project) end - def repo_check - SingleRepoCheckWorker.perform_async(@project.id) + def repository_check + SingleRepositoryCheckWorker.perform_async(@project.id) redirect_to( admin_namespace_project_path(@project.namespace, @project), - notice: 'Repo check was triggered' + notice: 'Repository check was triggered' ) end - def clear_repo_check_states + def clear_repository_check_states Project.update_all( - last_repo_check_failed: false, - last_repo_check_at: nil + last_repository_check_failed: false, + last_repository_check_at: nil ) redirect_to( admin_namespaces_projects_path, - notice: 'All project repo check states were cleared' + notice: 'All project states were cleared' ) end diff --git a/app/mailers/repo_check_mailer.rb b/app/mailers/repo_check_mailer.rb deleted file mode 100644 index d98533f120d..00000000000 --- a/app/mailers/repo_check_mailer.rb +++ /dev/null @@ -1,16 +0,0 @@ -class RepoCheckMailer < BaseMailer - include ActionView::Helpers::TextHelper - - def notify(failed_count) - if failed_count == 1 - @message = "One project failed its last repository check" - else - @message = "#{failed_count} projects failed their last repository check" - end - - mail( - to: User.admins.pluck(:email), - subject: @message - ) - end -end diff --git a/app/mailers/repository_check_mailer.rb b/app/mailers/repository_check_mailer.rb new file mode 100644 index 00000000000..994054c8769 --- /dev/null +++ b/app/mailers/repository_check_mailer.rb @@ -0,0 +1,16 @@ +class RepositoryCheckMailer < BaseMailer + include ActionView::Helpers::TextHelper + + def notify(failed_count) + if failed_count == 1 + @message = "One project failed its last repository check" + else + @message = "#{failed_count} projects failed their last repository check" + end + + mail( + to: User.admins.pluck(:email), + subject: @message + ) + end +end diff --git a/app/views/admin/logs/show.html.haml b/app/views/admin/logs/show.html.haml index abcc93f4046..4b475a4d8fa 100644 --- a/app/views/admin/logs/show.html.haml +++ b/app/views/admin/logs/show.html.haml @@ -1,7 +1,7 @@ - page_title "Logs" - loggers = [Gitlab::GitLogger, Gitlab::AppLogger, Gitlab::ProductionLogger, Gitlab::SidekiqLogger, - Gitlab::RepoCheckLogger] + Gitlab::RepositoryCheckLogger] %ul.nav-links.log-tabs - loggers.each do |klass| %li{ class: (klass == Gitlab::GitLogger ? 'active' : '') } diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml index ed360f2f012..c2bf0659841 100644 --- a/app/views/admin/projects/index.html.haml +++ b/app/views/admin/projects/index.html.haml @@ -41,19 +41,19 @@ %fieldset %strong Problems .checkbox - = label_tag :last_repo_check_failed do - = check_box_tag :last_repo_check_failed, 1, params[:last_repo_check_failed] - %span Last repo check failed + = label_tag :last_repository_check_failed do + = check_box_tag :last_repository_check_failed, 1, params[:last_repository_check_failed] + %span Last repository check failed = hidden_field_tag :sort, params[:sort] = button_tag "Search", class: "btn submit btn-primary" = link_to "Reset", admin_namespaces_projects_path, class: "btn btn-cancel" - .panel.panel-default.repo-check-states + .panel.panel-default.repository-check-states .panel-heading - Repo check states + Repository check states .panel-body - = link_to 'Clear all', clear_repo_check_states_admin_namespace_projects_path(0), data: { confirm: 'This will clear repo check states for ALL projects in the database. This cannot be undone. Are you sure?' }, method: :put, class: "btn btn-sm btn-remove" + = link_to 'Clear all', clear_repository_check_states_admin_namespace_projects_path(0), data: { confirm: 'This will clear repository check states for ALL projects in the database. This cannot be undone. Are you sure?' }, method: :put, class: "btn btn-sm btn-remove" %section.col-md-9 .panel.panel-default .panel-heading diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml index a7a3f6349ef..5bef8e3ad57 100644 --- a/app/views/admin/projects/show.html.haml +++ b/app/views/admin/projects/show.html.haml @@ -5,12 +5,12 @@ %i.fa.fa-pencil-square-o Edit %hr -- if @project.last_repo_check_failed? +- if @project.last_repository_check_failed? .row .col-md-12 .panel .panel-heading.alert.alert-danger - Last repo check failed. See + Last repository check failed. See = link_to 'repocheck.log', admin_logs_path for error messages. .row @@ -103,19 +103,19 @@ .col-sm-offset-2.col-sm-10 = f.submit 'Transfer', class: 'btn btn-primary' - .panel.panel-default.repo-check + .panel.panel-default.repository-check .panel-heading - Repo check + Repository check .panel-body - = form_for @project, url: repo_check_admin_namespace_project_path(@project.namespace, @project), method: :post do |f| + = form_for @project, url: repository_check_admin_namespace_project_path(@project.namespace, @project), method: :post do |f| .form-group - - if @project.last_repo_check_at.nil? + - if @project.last_repository_check_at.nil? This repository has never been checked. - else This repository was last checked - = @project.last_repo_check_at.to_s(:medium) + '.' + = @project.last_repository_check_at.to_s(:medium) + '.' The check - - if @project.last_repo_check_failed? + - if @project.last_repository_check_failed? = succeed '.' do %strong.cred failed See @@ -125,7 +125,7 @@ passed. .form-group - = f.submit 'Trigger repo check', class: 'btn btn-primary' + = f.submit 'Trigger repository check', class: 'btn btn-primary' .col-md-6 - if @group diff --git a/app/views/repo_check_mailer/notify.html.haml b/app/views/repo_check_mailer/notify.html.haml deleted file mode 100644 index ef0016f9d3e..00000000000 --- a/app/views/repo_check_mailer/notify.html.haml +++ /dev/null @@ -1,5 +0,0 @@ -%p - #{@message}. - -%p - = link_to "See the affected projects in the GitLab admin panel", admin_namespaces_projects_url(last_repo_check_failed: 1) diff --git a/app/views/repo_check_mailer/notify.text.haml b/app/views/repo_check_mailer/notify.text.haml deleted file mode 100644 index bdf8c2ad675..00000000000 --- a/app/views/repo_check_mailer/notify.text.haml +++ /dev/null @@ -1,3 +0,0 @@ -#{@message}. -\ -View details: #{admin_namespaces_projects_url(last_repo_check_failed: 1)} diff --git a/app/views/repository_check_mailer/notify.html.haml b/app/views/repository_check_mailer/notify.html.haml new file mode 100644 index 00000000000..df16f503570 --- /dev/null +++ b/app/views/repository_check_mailer/notify.html.haml @@ -0,0 +1,5 @@ +%p + #{@message}. + +%p + = link_to "See the affected projects in the GitLab admin panel", admin_namespaces_projects_url(last_repository_check_failed: 1) diff --git a/app/views/repository_check_mailer/notify.text.haml b/app/views/repository_check_mailer/notify.text.haml new file mode 100644 index 00000000000..02f3f80288a --- /dev/null +++ b/app/views/repository_check_mailer/notify.text.haml @@ -0,0 +1,3 @@ +#{@message}. +\ +View details: #{admin_namespaces_projects_url(last_repository_check_failed: 1)} diff --git a/app/workers/admin_email_worker.rb b/app/workers/admin_email_worker.rb index fcccb9ea669..667fff031dd 100644 --- a/app/workers/admin_email_worker.rb +++ b/app/workers/admin_email_worker.rb @@ -4,9 +4,9 @@ class AdminEmailWorker sidekiq_options retry: false # this job auto-repeats via sidekiq-cron def perform - repo_check_failed_count = Project.where(last_repo_check_failed: true).count - return if repo_check_failed_count.zero? + repository_check_failed_count = Project.where(last_repository_check_failed: true).count + return if repository_check_failed_count.zero? - RepoCheckMailer.notify(repo_check_failed_count).deliver_now + RepositoryCheckMailer.notify(repository_check_failed_count).deliver_now end end diff --git a/app/workers/repo_check_worker.rb b/app/workers/repo_check_worker.rb deleted file mode 100644 index 9be795309e0..00000000000 --- a/app/workers/repo_check_worker.rb +++ /dev/null @@ -1,46 +0,0 @@ -class RepoCheckWorker - include Sidekiq::Worker - - RUN_TIME = 3600 - - sidekiq_options retry: false - - def perform - start = Time.now - - # This loop will break after a little more than one hour ('a little - # more' because `git fsck` may take a few minutes), or if it runs out of - # projects to check. By default sidekiq-cron will start a new - # RepoCheckWorker each hour so that as long as there are repositories to - # check, only one (or two) will be checked at a time. - project_ids.each do |project_id| - break if Time.now - start >= RUN_TIME - - next if !try_obtain_lease(project_id) - - SingleRepoCheckWorker.new.perform(project_id) - end - end - - private - - # In an ideal world we would use Project.where(...).find_each. - # Unfortunately, calling 'find_each' drops the 'where', so we must build - # an array of IDs instead. - def project_ids - limit = 10_000 - never_checked_projects = Project.where('last_repo_check_at IS NULL').limit(limit). - pluck(:id) - old_check_projects = Project.where('last_repo_check_at < ?', 1.week.ago). - reorder('last_repo_check_at ASC').limit(limit).pluck(:id) - never_checked_projects + old_check_projects - end - - def try_obtain_lease(id) - lease = Gitlab::ExclusiveLease.new( - "project_repo_check:#{id}", - timeout: RUN_TIME - ) - lease.try_obtain - end -end diff --git a/app/workers/repository_check_worker.rb b/app/workers/repository_check_worker.rb new file mode 100644 index 00000000000..2d75c8bafde --- /dev/null +++ b/app/workers/repository_check_worker.rb @@ -0,0 +1,46 @@ +class RepositoryCheckWorker + include Sidekiq::Worker + + RUN_TIME = 3600 + + sidekiq_options retry: false + + def perform + start = Time.now + + # This loop will break after a little more than one hour ('a little + # more' because `git fsck` may take a few minutes), or if it runs out of + # projects to check. By default sidekiq-cron will start a new + # RepositoryCheckWorker each hour so that as long as there are repositories to + # check, only one (or two) will be checked at a time. + project_ids.each do |project_id| + break if Time.now - start >= RUN_TIME + + next if !try_obtain_lease(project_id) + + SingleRepositoryCheckWorker.new.perform(project_id) + end + end + + private + + # In an ideal world we would use Project.where(...).find_each. + # Unfortunately, calling 'find_each' drops the 'where', so we must build + # an array of IDs instead. + def project_ids + limit = 10_000 + never_checked_projects = Project.where('last_repository_check_at IS NULL').limit(limit). + pluck(:id) + old_check_projects = Project.where('last_repository_check_at < ?', 1.week.ago). + reorder('last_repository_check_at ASC').limit(limit).pluck(:id) + never_checked_projects + old_check_projects + end + + def try_obtain_lease(id) + lease = Gitlab::ExclusiveLease.new( + "project_repository_check:#{id}", + timeout: RUN_TIME + ) + lease.try_obtain + end +end diff --git a/app/workers/single_repo_check_worker.rb b/app/workers/single_repo_check_worker.rb deleted file mode 100644 index f8b245247c5..00000000000 --- a/app/workers/single_repo_check_worker.rb +++ /dev/null @@ -1,34 +0,0 @@ -class SingleRepoCheckWorker - include Sidekiq::Worker - - sidekiq_options retry: false - - def perform(project_id) - project = Project.find(project_id) - update(project, success: check(project)) - end - - private - - def check(project) - [project.repository.path_to_repo, project.wiki.wiki.path].all? do |path| - git_fsck(path) - end - end - - def git_fsck(path) - cmd = %W(nice git --git-dir=#{path} fsck) - output, status = Gitlab::Popen.popen(cmd) - return true if status.zero? - - Gitlab::RepoCheckLogger.error("command failed: #{cmd.join(' ')}\n#{output}") - false - end - - def update(project, success:) - project.update_columns( - last_repo_check_failed: !success, - last_repo_check_at: Time.now, - ) - end -end diff --git a/app/workers/single_repository_check_worker.rb b/app/workers/single_repository_check_worker.rb new file mode 100644 index 00000000000..d9eed9bd708 --- /dev/null +++ b/app/workers/single_repository_check_worker.rb @@ -0,0 +1,34 @@ +class SingleRepositoryCheckWorker + include Sidekiq::Worker + + sidekiq_options retry: false + + def perform(project_id) + project = Project.find(project_id) + update(project, success: check(project)) + end + + private + + def check(project) + [project.repository.path_to_repo, project.wiki.wiki.path].all? do |path| + git_fsck(path) + end + end + + def git_fsck(path) + cmd = %W(nice git --git-dir=#{path} fsck) + output, status = Gitlab::Popen.popen(cmd) + return true if status.zero? + + Gitlab::RepositoryCheckLogger.error("command failed: #{cmd.join(' ')}\n#{output}") + false + end + + def update(project, success:) + project.update_columns( + last_repository_check_failed: !success, + last_repository_check_at: Time.now, + ) + end +end diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index cf20fc9c63a..4fbef653bc1 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -157,7 +157,7 @@ production: &base cron: "0 0 * * *" # Periodically run 'git fsck' on all repositories. If started more than # once per hour you will have concurrent 'git fsck' jobs. - repo_check_worker: + repository_check_worker: cron: "20 * * * *" # Send admin emails once a day admin_email_worker: diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 8240978ef06..23771553c45 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -239,9 +239,9 @@ Settings['cron_jobs'] ||= Settingslogic.new({}) Settings.cron_jobs['stuck_ci_builds_worker'] ||= Settingslogic.new({}) Settings.cron_jobs['stuck_ci_builds_worker']['cron'] ||= '0 0 * * *' Settings.cron_jobs['stuck_ci_builds_worker']['job_class'] = 'StuckCiBuildsWorker' -Settings.cron_jobs['repo_check_worker'] ||= Settingslogic.new({}) -Settings.cron_jobs['repo_check_worker']['cron'] ||= '20 * * * *' -Settings.cron_jobs['repo_check_worker']['job_class'] = 'RepoCheckWorker' +Settings.cron_jobs['repository_check_worker'] ||= Settingslogic.new({}) +Settings.cron_jobs['repository_check_worker']['cron'] ||= '20 * * * *' +Settings.cron_jobs['repository_check_worker']['job_class'] = 'RepositoryCheckWorker' Settings.cron_jobs['admin_email_worker'] ||= Settingslogic.new({}) Settings.cron_jobs['admin_email_worker']['cron'] ||= '0 0 * * *' Settings.cron_jobs['admin_email_worker']['job_class'] = 'AdminEmailWorker' diff --git a/config/routes.rb b/config/routes.rb index fad8600b77d..c0ed99b1964 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -264,11 +264,11 @@ Rails.application.routes.draw do member do put :transfer - post :repo_check + post :repository_check end collection do - put :clear_repo_check_states + put :clear_repository_check_states end resources :runner_projects diff --git a/db/migrate/20160315135439_project_add_repo_check.rb b/db/migrate/20160315135439_project_add_repo_check.rb deleted file mode 100644 index ba7ddc9ecb4..00000000000 --- a/db/migrate/20160315135439_project_add_repo_check.rb +++ /dev/null @@ -1,6 +0,0 @@ -class ProjectAddRepoCheck < ActiveRecord::Migration - def change - add_column :projects, :last_repo_check_failed, :boolean, default: false - add_column :projects, :last_repo_check_at, :datetime - end -end diff --git a/db/migrate/20160315135439_project_add_repository_check.rb b/db/migrate/20160315135439_project_add_repository_check.rb new file mode 100644 index 00000000000..5a0859a30b2 --- /dev/null +++ b/db/migrate/20160315135439_project_add_repository_check.rb @@ -0,0 +1,6 @@ +class ProjectAddRepositoryCheck < ActiveRecord::Migration + def change + add_column :projects, :last_repository_check_failed, :boolean, default: false + add_column :projects, :last_repository_check_at, :datetime + end +end diff --git a/db/schema.rb b/db/schema.rb index d2c183f968b..53509956888 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -732,8 +732,8 @@ ActiveRecord::Schema.define(version: 20160331133914) do t.boolean "public_builds", default: true, null: false t.string "main_language" t.integer "pushes_since_gc", default: 0 - t.boolean "last_repo_check_failed", default: false - t.datetime "last_repo_check_at" + t.boolean "last_repository_check_failed", default: false + t.datetime "last_repository_check_at" end add_index "projects", ["builds_enabled", "shared_runners_enabled"], name: "index_projects_on_builds_enabled_and_shared_runners_enabled", using: :btree diff --git a/doc/administration/repo_checks.md b/doc/administration/repo_checks.md deleted file mode 100644 index 81087f3ac1c..00000000000 --- a/doc/administration/repo_checks.md +++ /dev/null @@ -1,55 +0,0 @@ -# Repo checks - -_**Note:** This feature was [introduced][ce-3232] in GitLab 8.7_ - ---- - -Git has a built-in mechanism [git fsck][git-fsck] to verify the -integrity of all data commited to a repository. GitLab administrators can -trigger such a check for a project via the admin panel. The checks run -asynchronously so it may take a few minutes before the check result is -visible on the project admin page. If the checks failed you can see their -output on the admin log page under 'repocheck.log'. - -## Periodical checks - -GitLab periodically runs a repo check on all project repositories and -wiki repositories in order to detect data corruption problems. A -project will be checked no more than once per week. If any projects -fail their repo checks all GitLab administrators will receive an email -notification of the situation. This notification is sent out no more -than once a day. - -## Disabling periodic checks - -You can disable the periodic checks by giving them an empty cron -schedule in gitlab.yml. - -``` -# For omnibus installations, in /etc/gitlab/gitlab.rb: -gitlab_rails['cron_jobs_repo_check_worker_cron'] = '' -``` - -``` -# For installations from source, in config/gitlab.yml: - cron_jobs: - repo_check_worker: - cron: "" -``` - -## What to do if a check failed - -If the repo check fails for some repository you should look up the error -in repocheck.log (in the admin panel or on disk; see -`/var/log/gitlab/gitlab-rails` for Omnibus installations or -`/home/git/gitlab/log` for installations from source). Once you have -resolved the issue use the admin panel to trigger a new repo check on -the project. This will clear the 'check failed' state. - -If for some reason the periodical repo check caused a lot of false -alarms you can choose to clear ALL repo check states from the admin -project index page. - ---- -[ce-3232]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3232 "Auto git fsck" -[git-fsck]: https://www.kernel.org/pub/software/scm/git/docs/git-fsck.html "git fsck documentation" \ No newline at end of file diff --git a/doc/administration/repository_checks.md b/doc/administration/repository_checks.md new file mode 100644 index 00000000000..77f28209f2f --- /dev/null +++ b/doc/administration/repository_checks.md @@ -0,0 +1,55 @@ +# Repository checks + +_**Note:** This feature was [introduced][ce-3232] in GitLab 8.7_ + +--- + +Git has a built-in mechanism [git fsck][git-fsck] to verify the +integrity of all data commited to a repository. GitLab administrators can +trigger such a check for a project via the admin panel. The checks run +asynchronously so it may take a few minutes before the check result is +visible on the project admin page. If the checks failed you can see their +output on the admin log page under 'repocheck.log'. + +## Periodical checks + +GitLab periodically runs a repository check on all project repositories and +wiki repositories in order to detect data corruption problems. A +project will be checked no more than once per week. If any projects +fail their repository checks all GitLab administrators will receive an email +notification of the situation. This notification is sent out no more +than once a day. + +## Disabling periodic checks + +You can disable the periodic checks by giving them an empty cron +schedule in gitlab.yml. + +``` +# For omnibus installations, in /etc/gitlab/gitlab.rb: +gitlab_rails['cron_jobs_repository_check_worker_cron'] = '' +``` + +``` +# For installations from source, in config/gitlab.yml: + cron_jobs: + repository_check_worker: + cron: "" +``` + +## What to do if a check failed + +If the repository check fails for some repository you should look up the error +in repocheck.log (in the admin panel or on disk; see +`/var/log/gitlab/gitlab-rails` for Omnibus installations or +`/home/git/gitlab/log` for installations from source). Once you have +resolved the issue use the admin panel to trigger a new repository check on +the project. This will clear the 'check failed' state. + +If for some reason the periodical repository check caused a lot of false +alarms you can choose to clear ALL repository check states from the admin +project index page. + +--- +[ce-3232]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3232 "Auto git fsck" +[git-fsck]: https://www.kernel.org/pub/software/scm/git/docs/git-fsck.html "git fsck documentation" \ No newline at end of file diff --git a/lib/gitlab/repo_check_logger.rb b/lib/gitlab/repo_check_logger.rb deleted file mode 100644 index 9409f68722c..00000000000 --- a/lib/gitlab/repo_check_logger.rb +++ /dev/null @@ -1,7 +0,0 @@ -module Gitlab - class RepoCheckLogger < Gitlab::Logger - def self.file_name_noext - 'repocheck' - end - end -end diff --git a/lib/gitlab/repository_check_logger.rb b/lib/gitlab/repository_check_logger.rb new file mode 100644 index 00000000000..485b596ca57 --- /dev/null +++ b/lib/gitlab/repository_check_logger.rb @@ -0,0 +1,7 @@ +module Gitlab + class RepositoryCheckLogger < Gitlab::Logger + def self.file_name_noext + 'repocheck' + end + end +end diff --git a/spec/features/admin/admin_projects_spec.rb b/spec/features/admin/admin_projects_spec.rb index e3991d48ed6..95a230a72c3 100644 --- a/spec/features/admin/admin_projects_spec.rb +++ b/spec/features/admin/admin_projects_spec.rb @@ -33,33 +33,33 @@ describe "Admin Projects", feature: true do end end - feature 'repo checks' do - scenario 'trigger repo check' do + feature 'repository checks' do + scenario 'trigger repository check' do visit_admin_project_page - page.within('.repo-check') do - click_button 'Trigger repo check' + page.within('.repository-check') do + click_button 'Trigger repository check' end - expect(page).to have_content('Repo check was triggered') + expect(page).to have_content('Repository check was triggered') end - scenario 'see failed repo check' do - @project.update_column(:last_repo_check_failed, true) + scenario 'see failed repository check' do + @project.update_column(:last_repository_check_failed, true) visit_admin_project_page - expect(page).to have_content('Last repo check failed') + expect(page).to have_content('Last repository check failed') end - scenario 'clear repo checks', js: true do - @project.update_column(:last_repo_check_failed, true) + scenario 'clear repository checks', js: true do + @project.update_column(:last_repository_check_failed, true) visit admin_namespaces_projects_path - page.within('.repo-check-states') do + page.within('.repository-check-states') do click_link 'Clear all' # pop-up should be auto confirmed end - expect(@project.reload.last_repo_check_failed).to eq(false) + expect(@project.reload.last_repository_check_failed).to eq(false) end end diff --git a/spec/mailers/repo_check_mailer_spec.rb b/spec/mailers/repo_check_mailer_spec.rb deleted file mode 100644 index d49a6ae0c05..00000000000 --- a/spec/mailers/repo_check_mailer_spec.rb +++ /dev/null @@ -1,21 +0,0 @@ -require 'rails_helper' - -describe RepoCheckMailer do - include EmailSpec::Matchers - - describe '.notify' do - it 'emails all admins' do - admins = 3.times.map { create(:admin) } - - mail = described_class.notify(1) - - expect(mail).to deliver_to admins.map(&:email) - end - - it 'mentions the number of failed checks' do - mail = described_class.notify(3) - - expect(mail).to have_subject '3 projects failed their last repository check' - end - end -end diff --git a/spec/mailers/repository_check_mailer_spec.rb b/spec/mailers/repository_check_mailer_spec.rb new file mode 100644 index 00000000000..6ae9a93aaac --- /dev/null +++ b/spec/mailers/repository_check_mailer_spec.rb @@ -0,0 +1,21 @@ +require 'rails_helper' + +describe RepositoryCheckMailer do + include EmailSpec::Matchers + + describe '.notify' do + it 'emails all admins' do + admins = 3.times.map { create(:admin) } + + mail = described_class.notify(1) + + expect(mail).to deliver_to admins.map(&:email) + end + + it 'mentions the number of failed checks' do + mail = described_class.notify(3) + + expect(mail).to have_subject '3 projects failed their last repository check' + end + end +end diff --git a/spec/workers/repo_check_worker_spec.rb b/spec/workers/repo_check_worker_spec.rb deleted file mode 100644 index 7ef3eba9ac5..00000000000 --- a/spec/workers/repo_check_worker_spec.rb +++ /dev/null @@ -1,31 +0,0 @@ -require 'spec_helper' - -describe RepoCheckWorker do - subject { RepoCheckWorker.new } - - it 'prefers projects that have never been checked' do - projects = 3.times.map { create(:project) } - projects[0].update_column(:last_repo_check_at, 1.month.ago) - projects[2].update_column(:last_repo_check_at, 3.weeks.ago) - - expect(subject.perform).to eq(projects.values_at(1, 0, 2).map(&:id)) - end - - it 'sorts projects by last_repo_check_at' do - projects = 3.times.map { create(:project) } - projects[0].update_column(:last_repo_check_at, 2.weeks.ago) - projects[1].update_column(:last_repo_check_at, 1.month.ago) - projects[2].update_column(:last_repo_check_at, 3.weeks.ago) - - expect(subject.perform).to eq(projects.values_at(1, 2, 0).map(&:id)) - end - - it 'excludes projects that were checked recently' do - projects = 3.times.map { create(:project) } - projects[0].update_column(:last_repo_check_at, 2.days.ago) - projects[1].update_column(:last_repo_check_at, 1.month.ago) - projects[2].update_column(:last_repo_check_at, 3.days.ago) - - expect(subject.perform).to eq([projects[1].id]) - end -end diff --git a/spec/workers/repository_check_worker_spec.rb b/spec/workers/repository_check_worker_spec.rb new file mode 100644 index 00000000000..d1849321f56 --- /dev/null +++ b/spec/workers/repository_check_worker_spec.rb @@ -0,0 +1,31 @@ +require 'spec_helper' + +describe RepositoryCheckWorker do + subject { RepositoryCheckWorker.new } + + it 'prefers projects that have never been checked' do + projects = 3.times.map { create(:project) } + projects[0].update_column(:last_repository_check_at, 1.month.ago) + projects[2].update_column(:last_repository_check_at, 3.weeks.ago) + + expect(subject.perform).to eq(projects.values_at(1, 0, 2).map(&:id)) + end + + it 'sorts projects by last_repository_check_at' do + projects = 3.times.map { create(:project) } + projects[0].update_column(:last_repository_check_at, 2.weeks.ago) + projects[1].update_column(:last_repository_check_at, 1.month.ago) + projects[2].update_column(:last_repository_check_at, 3.weeks.ago) + + expect(subject.perform).to eq(projects.values_at(1, 2, 0).map(&:id)) + end + + it 'excludes projects that were checked recently' do + projects = 3.times.map { create(:project) } + projects[0].update_column(:last_repository_check_at, 2.days.ago) + projects[1].update_column(:last_repository_check_at, 1.month.ago) + projects[2].update_column(:last_repository_check_at, 3.days.ago) + + expect(subject.perform).to eq([projects[1].id]) + end +end -- cgit v1.2.1 From 5ffa8f057095fb2fe12a60ffa0dd3a611d2f1aeb Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Sat, 9 Apr 2016 18:40:15 -0400 Subject: Escape the query argument provided to `git grep` by `search_files` Closes #14963. --- app/models/repository.rb | 2 +- spec/models/repository_spec.rb | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/models/repository.rb b/app/models/repository.rb index 8dead3a5884..090cccd2c72 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -795,7 +795,7 @@ class Repository def search_files(query, ref) offset = 2 - args = %W(#{Gitlab.config.git.bin_path} grep -i -I -n --before-context #{offset} --after-context #{offset} -e #{query} #{ref || root_ref}) + args = %W(#{Gitlab.config.git.bin_path} grep -i -I -n --before-context #{offset} --after-context #{offset} -e #{Regexp.escape(query)} #{ref || root_ref}) Gitlab::Popen.popen(args, path_to_repo).first.scrub.split(/^--$/) end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 4e49c413f23..bce30aafc4c 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -94,6 +94,12 @@ describe Repository, models: true do it { is_expected.to be_an Array } + it 'regex-escapes the query string' do + results = repository.search_files("test\\", 'master') + + expect(results.first).not_to start_with('fatal:') + end + describe 'result' do subject { results.first } -- cgit v1.2.1 From 02cfff4623cc447c20f1990d3e5d9b452e5a7190 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 12 Apr 2016 13:34:03 +0100 Subject: Removed references to subscribe-button CSS class These were being blocked by adblocks Closes #15043 --- app/assets/javascripts/subscription.js.coffee | 2 +- app/assets/stylesheets/pages/issuable.scss | 6 ------ app/assets/stylesheets/pages/labels.scss | 2 +- app/views/projects/labels/_label.html.haml | 2 +- app/views/shared/issuable/_sidebar.html.haml | 4 ++-- features/steps/project/issues/issues.rb | 4 ++-- features/steps/project/labels.rb | 2 +- features/steps/project/merge_requests.rb | 4 ++-- 8 files changed, 10 insertions(+), 16 deletions(-) diff --git a/app/assets/javascripts/subscription.js.coffee b/app/assets/javascripts/subscription.js.coffee index e4b7a3172ec..3894806d61d 100644 --- a/app/assets/javascripts/subscription.js.coffee +++ b/app/assets/javascripts/subscription.js.coffee @@ -2,7 +2,7 @@ class @Subscription constructor: (container) -> $container = $(container) @url = $container.attr('data-url') - @subscribe_button = $container.find('.subscribe-button') + @subscribe_button = $container.find('.issuable-subscribe-button') @subscription_status = $container.find('.subscription-status') @subscribe_button.unbind('click').click(@toggleSubscription) diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 88c1b614c74..f6bdcacda99 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -173,12 +173,6 @@ } } - .subscribe-button { - span { - margin-top: 0; - } - } - &.right-sidebar-collapsed { /* Extra small devices (phones, less than 768px) */ display: none; diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss index 3e0a3140be7..4f67981975a 100644 --- a/app/assets/stylesheets/pages/labels.scss +++ b/app/assets/stylesheets/pages/labels.scss @@ -106,7 +106,7 @@ padding: 6px; color: $gl-text-color; - &.subscribe-button { + &.label-subscribe-button { padding-left: 0; } } diff --git a/app/views/projects/labels/_label.html.haml b/app/views/projects/labels/_label.html.haml index 097a65969a6..979726b8def 100644 --- a/app/views/projects/labels/_label.html.haml +++ b/app/views/projects/labels/_label.html.haml @@ -14,7 +14,7 @@ .label-subscription{data: {url: toggle_subscription_namespace_project_label_path(@project.namespace, @project, label)}} .subscription-status{data: {status: label_subscription_status(label)}} - %a.subscribe-button.btn.action-buttons{data: {toggle: "tooltip"}} + %a.label-subscribe-button.btn.action-buttons{data: {toggle: "tooltip"}} %span= label_subscription_toggle_button_text(label) - if can? current_user, :admin_label, @project diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index 94affa4b59a..89ce356fedc 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -128,7 +128,7 @@ .title.hide-collapsed Notifications - subscribtion_status = subscribed ? 'subscribed' : 'unsubscribed' - %button.btn.btn-block.btn-gray.subscribe-button.hide-collapsed{:type => 'button'} + %button.btn.btn-block.btn-gray.issuable-subscribe-button.hide-collapsed{:type => 'button'} %span= subscribed ? 'Unsubscribe' : 'Subscribe' .subscription-status.hide-collapsed{data: {status: subscribtion_status}} .unsubscribed{class: ( 'hidden' if subscribed )} @@ -152,4 +152,4 @@ new LabelsSelect(); new IssuableContext('#{current_user.to_json(only: [:username, :id, :name])}'); new Subscription('.subscription') - new Sidebar(); \ No newline at end of file + new Sidebar(); diff --git a/features/steps/project/issues/issues.rb b/features/steps/project/issues/issues.rb index aff5ca676be..fc12843ea5c 100644 --- a/features/steps/project/issues/issues.rb +++ b/features/steps/project/issues/issues.rb @@ -20,11 +20,11 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps end step 'I should see that I am subscribed' do - expect(find('.subscribe-button span')).to have_content 'Unsubscribe' + expect(find('.issuable-subscribe-button span')).to have_content 'Unsubscribe' end step 'I should see that I am unsubscribed' do - expect(find('.subscribe-button span')).to have_content 'Subscribe' + expect(find('.issuable-subscribe-button span')).to have_content 'Subscribe' end step 'I click link "Closed"' do diff --git a/features/steps/project/labels.rb b/features/steps/project/labels.rb index 17944527e3a..5bb02189021 100644 --- a/features/steps/project/labels.rb +++ b/features/steps/project/labels.rb @@ -29,6 +29,6 @@ class Spinach::Features::Labels < Spinach::FeatureSteps private def subscribe_button - first('.subscribe-button span') + first('.label-subscribe-button span') end end diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb index f0af0d097fa..4f883fe7c27 100644 --- a/features/steps/project/merge_requests.rb +++ b/features/steps/project/merge_requests.rb @@ -77,11 +77,11 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps end step 'I should see that I am subscribed' do - expect(find('.subscribe-button span')).to have_content 'Unsubscribe' + expect(find('.issuable-subscribe-button span')).to have_content 'Unsubscribe' end step 'I should see that I am unsubscribed' do - expect(find('.subscribe-button span')).to have_content 'Subscribe' + expect(find('.issuable-subscribe-button span')).to have_content 'Subscribe' end step 'I click button "Unsubscribe"' do -- cgit v1.2.1 From 318314154e09d051cccd8b8391f2065133906bd7 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 12 Apr 2016 15:56:44 +0200 Subject: Increase fsck lock timeout to 24 hours --- app/workers/repository_check_worker.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/workers/repository_check_worker.rb b/app/workers/repository_check_worker.rb index 2d75c8bafde..afdc6a4c63a 100644 --- a/app/workers/repository_check_worker.rb +++ b/app/workers/repository_check_worker.rb @@ -37,9 +37,11 @@ class RepositoryCheckWorker end def try_obtain_lease(id) + # Use a 24-hour timeout because on servers/projects where 'git fsck' is + # super slow we definitely do not want to run it twice in parallel. lease = Gitlab::ExclusiveLease.new( "project_repository_check:#{id}", - timeout: RUN_TIME + timeout: 24.hours ) lease.try_obtain end -- cgit v1.2.1 From b37d3b9423991763ad03fca791a1daf473dafed1 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 12 Apr 2016 16:21:23 +0200 Subject: Add repository_checks_enabled setting --- app/models/application_setting.rb | 3 ++- db/migrate/20160412140240_add_repository_checks_enabled_setting.rb | 5 +++++ db/schema.rb | 3 ++- 3 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 db/migrate/20160412140240_add_repository_checks_enabled_setting.rb diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 052cd874733..36f88154232 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -153,7 +153,8 @@ class ApplicationSetting < ActiveRecord::Base require_two_factor_authentication: false, two_factor_grace_period: 48, recaptcha_enabled: false, - akismet_enabled: false + akismet_enabled: false, + repository_checks_enabled: true, ) end diff --git a/db/migrate/20160412140240_add_repository_checks_enabled_setting.rb b/db/migrate/20160412140240_add_repository_checks_enabled_setting.rb new file mode 100644 index 00000000000..ebfa4bcbc7b --- /dev/null +++ b/db/migrate/20160412140240_add_repository_checks_enabled_setting.rb @@ -0,0 +1,5 @@ +class AddRepositoryChecksEnabledSetting < ActiveRecord::Migration + def change + add_column :application_settings, :repository_checks_enabled, :boolean, default: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 53509956888..a9c595fe36d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160331133914) do +ActiveRecord::Schema.define(version: 20160412140240) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -78,6 +78,7 @@ ActiveRecord::Schema.define(version: 20160331133914) do t.string "akismet_api_key" t.boolean "email_author_in_body", default: false t.integer "default_group_visibility" + t.boolean "repository_checks_enabled", default: true end create_table "audit_events", force: :cascade do |t| -- cgit v1.2.1 From beaee0a71fbb1b08676107b3e619e833dc8902c0 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Tue, 12 Apr 2016 15:54:26 +0100 Subject: Fixed issue with failing tests --- app/assets/javascripts/subscription.js.coffee | 2 +- app/views/projects/labels/_label.html.haml | 2 +- app/views/shared/issuable/_sidebar.html.haml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/subscription.js.coffee b/app/assets/javascripts/subscription.js.coffee index 3894806d61d..1a430f3aa47 100644 --- a/app/assets/javascripts/subscription.js.coffee +++ b/app/assets/javascripts/subscription.js.coffee @@ -2,7 +2,7 @@ class @Subscription constructor: (container) -> $container = $(container) @url = $container.attr('data-url') - @subscribe_button = $container.find('.issuable-subscribe-button') + @subscribe_button = $container.find('.js-subscribe-button') @subscription_status = $container.find('.subscription-status') @subscribe_button.unbind('click').click(@toggleSubscription) diff --git a/app/views/projects/labels/_label.html.haml b/app/views/projects/labels/_label.html.haml index 979726b8def..c7b4bb1f6e6 100644 --- a/app/views/projects/labels/_label.html.haml +++ b/app/views/projects/labels/_label.html.haml @@ -14,7 +14,7 @@ .label-subscription{data: {url: toggle_subscription_namespace_project_label_path(@project.namespace, @project, label)}} .subscription-status{data: {status: label_subscription_status(label)}} - %a.label-subscribe-button.btn.action-buttons{data: {toggle: "tooltip"}} + %button.js-subscribe-button.label-subscribe-button.btn.action-buttons{ type: 'button', data: {toggle: "tooltip"} } %span= label_subscription_toggle_button_text(label) - if can? current_user, :admin_label, @project diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index 89ce356fedc..fe6e4128003 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -128,7 +128,7 @@ .title.hide-collapsed Notifications - subscribtion_status = subscribed ? 'subscribed' : 'unsubscribed' - %button.btn.btn-block.btn-gray.issuable-subscribe-button.hide-collapsed{:type => 'button'} + %button.btn.btn-block.btn-gray.js-subscribe-button.issuable-subscribe-button.hide-collapsed{:type => 'button'} %span= subscribed ? 'Unsubscribe' : 'Subscribe' .subscription-status.hide-collapsed{data: {status: subscribtion_status}} .unsubscribed{class: ( 'hidden' if subscribed )} -- cgit v1.2.1 From 97f4ffff1e7b5da94e18edc20c009ffb46784187 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 12 Apr 2016 17:07:54 +0200 Subject: Add a 'circuit breaker' for repo checks --- app/views/admin/application_settings/_form.html.haml | 13 +++++++++++++ app/workers/repository_check_worker.rb | 8 ++++++++ lib/gitlab/current_settings.rb | 3 ++- spec/workers/repository_check_worker_spec.rb | 8 ++++++++ 4 files changed, 31 insertions(+), 1 deletion(-) diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index de86dacbb12..afd88465a78 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -275,5 +275,18 @@ .col-sm-10 = f.text_field :sentry_dsn, class: 'form-control' + %fieldset + %legend Repository Checks + .form-group + .col-sm-offset-2.col-sm-10 + .checkbox + = f.label :repository_checks_enabled do + = f.check_box :repository_checks_enabled + Enable Repository Checks + .help-block + GitLab will periodically run + %a{ href: 'https://www.kernel.org/pub/software/scm/git/docs/git-fsck.html', target: 'blank' } 'git fsck' + in all project and wiki repositories to look for silent disk corruption issues. + .form-actions = f.submit 'Save', class: 'btn btn-save' diff --git a/app/workers/repository_check_worker.rb b/app/workers/repository_check_worker.rb index afdc6a4c63a..017f49de08c 100644 --- a/app/workers/repository_check_worker.rb +++ b/app/workers/repository_check_worker.rb @@ -15,6 +15,7 @@ class RepositoryCheckWorker # check, only one (or two) will be checked at a time. project_ids.each do |project_id| break if Time.now - start >= RUN_TIME + break unless current_settings.repository_checks_enabled next if !try_obtain_lease(project_id) @@ -45,4 +46,11 @@ class RepositoryCheckWorker ) lease.try_obtain end + + def current_settings + # No caching of the settings! If we cache them and an admin disables + # this feature, an active RepositoryCheckWorker would keep going for up + # to 1 hour after the feature was disabled. + ApplicationSetting.current || Gitlab::CurrentSettings.fake_application_settings + end end diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb index 1acc22fe5bf..f44d1b3a44e 100644 --- a/lib/gitlab/current_settings.rb +++ b/lib/gitlab/current_settings.rb @@ -34,7 +34,8 @@ module Gitlab max_artifacts_size: Settings.artifacts['max_size'], require_two_factor_authentication: false, two_factor_grace_period: 48, - akismet_enabled: false + akismet_enabled: false, + repository_checks_enabled: true, ) end diff --git a/spec/workers/repository_check_worker_spec.rb b/spec/workers/repository_check_worker_spec.rb index d1849321f56..13493ad2c6a 100644 --- a/spec/workers/repository_check_worker_spec.rb +++ b/spec/workers/repository_check_worker_spec.rb @@ -28,4 +28,12 @@ describe RepositoryCheckWorker do expect(subject.perform).to eq([projects[1].id]) end + + it 'does nothing when repository checks are disabled' do + create(:empty_project) + current_settings = double('settings', repository_checks_enabled: false) + expect(subject).to receive(:current_settings) { current_settings } + + expect(subject.perform).to eq(nil) + end end -- cgit v1.2.1 From ea787165b3a9604aa86304e29778066bb014824e Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 12 Apr 2016 17:32:58 +0200 Subject: Move 'clear checks' button to applicatoin settings --- .../admin/application_settings_controller.rb | 14 +++++++++ app/controllers/admin/projects_controller.rb | 12 -------- .../admin/application_settings/_form.html.haml | 6 ++++ app/views/admin/projects/index.html.haml | 5 ---- config/routes.rb | 5 +--- doc/administration/repository_checks.md | 33 ++++++++-------------- 6 files changed, 32 insertions(+), 43 deletions(-) diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index f010436bd36..993a70e63bc 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -19,6 +19,19 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController redirect_to admin_runners_path end + def clear_repository_check_states + Project.update_all( + last_repository_check_failed: false, + last_repository_check_at: nil + ) + + redirect_to( + admin_application_settings_path, + notice: 'All repository check states were cleared' + ) + end + + private def set_application_setting @@ -82,6 +95,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController :akismet_enabled, :akismet_api_key, :email_author_in_body, + :repository_checks_enabled, restricted_visibility_levels: [], import_sources: [] ) diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb index 01257a68616..d7cd9520cc6 100644 --- a/app/controllers/admin/projects_controller.rb +++ b/app/controllers/admin/projects_controller.rb @@ -40,18 +40,6 @@ class Admin::ProjectsController < Admin::ApplicationController ) end - def clear_repository_check_states - Project.update_all( - last_repository_check_failed: false, - last_repository_check_at: nil - ) - - redirect_to( - admin_namespaces_projects_path, - notice: 'All project states were cleared' - ) - end - protected def project diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index afd88465a78..c7c82da72c7 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -287,6 +287,12 @@ GitLab will periodically run %a{ href: 'https://www.kernel.org/pub/software/scm/git/docs/git-fsck.html', target: 'blank' } 'git fsck' in all project and wiki repositories to look for silent disk corruption issues. + .form-group + .col-sm-offset-2.col-sm-10 + = link_to 'Clear all repository checks', clear_repository_check_states_admin_application_settings_path, data: { confirm: 'This will clear repository check states for ALL projects in the database. This cannot be undone. Are you sure?' }, method: :put, class: "btn btn-sm btn-remove" + .help-block + If you got a lot of false alarms from repository checks (maybe your fileserver was temporarily unavailable) you can choose to clear all repository check information from the database. + .form-actions = f.submit 'Save', class: 'btn btn-save' diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml index c2bf0659841..aa07afa0d62 100644 --- a/app/views/admin/projects/index.html.haml +++ b/app/views/admin/projects/index.html.haml @@ -49,11 +49,6 @@ = button_tag "Search", class: "btn submit btn-primary" = link_to "Reset", admin_namespaces_projects_path, class: "btn btn-cancel" - .panel.panel-default.repository-check-states - .panel-heading - Repository check states - .panel-body - = link_to 'Clear all', clear_repository_check_states_admin_namespace_projects_path(0), data: { confirm: 'This will clear repository check states for ALL projects in the database. This cannot be undone. Are you sure?' }, method: :put, class: "btn btn-sm btn-remove" %section.col-md-9 .panel.panel-default .panel-heading diff --git a/config/routes.rb b/config/routes.rb index c0ed99b1964..c163602126d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -267,10 +267,6 @@ Rails.application.routes.draw do post :repository_check end - collection do - put :clear_repository_check_states - end - resources :runner_projects end end @@ -286,6 +282,7 @@ Rails.application.routes.draw do resource :application_settings, only: [:show, :update] do resources :services put :reset_runners_token + put :clear_repository_check_states end resources :labels diff --git a/doc/administration/repository_checks.md b/doc/administration/repository_checks.md index 77f28209f2f..e22b04928cc 100644 --- a/doc/administration/repository_checks.md +++ b/doc/administration/repository_checks.md @@ -4,12 +4,13 @@ _**Note:** This feature was [introduced][ce-3232] in GitLab 8.7_ --- -Git has a built-in mechanism [git fsck][git-fsck] to verify the -integrity of all data commited to a repository. GitLab administrators can -trigger such a check for a project via the admin panel. The checks run -asynchronously so it may take a few minutes before the check result is -visible on the project admin page. If the checks failed you can see their -output on the admin log page under 'repocheck.log'. +Git has a built-in mechanism \[git fsck\]\[git-fsck\] to verify the +integrity of all data commited to a repository. GitLab administrators +can trigger such a check for a project via the project page under the +admin panel. The checks run asynchronously so it may take a few minutes +before the check result is visible on the project admin page. If the +checks failed you can see their output on the admin log page under +'repocheck.log'. ## Periodical checks @@ -22,20 +23,8 @@ than once a day. ## Disabling periodic checks -You can disable the periodic checks by giving them an empty cron -schedule in gitlab.yml. - -``` -# For omnibus installations, in /etc/gitlab/gitlab.rb: -gitlab_rails['cron_jobs_repository_check_worker_cron'] = '' -``` - -``` -# For installations from source, in config/gitlab.yml: - cron_jobs: - repository_check_worker: - cron: "" -``` +You can disable the periodic checks on the 'Settings' page of the admin +panel. ## What to do if a check failed @@ -47,8 +36,8 @@ resolved the issue use the admin panel to trigger a new repository check on the project. This will clear the 'check failed' state. If for some reason the periodical repository check caused a lot of false -alarms you can choose to clear ALL repository check states from the admin -project index page. +alarms you can choose to clear ALL repository check states from the +'Settings' page of the admin panel. --- [ce-3232]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3232 "Auto git fsck" -- cgit v1.2.1 From 3170e5d226ee107409b4345b827519da64ba967e Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 12 Apr 2016 18:09:45 +0200 Subject: Basta --- app/controllers/admin/application_settings_controller.rb | 2 +- app/controllers/admin/projects_controller.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index 993a70e63bc..a54864480a2 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -27,7 +27,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController redirect_to( admin_application_settings_path, - notice: 'All repository check states were cleared' + notice: 'All repository check states were cleared.' ) end diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb index 0719c90b19b..6854e57b650 100644 --- a/app/controllers/admin/projects_controller.rb +++ b/app/controllers/admin/projects_controller.rb @@ -36,7 +36,7 @@ class Admin::ProjectsController < Admin::ApplicationController redirect_to( admin_namespace_project_path(@project.namespace, @project), - notice: 'Repository check was triggered' + notice: 'Repository check was triggered.' ) end -- cgit v1.2.1 From 525ab25ac81a6b81cca56d3cba403ab2a5f372eb Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Tue, 12 Apr 2016 18:15:15 +0200 Subject: Changes suggested by Robert --- app/mailers/repository_check_mailer.rb | 2 -- app/views/admin/application_settings/_form.html.haml | 2 +- app/workers/repository_check_worker.rb | 2 +- spec/workers/repository_check_worker_spec.rb | 6 +++--- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/app/mailers/repository_check_mailer.rb b/app/mailers/repository_check_mailer.rb index 994054c8769..2bff5b63cc4 100644 --- a/app/mailers/repository_check_mailer.rb +++ b/app/mailers/repository_check_mailer.rb @@ -1,6 +1,4 @@ class RepositoryCheckMailer < BaseMailer - include ActionView::Helpers::TextHelper - def notify(failed_count) if failed_count == 1 @message = "One project failed its last repository check" diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index 533a2f42973..555aea554f0 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -287,7 +287,7 @@ .col-sm-offset-2.col-sm-10 = link_to 'Clear all repository checks', clear_repository_check_states_admin_application_settings_path, data: { confirm: 'This will clear repository check states for ALL projects in the database. This cannot be undone. Are you sure?' }, method: :put, class: "btn btn-sm btn-remove" .help-block - If you got a lot of false alarms from repository checks (maybe your fileserver was temporarily unavailable) you can choose to clear all repository check information from the database. + If you got a lot of false alarms from repository checks you can choose to clear all repository check information from the database. .form-actions diff --git a/app/workers/repository_check_worker.rb b/app/workers/repository_check_worker.rb index 017f49de08c..bdacdd4c6b0 100644 --- a/app/workers/repository_check_worker.rb +++ b/app/workers/repository_check_worker.rb @@ -17,7 +17,7 @@ class RepositoryCheckWorker break if Time.now - start >= RUN_TIME break unless current_settings.repository_checks_enabled - next if !try_obtain_lease(project_id) + next unless try_obtain_lease(project_id) SingleRepositoryCheckWorker.new.perform(project_id) end diff --git a/spec/workers/repository_check_worker_spec.rb b/spec/workers/repository_check_worker_spec.rb index 13493ad2c6a..7a757658e97 100644 --- a/spec/workers/repository_check_worker_spec.rb +++ b/spec/workers/repository_check_worker_spec.rb @@ -4,7 +4,7 @@ describe RepositoryCheckWorker do subject { RepositoryCheckWorker.new } it 'prefers projects that have never been checked' do - projects = 3.times.map { create(:project) } + projects = create_list(:project, 3) projects[0].update_column(:last_repository_check_at, 1.month.ago) projects[2].update_column(:last_repository_check_at, 3.weeks.ago) @@ -12,7 +12,7 @@ describe RepositoryCheckWorker do end it 'sorts projects by last_repository_check_at' do - projects = 3.times.map { create(:project) } + projects = create_list(:project, 3) projects[0].update_column(:last_repository_check_at, 2.weeks.ago) projects[1].update_column(:last_repository_check_at, 1.month.ago) projects[2].update_column(:last_repository_check_at, 3.weeks.ago) @@ -21,7 +21,7 @@ describe RepositoryCheckWorker do end it 'excludes projects that were checked recently' do - projects = 3.times.map { create(:project) } + projects = create_list(:project, 3) projects[0].update_column(:last_repository_check_at, 2.days.ago) projects[1].update_column(:last_repository_check_at, 1.month.ago) projects[2].update_column(:last_repository_check_at, 3.days.ago) -- cgit v1.2.1 From a0bede92edf1f1df66e977c22540fced3414f3b7 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Wed, 13 Apr 2016 11:12:43 +0200 Subject: More code changes, thanks Robert --- app/workers/repository_check_worker.rb | 19 ++++++++++++------- app/workers/single_repository_check_worker.rb | 25 ++++++++++++------------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/app/workers/repository_check_worker.rb b/app/workers/repository_check_worker.rb index bdacdd4c6b0..d7ead91f94e 100644 --- a/app/workers/repository_check_worker.rb +++ b/app/workers/repository_check_worker.rb @@ -25,9 +25,11 @@ class RepositoryCheckWorker private - # In an ideal world we would use Project.where(...).find_each. - # Unfortunately, calling 'find_each' drops the 'where', so we must build - # an array of IDs instead. + # Project.find_each does not support WHERE clauses and + # Project.find_in_batches does not support ordering. So we just build an + # array of ID's. This is OK because we do it only once an hour, because + # getting ID's from Postgres is not terribly slow, and because no user + # has to sit and wait for this query to finish. def project_ids limit = 10_000 never_checked_projects = Project.where('last_repository_check_at IS NULL').limit(limit). @@ -40,17 +42,20 @@ class RepositoryCheckWorker def try_obtain_lease(id) # Use a 24-hour timeout because on servers/projects where 'git fsck' is # super slow we definitely do not want to run it twice in parallel. - lease = Gitlab::ExclusiveLease.new( + Gitlab::ExclusiveLease.new( "project_repository_check:#{id}", timeout: 24.hours - ) - lease.try_obtain + ).try_obtain end def current_settings # No caching of the settings! If we cache them and an admin disables # this feature, an active RepositoryCheckWorker would keep going for up # to 1 hour after the feature was disabled. - ApplicationSetting.current || Gitlab::CurrentSettings.fake_application_settings + if Rails.env.test? + Gitlab::CurrentSettings.fake_application_settings + else + ApplicationSetting.current + end end end diff --git a/app/workers/single_repository_check_worker.rb b/app/workers/single_repository_check_worker.rb index d9eed9bd708..6257f382d86 100644 --- a/app/workers/single_repository_check_worker.rb +++ b/app/workers/single_repository_check_worker.rb @@ -5,30 +5,29 @@ class SingleRepositoryCheckWorker def perform(project_id) project = Project.find(project_id) - update(project, success: check(project)) + project.update_columns( + last_repository_check_failed: !check(project), + last_repository_check_at: Time.now, + ) end private def check(project) - [project.repository.path_to_repo, project.wiki.wiki.path].all? do |path| - git_fsck(path) + [project.repository, project.wiki.repository].all? do |repository| + git_fsck(repository.path_to_repo) end end def git_fsck(path) cmd = %W(nice git --git-dir=#{path} fsck) output, status = Gitlab::Popen.popen(cmd) - return true if status.zero? - - Gitlab::RepositoryCheckLogger.error("command failed: #{cmd.join(' ')}\n#{output}") - false - end - def update(project, success:) - project.update_columns( - last_repository_check_failed: !success, - last_repository_check_at: Time.now, - ) + if status.zero? + true + else + Gitlab::RepositoryCheckLogger.error("command failed: #{cmd.join(' ')}\n#{output}") + false + end end end -- cgit v1.2.1 From 74783358268a6ce69375780400e661d9aa1fa741 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Wed, 13 Apr 2016 11:15:36 +0200 Subject: Improve English, thanks Robert --- doc/administration/repository_checks.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/administration/repository_checks.md b/doc/administration/repository_checks.md index e22b04928cc..f4c5a84fd37 100644 --- a/doc/administration/repository_checks.md +++ b/doc/administration/repository_checks.md @@ -4,7 +4,7 @@ _**Note:** This feature was [introduced][ce-3232] in GitLab 8.7_ --- -Git has a built-in mechanism \[git fsck\]\[git-fsck\] to verify the +Git has a built-in mechanism, \[git fsck\]\[git-fsck\], to verify the integrity of all data commited to a repository. GitLab administrators can trigger such a check for a project via the project page under the admin panel. The checks run asynchronously so it may take a few minutes @@ -12,7 +12,7 @@ before the check result is visible on the project admin page. If the checks failed you can see their output on the admin log page under 'repocheck.log'. -## Periodical checks +## Periodic checks GitLab periodically runs a repository check on all project repositories and wiki repositories in order to detect data corruption problems. A @@ -35,7 +35,7 @@ in repocheck.log (in the admin panel or on disk; see resolved the issue use the admin panel to trigger a new repository check on the project. This will clear the 'check failed' state. -If for some reason the periodical repository check caused a lot of false +If for some reason the periodic repository check caused a lot of false alarms you can choose to clear ALL repository check states from the 'Settings' page of the admin panel. -- cgit v1.2.1 From da87fa8a9fb2e786a4115d2da3898e0ec83c446c Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Wed, 13 Apr 2016 11:50:39 +0200 Subject: Make check result staleness more obvious --- app/views/admin/projects/show.html.haml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml index 5bef8e3ad57..1e44172f066 100644 --- a/app/views/admin/projects/show.html.haml +++ b/app/views/admin/projects/show.html.haml @@ -10,7 +10,9 @@ .col-md-12 .panel .panel-heading.alert.alert-danger - Last repository check failed. See + Last repository check + = "(#{time_ago_in_words(@project.last_repository_check_at)} ago)" + failed. See = link_to 'repocheck.log', admin_logs_path for error messages. .row -- cgit v1.2.1 From b60b9094d3581e72fa614771456fa9003bee6426 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Wed, 13 Apr 2016 11:50:55 +0200 Subject: Always check the wiki too --- app/workers/single_repository_check_worker.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/workers/single_repository_check_worker.rb b/app/workers/single_repository_check_worker.rb index 6257f382d86..f6c345df8b5 100644 --- a/app/workers/single_repository_check_worker.rb +++ b/app/workers/single_repository_check_worker.rb @@ -14,9 +14,10 @@ class SingleRepositoryCheckWorker private def check(project) - [project.repository, project.wiki.repository].all? do |repository| + # Use 'map do', not 'all? do', to prevent short-circuiting + [project.repository, project.wiki.repository].map do |repository| git_fsck(repository.path_to_repo) - end + end.all? end def git_fsck(path) -- cgit v1.2.1 From fa864716a80eba26f75a1d69ca3e5183bee96542 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Wed, 13 Apr 2016 12:13:53 +0200 Subject: Use new "clear all" button in tests --- spec/features/admin/admin_projects_spec.rb | 37 +---------------- .../admin/admin_uses_repository_checks_spec.rb | 48 ++++++++++++++++++++++ 2 files changed, 49 insertions(+), 36 deletions(-) create mode 100644 spec/features/admin/admin_uses_repository_checks_spec.rb diff --git a/spec/features/admin/admin_projects_spec.rb b/spec/features/admin/admin_projects_spec.rb index 95a230a72c3..101d955d693 100644 --- a/spec/features/admin/admin_projects_spec.rb +++ b/spec/features/admin/admin_projects_spec.rb @@ -1,7 +1,6 @@ require 'spec_helper' -require 'rails_helper' -describe "Admin Projects", feature: true do +describe "Admin::Projects", feature: true do before do @project = create(:project) login_as :admin @@ -32,38 +31,4 @@ describe "Admin Projects", feature: true do expect(page).to have_content(@project.name) end end - - feature 'repository checks' do - scenario 'trigger repository check' do - visit_admin_project_page - - page.within('.repository-check') do - click_button 'Trigger repository check' - end - - expect(page).to have_content('Repository check was triggered') - end - - scenario 'see failed repository check' do - @project.update_column(:last_repository_check_failed, true) - visit_admin_project_page - - expect(page).to have_content('Last repository check failed') - end - - scenario 'clear repository checks', js: true do - @project.update_column(:last_repository_check_failed, true) - visit admin_namespaces_projects_path - - page.within('.repository-check-states') do - click_link 'Clear all' # pop-up should be auto confirmed - end - - expect(@project.reload.last_repository_check_failed).to eq(false) - end - end - - def visit_admin_project_page - visit admin_namespace_project_path(@project.namespace, @project) - end end diff --git a/spec/features/admin/admin_uses_repository_checks_spec.rb b/spec/features/admin/admin_uses_repository_checks_spec.rb new file mode 100644 index 00000000000..69b82441916 --- /dev/null +++ b/spec/features/admin/admin_uses_repository_checks_spec.rb @@ -0,0 +1,48 @@ +require 'rails_helper' + +feature 'Admin uses repository checks', feature: true do + before do + login_as :admin + end + + scenario 'to trigger a single check' do + project = create(:empty_project) + visit_admin_project_page(project) + + page.within('.repository-check') do + click_button 'Trigger repository check' + end + + expect(page).to have_content('Repository check was triggered') + end + + scenario 'to see a single failed repository check' do + visit_admin_project_page(broken_project) + + page.within('.alert') do + expect(page.text).to match(/Last repository check \(.* ago\) failed/) + end + end + + scenario 'to clear all repository checks', js: true do + project = broken_project + visit admin_application_settings_path + + click_link 'Clear all repository checks' # pop-up should be auto confirmed + + expect(project.reload.last_repository_check_failed).to eq(false) + end + + def visit_admin_project_page(project) + visit admin_namespace_project_path(project.namespace, project) + end + + def broken_project + project = create(:empty_project) + project.update_columns( + last_repository_check_failed: true, + last_repository_check_at: Time.now, + ) + project + end +end -- cgit v1.2.1 From 9a30d3b5aef732e782e9496b2e8ae62069ba521a Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Wed, 13 Apr 2016 12:20:43 +0200 Subject: Remove \f. WT\F ?? --- doc/administration/repository_checks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/administration/repository_checks.md b/doc/administration/repository_checks.md index f4c5a84fd37..16f4627a596 100644 --- a/doc/administration/repository_checks.md +++ b/doc/administration/repository_checks.md @@ -28,7 +28,7 @@ panel. ## What to do if a check failed -If the repository check fails for some repository you should look up the error +If the repository check fails for some repository you should look up the error in repocheck.log (in the admin panel or on disk; see `/var/log/gitlab/gitlab-rails` for Omnibus installations or `/home/git/gitlab/log` for installations from source). Once you have -- cgit v1.2.1 From 0f602be99f99f1ae493478a8a28df2907cfa0082 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Wed, 13 Apr 2016 15:56:05 +0200 Subject: Clear repository check columns asynchronously --- .../admin/application_settings_controller.rb | 8 +-- app/controllers/admin/projects_controller.rb | 2 +- app/workers/repository_check/batch_worker.rb | 63 ++++++++++++++++++++++ app/workers/repository_check/clear_worker.rb | 17 ++++++ .../repository_check/single_repository_worker.rb | 36 +++++++++++++ app/workers/repository_check_worker.rb | 61 --------------------- app/workers/single_repository_check_worker.rb | 34 ------------ config/initializers/1_settings.rb | 2 +- .../admin/admin_uses_repository_checks_spec.rb | 27 ++++------ spec/workers/repository_check/batch_worker_spec.rb | 39 ++++++++++++++ spec/workers/repository_check/clear_worker_spec.rb | 17 ++++++ spec/workers/repository_check_worker_spec.rb | 39 -------------- 12 files changed, 187 insertions(+), 158 deletions(-) create mode 100644 app/workers/repository_check/batch_worker.rb create mode 100644 app/workers/repository_check/clear_worker.rb create mode 100644 app/workers/repository_check/single_repository_worker.rb delete mode 100644 app/workers/repository_check_worker.rb delete mode 100644 app/workers/single_repository_check_worker.rb create mode 100644 spec/workers/repository_check/batch_worker_spec.rb create mode 100644 spec/workers/repository_check/clear_worker_spec.rb delete mode 100644 spec/workers/repository_check_worker_spec.rb diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index a54864480a2..b4a28b8dd3f 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -20,18 +20,14 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController end def clear_repository_check_states - Project.update_all( - last_repository_check_failed: false, - last_repository_check_at: nil - ) + RepositoryCheck::ClearWorker.perform_async redirect_to( admin_application_settings_path, - notice: 'All repository check states were cleared.' + notice: 'Started asynchronous removal of all repository check states.' ) end - private def set_application_setting diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb index 6854e57b650..87986fdf8b1 100644 --- a/app/controllers/admin/projects_controller.rb +++ b/app/controllers/admin/projects_controller.rb @@ -32,7 +32,7 @@ class Admin::ProjectsController < Admin::ApplicationController end def repository_check - SingleRepositoryCheckWorker.perform_async(@project.id) + RepositoryCheck::SingleRepositoryWorker.perform_async(@project.id) redirect_to( admin_namespace_project_path(@project.namespace, @project), diff --git a/app/workers/repository_check/batch_worker.rb b/app/workers/repository_check/batch_worker.rb new file mode 100644 index 00000000000..16cd77a9bf0 --- /dev/null +++ b/app/workers/repository_check/batch_worker.rb @@ -0,0 +1,63 @@ +module RepositoryCheck + class BatchWorker + include Sidekiq::Worker + + RUN_TIME = 3600 + + sidekiq_options retry: false + + def perform + start = Time.now + + # This loop will break after a little more than one hour ('a little + # more' because `git fsck` may take a few minutes), or if it runs out of + # projects to check. By default sidekiq-cron will start a new + # RepositoryCheckWorker each hour so that as long as there are repositories to + # check, only one (or two) will be checked at a time. + project_ids.each do |project_id| + break if Time.now - start >= RUN_TIME + break unless current_settings.repository_checks_enabled + + next unless try_obtain_lease(project_id) + + SingleRepositoryWorker.new.perform(project_id) + end + end + + private + + # Project.find_each does not support WHERE clauses and + # Project.find_in_batches does not support ordering. So we just build an + # array of ID's. This is OK because we do it only once an hour, because + # getting ID's from Postgres is not terribly slow, and because no user + # has to sit and wait for this query to finish. + def project_ids + limit = 10_000 + never_checked_projects = Project.where('last_repository_check_at IS NULL').limit(limit). + pluck(:id) + old_check_projects = Project.where('last_repository_check_at < ?', 1.week.ago). + reorder('last_repository_check_at ASC').limit(limit).pluck(:id) + never_checked_projects + old_check_projects + end + + def try_obtain_lease(id) + # Use a 24-hour timeout because on servers/projects where 'git fsck' is + # super slow we definitely do not want to run it twice in parallel. + Gitlab::ExclusiveLease.new( + "project_repository_check:#{id}", + timeout: 24.hours + ).try_obtain + end + + def current_settings + # No caching of the settings! If we cache them and an admin disables + # this feature, an active RepositoryCheckWorker would keep going for up + # to 1 hour after the feature was disabled. + if Rails.env.test? + Gitlab::CurrentSettings.fake_application_settings + else + ApplicationSetting.current + end + end + end +end diff --git a/app/workers/repository_check/clear_worker.rb b/app/workers/repository_check/clear_worker.rb new file mode 100644 index 00000000000..fe0cce9aab7 --- /dev/null +++ b/app/workers/repository_check/clear_worker.rb @@ -0,0 +1,17 @@ +module RepositoryCheck + class ClearWorker + include Sidekiq::Worker + + sidekiq_options retry: false + + def perform + # Do batched updates because these updates will be slow and locking + Project.select(:id).find_in_batches(batch_size: 1000) do |batch| + Project.where(id: batch.map(&:id)).update_all( + last_repository_check_failed: nil, + last_repository_check_at: nil, + ) + end + end + end +end \ No newline at end of file diff --git a/app/workers/repository_check/single_repository_worker.rb b/app/workers/repository_check/single_repository_worker.rb new file mode 100644 index 00000000000..e54ae86d06c --- /dev/null +++ b/app/workers/repository_check/single_repository_worker.rb @@ -0,0 +1,36 @@ +module RepositoryCheck + class SingleRepositoryWorker + include Sidekiq::Worker + + sidekiq_options retry: false + + def perform(project_id) + project = Project.find(project_id) + project.update_columns( + last_repository_check_failed: !check(project), + last_repository_check_at: Time.now, + ) + end + + private + + def check(project) + # Use 'map do', not 'all? do', to prevent short-circuiting + [project.repository, project.wiki.repository].map do |repository| + git_fsck(repository.path_to_repo) + end.all? + end + + def git_fsck(path) + cmd = %W(nice git --git-dir=#{path} fsck) + output, status = Gitlab::Popen.popen(cmd) + + if status.zero? + true + else + Gitlab::RepositoryCheckLogger.error("command failed: #{cmd.join(' ')}\n#{output}") + false + end + end + end +end diff --git a/app/workers/repository_check_worker.rb b/app/workers/repository_check_worker.rb deleted file mode 100644 index d7ead91f94e..00000000000 --- a/app/workers/repository_check_worker.rb +++ /dev/null @@ -1,61 +0,0 @@ -class RepositoryCheckWorker - include Sidekiq::Worker - - RUN_TIME = 3600 - - sidekiq_options retry: false - - def perform - start = Time.now - - # This loop will break after a little more than one hour ('a little - # more' because `git fsck` may take a few minutes), or if it runs out of - # projects to check. By default sidekiq-cron will start a new - # RepositoryCheckWorker each hour so that as long as there are repositories to - # check, only one (or two) will be checked at a time. - project_ids.each do |project_id| - break if Time.now - start >= RUN_TIME - break unless current_settings.repository_checks_enabled - - next unless try_obtain_lease(project_id) - - SingleRepositoryCheckWorker.new.perform(project_id) - end - end - - private - - # Project.find_each does not support WHERE clauses and - # Project.find_in_batches does not support ordering. So we just build an - # array of ID's. This is OK because we do it only once an hour, because - # getting ID's from Postgres is not terribly slow, and because no user - # has to sit and wait for this query to finish. - def project_ids - limit = 10_000 - never_checked_projects = Project.where('last_repository_check_at IS NULL').limit(limit). - pluck(:id) - old_check_projects = Project.where('last_repository_check_at < ?', 1.week.ago). - reorder('last_repository_check_at ASC').limit(limit).pluck(:id) - never_checked_projects + old_check_projects - end - - def try_obtain_lease(id) - # Use a 24-hour timeout because on servers/projects where 'git fsck' is - # super slow we definitely do not want to run it twice in parallel. - Gitlab::ExclusiveLease.new( - "project_repository_check:#{id}", - timeout: 24.hours - ).try_obtain - end - - def current_settings - # No caching of the settings! If we cache them and an admin disables - # this feature, an active RepositoryCheckWorker would keep going for up - # to 1 hour after the feature was disabled. - if Rails.env.test? - Gitlab::CurrentSettings.fake_application_settings - else - ApplicationSetting.current - end - end -end diff --git a/app/workers/single_repository_check_worker.rb b/app/workers/single_repository_check_worker.rb deleted file mode 100644 index f6c345df8b5..00000000000 --- a/app/workers/single_repository_check_worker.rb +++ /dev/null @@ -1,34 +0,0 @@ -class SingleRepositoryCheckWorker - include Sidekiq::Worker - - sidekiq_options retry: false - - def perform(project_id) - project = Project.find(project_id) - project.update_columns( - last_repository_check_failed: !check(project), - last_repository_check_at: Time.now, - ) - end - - private - - def check(project) - # Use 'map do', not 'all? do', to prevent short-circuiting - [project.repository, project.wiki.repository].map do |repository| - git_fsck(repository.path_to_repo) - end.all? - end - - def git_fsck(path) - cmd = %W(nice git --git-dir=#{path} fsck) - output, status = Gitlab::Popen.popen(cmd) - - if status.zero? - true - else - Gitlab::RepositoryCheckLogger.error("command failed: #{cmd.join(' ')}\n#{output}") - false - end - end -end diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 5eb7fdff551..3124dc43065 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -241,7 +241,7 @@ Settings.cron_jobs['stuck_ci_builds_worker']['cron'] ||= '0 0 * * *' Settings.cron_jobs['stuck_ci_builds_worker']['job_class'] = 'StuckCiBuildsWorker' Settings.cron_jobs['repository_check_worker'] ||= Settingslogic.new({}) Settings.cron_jobs['repository_check_worker']['cron'] ||= '20 * * * *' -Settings.cron_jobs['repository_check_worker']['job_class'] = 'RepositoryCheckWorker' +Settings.cron_jobs['repository_check_worker']['job_class'] = 'RepositoryCheck::BatchWorker' Settings.cron_jobs['admin_email_worker'] ||= Settingslogic.new({}) Settings.cron_jobs['admin_email_worker']['cron'] ||= '0 0 * * *' Settings.cron_jobs['admin_email_worker']['job_class'] = 'AdminEmailWorker' diff --git a/spec/features/admin/admin_uses_repository_checks_spec.rb b/spec/features/admin/admin_uses_repository_checks_spec.rb index 69b82441916..661fb761809 100644 --- a/spec/features/admin/admin_uses_repository_checks_spec.rb +++ b/spec/features/admin/admin_uses_repository_checks_spec.rb @@ -1,9 +1,7 @@ require 'rails_helper' feature 'Admin uses repository checks', feature: true do - before do - login_as :admin - end + before { login_as :admin } scenario 'to trigger a single check' do project = create(:empty_project) @@ -17,7 +15,12 @@ feature 'Admin uses repository checks', feature: true do end scenario 'to see a single failed repository check' do - visit_admin_project_page(broken_project) + project = create(:empty_project) + project.update_columns( + last_repository_check_failed: true, + last_repository_check_at: Time.now, + ) + visit_admin_project_page(project) page.within('.alert') do expect(page.text).to match(/Last repository check \(.* ago\) failed/) @@ -25,24 +28,16 @@ feature 'Admin uses repository checks', feature: true do end scenario 'to clear all repository checks', js: true do - project = broken_project visit admin_application_settings_path + + expect(RepositoryCheck::ClearWorker).to receive(:perform_async) - click_link 'Clear all repository checks' # pop-up should be auto confirmed + click_link 'Clear all repository checks' - expect(project.reload.last_repository_check_failed).to eq(false) + expect(page).to have_content('Started asynchronous removal of all repository check states.') end def visit_admin_project_page(project) visit admin_namespace_project_path(project.namespace, project) end - - def broken_project - project = create(:empty_project) - project.update_columns( - last_repository_check_failed: true, - last_repository_check_at: Time.now, - ) - project - end end diff --git a/spec/workers/repository_check/batch_worker_spec.rb b/spec/workers/repository_check/batch_worker_spec.rb new file mode 100644 index 00000000000..51caa645f47 --- /dev/null +++ b/spec/workers/repository_check/batch_worker_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper' + +describe RepositoryCheck::BatchWorker do + subject { described_class.new } + + it 'prefers projects that have never been checked' do + projects = create_list(:project, 3) + projects[0].update_column(:last_repository_check_at, 1.month.ago) + projects[2].update_column(:last_repository_check_at, 3.weeks.ago) + + expect(subject.perform).to eq(projects.values_at(1, 0, 2).map(&:id)) + end + + it 'sorts projects by last_repository_check_at' do + projects = create_list(:project, 3) + projects[0].update_column(:last_repository_check_at, 2.weeks.ago) + projects[1].update_column(:last_repository_check_at, 1.month.ago) + projects[2].update_column(:last_repository_check_at, 3.weeks.ago) + + expect(subject.perform).to eq(projects.values_at(1, 2, 0).map(&:id)) + end + + it 'excludes projects that were checked recently' do + projects = create_list(:project, 3) + projects[0].update_column(:last_repository_check_at, 2.days.ago) + projects[1].update_column(:last_repository_check_at, 1.month.ago) + projects[2].update_column(:last_repository_check_at, 3.days.ago) + + expect(subject.perform).to eq([projects[1].id]) + end + + it 'does nothing when repository checks are disabled' do + create(:empty_project) + current_settings = double('settings', repository_checks_enabled: false) + expect(subject).to receive(:current_settings) { current_settings } + + expect(subject.perform).to eq(nil) + end +end diff --git a/spec/workers/repository_check/clear_worker_spec.rb b/spec/workers/repository_check/clear_worker_spec.rb new file mode 100644 index 00000000000..a3b70c74787 --- /dev/null +++ b/spec/workers/repository_check/clear_worker_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper' + +describe RepositoryCheck::ClearWorker do + it 'clears repository check columns' do + project = create(:empty_project) + project.update_columns( + last_repository_check_failed: true, + last_repository_check_at: Time.now, + ) + + described_class.new.perform + project.reload + + expect(project.last_repository_check_failed).to be_nil + expect(project.last_repository_check_at).to be_nil + end +end diff --git a/spec/workers/repository_check_worker_spec.rb b/spec/workers/repository_check_worker_spec.rb deleted file mode 100644 index 7a757658e97..00000000000 --- a/spec/workers/repository_check_worker_spec.rb +++ /dev/null @@ -1,39 +0,0 @@ -require 'spec_helper' - -describe RepositoryCheckWorker do - subject { RepositoryCheckWorker.new } - - it 'prefers projects that have never been checked' do - projects = create_list(:project, 3) - projects[0].update_column(:last_repository_check_at, 1.month.ago) - projects[2].update_column(:last_repository_check_at, 3.weeks.ago) - - expect(subject.perform).to eq(projects.values_at(1, 0, 2).map(&:id)) - end - - it 'sorts projects by last_repository_check_at' do - projects = create_list(:project, 3) - projects[0].update_column(:last_repository_check_at, 2.weeks.ago) - projects[1].update_column(:last_repository_check_at, 1.month.ago) - projects[2].update_column(:last_repository_check_at, 3.weeks.ago) - - expect(subject.perform).to eq(projects.values_at(1, 2, 0).map(&:id)) - end - - it 'excludes projects that were checked recently' do - projects = create_list(:project, 3) - projects[0].update_column(:last_repository_check_at, 2.days.ago) - projects[1].update_column(:last_repository_check_at, 1.month.ago) - projects[2].update_column(:last_repository_check_at, 3.days.ago) - - expect(subject.perform).to eq([projects[1].id]) - end - - it 'does nothing when repository checks are disabled' do - create(:empty_project) - current_settings = double('settings', repository_checks_enabled: false) - expect(subject).to receive(:current_settings) { current_settings } - - expect(subject.perform).to eq(nil) - end -end -- cgit v1.2.1 From bf31b4495e020ef5fa45b204d2494d8d3b4d1494 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Wed, 13 Apr 2016 17:07:12 +0200 Subject: Schema improvements suggested by Yorick --- db/migrate/20160315135439_project_add_repository_check.rb | 4 +++- db/schema.rb | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/db/migrate/20160315135439_project_add_repository_check.rb b/db/migrate/20160315135439_project_add_repository_check.rb index 5a0859a30b2..8687d5d6296 100644 --- a/db/migrate/20160315135439_project_add_repository_check.rb +++ b/db/migrate/20160315135439_project_add_repository_check.rb @@ -1,6 +1,8 @@ class ProjectAddRepositoryCheck < ActiveRecord::Migration def change - add_column :projects, :last_repository_check_failed, :boolean, default: false + add_column :projects, :last_repository_check_failed, :boolean + add_index :projects, :last_repository_check_failed + add_column :projects, :last_repository_check_at, :datetime end end diff --git a/db/schema.rb b/db/schema.rb index db18d56f9cd..863e1f3f075 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -732,7 +732,7 @@ ActiveRecord::Schema.define(version: 20160412140240) do t.boolean "public_builds", default: true, null: false t.string "main_language" t.integer "pushes_since_gc", default: 0 - t.boolean "last_repository_check_failed", default: false + t.boolean "last_repository_check_failed" t.datetime "last_repository_check_at" end @@ -743,6 +743,7 @@ ActiveRecord::Schema.define(version: 20160412140240) do add_index "projects", ["creator_id"], name: "index_projects_on_creator_id", using: :btree add_index "projects", ["description"], name: "index_projects_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"} add_index "projects", ["last_activity_at"], name: "index_projects_on_last_activity_at", using: :btree + add_index "projects", ["last_repository_check_failed"], name: "index_projects_on_last_repository_check_failed", using: :btree add_index "projects", ["name"], name: "index_projects_on_name_trigram", using: :gin, opclasses: {"name"=>"gin_trgm_ops"} add_index "projects", ["namespace_id"], name: "index_projects_on_namespace_id", using: :btree add_index "projects", ["path"], name: "index_projects_on_path", using: :btree -- cgit v1.2.1 From 6f6d2d0ad5039fa8ace7e09c4f5867bb680b0f79 Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Wed, 13 Apr 2016 17:33:25 +0200 Subject: Use more conservative limits --- app/workers/repository_check/batch_worker.rb | 2 +- app/workers/repository_check/clear_worker.rb | 4 ++-- spec/workers/repository_check/batch_worker_spec.rb | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/workers/repository_check/batch_worker.rb b/app/workers/repository_check/batch_worker.rb index 16cd77a9bf0..44b3145d50f 100644 --- a/app/workers/repository_check/batch_worker.rb +++ b/app/workers/repository_check/batch_worker.rb @@ -35,7 +35,7 @@ module RepositoryCheck limit = 10_000 never_checked_projects = Project.where('last_repository_check_at IS NULL').limit(limit). pluck(:id) - old_check_projects = Project.where('last_repository_check_at < ?', 1.week.ago). + old_check_projects = Project.where('last_repository_check_at < ?', 1.month.ago). reorder('last_repository_check_at ASC').limit(limit).pluck(:id) never_checked_projects + old_check_projects end diff --git a/app/workers/repository_check/clear_worker.rb b/app/workers/repository_check/clear_worker.rb index fe0cce9aab7..9c3347a7040 100644 --- a/app/workers/repository_check/clear_worker.rb +++ b/app/workers/repository_check/clear_worker.rb @@ -5,8 +5,8 @@ module RepositoryCheck sidekiq_options retry: false def perform - # Do batched updates because these updates will be slow and locking - Project.select(:id).find_in_batches(batch_size: 1000) do |batch| + # Do small batched updates because these updates will be slow and locking + Project.select(:id).find_in_batches(batch_size: 100) do |batch| Project.where(id: batch.map(&:id)).update_all( last_repository_check_failed: nil, last_repository_check_at: nil, diff --git a/spec/workers/repository_check/batch_worker_spec.rb b/spec/workers/repository_check/batch_worker_spec.rb index 51caa645f47..f486e45ddad 100644 --- a/spec/workers/repository_check/batch_worker_spec.rb +++ b/spec/workers/repository_check/batch_worker_spec.rb @@ -5,17 +5,17 @@ describe RepositoryCheck::BatchWorker do it 'prefers projects that have never been checked' do projects = create_list(:project, 3) - projects[0].update_column(:last_repository_check_at, 1.month.ago) - projects[2].update_column(:last_repository_check_at, 3.weeks.ago) + projects[0].update_column(:last_repository_check_at, 4.months.ago) + projects[2].update_column(:last_repository_check_at, 3.months.ago) expect(subject.perform).to eq(projects.values_at(1, 0, 2).map(&:id)) end it 'sorts projects by last_repository_check_at' do projects = create_list(:project, 3) - projects[0].update_column(:last_repository_check_at, 2.weeks.ago) - projects[1].update_column(:last_repository_check_at, 1.month.ago) - projects[2].update_column(:last_repository_check_at, 3.weeks.ago) + projects[0].update_column(:last_repository_check_at, 2.months.ago) + projects[1].update_column(:last_repository_check_at, 4.months.ago) + projects[2].update_column(:last_repository_check_at, 3.months.ago) expect(subject.perform).to eq(projects.values_at(1, 2, 0).map(&:id)) end @@ -23,7 +23,7 @@ describe RepositoryCheck::BatchWorker do it 'excludes projects that were checked recently' do projects = create_list(:project, 3) projects[0].update_column(:last_repository_check_at, 2.days.ago) - projects[1].update_column(:last_repository_check_at, 1.month.ago) + projects[1].update_column(:last_repository_check_at, 2.months.ago) projects[2].update_column(:last_repository_check_at, 3.days.ago) expect(subject.perform).to eq([projects[1].id]) -- cgit v1.2.1 From f5e8667fc5fd7f650704e9507edad48c7fbdef79 Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 14 Apr 2016 09:13:32 +0100 Subject: Fixed haml style issues --- app/views/projects/labels/_label.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/projects/labels/_label.html.haml b/app/views/projects/labels/_label.html.haml index c7b4bb1f6e6..8bf544b8371 100644 --- a/app/views/projects/labels/_label.html.haml +++ b/app/views/projects/labels/_label.html.haml @@ -14,7 +14,7 @@ .label-subscription{data: {url: toggle_subscription_namespace_project_label_path(@project.namespace, @project, label)}} .subscription-status{data: {status: label_subscription_status(label)}} - %button.js-subscribe-button.label-subscribe-button.btn.action-buttons{ type: 'button', data: {toggle: "tooltip"} } + %button.js-subscribe-button.label-subscribe-button.btn.action-buttons{ type: "button", data: { toggle: "tooltip" } } %span= label_subscription_toggle_button_text(label) - if can? current_user, :admin_label, @project -- cgit v1.2.1 From ec3f4f5bf06aa662b86079d815f9a68b556190f3 Mon Sep 17 00:00:00 2001 From: Dmitriy Zaporozhets Date: Thu, 14 Apr 2016 10:57:13 +0200 Subject: Projects on group page should be sorted by last activity instead of id/created_at Signed-off-by: Dmitriy Zaporozhets --- app/controllers/groups_controller.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index c1adc999567..ee4fcc4e360 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -40,6 +40,7 @@ class GroupsController < Groups::ApplicationController @last_push = current_user.recent_push if current_user @projects = @projects.includes(:namespace) + @projects = @projects.sorted_by_activity @projects = filter_projects(@projects) @projects = @projects.sort(@sort = params[:sort]) @projects = @projects.page(params[:page]) if params[:filter_projects].blank? -- cgit v1.2.1 From a2931a392a4a96ad6df00c98578c193b91e4027f Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Thu, 14 Apr 2016 14:06:33 +0100 Subject: Fixed style issues --- app/views/shared/issuable/_sidebar.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index fe6e4128003..56c8eaa0597 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -128,7 +128,7 @@ .title.hide-collapsed Notifications - subscribtion_status = subscribed ? 'subscribed' : 'unsubscribed' - %button.btn.btn-block.btn-gray.js-subscribe-button.issuable-subscribe-button.hide-collapsed{:type => 'button'} + %button.btn.btn-block.btn-gray.js-subscribe-button.issuable-subscribe-button.hide-collapsed{ type: "button" } %span= subscribed ? 'Unsubscribe' : 'Subscribe' .subscription-status.hide-collapsed{data: {status: subscribtion_status}} .unsubscribed{class: ( 'hidden' if subscribed )} -- cgit v1.2.1 From 7030ffb0e09dbcc0d03d377846a04fe6cf7d20b6 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Date: Fri, 8 Apr 2016 14:43:21 -0700 Subject: Copying and pasting doesn't grab line numbers or +/- --- app/assets/stylesheets/pages/diff.scss | 20 ++++++++++++++++++++ app/helpers/diff_helper.rb | 1 + app/views/projects/blob/diff.html.haml | 6 +++--- app/views/projects/diffs/_line.html.haml | 6 +++--- 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss index d0855f66911..ca7fa2094b6 100644 --- a/app/assets/stylesheets/pages/diff.scss +++ b/app/assets/stylesheets/pages/diff.scss @@ -67,6 +67,14 @@ line-height: $code_line_height; font-size: $code_font_size; + &.noteable_line.old:before { + content: '-'; + } + + &.noteable_line.new:before { + content: '+'; + } + span { white-space: pre; } @@ -391,3 +399,15 @@ margin-bottom: 0; } } + +.diff-line-num:not(.js-unfold-bottom) { + a { + &:before { + content: attr(data-linenumber); + } + -moz-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; + } +} diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb index ff32e834499..f1e213b34e8 100644 --- a/app/helpers/diff_helper.rb +++ b/app/helpers/diff_helper.rb @@ -44,6 +44,7 @@ module DiffHelper if line.blank? "  ".html_safe else + line[0] = '' line end end diff --git a/app/views/projects/blob/diff.html.haml b/app/views/projects/blob/diff.html.haml index abcfca4cd11..ea6d4df7255 100644 --- a/app/views/projects/blob/diff.html.haml +++ b/app/views/projects/blob/diff.html.haml @@ -9,9 +9,9 @@ - line_old = line_new - @form.offset %tr.line_holder %td.old_line.diff-line-num{data: {linenumber: line_old}} - = link_to raw(line_old), "#" - %td.new_line.diff-line-num - = link_to raw(line_new) , "#" + / = link_to raw(line_old), "#" + %td.new_line.diff-line-num{data: {linenumber: line_old}} + / = link_to raw(line_new) , "#" %td.line_content.noteable_line==#{' ' * @form.indent}#{line} - if @form.unfold? && @form.bottom? && @form.to < @blob.loc diff --git a/app/views/projects/diffs/_line.html.haml b/app/views/projects/diffs/_line.html.haml index 9464c8dc996..2dc6f548437 100644 --- a/app/views/projects/diffs/_line.html.haml +++ b/app/views/projects/diffs/_line.html.haml @@ -9,12 +9,12 @@ %td.new_line.diff-line-num %td.line_content.match= line.text - else - %td.old_line.diff-line-num{class: type} + %td.old_line.diff-line-num{class: type, data: {linenumber: line.new_pos}} - link_text = raw(type == "new" ? " " : line.old_pos) - if defined?(plain) && plain = link_text - else - = link_to link_text, "##{line_code}", id: line_code + = link_to "", "##{line_code}", id: line_code, data: { linenumber: link_text } - if @comments_allowed && can?(current_user, :create_note, @project) = link_to_new_diff_note(line_code) %td.new_line.diff-line-num{class: type, data: {linenumber: line.new_pos}} @@ -22,5 +22,5 @@ - if defined?(plain) && plain = link_text - else - = link_to link_text, "##{line_code}", id: line_code + = link_to "", "##{line_code}", id: line_code, data: { linenumber: link_text } %td.line_content{class: "noteable_line #{type} #{line_code}", data: { line_code: line_code }}= diff_line_content(line.text) -- cgit v1.2.1 From d176f873e1fdf23ebeeebf6d2d25927941016390 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Date: Wed, 13 Apr 2016 12:54:21 -0500 Subject: Add content attributes to discussion diffs; change styling of add-note icon to prevent white spaces in paste --- app/assets/stylesheets/pages/diff.scss | 26 +++++++++++++++------- app/assets/stylesheets/pages/notes.scss | 10 ++------- app/views/projects/blob/diff.html.haml | 4 ++-- .../projects/notes/discussions/_diff.html.haml | 6 ++--- 4 files changed, 24 insertions(+), 22 deletions(-) diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss index ca7fa2094b6..bd7640db3b7 100644 --- a/app/assets/stylesheets/pages/diff.scss +++ b/app/assets/stylesheets/pages/diff.scss @@ -69,10 +69,12 @@ &.noteable_line.old:before { content: '-'; + position: absolute; } &.noteable_line.new:before { content: '+'; + position: absolute; } span { @@ -400,14 +402,22 @@ } } -.diff-line-num:not(.js-unfold-bottom) { - a { - &:before { - content: attr(data-linenumber); +.file-holder { + .diff-line-num:not(.js-unfold-bottom) { + a { + &:before { + content: attr(data-linenumber); + } + } + } +} + +.discussion { + .diff-content { + .diff-line-num { + &:before { + content: attr(data-linenumber); + } } - -moz-user-select: none; - -webkit-user-select: none; - -ms-user-select: none; - user-select: none; } } diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index e421a31549a..ce44f5aa13b 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -276,8 +276,7 @@ ul.notes { .diff-file tr.line_holder { @mixin show-add-diff-note { - filter: alpha(opacity=100); - opacity: 1.0; + display: inline-block; } .add-diff-note { @@ -291,13 +290,8 @@ ul.notes { position: absolute; z-index: 10; width: 32px; - - transition: all 0.2s ease; - // "hide" it by default - opacity: 0.0; - filter: alpha(opacity=0); - + display: none; &:hover { background: $gl-info; color: #fff; diff --git a/app/views/projects/blob/diff.html.haml b/app/views/projects/blob/diff.html.haml index ea6d4df7255..c6ed78aadf6 100644 --- a/app/views/projects/blob/diff.html.haml +++ b/app/views/projects/blob/diff.html.haml @@ -9,9 +9,9 @@ - line_old = line_new - @form.offset %tr.line_holder %td.old_line.diff-line-num{data: {linenumber: line_old}} - / = link_to raw(line_old), "#" + = link_to raw(line_old), "#" %td.new_line.diff-line-num{data: {linenumber: line_old}} - / = link_to raw(line_new) , "#" + = link_to raw(line_new) , "#" %td.line_content.noteable_line==#{' ' * @form.indent}#{line} - if @form.unfold? && @form.bottom? && @form.to < @blob.loc diff --git a/app/views/projects/notes/discussions/_diff.html.haml b/app/views/projects/notes/discussions/_diff.html.haml index 820e31ccd61..6abfb3abc3b 100644 --- a/app/views/projects/notes/discussions/_diff.html.haml +++ b/app/views/projects/notes/discussions/_diff.html.haml @@ -20,10 +20,8 @@ %td.new_line.diff-line-num= "..." %td.line_content.match= line.text - else - %td.old_line.diff-line-num - = raw(type == "new" ? " " : line.old_pos) - %td.new_line.diff-line-num - = raw(type == "old" ? " " : line.new_pos) + %td.old_line.diff-line-num{data: {linenumber: raw(type == "new" ? " " : line.old_pos)}} + %td.new_line.diff-line-num{data: {linenumber: raw(type == "old" ? " " : line.new_pos)}} %td.line_content{class: "noteable_line #{type} #{line_code}", line_code: line_code}= diff_line_content(line.text) - if line_code == note.line_code -- cgit v1.2.1 From 82d0221b632fbb2e7711678b11e9ff26214d9d69 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Date: Wed, 13 Apr 2016 15:33:44 -0500 Subject: Add line type conditional to diff line helper --- app/assets/stylesheets/pages/diff.scss | 18 +++++++++++------- app/helpers/diff_helper.rb | 6 ++++-- app/views/projects/diffs/_line.html.haml | 2 +- app/views/projects/notes/discussions/_diff.html.haml | 2 +- 4 files changed, 17 insertions(+), 11 deletions(-) diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss index bd7640db3b7..77d7a3024d5 100644 --- a/app/assets/stylesheets/pages/diff.scss +++ b/app/assets/stylesheets/pages/diff.scss @@ -67,14 +67,18 @@ line-height: $code_line_height; font-size: $code_font_size; - &.noteable_line.old:before { - content: '-'; - position: absolute; + &.noteable_line.old { + &:before { + content: '-'; + position: absolute; + } } - &.noteable_line.new:before { - content: '+'; - position: absolute; + &.noteable_line.new { + &:before { + content: '+'; + position: absolute; + } } span { @@ -406,7 +410,7 @@ .diff-line-num:not(.js-unfold-bottom) { a { &:before { - content: attr(data-linenumber); + content: attr(data-linenumber); } } } diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb index f1e213b34e8..0504cfb7591 100644 --- a/app/helpers/diff_helper.rb +++ b/app/helpers/diff_helper.rb @@ -40,11 +40,13 @@ module DiffHelper (unfold) ? 'unfold js-unfold' : '' end - def diff_line_content(line) + def diff_line_content(line, line_type = nil) if line.blank? "  ".html_safe else - line[0] = '' + if line_type == 'new' || line_type == 'old' + line[0] = " " + end line end end diff --git a/app/views/projects/diffs/_line.html.haml b/app/views/projects/diffs/_line.html.haml index 2dc6f548437..6c5602acd43 100644 --- a/app/views/projects/diffs/_line.html.haml +++ b/app/views/projects/diffs/_line.html.haml @@ -23,4 +23,4 @@ = link_text - else = link_to "", "##{line_code}", id: line_code, data: { linenumber: link_text } - %td.line_content{class: "noteable_line #{type} #{line_code}", data: { line_code: line_code }}= diff_line_content(line.text) + %td.line_content{class: "noteable_line #{type} #{line_code}", data: { line_code: line_code }}= diff_line_content(line.text, type) diff --git a/app/views/projects/notes/discussions/_diff.html.haml b/app/views/projects/notes/discussions/_diff.html.haml index 6abfb3abc3b..9fd9d5bb2aa 100644 --- a/app/views/projects/notes/discussions/_diff.html.haml +++ b/app/views/projects/notes/discussions/_diff.html.haml @@ -22,7 +22,7 @@ - else %td.old_line.diff-line-num{data: {linenumber: raw(type == "new" ? " " : line.old_pos)}} %td.new_line.diff-line-num{data: {linenumber: raw(type == "old" ? " " : line.new_pos)}} - %td.line_content{class: "noteable_line #{type} #{line_code}", line_code: line_code}= diff_line_content(line.text) + %td.line_content{class: "noteable_line #{type} #{line_code}", line_code: line_code}= diff_line_content(line.text, type) - if line_code == note.line_code = render "projects/notes/diff_notes_with_reply", notes: discussion_notes -- cgit v1.2.1 From 2f81baf4767df7ce0144a63b4e451b7d8e09f86a Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Date: Wed, 13 Apr 2016 18:40:14 -0500 Subject: Update click_diff_line --- features/steps/shared/diff_note.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/features/steps/shared/diff_note.rb b/features/steps/shared/diff_note.rb index 1448c3f44cc..e846c52d474 100644 --- a/features/steps/shared/diff_note.rb +++ b/features/steps/shared/diff_note.rb @@ -227,7 +227,7 @@ module SharedDiffNote end def click_diff_line(code) - find("button[data-line-code='#{code}']").click + find("button[data-line-code='#{code}']").trigger('click') end def click_parallel_diff_line(code, line_type) -- cgit v1.2.1 From 91cebb7289df872ae4887f7103f68a6f948ee61c Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Thu, 14 Apr 2016 17:04:18 +0200 Subject: Documentation / help improvements --- app/views/admin/projects/show.html.haml | 4 +++- doc/README.md | 1 + doc/administration/repository_checks.md | 7 +++---- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml index 1e44172f066..73986d21bcf 100644 --- a/app/views/admin/projects/show.html.haml +++ b/app/views/admin/projects/show.html.haml @@ -10,7 +10,7 @@ .col-md-12 .panel .panel-heading.alert.alert-danger - Last repository check + Last repository check = "(#{time_ago_in_words(@project.last_repository_check_at)} ago)" failed. See = link_to 'repocheck.log', admin_logs_path @@ -126,6 +126,8 @@ - else passed. + = link_to icon('question-circle'), help_page_path('administration', 'repository_checks') + .form-group = f.submit 'Trigger repository check', class: 'btn btn-primary' diff --git a/doc/README.md b/doc/README.md index d2660930653..e6ac4794827 100644 --- a/doc/README.md +++ b/doc/README.md @@ -31,6 +31,7 @@ - [Environment Variables](administration/environment_variables.md) to configure GitLab. - [Operations](operations/README.md) Keeping GitLab up and running - [Raketasks](raketasks/README.md) Backups, maintenance, automatic webhook setup and the importing of projects. +- [Repository checks](administration/repository_checks.md) Periodic Git repository checks - [Security](security/README.md) Learn what you can do to further secure your GitLab instance. - [System hooks](system_hooks/system_hooks.md) Notifications when users, projects and keys are changed. - [Update](update/README.md) Update guides to upgrade your installation. diff --git a/doc/administration/repository_checks.md b/doc/administration/repository_checks.md index 16f4627a596..61bf8ce6161 100644 --- a/doc/administration/repository_checks.md +++ b/doc/administration/repository_checks.md @@ -1,10 +1,9 @@ # Repository checks -_**Note:** This feature was [introduced][ce-3232] in GitLab 8.7_ +>**Note:** +This feature was [introduced][ce-3232] in GitLab 8.7. ---- - -Git has a built-in mechanism, \[git fsck\]\[git-fsck\], to verify the +Git has a built-in mechanism, [git fsck][git-fsck], to verify the integrity of all data commited to a repository. GitLab administrators can trigger such a check for a project via the project page under the admin panel. The checks run asynchronously so it may take a few minutes -- cgit v1.2.1 From 77daebe660eb3f4189ca6339e0fcd634abffe3fa Mon Sep 17 00:00:00 2001 From: Jacob Vosmaer Date: Thu, 14 Apr 2016 17:05:41 +0200 Subject: More create_list --- spec/mailers/repository_check_mailer_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/mailers/repository_check_mailer_spec.rb b/spec/mailers/repository_check_mailer_spec.rb index 6ae9a93aaac..583bf15176f 100644 --- a/spec/mailers/repository_check_mailer_spec.rb +++ b/spec/mailers/repository_check_mailer_spec.rb @@ -5,7 +5,7 @@ describe RepositoryCheckMailer do describe '.notify' do it 'emails all admins' do - admins = 3.times.map { create(:admin) } + admins = create_list(:admin, 3) mail = described_class.notify(1) -- cgit v1.2.1 From b49069b72cbfa73b6f7bb7195659ba2958f02f7e Mon Sep 17 00:00:00 2001 From: Jamie Neubert Pedersen Date: Thu, 14 Apr 2016 16:04:18 +0000 Subject: change: "very demand" changed to "specific demand" This must be an error as the sentence doesn't make sense otherwise --- doc/ci/runners/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/ci/runners/README.md b/doc/ci/runners/README.md index 295d953db11..c7df0713a3d 100644 --- a/doc/ci/runners/README.md +++ b/doc/ci/runners/README.md @@ -19,7 +19,7 @@ many projects, you can have a single or a small number of runners that handle multiple projects. This makes it easier to maintain and update runners. **Specific runners** are useful for jobs that have special requirements or for -projects with a very demand. If a job has certain requirements, you can set +projects with a specific demand. If a job has certain requirements, you can set up the specific runner with this in mind, while not having to do this for all runners. For example, if you want to deploy a certain project, you can setup a specific runner to have the right credentials for this. -- cgit v1.2.1 From 7ef35b9c61b61b6cf03f20ce4ab34272404dca40 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 14 Apr 2016 12:28:48 -0400 Subject: Shut up, Rubocop --- app/workers/repository_check/clear_worker.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/workers/repository_check/clear_worker.rb b/app/workers/repository_check/clear_worker.rb index 9c3347a7040..b7202ddff34 100644 --- a/app/workers/repository_check/clear_worker.rb +++ b/app/workers/repository_check/clear_worker.rb @@ -1,9 +1,9 @@ module RepositoryCheck class ClearWorker include Sidekiq::Worker - + sidekiq_options retry: false - + def perform # Do small batched updates because these updates will be slow and locking Project.select(:id).find_in_batches(batch_size: 100) do |batch| @@ -14,4 +14,4 @@ module RepositoryCheck end end end -end \ No newline at end of file +end -- cgit v1.2.1 From cce21e7490d8684fb8fbaa6c9d5a8ce76c36d975 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 14 Apr 2016 12:32:33 -0400 Subject: Update CHANGELOG for "Auto git fsck" --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 41b6e2c67fd..1f94a574d5b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -27,6 +27,7 @@ v 8.7.0 (unreleased) - Add endpoints to archive or unarchive a project !3372 - Add links to CI setup documentation from project settings and builds pages - Handle nil descriptions in Slack issue messages (Stan Hu) + - Add automated repository integrity checks - API: Expose open_issues_count, closed_issues_count, open_merge_requests_count for labels (Robert Schilling) - API: Ability to star and unstar a project (Robert Schilling) - Add default scope to projects to exclude projects pending deletion -- cgit v1.2.1 From 2165bbc7853016ea68f36b44ad0590623add7bcf Mon Sep 17 00:00:00 2001 From: Achilleas Pipinellis Date: Thu, 14 Apr 2016 20:31:19 +0300 Subject: Remove deprecated NGINX CI config --- lib/support/nginx/gitlab_ci | 29 ----------------------------- 1 file changed, 29 deletions(-) delete mode 100644 lib/support/nginx/gitlab_ci diff --git a/lib/support/nginx/gitlab_ci b/lib/support/nginx/gitlab_ci deleted file mode 100644 index bf05edfd780..00000000000 --- a/lib/support/nginx/gitlab_ci +++ /dev/null @@ -1,29 +0,0 @@ -# GITLAB CI -server { - listen 80 default_server; # e.g., listen 192.168.1.1:80; - server_name YOUR_CI_SERVER_FQDN; # e.g., server_name source.example.com; - - access_log /var/log/nginx/gitlab_ci_access.log; - error_log /var/log/nginx/gitlab_ci_error.log; - - # expose API to fix runners - location /api { - proxy_read_timeout 300; - proxy_connect_timeout 300; - proxy_redirect off; - proxy_set_header X-Real-IP $remote_addr; - - # You need to specify your DNS servers that are able to resolve YOUR_GITLAB_SERVER_FQDN - resolver 8.8.8.8 8.8.4.4; - proxy_pass $scheme://YOUR_GITLAB_SERVER_FQDN/ci$request_uri; - } - - # redirect all other CI requests - location / { - return 301 $scheme://YOUR_GITLAB_SERVER_FQDN/ci$request_uri; - } - - # adjust this to match the largest build log your runners might submit, - # set to 0 to disable limit - client_max_body_size 10m; -} \ No newline at end of file -- cgit v1.2.1 From 80d8f8b87609ffb9b0fcc1f74fbeb4520a193e07 Mon Sep 17 00:00:00 2001 From: Annabel Dunstone Date: Thu, 14 Apr 2016 09:10:36 -0500 Subject: Syntax & style updates --- app/assets/stylesheets/pages/diff.scss | 22 +++++++++++++--------- app/helpers/diff_helper.rb | 4 +--- app/views/projects/blob/diff.html.haml | 12 ++++++------ app/views/projects/diffs/_line.html.haml | 16 ++++++++-------- .../projects/notes/discussions/_diff.html.haml | 6 +++--- 5 files changed, 31 insertions(+), 29 deletions(-) diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss index 77d7a3024d5..183f22a1b24 100644 --- a/app/assets/stylesheets/pages/diff.scss +++ b/app/assets/stylesheets/pages/diff.scss @@ -67,17 +67,21 @@ line-height: $code_line_height; font-size: $code_font_size; - &.noteable_line.old { - &:before { - content: '-'; - position: absolute; + &.noteable_line { + position: relative; + + &.old { + &:before { + content: '-'; + position: absolute; + } } - } - &.noteable_line.new { - &:before { - content: '+'; - position: absolute; + &.new { + &:before { + content: '+'; + position: absolute; + } } } diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb index 0504cfb7591..6a3ec83b8c0 100644 --- a/app/helpers/diff_helper.rb +++ b/app/helpers/diff_helper.rb @@ -44,9 +44,7 @@ module DiffHelper if line.blank? "  ".html_safe else - if line_type == 'new' || line_type == 'old' - line[0] = " " - end + line[0] = ' ' if %w[new old].include?(line_type) line end end diff --git a/app/views/projects/blob/diff.html.haml b/app/views/projects/blob/diff.html.haml index c6ed78aadf6..38e62c81fed 100644 --- a/app/views/projects/blob/diff.html.haml +++ b/app/views/projects/blob/diff.html.haml @@ -1,20 +1,20 @@ - if @lines.present? - if @form.unfold? && @form.since != 1 && !@form.bottom? %tr.line_holder{ id: @form.since } - = render "projects/diffs/match_line", {line: @match_line, - line_old: @form.since, line_new: @form.since, bottom: false, new_file: false} + = render "projects/diffs/match_line", { line: @match_line, + line_old: @form.since, line_new: @form.since, bottom: false, new_file: false } - @lines.each_with_index do |line, index| - line_new = index + @form.since - line_old = line_new - @form.offset %tr.line_holder - %td.old_line.diff-line-num{data: {linenumber: line_old}} + %td.old_line.diff-line-num{ data: { linenumber: line_old } } = link_to raw(line_old), "#" - %td.new_line.diff-line-num{data: {linenumber: line_old}} + %td.new_line.diff-line-num{ data: { linenumber: line_old } } = link_to raw(line_new) , "#" %td.line_content.noteable_line==#{' ' * @form.indent}#{line} - if @form.unfold? && @form.bottom? && @form.to < @blob.loc %tr.line_holder{ id: @form.to } - = render "projects/diffs/match_line", {line: @match_line, - line_old: @form.to, line_new: @form.to, bottom: true, new_file: false} + = render "projects/diffs/match_line", { line: @match_line, + line_old: @form.to, line_new: @form.to, bottom: true, new_file: false } diff --git a/app/views/projects/diffs/_line.html.haml b/app/views/projects/diffs/_line.html.haml index 6c5602acd43..107097ad963 100644 --- a/app/views/projects/diffs/_line.html.haml +++ b/app/views/projects/diffs/_line.html.haml @@ -1,26 +1,26 @@ - type = line.type -%tr.line_holder{id: line_code, class: type} +%tr.line_holder{ id: line_code, class: type } - case type - when 'match' - = render "projects/diffs/match_line", {line: line.text, - line_old: line.old_pos, line_new: line.new_pos, bottom: false, new_file: diff_file.new_file} + = render "projects/diffs/match_line", { line: line.text, + line_old: line.old_pos, line_new: line.new_pos, bottom: false, new_file: diff_file.new_file } - when 'nonewline' %td.old_line.diff-line-num %td.new_line.diff-line-num %td.line_content.match= line.text - else - %td.old_line.diff-line-num{class: type, data: {linenumber: line.new_pos}} - - link_text = raw(type == "new" ? " " : line.old_pos) + %td.old_line.diff-line-num{ class: type, data: { linenumber: line.new_pos } } + - link_text = type == "new" ? " ".html_safe : line.old_pos - if defined?(plain) && plain = link_text - else = link_to "", "##{line_code}", id: line_code, data: { linenumber: link_text } - if @comments_allowed && can?(current_user, :create_note, @project) = link_to_new_diff_note(line_code) - %td.new_line.diff-line-num{class: type, data: {linenumber: line.new_pos}} - - link_text = raw(type == "old" ? " " : line.new_pos) + %td.new_line.diff-line-num{ class: type, data: { linenumber: line.new_pos } } + - link_text = type == "old" ? " ".html_safe : line.new_pos - if defined?(plain) && plain = link_text - else = link_to "", "##{line_code}", id: line_code, data: { linenumber: link_text } - %td.line_content{class: "noteable_line #{type} #{line_code}", data: { line_code: line_code }}= diff_line_content(line.text, type) + %td.line_content{ class: ['noteable_line', type, line_code], data: { line_code: line_code } }= diff_line_content(line.text, type) diff --git a/app/views/projects/notes/discussions/_diff.html.haml b/app/views/projects/notes/discussions/_diff.html.haml index 9fd9d5bb2aa..d46aab000c3 100644 --- a/app/views/projects/notes/discussions/_diff.html.haml +++ b/app/views/projects/notes/discussions/_diff.html.haml @@ -20,9 +20,9 @@ %td.new_line.diff-line-num= "..." %td.line_content.match= line.text - else - %td.old_line.diff-line-num{data: {linenumber: raw(type == "new" ? " " : line.old_pos)}} - %td.new_line.diff-line-num{data: {linenumber: raw(type == "old" ? " " : line.new_pos)}} - %td.line_content{class: "noteable_line #{type} #{line_code}", line_code: line_code}= diff_line_content(line.text, type) + %td.old_line.diff-line-num{ data: { linenumber: type == "new" ? " ".html_safe : line.old_pos } } + %td.new_line.diff-line-num{ data: { linenumber: type == "old" ? " ".html_safe : line.new_pos } } + %td.line_content{ class: ['noteable_line', type, line_code], line_code: line_code }= diff_line_content(line.text, type) - if line_code == note.line_code = render "projects/notes/diff_notes_with_reply", notes: discussion_notes -- cgit v1.2.1 From 6ae2680ce7159eedd37e89c7aa99688949139f11 Mon Sep 17 00:00:00 2001 From: Valery Sizov Date: Thu, 14 Apr 2016 19:38:17 +0300 Subject: Emoji categories fix --- CHANGELOG | 1 + lib/award_emoji.rb | 16 +++++++++++++--- spec/lib/award_emoji_spec.rb | 7 +++++++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 9aa40fae18e..4a9a5c1bc36 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -66,6 +66,7 @@ v 8.7.0 (unreleased) - Improved markdown forms - Diffs load at the correct point when linking from from number - Selected diff rows highlight + - Fix emoji catgories in the emoji picker v 8.6.6 - Fix error on language detection when repository has no HEAD (e.g., master branch). !3654 (Jeroen Bobbeldijk) diff --git a/lib/award_emoji.rb b/lib/award_emoji.rb index 4fc3443ac68..5f8ff01b0a9 100644 --- a/lib/award_emoji.rb +++ b/lib/award_emoji.rb @@ -14,19 +14,29 @@ class AwardEmoji food_drink: "Food" }.with_indifferent_access + CATEGORY_ALIASES = { + symbols: "objects_symbols", + foods: "food_drink", + travel: "travel_places" + }.with_indifferent_access + def self.normilize_emoji_name(name) aliases[name] || name end def self.emoji_by_category unless @emoji_by_category - @emoji_by_category = {} + @emoji_by_category = Hash.new { |h, key| h[key] = [] } emojis.each do |emoji_name, data| data["name"] = emoji_name - @emoji_by_category[data["category"]] ||= [] - @emoji_by_category[data["category"]] << data + # Skip Fitzpatrick(tone) modifiers + next if data["category"] == "modifier" + + category = CATEGORY_ALIASES[data["category"]] || data["category"] + + @emoji_by_category[category] << data end @emoji_by_category = @emoji_by_category.sort.to_h diff --git a/spec/lib/award_emoji_spec.rb b/spec/lib/award_emoji_spec.rb index 330678f7f16..88c22912950 100644 --- a/spec/lib/award_emoji_spec.rb +++ b/spec/lib/award_emoji_spec.rb @@ -16,4 +16,11 @@ describe AwardEmoji do end end end + + describe '.emoji_by_category' do + it "only contains known categories" do + undefined_categories = AwardEmoji.emoji_by_category.keys - AwardEmoji::CATEGORIES.keys + expect(undefined_categories).to be_empty + end + end end -- cgit v1.2.1 From f58312976733fadb2b0cbf4b734f8d94220cb501 Mon Sep 17 00:00:00 2001 From: Robert Speicher Date: Thu, 14 Apr 2016 13:56:30 -0400 Subject: Add Sentry program context even without a current user --- app/controllers/application_controller.rb | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index ce5c84ee9bc..1c53b0b21a3 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -14,7 +14,7 @@ class ApplicationController < ActionController::Base before_action :check_password_expiration before_action :check_2fa_requirement before_action :ldap_security_check - before_action :sentry_user_context + before_action :sentry_context before_action :default_headers before_action :add_gon_variables before_action :configure_permitted_parameters, if: :devise_controller? @@ -41,13 +41,15 @@ class ApplicationController < ActionController::Base protected - def sentry_user_context - if Rails.env.production? && current_application_settings.sentry_enabled && current_user - Raven.user_context( - id: current_user.id, - email: current_user.email, - username: current_user.username, - ) + def sentry_context + if Rails.env.production? && current_application_settings.sentry_enabled + if current_user + Raven.user_context( + id: current_user.id, + email: current_user.email, + username: current_user.username, + ) + end Raven.tags_context(program: sentry_program_context) end -- cgit v1.2.1 From 70c9ceb7b1bd815f4a3e79a809a9279f841cee63 Mon Sep 17 00:00:00 2001 From: Douglas Barbosa Alexandre Date: Thu, 14 Apr 2016 18:46:18 -0300 Subject: Fix datetime format when migrating new notification settings on MySQL --- db/migrate/20160328115649_migrate_new_notification_setting.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/migrate/20160328115649_migrate_new_notification_setting.rb b/db/migrate/20160328115649_migrate_new_notification_setting.rb index 0a110869027..3c81b2c37bf 100644 --- a/db/migrate/20160328115649_migrate_new_notification_setting.rb +++ b/db/migrate/20160328115649_migrate_new_notification_setting.rb @@ -7,7 +7,7 @@ # class MigrateNewNotificationSetting < ActiveRecord::Migration def up - timestamp = Time.now + timestamp = Time.now.strftime('%F %T') execute "INSERT INTO notification_settings ( user_id, source_id, source_type, level, created_at, updated_at ) SELECT user_id, source_id, source_type, notification_level, '#{timestamp}', '#{timestamp}' FROM members WHERE user_id IS NOT NULL" end -- cgit v1.2.1