namespace :gitlab do desc "GITLAB | Check the configuration of GitLab and its environment" task check: %w{gitlab:env:check gitlab:gitolite:check gitlab:resque:check gitlab:app:check} namespace :app do desc "GITLAB | Check the configuration of the GitLab Rails app" task check: :environment do warn_user_is_not_gitlab start_checking "GitLab" check_database_config_exists check_database_is_not_sqlite check_migrations_are_up check_gitlab_config_exists check_gitlab_config_not_outdated check_log_writable check_tmp_writable check_init_script_exists check_init_script_up_to_date check_satellites_exist finished_checking "GitLab" end # Checks ######################## def check_database_config_exists print "Database config exists? ... " database_config_file = Rails.root.join("config", "database.yml") if File.exists?(database_config_file) puts "yes".green else puts "no".red try_fixing_it( "Copy config/database.yml. to config/database.yml", "Check that the information in config/database.yml is correct" ) for_more_information( see_database_guide, "http://guides.rubyonrails.org/getting_started.html#configuring-a-database" ) check_failed end end def check_database_is_not_sqlite print "Database is not SQLite ... " database_config_file = Rails.root.join("config", "database.yml") unless File.read(database_config_file) =~ /sqlite/ puts "yes".green else puts "no".red for_more_information( "https://github.com/gitlabhq/gitlabhq/wiki/Migrate-from-SQLite-to-MySQL", see_database_guide ) check_failed end end def check_gitlab_config_exists print "GitLab config exists? ... " gitlab_config_file = Rails.root.join("config", "gitlab.yml") if File.exists?(gitlab_config_file) puts "yes".green else puts "no".red try_fixing_it( "Copy config/gitlab.yml.example to config/gitlab.yml", "Update config/gitlab.yml to match your setup" ) for_more_information( see_installation_guide_section "GitLab" ) check_failed end end def check_gitlab_config_not_outdated print "GitLab config outdated? ... " gitlab_config_file = Rails.root.join("config", "gitlab.yml") unless File.exists?(gitlab_config_file) puts "can't check because of previous errors".magenta end # omniauth or ldap could have been deleted from the file unless Gitlab.config.pre_40_config puts "no".green else puts "yes".red try_fixing_it( "Backup your config/gitlab.yml", "Copy config/gitlab.yml.example to config/gitlab.yml", "Update config/gitlab.yml to match your setup" ) for_more_information( see_installation_guide_section "GitLab" ) check_failed end end def check_init_script_exists print "Init script exists? ... " script_path = "/etc/init.d/gitlab" if File.exists?(script_path) puts "yes".green else puts "no".red try_fixing_it( "Install the init script" ) for_more_information( see_installation_guide_section "Install Init Script" ) check_failed end end def check_init_script_up_to_date print "Init script up-to-date? ... " script_path = "/etc/init.d/gitlab" unless File.exists?(script_path) puts "can't check because of previous errors".magenta return end recipe_content = `curl https://raw.github.com/gitlabhq/gitlab-recipes/master/init.d/gitlab 2>/dev/null` script_content = File.read(script_path) if recipe_content == script_content puts "yes".green else puts "no".red try_fixing_it( "Redownload the init script" ) for_more_information( see_installation_guide_section "Install Init Script" ) check_failed end end def check_migrations_are_up print "All migrations up? ... " migration_status = `bundle exec rake db:migrate:status` unless migration_status =~ /down\s+\d{14}/ puts "yes".green else puts "no".red try_fixing_it( "sudo -u gitlab -H bundle exec rake db:migrate" ) check_failed end end def check_satellites_exist print "Projects have satellites? ... " unless Project.count > 0 puts "can't check, you have no projects".magenta return end puts "" Project.find_each(batch_size: 100) do |project| print "#{project.name_with_namespace.yellow} ... " if project.satellite.exists? puts "yes".green else puts "no".red try_fixing_it( "sudo -u gitlab -H bundle exec rake gitlab:satellites:create", "If necessary, remove the tmp/repo_satellites directory ...", "... and rerun the above command" ) for_more_information( "doc/raketasks/maintenance.md " ) check_failed end end end def check_log_writable print "Log directory writable? ... " log_path = Rails.root.join("log") if File.writable?(log_path) puts "yes".green else puts "no".red try_fixing_it( "sudo chown -R gitlab #{log_path}", "sudo chmod -R rwX #{log_path}" ) for_more_information( see_installation_guide_section "GitLab" ) check_failed end end def check_tmp_writable print "Tmp directory writable? ... " tmp_path = Rails.root.join("tmp") if File.writable?(tmp_path) puts "yes".green else puts "no".red try_fixing_it( "sudo chown -R gitlab #{tmp_path}", "sudo chmod -R rwX #{tmp_path}" ) for_more_information( see_installation_guide_section "GitLab" ) check_failed end end end namespace :env do desc "GITLAB | Check the configuration of the environment" task check: :environment do warn_user_is_not_gitlab start_checking "Environment" check_gitlab_in_git_group check_issue_1056_shell_profile_error check_gitlab_git_config check_python2_exists check_python2_version finished_checking "Environment" end # Checks ######################## def check_gitlab_git_config print "Git configured for gitlab user? ... " options = { "user.name" => "GitLab", "user.email" => Gitlab.config.gitlab.email_from } correct_options = options.map do |name, value| run("git config --global --get #{name}").try(:squish) == value end if correct_options.all? puts "yes".green else puts "no".red try_fixing_it( "sudo -u gitlab -H git config --global user.name \"#{options["user.name"]}\"", "sudo -u gitlab -H git config --global user.email \"#{options["user.email"]}\"" ) for_more_information( see_installation_guide_section "GitLab" ) check_failed end end def check_gitlab_in_git_group gitolite_ssh_user = Gitlab.config.gitolite.ssh_user print "gitlab user is in #{gitolite_ssh_user} group? ... " if run_and_match("id -rnG", /\Wgit\W/) puts "yes".green else puts "no".red try_fixing_it( "sudo usermod -a -G #{gitolite_ssh_user} gitlab" ) for_more_information( see_installation_guide_section "System Users" ) check_failed end end # see https://github.com/gitlabhq/gitlabhq/issues/1059 def check_issue_1056_shell_profile_error gitolite_ssh_user = Gitlab.config.gitolite.ssh_user print "Has no \"-e\" in ~#{gitolite_ssh_user}/.profile ... " profile_file = File.expand_path("~#{Gitlab.config.gitolite.ssh_user}/.profile") unless File.read(profile_file) =~ /^-e PATH/ puts "yes".green else puts "no".red try_fixing_it( "Open #{profile_file}", "Find the line starting with \"-e PATH\"", "Remove \"-e \" so the line starts with PATH" ) for_more_information( see_installation_guide_section("Gitolite"), "https://github.com/gitlabhq/gitlabhq/issues/1059" ) check_failed end end def check_python2_exists print "Has python2? ... " # Python prints its version to STDERR # so we can't just use run("python2 --version") if run_and_match("which python2", /python2$/) puts "yes".green else puts "no".red try_fixing_it( "Make sure you have Python 2.5+ installed", "Link it to python2" ) for_more_information( see_installation_guide_section "Packages / Dependencies" ) check_failed end end def check_python2_version print "python2 is supported version? ... " # Python prints its version to STDERR # so we can't just use run("python2 --version") unless run_and_match("which python2", /python2$/) puts "can't check because of previous errors".magenta return end if `python2 --version 2>&1` =~ /2\.[567]\.\d/ puts "yes".green else puts "no".red try_fixing_it( "Make sure you have Python 2.5+ installed", "Link it to python2" ) for_more_information( see_installation_guide_section "Packages / Dependencies" ) check_failed end end end namespace :gitolite do desc "GITLAB | Check the configuration of Gitolite" task check: :environment do warn_user_is_not_gitlab start_checking "Gitolite" check_gitolite_is_up_to_date check_gitoliterc_repo_umask check_gitoliterc_git_config_keys check_dot_gitolite_exists check_dot_gitolite_user_and_group check_dot_gitolite_permissions check_repo_base_exists check_repo_base_user_and_group check_repo_base_permissions check_can_clone_gitolite_admin check_can_commit_to_gitolite_admin check_post_receive_hook_exists check_post_receive_hook_is_up_to_date check_repos_post_receive_hooks_is_link check_repos_git_config finished_checking "Gitolite" end # Checks ######################## def check_can_clone_gitolite_admin print "Can clone gitolite-admin? ... " test_path = "/tmp/gitlab_gitolite_admin_test" FileUtils.rm_rf(test_path) `git clone -q #{Gitlab.config.gitolite.admin_uri} #{test_path}` raise unless $?.success? puts "yes".green rescue puts "no".red try_fixing_it( "Make sure the \"admin_uri\" is set correctly in config/gitlab.yml", "Try cloning it yourself with:", " git clone -q #{Gitlab.config.gitolite.admin_uri} /tmp/gitolite-admin", "Make sure Gitolite is installed correctly." ) for_more_information( see_installation_guide_section "Gitolite" ) check_failed end # assumes #check_can_clone_gitolite_admin has been run before def check_can_commit_to_gitolite_admin print "Can commit to gitolite-admin? ... " test_path = "/tmp/gitlab_gitolite_admin_test" unless File.exists?(test_path) puts "can't check because of previous errors".magenta return end Dir.chdir(test_path) do `touch foo && git add foo && git commit -qm foo` raise unless $?.success? end puts "yes".green rescue puts "no".red try_fixing_it( "Try committing to it yourself with:", " git clone -q #{Gitlab.config.gitolite.admin_uri} /tmp/gitolite-admin", " touch foo", " git add foo", " git commit -m \"foo\"", "Make sure Gitolite is installed correctly." ) for_more_information( see_installation_guide_section "Gitolite" ) check_failed ensure FileUtils.rm_rf("/tmp/gitolite_gitlab_test") end def check_dot_gitolite_exists print "Config directory exists? ... " gitolite_config_path = File.expand_path("~#{Gitlab.config.gitolite.ssh_user}/.gitolite") if File.directory?(gitolite_config_path) puts "yes".green else puts "no".red puts "#{gitolite_config_path} is missing".red try_fixing_it( "This should have been created when setting up Gitolite.", "Make sure Gitolite is installed correctly." ) for_more_information( see_installation_guide_section "Gitolite" ) check_failed end end def check_dot_gitolite_permissions print "Config directory access is drwxr-x---? ... " gitolite_config_path = File.expand_path("~#{Gitlab.config.gitolite.ssh_user}/.gitolite") unless File.exists?(gitolite_config_path) puts "can't check because of previous errors".magenta return end if `stat --printf %a #{gitolite_config_path}` == "750" puts "yes".green else puts "no".red puts "#{gitolite_config_path} is not writable".red try_fixing_it( "sudo chmod 750 #{gitolite_config_path}" ) for_more_information( see_installation_guide_section "Gitolite" ) check_failed end end def check_dot_gitolite_user_and_group gitolite_ssh_user = Gitlab.config.gitolite.ssh_user print "Config directory owned by #{gitolite_ssh_user}:#{gitolite_ssh_user} ... " gitolite_config_path = File.expand_path("~#{gitolite_ssh_user}/.gitolite") unless File.exists?(gitolite_config_path) puts "can't check because of previous errors".magenta return end if `stat --printf %U #{gitolite_config_path}` == gitolite_ssh_user && # user `stat --printf %G #{gitolite_config_path}` == gitolite_ssh_user #group puts "yes".green else puts "no".red puts "#{gitolite_config_path} is not owned by #{gitolite_ssh_user}".red try_fixing_it( "sudo chown -R #{gitolite_ssh_user}:#{gitolite_ssh_user} #{gitolite_config_path}" ) for_more_information( see_installation_guide_section "Gitolite" ) check_failed end end def check_gitolite_is_up_to_date print "Using recommended version ... " if gitolite_version.try(:start_with?, "v3.04") puts "yes".green else puts "no".red try_fixing_it( "We strongly recommend using the version pointed out in the installation guide." ) for_more_information( see_installation_guide_section "Gitolite" ) # this is not a "hard" failure end end def check_gitoliterc_git_config_keys gitoliterc_path = File.join(gitolite_home, ".gitolite.rc") print "Allow all Git config keys in .gitolite.rc ... " option_name = if has_gitolite3? # see https://github.com/sitaramc/gitolite/blob/v3.04/src/lib/Gitolite/Rc.pm#L329 "GIT_CONFIG_KEYS" else # assume older version # see https://github.com/sitaramc/gitolite/blob/v2.3/conf/example.gitolite.rc#L49 "$GL_GITCONFIG_KEYS" end option_value = ".*" if open(gitoliterc_path).grep(/#{option_name}\s*=[>]?\s*["']#{option_value}["']/).any? puts "yes".green else puts "no".red try_fixing_it( "Open #{gitoliterc_path}", "Find the \"#{option_name}\" option", "Change its value to \".*\"" ) for_more_information( see_installation_guide_section "Gitolite" ) check_failed end end def check_gitoliterc_repo_umask gitoliterc_path = File.join(gitolite_home, ".gitolite.rc") print "Repo umask is 0007 in .gitolite.rc? ... " option_name = if has_gitolite3? # see https://github.com/sitaramc/gitolite/blob/v3.04/src/lib/Gitolite/Rc.pm#L328 "UMASK" else # assume older version # see https://github.com/sitaramc/gitolite/blob/v2.3/conf/example.gitolite.rc#L32 "$REPO_UMASK" end option_value = "0007" if open(gitoliterc_path).grep(/#{option_name}\s*=[>]?\s*#{option_value}/).any? puts "yes".green else puts "no".red try_fixing_it( "Open #{gitoliterc_path}", "Find the \"#{option_name}\" option", "Change its value to \"0007\"" ) for_more_information( see_installation_guide_section "Gitolite" ) check_failed end end def check_post_receive_hook_exists print "post-receive hook exists? ... " hook_file = "post-receive" gitolite_hooks_path = File.join(Gitlab.config.gitolite.hooks_path, "common") gitolite_hook_file = File.join(gitolite_hooks_path, hook_file) gitolite_ssh_user = Gitlab.config.gitolite.ssh_user gitlab_hook_file = Rails.root.join.join("lib", "hooks", hook_file) if File.exists?(gitolite_hook_file) puts "yes".green else puts "no".red try_fixing_it( "sudo -u #{gitolite_ssh_user} cp #{gitlab_hook_file} #{gitolite_hook_file}" ) for_more_information( see_installation_guide_section "Setup GitLab Hooks" ) check_failed end end def check_post_receive_hook_is_up_to_date print "post-receive hook up-to-date? ... " hook_file = "post-receive" gitolite_hooks_path = File.join(Gitlab.config.gitolite.hooks_path, "common") gitolite_hook_file = File.join(gitolite_hooks_path, hook_file) gitolite_hook_content = File.read(gitolite_hook_file) gitolite_ssh_user = Gitlab.config.gitolite.ssh_user unless File.exists?(gitolite_hook_file) puts "can't check because of previous errors".magenta return end gitlab_hook_file = Rails.root.join.join("lib", "hooks", hook_file) gitlab_hook_content = File.read(gitlab_hook_file) if gitolite_hook_content == gitlab_hook_content puts "yes".green else puts "no".red try_fixing_it( "sudo -u #{gitolite_ssh_user} cp #{gitlab_hook_file} #{gitolite_hook_file}" ) for_more_information( see_installation_guide_section "Setup GitLab Hooks" ) check_failed end end def check_repo_base_exists print "Repo base directory exists? ... " repo_base_path = Gitlab.config.gitolite.repos_path if File.exists?(repo_base_path) puts "yes".green else puts "no".red puts "#{repo_base_path} is missing".red try_fixing_it( "This should have been created when setting up Gitolite.", "Make sure it's set correctly in config/gitlab.yml", "Make sure Gitolite is installed correctly." ) for_more_information( see_installation_guide_section "Gitolite" ) check_failed end end def check_repo_base_permissions print "Repo base access is drwsrws---? ... " repo_base_path = Gitlab.config.gitolite.repos_path unless File.exists?(repo_base_path) puts "can't check because of previous errors".magenta return end if `stat --printf %a #{repo_base_path}` == "6770" puts "yes".green else puts "no".red puts "#{repo_base_path} is not writable".red try_fixing_it( "sudo chmod -R ug+rwXs,o-rwx #{repo_base_path}" ) for_more_information( see_installation_guide_section "Gitolite" ) check_failed end end def check_repo_base_user_and_group gitolite_ssh_user = Gitlab.config.gitolite.ssh_user print "Repo base owned by #{gitolite_ssh_user}:#{gitolite_ssh_user}? ... " repo_base_path = Gitlab.config.gitolite.repos_path unless File.exists?(repo_base_path) puts "can't check because of previous errors".magenta return end if `stat --printf %U #{repo_base_path}` == gitolite_ssh_user && # user `stat --printf %G #{repo_base_path}` == gitolite_ssh_user #group puts "yes".green else puts "no".red puts "#{repo_base_path} is not owned by #{gitolite_ssh_user}".red try_fixing_it( "sudo chown -R #{gitolite_ssh_user}:#{gitolite_ssh_user} #{repo_base_path}" ) for_more_information( see_installation_guide_section "Gitolite" ) check_failed end end def check_repos_git_config print "Git config in repos: ... " unless Project.count > 0 puts "can't check, you have no projects".magenta return end puts "" options = { "core.sharedRepository" => "0660", } Project.find_each(batch_size: 100) do |project| print "#{project.name_with_namespace.yellow} ... " correct_options = options.map do |name, value| run("git --git-dir=\"#{project.path_to_repo}\" config --get #{name}").try(:chomp) == value end if correct_options.all? puts "ok".green else puts "wrong or missing".red try_fixing_it( "sudo -u gitlab -H bundle exec rake gitlab:gitolite:update_repos" ) for_more_information( "doc/raketasks/maintenance.md" ) check_failed end end end def check_repos_post_receive_hooks_is_link print "post-receive hooks in repos are links: ... " hook_file = "post-receive" gitolite_hooks_path = File.join(Gitlab.config.gitolite.hooks_path, "common") gitolite_hook_file = File.join(gitolite_hooks_path, hook_file) gitolite_ssh_user = Gitlab.config.gitolite.ssh_user unless File.exists?(gitolite_hook_file) puts "can't check because of previous errors".magenta return end unless Project.count > 0 puts "can't check, you have no projects".magenta return end puts "" Project.find_each(batch_size: 100) do |project| print "#{project.name_with_namespace.yellow} ... " project_hook_file = File.join(project.path_to_repo, "hooks", hook_file) unless File.exists?(project_hook_file) puts "missing".red try_fixing_it( "sudo -u #{gitolite_ssh_user} ln -sf #{gitolite_hook_file} #{project_hook_file}" ) for_more_information( "lib/support/rewrite-hooks.sh" ) check_failed next end if run_and_match("stat --format %N #{project_hook_file}", /#{hook_file}.+->.+#{gitolite_hook_file}/) puts "ok".green else puts "not a link to Gitolite's hook".red try_fixing_it( "sudo -u #{gitolite_ssh_user} ln -sf #{gitolite_hook_file} #{project_hook_file}" ) for_more_information( "lib/support/rewrite-hooks.sh" ) check_failed end end end # Helper methods ######################## def gitolite_home File.expand_path("~#{Gitlab.config.gitolite.ssh_user}") end def gitolite_version gitolite_version_file = "#{gitolite_home}/gitolite/src/VERSION" if File.readable?(gitolite_version_file) File.read(gitolite_version_file) end end def has_gitolite3? gitolite_version.try(:start_with?, "v3.") end end namespace :resque do desc "GITLAB | Check the configuration of Resque" task check: :environment do warn_user_is_not_gitlab start_checking "Resque" check_resque_running finished_checking "Resque" end # Checks ######################## def check_resque_running print "Running? ... " if run_and_match("ps aux | grep -i resque", /resque-[\d\.]+:.+$/) puts "yes".green else puts "no".red try_fixing_it( "sudo service gitlab restart", "or", "sudo /etc/init.d/gitlab restart" ) for_more_information( see_installation_guide_section("Install Init Script"), "see log/resque.log for possible errors" ) check_failed end end end # Helper methods ########################## def check_failed puts " Please #{"fix the error above"} and rerun the checks.".red end def for_more_information(*sources) sources = sources.shift if sources.first.is_a?(Array) puts " For more information see:".blue sources.each do |source| puts " #{source}" end end def finished_checking(component) puts "" puts "Checking #{component.yellow} ... #{"Finished".green}" puts "" end # Runs the given command # # Returns nil if the command was not found # Returns the output of the command otherwise # # see also #run_and_match def run(command) unless `#{command} 2>/dev/null`.blank? `#{command}` end end # Runs the given command and matches the output agains the given pattern # # Returns nil if nothing matched # Retunrs the MatchData if the pattern matched # # see also #run # see also String#match def run_and_match(command, pattern) run(command).try(:match, pattern) end def see_database_guide "doc/install/databases.md" end def see_installation_guide_section(section) "doc/install/installation.md in section \"#{section}\"" end def start_checking(component) puts "Checking #{component.yellow} ..." puts "" end def try_fixing_it(*steps) steps = steps.shift if steps.first.is_a?(Array) puts " Try fixing it:".blue steps.each do |step| puts " #{step}" end end def warn_user_is_not_gitlab unless @warned_user_not_gitlab current_user = run("whoami").chomp unless current_user == "gitlab" puts "#{Colored.color(:black)+Colored.color(:on_yellow)} Warning #{Colored.extra(:clear)}" puts " You are running as user #{current_user.magenta}, we hope you know what you are doing." puts " Some tests may pass\/fail for the wrong reason." puts " For meaningful results you should run this as user #{"gitlab".magenta}." puts "" end @warned_user_not_gitlab = true end end end