diff --git a/CHANGELOG b/CHANGELOG index 45f479aaf82b041d735ae972d11041615869845c..0efc75e51d9a702255ef446c79e7645b8a134057 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,24 @@ Please view this file on the master branch, on stable branches it's out of date. +v 7.14.0 (unreleased) + - Fix full screen mode for snippet comments (Daniel Gerhardt) + - Fix 404 error in files view after deleting the last file in a repository (Stan Hu) + - Fix label read access for unauthenticated users (Daniel Gerhardt) + - Fix access to disabled features for unauthenticated users (Daniel Gerhardt) + - Fix OAuth provider bug where GitLab would not go return to the redirect_uri after sign-in (Stan Hu) + - Fix file upload dialog for comment editing (Daniel Gerhardt) + - Set OmniAuth full_host parameter to ensure redirect URIs are correct (Stan Hu) + - Expire Rails cache entries after two weeks to prevent endless Redis growth + - Add support for destroying project milestones (Stan Hu) + - Add fetch command to the MR page. + - Fix bug causing "Remove source-branch" option not to work for merge requests from the same project. + +v 7.13.1 + - Revert issue caching + - Reverted cache for events + v 7.13.0 (unreleased) + - Remove repository graph log to fix slow cache updates after push event (Stan Hu) - Only enable HSTS header for HTTPS and port 443 (Stan Hu) - Fix user autocomplete for unauthenticated users accessing public projects (Stan Hu) - Fix redirection to home page URL for unauthorized users (Daniel Gerhardt) @@ -15,7 +33,6 @@ v 7.13.0 (unreleased) - Add `two_factor_enabled` field to admin user API (Stan Hu) - Fix invalid timestamps in RSS feeds (Rowan Wookey) - Fix downloading of patches on public merge requests when user logged out (Stan Hu) - - The password for the default administrator (root) account has been changed from "5iveL!fe" to "password". - Fix Error 500 when relative submodule resolves to a namespace that has a different name from its path (Stan Hu) - Extract the longest-matching ref from a commit path when multiple matches occur (Stan Hu) - Update maintenance documentation to explain no need to recompile asssets for omnibus installations (Stan Hu) @@ -47,6 +64,8 @@ v 7.13.0 (unreleased) - Faster code search in repository and wiki. Fixes search page timeout for big repositories - Allow administrators to disable 2FA for a specific user - Add error message for SSH key linebreaks + - Store commits count in database (will populate with valid values only after first push) + - Rebuild cache after push to repository in background job v 7.12.2 - Correctly show anonymous authorized applications under Profile > Applications. @@ -122,6 +141,7 @@ v 7.12.0 - Improve group removing logic - Trigger create-hooks on backup restore task - Add option to automatically link omniauth and LDAP identities + - Allow special character in users bio. I.e.: I <3 GitLab v 7.11.4 - Fix missing bullets when creating lists @@ -140,9 +160,6 @@ v 7.11.1 v 7.11.0 - Fall back to Plaintext when Syntaxhighlighting doesn't work. Fixes some buggy lexers (Hannes Rosenögger) - Get editing comments to work in Chrome 43 again. - - Allow special character in users bio. I.e.: I <3 GitLab - -v 7.11.0 - Fix broken view when viewing history of a file that includes a path that used to be another file (Stan Hu) - Don't show duplicate deploy keys - Fix commit time being displayed in the wrong timezone in some cases (Hannes Rosenögger) diff --git a/Gemfile b/Gemfile index a00200574a941bea9d7c30faed157f5eba55f6dd..14ea25d62736568e0e148a7497245a56de1a8806 100644 --- a/Gemfile +++ b/Gemfile @@ -46,7 +46,7 @@ gem "gitlab_git", '~> 7.2.5' gem 'gitlab-grack', '~> 2.0.2', require: 'grack' # LDAP Auth -# GitLab fork with several improvements to original library. For full list of changes +# GitLab fork with several improvements to original library. For full list of changes # see https://github.com/intridea/omniauth-ldap/compare/master...gitlabhq:master gem 'gitlab_omniauth-ldap', '1.2.1', require: "omniauth-ldap" gem 'net-ldap' @@ -55,9 +55,9 @@ gem 'net-ldap' gem 'gollum-lib', '~> 4.0.2' # Language detection -# GitLab fork of linguist does not require pygments/python dependency. -# New version of original gem also dropped pygments support but it has strict -# dependency to unstable rugged version. We have internal issue for replacing +# GitLab fork of linguist does not require pygments/python dependency. +# New version of original gem also dropped pygments support but it has strict +# dependency to unstable rugged version. We have internal issue for replacing # fork with original gem when we meet on same rugged version - https://dev.gitlab.org/gitlab/gitlabhq/issues/2052. gem "gitlab-linguist", "~> 3.0.1", require: "linguist" @@ -230,7 +230,7 @@ end group :development, :test do gem 'awesome_print' - gem 'byebug' + gem 'byebug', platform: :mri gem 'fuubar', '~> 2.0.0' gem 'pry-rails' diff --git a/README.md b/README.md index c96a9028fd6a6206f9e914830e8242c1c22c35b6..d535e5790e7c27cfb8183f546ab6156a56a89921 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,10 @@ +# GitLab + +[](https://ci.gitlab.com/projects/1?ref=master) +[](https://semaphoreapp.com/gitlabhq/gitlabhq) +[](https://codeclimate.com/github/gitlabhq/gitlabhq) +[](https://coveralls.io/r/gitlabhq/gitlabhq?branch=master) + ## Canonical source The source of GitLab Community Edition is [hosted on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/) and there are mirrors to make [contributing](CONTRIBUTING.md) as easy as possible. @@ -41,21 +48,12 @@ To see how GitLab looks please see the [features page on our website](https://ab ## Editions -There are two editions of GitLab. -*GitLab [Community Edition](https://about.gitlab.com/features/) (CE)* is available without any costs under an MIT license. - -*GitLab Enterprise Edition (EE)* includes [extra features](https://about.gitlab.com/features/#compare) that are most useful for organizations with more than 100 users. -To use EE and get official support please [become a subscriber](https://about.gitlab.com/pricing/). - -## Code status - -- [](https://ci.gitlab.com/projects/1?ref=master) on ci.gitlab.com (master branch) - -- [](https://semaphoreapp.com/gitlabhq/gitlabhq) +There are two editions of GitLab: -- [](https://codeclimate.com/github/gitlabhq/gitlabhq) +- GitLab Community Edition (CE) is available freely under the MIT Expat license. +- GitLab Enterprise Edition (EE) includes [extra features](https://about.gitlab.com/features/#compare) that are more useful for organizations with more than 100 users. To use EE and get official support please [become a subscriber](https://about.gitlab.com/pricing/). -- [](https://coveralls.io/r/gitlabhq/gitlabhq?branch=master) +Included with the GitLab Omnibus Packages is [GitLab CI](https://about.gitlab.com/gitlab-ci/) that can easily build, test and deploy code. ## Website @@ -70,23 +68,39 @@ On [about.gitlab.com](https://about.gitlab.com/) you can find more information a ## Requirements -GitLab requires the following software: - -- Ubuntu/Debian/CentOS/RHEL -- Ruby (MRI) 2.0 or 2.1 -- Git 1.7.10+ -- Redis 2.0+ -- MySQL or PostgreSQL - Please see the [requirements documentation](doc/install/requirements.md) for system requirements and more information about the supported operating systems. ## Installation -The recommended way to install GitLab is using the provided [Omnibus packages](https://about.gitlab.com/downloads/). Compared to an installation from source, this is faster and less error prone. Just select your operating system, download the respective package (Debian or RPM) and install it using the system's package manager. +The recommended way to install GitLab is with the [Omnibus packages](https://about.gitlab.com/downloads/) on our package server. +Compared to an installation from source, this is faster and less error prone. +Just select your operating system, download the respective package (Debian or RPM) and install it using the system's package manager. There are various other options to install GitLab, please refer to the [installation page on the GitLab website](https://about.gitlab.com/installation/) for more information. -You can access a new installation with the login **`root`** and password **`password`**, after login you are required to set a unique password. +You can access a new installation with the login **`root`** and password **`5iveL!fe`**, after login you are required to set a unique password. + +## Install a development environment + +To work on GitLab itself, we recommend setting up your development environment with [the GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit). +If you do not use the GitLab Development Kit you need to install and setup all the dependencies yourself, this is a lot of work and error prone. +One small thing you also have to do when installing it yourself is to copy the example development unicorn configuration file: + + cp config/unicorn.rb.example.development config/unicorn.rb + +Instructions on how to start GitLab and how to run the tests can be found in the [development section of the GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit#development). + +## Software stack + +GitLab is a Ruby on Rails application that runs on the following software: + +- Ubuntu/Debian/CentOS/RHEL +- Ruby (MRI) 2.0 or 2.1 +- Git 1.7.10+ +- Redis 2.0+ +- MySQL or PostgreSQL + +For more information please see the [architecture documentation](http://doc.gitlab.com/ce/development/architecture.html). ## Third-party applications @@ -100,16 +114,6 @@ Since 2011 a minor or major version of GitLab is released on the 22nd of every m For upgrading information please see our [update page](https://about.gitlab.com/update/). -## Install a development environment - -To work on GitLab itself, we recommend setting up your development environment with [the GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit). -If you do not use the GitLab Development Kit you need to install and setup all the dependencies yourself, this is a lot of work and error prone. -One small thing you also have to do when installing it yourself is to copy the example development unicorn configuration file: - - cp config/unicorn.rb.example.development config/unicorn.rb - -Instructions on how to start GitLab and how to run the tests can be found in the [development section of the GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit#development). - ## Documentation All documentation can be found on [doc.gitlab.com/ee/](http://doc.gitlab.com/ee/). @@ -125,4 +129,4 @@ Please see [Getting help for GitLab](https://about.gitlab.com/getting-help/) on ## Is it awesome? Thanks for [asking this question](https://twitter.com/supersloth/status/489462789384056832) Joshua. -[These people](https://twitter.com/gitlab/favorites) seem to like it. \ No newline at end of file +[These people](https://twitter.com/gitlab/favorites) seem to like it. diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee index fd766738ae4b248a81f13b7a20b42be47c7ab213..9dbd7fa676341c0ff1245eb729157e14e3f8416f 100644 --- a/app/assets/javascripts/dispatcher.js.coffee +++ b/app/assets/javascripts/dispatcher.js.coffee @@ -132,7 +132,10 @@ class Dispatcher shortcut_handler = new ShortcutsNavigation() new ZenMode() new DropzoneInput($('.wiki-form')) - when 'snippets', 'labels', 'graphs' + when 'snippets' + shortcut_handler = new ShortcutsNavigation() + new ZenMode() if path[2] == 'show' + when 'labels', 'graphs' shortcut_handler = new ShortcutsNavigation() when 'project_members', 'deploy_keys', 'hooks', 'services', 'protected_branches' shortcut_handler = new ShortcutsNavigation() diff --git a/app/assets/javascripts/line_highlighter.js.coffee b/app/assets/javascripts/line_highlighter.js.coffee index a8b3c1fa33e9fc755fe8ee81af29c0ddbcb81321..e604e6025c2fb0caf51f826c08493126f3a2f268 100644 --- a/app/assets/javascripts/line_highlighter.js.coffee +++ b/app/assets/javascripts/line_highlighter.js.coffee @@ -70,7 +70,7 @@ class @LineHighlighter @clearHighlight() - lineNumber = $(event.target).data('line-number') + lineNumber = $(event.target).closest('a').data('line-number') current = @hashToRange(@_hash) unless current[0] && event.shiftKey diff --git a/app/assets/javascripts/merge_request.js.coffee b/app/assets/javascripts/merge_request.js.coffee index 7462975bd3de8e82b769ed3c4b1f0189f3334866..b21cb7904b56fdd99739d81c6458ce556af6b414 100644 --- a/app/assets/javascripts/merge_request.js.coffee +++ b/app/assets/javascripts/merge_request.js.coffee @@ -15,9 +15,7 @@ class @MergeRequest this.$('.show-all-commits').on 'click', => this.showAllCommits() - # `MergeRequests#new` has no tab-persisting or lazy-loading behavior - unless @opts.action == 'new' - new MergeRequestTabs(@opts) + @initTabs() # Prevent duplicate event bindings @disableTaskList() @@ -29,6 +27,14 @@ class @MergeRequest $: (selector) -> this.$el.find(selector) + initTabs: -> + if @opts.action != 'new' + # `MergeRequests#new` has no tab-persisting or lazy-loading behavior + new MergeRequestTabs(@opts) + else + # Show the first tab (Commits) + $('.merge-request-tabs a[data-toggle="tab"]:first').tab('show') + showAllCommits: -> this.$('.first-commits').remove() this.$('.all-commits').removeClass 'hide' diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index 1c05a2b9fe8dd443e0133914416d8546613436ce..bcff7bcc49e115772556166a3d8d62ab8b7d4090 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -298,7 +298,7 @@ class @Notes note.find(".note-header").hide() base_form = note.find(".note-edit-form") form = base_form.clone().insertAfter(base_form) - form.addClass('current-note-edit-form') + form.addClass('current-note-edit-form gfm-form') form.find('.div-dropzone').remove() # Show the attachment delete link diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb index 61689488d13b24961453086462a414c13b16c0bf..9efe9704d1ea92245bdda94077aa51aa7016ac5e 100644 --- a/app/controllers/projects/milestones_controller.rb +++ b/app/controllers/projects/milestones_controller.rb @@ -64,7 +64,12 @@ def update end def destroy - return access_denied! unless can?(current_user, :admin_milestone, @milestone) + return access_denied! unless can?(current_user, :admin_milestone, @project) + + update_params = { milestone: nil } + @milestone.issues.each do |issue| + Issues::UpdateService.new(@project, current_user, update_params).execute(issue) + end @milestone.destroy diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb index b659e15f2429f9fdb03cdeb448b171e9ec3415b0..92e4bc16d9d772e91b33bd76a57e2c854be2e5af 100644 --- a/app/controllers/projects/tree_controller.rb +++ b/app/controllers/projects/tree_controller.rb @@ -7,13 +7,15 @@ class Projects::TreeController < Projects::ApplicationController before_action :authorize_download_code! def show + return not_found! unless @repository.commit(@ref) + if tree.entries.empty? if @repository.blob_at(@commit.id, @path) redirect_to( namespace_project_blob_path(@project.namespace, @project, File.join(@ref, @path)) ) and return - else + elsif @path.present? return not_found! end end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 39ddb74e108eb223457c42715f03337936658dd2..399deb86a03084024cdea06b88d76d1d4c3239ad 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -1,6 +1,6 @@ class ProjectsController < ApplicationController - prepend_before_action :render_go_import, only: [:show] - skip_before_action :authenticate_user!, only: [:show] + prepend_before_filter :render_go_import, only: [:show] + skip_before_action :authenticate_user!, only: [:show, :activity] before_action :project, except: [:new, :create] before_action :repository, except: [:new, :create] diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index f5814a8f9f562901594b4838d0833c6f64de89d5..d4cdd2ab7e90621d0cf12d38b93672ec6f7678a2 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -131,8 +131,12 @@ def get_project_nav_tabs(project, current_user) nav_tabs << :snippets end + if can?(current_user, :read_label, project) + nav_tabs << :labels + end + if can?(current_user, :read_milestone, project) - nav_tabs << [:milestones, :labels] + nav_tabs << :milestones end nav_tabs.flatten @@ -284,4 +288,18 @@ def last_push_event def readme_cache_key [@project.id, @project.commit.sha, "readme"].join('-') end + + def round_commit_count(project) + count = project.commit_count + + if count > 10000 + '10000+' + elsif count > 5000 + '5000+' + elsif count > 1000 + '1000+' + else + count + end + end end diff --git a/app/models/ability.rb b/app/models/ability.rb index 625f49108c9b73eac2a5dec30398c40f5dacb3f6..2d33f3f5e7bf5a78d9664d2a0b70136796b191c2 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -47,10 +47,11 @@ def not_auth_abilities(user, subject) end if project && project.public? - [ + rules = [ :read_project, :read_wiki, :read_issue, + :read_label, :read_milestone, :read_project_snippet, :read_project_member, @@ -58,6 +59,8 @@ def not_auth_abilities(user, subject) :read_note, :download_code ] + + rules - project_disabled_features_rules(project) else group = if subject.kind_of?(Group) subject @@ -118,28 +121,7 @@ def project_abilities(user, project) rules -= project_archived_rules end - unless project.issues_enabled - rules -= named_abilities('issue') - end - - unless project.merge_requests_enabled - rules -= named_abilities('merge_request') - end - - unless project.issues_enabled or project.merge_requests_enabled - rules -= named_abilities('label') - rules -= named_abilities('milestone') - end - - unless project.snippets_enabled - rules -= named_abilities('project_snippet') - end - - unless project.wiki_enabled - rules -= named_abilities('wiki') - end - - rules + rules - project_disabled_features_rules(project) end end @@ -221,6 +203,33 @@ def project_admin_rules ] end + def project_disabled_features_rules(project) + rules = [] + + unless project.issues_enabled + rules += named_abilities('issue') + end + + unless project.merge_requests_enabled + rules += named_abilities('merge_request') + end + + unless project.issues_enabled or project.merge_requests_enabled + rules += named_abilities('label') + rules += named_abilities('milestone') + end + + unless project.snippets_enabled + rules += named_abilities('project_snippet') + end + + unless project.wiki_enabled + rules += named_abilities('wiki') + end + + rules + end + def group_abilities(user, group) rules = [] diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 18813075c5e9b2c46eaecfe1fa08fc90449b9369..aa30f119b2cd6d498ccebba244c41b3a84cf5f06 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -240,6 +240,10 @@ def automerge!(current_user, commit_message = nil) execute(self, commit_message) end + def remove_source_branch? + self.should_remove_source_branch && !self.source_project.root_ref?(self.source_branch) && !self.for_fork? + end + def open? opened? || reopened? end diff --git a/app/models/project.rb b/app/models/project.rb index f897b43f41f85306e87b51b81b38b4227fb0508d..b1135d1e575ace54bf6f260103a44ed11a4932f3 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -703,6 +703,10 @@ def update_repository_size update_attribute(:repository_size, repository.size) end + def update_commit_count + update_attribute(:commit_count, repository.commit_count) + end + def forks_count ForkedProjectLink.where(forked_from_project_id: self.id).count end diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb index c284e19fe501d180f1b7d9bb72aa2e9677e891d1..5aaa4e85cbcce8c2849b061eeb7b953dac0f8051 100644 --- a/app/models/project_services/gitlab_ci_service.rb +++ b/app/models/project_services/gitlab_ci_service.rb @@ -22,8 +22,12 @@ class GitlabCiService < CiService API_PREFIX = "api/v1" prop_accessor :project_url, :token - validates :project_url, presence: true, if: :activated? - validates :token, presence: true, if: :activated? + validates :project_url, + presence: true, + format: { with: /\A#{URI.regexp(%w(http https))}\z/, message: "should be a valid url" }, if: :activated? + validates :token, + presence: true, + format: { with: /\A([A-Za-z0-9]+)\z/ }, if: :activated? after_save :compose_service_hook, if: :activated? diff --git a/app/models/repository.rb b/app/models/repository.rb index 6262b5c4c92165bacedcf4f81b238eab8216dc42..807b33b2a3e515387cb00387680568a83aec0cd0 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -94,18 +94,6 @@ def rm_tag(tag_name) gitlab_shell.rm_tag(path_with_namespace, tag_name) end - def round_commit_count - if commit_count > 10000 - '10000+' - elsif commit_count > 5000 - '5000+' - elsif commit_count > 1000 - '1000+' - else - commit_count - end - end - def branch_names cache.fetch(:branch_names) { raw_repository.branch_names } end @@ -130,28 +118,29 @@ def size cache.fetch(:size) { raw_repository.size } end + def cache_keys + %i(size branch_names tag_names commit_count + readme version contribution_guide changelog license) + end + + def build_cache + cache_keys.each do |key| + unless cache.exist?(key) + send(key) + end + end + end + def expire_cache - %i(size branch_names tag_names commit_count graph_log - readme version contribution_guide changelog license).each do |key| + cache_keys.each do |key| cache.expire(key) end end - def graph_log - cache.fetch(:graph_log) do - commits = raw_repository.log(limit: 6000, skip_merges: true, - ref: root_ref) - - commits.map do |rugged_commit| - commit = Gitlab::Git::Commit.new(rugged_commit) - - { - author_name: commit.author_name, - author_email: commit.author_email, - additions: commit.stats.additions, - deletions: commit.stats.deletions, - } - end + def rebuild_cache + cache_keys.each do |key| + cache.expire(key) + send(key) end end @@ -442,8 +431,7 @@ def parse_search_result(result) filename = nil startline = 0 - lines = result.lines - lines.each_with_index do |line, index| + result.each_line.each_with_index do |line, index| if line =~ /^.*:.*:\d+:/ ref, filename, startline = line.split(':') startline = startline.to_i - index @@ -451,11 +439,11 @@ def parse_search_result(result) end end - data = lines.map do |line| - line.sub(ref, '').sub(filename, '').sub(/^:-\d+-/, '').sub(/^::\d+:/, '') - end + data = "" - data = data.join("") + result.each_line do |line| + data << line.sub(ref, '').sub(filename, '').sub(/^:-\d+-/, '').sub(/^::\d+:/, '') + end OpenStruct.new( filename: filename, diff --git a/app/services/files/base_service.rb b/app/services/files/base_service.rb index 5ab79c9b299c65b8d718174995b86eb1b8a0237b..a96dc3238c9ed991c07c74a56ca57f28748aedc5 100644 --- a/app/services/files/base_service.rb +++ b/app/services/files/base_service.rb @@ -43,7 +43,7 @@ def git_hook def after_commit(sha, branch) commit = repository.commit(sha) - full_ref = 'refs/heads/' + branch + full_ref = "#{Gitlab::Git::BRANCH_REF_PREFIX}#{branch}" old_sha = commit.parent_id || Gitlab::Git::BLANK_SHA GitPushService.new.execute(project, current_user, old_sha, sha, full_ref) end diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index de18d0f91a8fc9f1070c9348fc7a2f232d596b7b..f679101ac5252aeb6f854ee6dd59780c12c304bc 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -21,7 +21,6 @@ def execute(project, user, oldrev, newrev, ref) project.ensure_satellite_exists project.repository.expire_cache - project.update_repository_size if push_remove_branch?(ref, newrev) @push_commits = [] @@ -61,6 +60,7 @@ def execute(project, user, oldrev, newrev, ref) EventCreateService.new.push(project, user, @push_data) project.execute_hooks(@push_data.dup, :push_hooks) project.execute_services(@push_data.dup, :push_hooks) + ProjectCacheWorker.perform_async(project.id) end protected diff --git a/app/services/git_tag_push_service.rb b/app/services/git_tag_push_service.rb index 075a6118da2054f285c90720f87dc4b392a04faa..1cc42b0b0ad21e2066f987140d612372f86ea33e 100644 --- a/app/services/git_tag_push_service.rb +++ b/app/services/git_tag_push_service.rb @@ -2,15 +2,15 @@ class GitTagPushService attr_accessor :project, :user, :push_data def execute(project, user, oldrev, newrev, ref) - @project, @user = project, user + project.repository.expire_cache + @project, @user = project, user @push_data = build_push_data(oldrev, newrev, ref) EventCreateService.new.push(project, user, @push_data) project.execute_hooks(@push_data.dup, :tag_push_hooks) project.execute_services(@push_data.dup, :tag_push_hooks) - - project.repository.expire_cache + ProjectCacheWorker.perform_async(project.id) true end diff --git a/app/services/merge_requests/auto_merge_service.rb b/app/services/merge_requests/auto_merge_service.rb index df793fc997d02b364aa5afe5fc3774e95c008c5f..db824d452d0cecd9494a400aa3ecdb84668271fe 100644 --- a/app/services/merge_requests/auto_merge_service.rb +++ b/app/services/merge_requests/auto_merge_service.rb @@ -37,6 +37,14 @@ def merge! # Merge local branches using rugged instead of satellites if sha = commit after_commit(sha, merge_request.target_branch) + + if merge_request.remove_source_branch? + DeleteBranchService.new(merge_request.source_project, current_user).execute(merge_request.source_branch) + end + + true + else + false end end end @@ -55,7 +63,7 @@ def commit def after_commit(sha, branch) commit = repository.commit(sha) - full_ref = 'refs/heads/' + branch + full_ref = "#{Gitlab::Git::BRANCH_REF_PREFIX}#{branch}" old_sha = commit.parent_id || Gitlab::Git::BLANK_SHA GitPushService.new.execute(project, current_user, old_sha, sha, full_ref) end diff --git a/app/views/events/_event.html.haml b/app/views/events/_event.html.haml index b8409f64665a397514103e611ef333e707c3674f..5ab5ffc238c83d939c54986567978937bb0bd995 100644 --- a/app/views/events/_event.html.haml +++ b/app/views/events/_event.html.haml @@ -8,11 +8,10 @@ = image_tag avatar_icon(event.author_email, 24), class: "avatar s24", alt:'' = render "events/event/created_project", event: event - else - = cache event do - = image_tag avatar_icon(event.author_email, 24), class: "avatar s24", alt:'' - - if event.push? - = render "events/event/push", event: event - - elsif event.commented? - = render "events/event/note", event: event - - else - = render "events/event/common", event: event + = image_tag avatar_icon(event.author_email, 24), class: "avatar s24", alt:'' + - if event.push? + = render "events/event/push", event: event + - elsif event.commented? + = render "events/event/note", event: event + - else + = render "events/event/common", event: event diff --git a/app/views/explore/projects/_project.html.haml b/app/views/explore/projects/_project.html.haml index d65fb529373131b7bae68e3f217644124380854b..d769c91545d0a9bc201b876bba295f9f82cd3ec5 100644 --- a/app/views/explore/projects/_project.html.haml +++ b/app/views/explore/projects/_project.html.haml @@ -14,7 +14,7 @@ .repo-info - unless project.empty_repo? - = link_to pluralize(project.repository.round_commit_count, 'commit'), namespace_project_commits_path(project.namespace, project, project.default_branch) + = link_to pluralize(round_commit_count(project), 'commit'), namespace_project_commits_path(project.namespace, project, project.default_branch) · = link_to pluralize(project.repository.branch_names.count, 'branch'), namespace_project_branches_path(project.namespace, project) · diff --git a/app/views/profiles/two_factor_auths/new.html.haml b/app/views/profiles/two_factor_auths/new.html.haml index 74268c9bde2e9883f5ef009215192bd9ca3e774e..92dc58c10d71f643e733a15773f2c02e39141395 100644 --- a/app/views/profiles/two_factor_auths/new.html.haml +++ b/app/views/profiles/two_factor_auths/new.html.haml @@ -5,7 +5,7 @@ Download the Google Authenticator application from App Store for iOS or Google Play for Android and scan this code. - More information is available in the #{link_to('documentation', help_page_path('workflow', 'two_factor_authentication'))}. + More information is available in the #{link_to('documentation', help_page_path('profile', 'two_factor_authentication'))}. %hr diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml index 1b45bb1af0c722adf5d293f5e3d7b142e469e160..b6910c8f79651e726642c309a38d8c71fa973f37 100644 --- a/app/views/projects/issues/_issue.html.haml +++ b/app/views/projects/issues/_issue.html.haml @@ -3,43 +3,42 @@ .issue-check = check_box_tag dom_id(issue,"selected"), nil, false, 'data-id' => issue.id, class: "selected_issue" - = cache issue do - .issue-title - %span.issue-title-text - = link_to_gfm issue.title, issue_path(issue), class: "row_title" - .issue-labels - - issue.labels.each do |label| - = link_to_label(label, project: issue.project) - .pull-right.light - - if issue.closed? - %span - CLOSED - - if issue.assignee - = link_to_member(@project, issue.assignee, name: false) - - note_count = issue.notes.user.count - - if note_count > 0 - - %span - %i.fa.fa-comments - = note_count - - else - - %span.issue-no-comments - %i.fa.fa-comments - = 0 - - .issue-info - = "#{issue.to_reference} opened #{time_ago_with_tooltip(issue.created_at, placement: 'bottom')} by #{link_to_member(@project, issue.author, avatar: false)}".html_safe - - if issue.votes_count > 0 - = render 'votes/votes_inline', votable: issue - - if issue.milestone + .issue-title + %span.issue-title-text + = link_to_gfm issue.title, issue_path(issue), class: "row_title" + .issue-labels + - issue.labels.each do |label| + = link_to_label(label, project: issue.project) + .pull-right.light + - if issue.closed? + %span + CLOSED + - if issue.assignee + = link_to_member(@project, issue.assignee, name: false) + - note_count = issue.notes.user.count + - if note_count > 0 %span - %i.fa.fa-clock-o - = issue.milestone.title - - if issue.tasks? - %span.task-status - = issue.task_status + %i.fa.fa-comments + = note_count + - else + + %span.issue-no-comments + %i.fa.fa-comments + = 0 + + .issue-info + = "#{issue.to_reference} opened #{time_ago_with_tooltip(issue.created_at, placement: 'bottom')} by #{link_to_member(@project, issue.author, avatar: false)}".html_safe + - if issue.votes_count > 0 + = render 'votes/votes_inline', votable: issue + - if issue.milestone + + %span + %i.fa.fa-clock-o + = issue.milestone.title + - if issue.tasks? + %span.task-status + = issue.task_status - .pull-right.issue-updated-at - %small updated #{time_ago_with_tooltip(issue.updated_at, placement: 'bottom', html_class: 'issue_update_ago')} + .pull-right.issue-updated-at + %small updated #{time_ago_with_tooltip(issue.updated_at, placement: 'bottom', html_class: 'issue_update_ago')} diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index b6d9b135c70c6ab2c0c341a96b4164329f551f93..faaa85896cf3535996ce3e113bab099015a5a24d 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -31,6 +31,16 @@ %li= link_to "Email Patches", merge_request_path(@merge_request, format: :patch) %li= link_to "Plain Diff", merge_request_path(@merge_request, format: :diff) + - if @merge_request.open? and @merge_request.source_branch_exists? + .append-bottom-20 + .slead + %span + Fetch the branch with + %strong.label-branch< + git fetch + \ #{@merge_request.source_project.http_url_to_repo} + \ #{@merge_request.source_branch} + = render "projects/merge_requests/show/how_to_merge" = render "projects/merge_requests/widget/show.html.haml" diff --git a/app/views/projects/merge_requests/branch_from.js.haml b/app/views/projects/merge_requests/branch_from.js.haml index 8372afa61b53b4cb5eb1de3615f725ec2b48cf93..9210798f39c051f4fff1f98910bf68f3147e817d 100644 --- a/app/views/projects/merge_requests/branch_from.js.haml +++ b/app/views/projects/merge_requests/branch_from.js.haml @@ -1,2 +1,3 @@ :plain $(".mr_source_commit").html("#{commit_to_html(@commit, @source_project, false)}"); + $('.js-timeago').timeago() diff --git a/app/views/projects/merge_requests/branch_to.js.haml b/app/views/projects/merge_requests/branch_to.js.haml index f7ede0ded53d42ce7b1a801ec182fd9e84f6c296..32fe2d535f319ff7bac1077fcf7fbd22c85bace4 100644 --- a/app/views/projects/merge_requests/branch_to.js.haml +++ b/app/views/projects/merge_requests/branch_to.js.haml @@ -1,2 +1,3 @@ :plain $(".mr_target_commit").html("#{commit_to_html(@commit, @target_project, false)}"); + $('.js-timeago').timeago() diff --git a/app/views/projects/milestones/_milestone.html.haml b/app/views/projects/milestones/_milestone.html.haml index 14a0580f96681f9ef262be62bdfc8b01e3c2afbe..2ce5358fa7421b0713216fe3306d42f209142181 100644 --- a/app/views/projects/milestones/_milestone.html.haml +++ b/app/views/projects/milestones/_milestone.html.haml @@ -5,6 +5,10 @@ %i.fa.fa-pencil-square-o Edit = link_to 'Close Milestone', namespace_project_milestone_path(@project.namespace, @project, milestone, milestone: {state_event: :close }), method: :put, remote: true, class: "btn btn-sm btn-close" + = link_to namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-sm btn-remove" do + %i.fa.fa-trash-o + Remove + %h4 = link_to_gfm truncate(milestone.title, length: 100), namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone) - if milestone.expired? and not milestone.closed? diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml index 5947498e379b1ccaac58d3a5a67ff0bd3a29ba48..7b1681df336a863365973d1a468f2042db0fc194 100644 --- a/app/views/projects/milestones/show.html.haml +++ b/app/views/projects/milestones/show.html.haml @@ -19,6 +19,9 @@ = link_to 'Close Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :close }), method: :put, class: "btn btn-close btn-grouped" - else = link_to 'Reopen Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :activate }), method: :put, class: "btn btn-reopen btn-grouped" + = link_to namespace_project_milestone_path(@project.namespace, @project, @milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-grouped btn-remove" do + %i.fa.fa-trash-o + Remove %hr - if @milestone.issues.any? && @milestone.can_be_closed? diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml index 5478a887f91287cec6e471aa312dc1238395ae2f..4a1009686c6f2cb07b17cd9b48a2915fda15f824 100644 --- a/app/views/projects/notes/_note.html.haml +++ b/app/views/projects/notes/_note.html.haml @@ -56,10 +56,9 @@ .note-body{class: note_editable?(note) ? 'js-task-list-container' : ''} - = cache [note, 'markdown'] do - .note-text - = preserve do - = markdown(note.note, {no_header_anchors: true}) + .note-text + = preserve do + = markdown(note.note, {no_header_anchors: true}) = render 'projects/notes/edit_form', note: note - if note.attachment.url diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index 98d9053eb1d98cd8ff52e740cc02f634d2f9bf01..769dd68f089b23c9e1edbf58c5a12968fa55ba39 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -6,14 +6,16 @@ = render 'shared/no_ssh' = render 'shared/no_password' -= render 'projects/last_push' +- if prefer_readme? + = render 'projects/last_push' + = render "home_panel" .project-stats %ul.nav.nav-pills %li = link_to namespace_project_commits_path(@project.namespace, @project, @ref || @repository.root_ref) do - = pluralize(number_with_delimiter(@repository.commit_count), 'commit') + = pluralize(number_with_delimiter(@project.commit_count), 'commit') %li = link_to namespace_project_branches_path(@project.namespace, @project) do = pluralize(number_with_delimiter(@repository.branch_names.count), 'branch') diff --git a/app/views/shared/issuable/_context.html.haml b/app/views/shared/issuable/_context.html.haml index 46990895d33e0e6a905506b28bf28fc64ea78c79..d1bd5ef968dd0858ad4b790aee4dcaeb2b439d3e 100644 --- a/app/views/shared/issuable/_context.html.haml +++ b/app/views/shared/issuable/_context.html.haml @@ -8,7 +8,7 @@ - else none .issuable-context-selectbox - - if can?(current_user, :admin_issue, @project) + - if can?(current_user, :"admin_#{issuable.class.to_s.underscore}", @project) = users_select_tag("#{issuable.class.table_name.singularize}[assignee_id]", placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: issuable.assignee_id, project: @target_project, null_user: true) %div.prepend-top-20.clearfix @@ -24,7 +24,7 @@ - else none .issuable-context-selectbox - - if can?(current_user, :admin_issue, @project) + - if can?(current_user, :"admin_#{issuable.class.to_s.underscore}", @project) = f.select(:milestone_id, milestone_options(issuable), { include_blank: 'Select milestone' }, {class: 'select2 select2-compact js-select2 js-milestone'}) = hidden_field_tag :issuable_context = f.submit class: 'btn hide' diff --git a/app/workers/project_cache_worker.rb b/app/workers/project_cache_worker.rb new file mode 100644 index 0000000000000000000000000000000000000000..55cb6af232e97c68496b93e42c0816bfdea789f0 --- /dev/null +++ b/app/workers/project_cache_worker.rb @@ -0,0 +1,15 @@ +class ProjectCacheWorker + include Sidekiq::Worker + + sidekiq_options queue: :default + + def perform(project_id) + project = Project.find(project_id) + project.update_repository_size + project.update_commit_count + + if project.repository.root_ref + project.repository.build_cache + end + end +end diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb index e6a50afedb12003bb99c3ebb09056097767733c3..94832872d13ddccb192eaf05ac1d29f269b1bd57 100644 --- a/app/workers/repository_import_worker.rb +++ b/app/workers/repository_import_worker.rb @@ -28,7 +28,7 @@ def perform(project_id) project.import_finish project.save project.satellite.create unless project.satellite.exists? - project.update_repository_size + ProjectCacheWorker.perform_async(project.id) Gitlab::BitbucketImport::KeyDeleter.new(project).execute if project.import_type == 'bitbucket' end end diff --git a/config/application.rb b/config/application.rb index 7e899cc3b5b0f3831e49f66b7b67dd4c6c82b6b3..a96e22211e640a4b826552cd9ce4d61a46ff4885 100644 --- a/config/application.rb +++ b/config/application.rb @@ -96,6 +96,7 @@ class Application < Rails::Application end redis_config_hash[:namespace] = 'cache:gitlab' + redis_config_hash[:expires_in] = 2.weeks # Cache should not grow forever config.cache_store = :redis_store, redis_config_hash # This is needed for gitlab-shell diff --git a/config/initializers/7_omniauth.rb b/config/initializers/7_omniauth.rb index 2931bd10949bfb6513ffc8cc14933d86c8856b02..33ad25d2204ff620939ed37e380b0fd09b08b8c8 100644 --- a/config/initializers/7_omniauth.rb +++ b/config/initializers/7_omniauth.rb @@ -14,6 +14,7 @@ module OmniAuth::Strategies end end +OmniAuth.config.full_host = Settings.gitlab['url'] OmniAuth.config.allowed_request_methods = [:post] #In case of auto sign-in, the GET method is used (users don't get to click on a button) OmniAuth.config.allowed_request_methods << :get if Gitlab.config.omniauth.auto_sign_in_with_provider.present? diff --git a/config/initializers/doorkeeper.rb b/config/initializers/doorkeeper.rb index d422acb31d6bc842434580517934374282b50edb..6139ddbe6cd7d9d76e8234269d4ded5c6ce7891e 100644 --- a/config/initializers/doorkeeper.rb +++ b/config/initializers/doorkeeper.rb @@ -6,7 +6,8 @@ # This block will be called to check whether the resource owner is authenticated or not. resource_owner_authenticator do # Put your resource owner authentication logic here. - # Example implementation: + # Ensure user is redirected to redirect_uri after login + session[:user_return_to] = request.fullpath current_user || redirect_to(new_user_session_url) end diff --git a/config/routes.rb b/config/routes.rb index 64f12e6bc23f9cba5c5efbbe906ba945f4202555..61b30d0ebb8523b9f19d8f38cbb32f4367417eed 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -520,7 +520,7 @@ end end - resources :milestones, except: [:destroy], constraints: { id: /\d+/ } do + resources :milestones, constraints: { id: /\d+/ } do member do put :sort_issues put :sort_merge_requests diff --git a/db/fixtures/development/01_admin.rb b/db/fixtures/development/01_admin.rb index b25d0dfc701388498edc2ea2be9ca31f72bae1fb..bba2fc4b186ea050f64b106928d22db3011d38ef 100644 --- a/db/fixtures/development/01_admin.rb +++ b/db/fixtures/development/01_admin.rb @@ -5,7 +5,7 @@ s.email = 'admin@example.com' s.notification_email = 'admin@example.com' s.username = 'root' - s.password = 'password' + s.password = '5iveL!fe' s.admin = true s.projects_limit = 100 s.confirmed_at = DateTime.now diff --git a/db/fixtures/development/04_project.rb b/db/fixtures/development/04_project.rb index 8783977092410293deab6aec4a4acdcee2370e4a..8f71198e47f9de7a1cc061148dfcb563d9a6084f 100644 --- a/db/fixtures/development/04_project.rb +++ b/db/fixtures/development/04_project.rb @@ -11,9 +11,42 @@ 'https://github.com/twitter/flight.git', 'https://github.com/twitter/typeahead.js.git', 'https://github.com/h5bp/html5-boilerplate.git', + 'https://github.com/google/material-design-lite.git', + 'https://github.com/jlevy/the-art-of-command-line.git', + 'https://github.com/FreeCodeCamp/freecodecamp.git', + 'https://github.com/google/deepdream.git', + 'https://github.com/jtleek/datasharing.git', + 'https://github.com/WebAssembly/design.git', + 'https://github.com/airbnb/javascript.git', + 'https://github.com/tessalt/echo-chamber-js.git', + 'https://github.com/atom/atom.git', + 'https://github.com/ipselon/react-ui-builder.git', + 'https://github.com/mattermost/platform.git', + 'https://github.com/purifycss/purifycss.git', + 'https://github.com/facebook/nuclide.git', + 'https://github.com/wbkd/awesome-d3.git', + 'https://github.com/kilimchoi/engineering-blogs.git', + 'https://github.com/gilbarbara/logos.git', + 'https://github.com/gaearon/redux.git', + 'https://github.com/awslabs/s2n.git', + 'https://github.com/arkency/reactjs_koans.git', + 'https://github.com/twbs/bootstrap.git', + 'https://github.com/chjj/ttystudio.git', + 'https://github.com/DrBoolean/mostly-adequate-guide.git', + 'https://github.com/octocat/Spoon-Knife.git', + 'https://github.com/opencontainers/runc.git', + 'https://github.com/googlesamples/android-topeka.git' ] - project_urls.each_with_index do |url, i| + # You can specify how many projects you need during seed execution + size = if ENV['SIZE'].present? + ENV['SIZE'].to_i + else + 8 + end + + + project_urls.first(size).each_with_index do |url, i| group_path, project_path = url.split('/')[-2..-1] group = Group.find_by(path: group_path) diff --git a/db/fixtures/production/001_admin.rb b/db/fixtures/production/001_admin.rb index 1af8dfc0ef0cf014f9baf1ce6e4ea7114556d0b4..1c8740f6ba9ad847d038a482fce1252d67fcac5a 100644 --- a/db/fixtures/production/001_admin.rb +++ b/db/fixtures/production/001_admin.rb @@ -1,5 +1,5 @@ if ENV['GITLAB_ROOT_PASSWORD'].blank? - password = 'password' + password = '5iveL!fe' expire_time = Time.now else password = ENV['GITLAB_ROOT_PASSWORD'] diff --git a/db/migrate/20150609141121_add_session_expire_delay_for_application_settings.rb b/db/migrate/20150609141121_add_session_expire_delay_for_application_settings.rb index ffa22e6d5efc2df975987d51b0621d95b3eb26ac..61ff0af41f4c9dce2143c666833a3e641399ac07 100644 --- a/db/migrate/20150609141121_add_session_expire_delay_for_application_settings.rb +++ b/db/migrate/20150609141121_add_session_expire_delay_for_application_settings.rb @@ -1,5 +1,7 @@ class AddSessionExpireDelayForApplicationSettings < ActiveRecord::Migration def change - add_column :application_settings, :session_expire_delay, :integer, default: 10080, null: false + unless column_exists?(:application_settings, :session_expire_delay) + add_column :application_settings, :session_expire_delay, :integer, default: 10080, null: false + end end -end \ No newline at end of file +end diff --git a/db/migrate/20150717130904_add_commits_count_to_project.rb b/db/migrate/20150717130904_add_commits_count_to_project.rb new file mode 100644 index 0000000000000000000000000000000000000000..9b46daa5933901797740130f80a672c965f21463 --- /dev/null +++ b/db/migrate/20150717130904_add_commits_count_to_project.rb @@ -0,0 +1,5 @@ +class AddCommitsCountToProject < ActiveRecord::Migration + def change + add_column :projects, :commit_count, :integer, default: 0 + end +end diff --git a/db/schema.rb b/db/schema.rb index 0f64621b23bbd8fdb69214d634ea2f7fc99fe05b..3754b72bf810cdb00566f6bab5e1a9bb13f5380e 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -455,6 +455,7 @@ t.boolean "merge_requests_rebase_default", default: true t.integer "approvals_before_merge", default: 0, null: false t.boolean "reset_approvals_on_push", default: true + t.integer "commit_count", default: 0 end add_index "projects", ["created_at", "id"], name: "index_projects_on_created_at_and_id", using: :btree diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index 7b0873a9111c4e26d6fa28fa95185700280fc994..bb551fc67f751dd7029b50416fb0d7e54ac313d6 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -49,7 +49,8 @@ Parameters: "state": "active", "created_at": "2012-04-29T08:46:00Z" }, - "description":"fixed login page css paddings" + "description":"fixed login page css paddings", + "work_in_progress": false } ] ``` @@ -94,7 +95,8 @@ Parameters: "state": "active", "created_at": "2012-04-29T08:46:00Z" }, - "description":"fixed login page css paddings" + "description":"fixed login page css paddings", + "work_in_progress": false } ``` @@ -118,6 +120,7 @@ Parameters: "project_id": 4, "title": "Blanditiis beatae suscipit hic assumenda et molestias nisi asperiores repellat et.", "description": "Qui voluptatibus placeat ipsa alias quasi. Deleniti rem ut sint. Optio velit qui distinctio.", + "work_in_progress": false, "state": "reopened", "created_at": "2015-02-02T19:49:39.159Z", "updated_at": "2015-02-02T20:08:49.959Z", @@ -336,14 +339,6 @@ Parameters: ```json { - "author": { - "id": 1, - "username": "admin", - "email": "admin@example.com", - "name": "Administrator", - "blocked": false, - "created_at": "2012-04-29T08:46:00Z" - }, "note": "text1" } ``` diff --git a/doc/gitlab-basics/README.md b/doc/gitlab-basics/README.md index 3e4400b544b58985073c7de6bd2b2456baa115ae..d6272cd591267e4ef462494beb82eb21665f6d9e 100644 --- a/doc/gitlab-basics/README.md +++ b/doc/gitlab-basics/README.md @@ -13,3 +13,7 @@ Step-by-step guides on the basics of working with Git and GitLab. * [Create a project](create-project.md) * [Create a group](create-group.md) + +* [Create a branch](create-branch.md) + +* [Fork a project](fork-project.md) diff --git a/doc/gitlab-basics/basic-git-commands.md b/doc/gitlab-basics/basic-git-commands.md index ed210ba542035f0432746d190df1610fb7166072..2b5767dd2d360dc1230779834c203d6aa2952432 100644 --- a/doc/gitlab-basics/basic-git-commands.md +++ b/doc/gitlab-basics/basic-git-commands.md @@ -1,58 +1,58 @@ # Basic Git commands -* Go to the master branch to pull the latest changes from there +### Go to the master branch to pull the latest changes from there ``` git checkout master ``` -* Download the latest changes in the project, so that you work on an up-to-date copy (this is important to do every time you work on a project), while you setup tracking branches +### Download the latest changes in the project +This is for you to work on an up-to-date copy (it is important to do every time you work on a project), while you setup tracking branches. ``` git pull REMOTE NAME-OF-BRANCH -u ``` (REMOTE: origin) (NAME-OF-BRANCH: could be "master" or an existing branch) -* Create a branch (remember that spaces won't be recognized, you need to use a hyphen or underscore) +### Create a branch +Spaces won't be recognized, so you need to use a hyphen or underscore. ``` git checkout -b NAME-OF-BRANCH ``` -* Work on a branch that has already been created +### Work on a branch that has already been created ``` git checkout NAME-OF-BRANCH ``` -* To see the changes you've made (it's important to be aware of what's happening and what's the status of your changes) +### View the changes you've made +It's important to be aware of what's happening and what's the status of your changes. ``` git status ``` -* Add changes to commit (you'll be able to see your changes in red when you type "git status") +### Add changes to commit +You'll see your changes in red when you type "git status". ``` git add CHANGES IN RED git commit -m "DESCRIBE THE INTENTION OF THE COMMIT" ``` -* Send changes to gitlab.com +### Send changes to gitlab.com ``` -git push origin NAME-OF-BRANCH +git push REMOTE NAME-OF-BRANCH ``` -* Throw away all changes in the Git repository, but leave unstaged things +### Delete all changes in the Git repository, but leave unstaged things ``` git checkout . ``` -* Delete all changes in the Git repository, including untracked files +### Delete all changes in the Git repository, including untracked files ``` git clean -f ``` -* Remove all the changes that you don't want to send to gitlab.com -``` -git add NAME-OF-FILE -all -``` - -* Merge created branch with master branch. You need to be in the created branch +### Merge created branch with master branch +You need to be in the created branch. ``` git checkout NAME-OF-BRANCH git merge master diff --git a/doc/gitlab-basics/command-line-commands.md b/doc/gitlab-basics/command-line-commands.md index a596bf20c74a2a56a43788a604c8d975a5861b73..a8223a9b161a6f049e22c255c0a6a60d214a0b84 100644 --- a/doc/gitlab-basics/command-line-commands.md +++ b/doc/gitlab-basics/command-line-commands.md @@ -2,46 +2,47 @@ ## Start working on your project -* In Git, when you copy a project you say you "clone" it. To work on a git project locally (from your own computer), you will need to clone it. To do this, sign in to [GitLab.com](https://gitlab.com) +In Git, when you copy a project you say you "clone" it. To work on a git project locally (from your own computer), you will need to clone it. To do this, sign in to [GitLab.com](https://gitlab.com). -* When you are on your Dashboard, click on the project that you'd like to clone, which you'll find at the right side of your screen +When you are on your Dashboard, click on the project that you'd like to clone, which you'll find at the right side of your screen.  -* To work in the project, you can copy a link to the Git repository through a SSH or a HTTPS protocol. SSH is easier to use after it's been [setup](create-your-ssh-keys.md). When you're in the project, click on the HTTPS or SSH button at the right side of your screen. Then copy the link (you'll have to paste it on your shell in the next step) +To work in the project, you can copy a link to the Git repository through a SSH or a HTTPS protocol. SSH is easier to use after it's been [setup](create-your-ssh-keys.md). When you're in the project, click on the HTTPS or SSH button at the right side of your screen. Then copy the link (you'll have to paste it on your shell in the next step).  ## On the command line -* To clone your project, go to your computer's shell and type the following command +### Clone your project +Go to your computer's shell and type the following command: ``` git clone PASTE HTTPS OR SSH HERE ``` -* A clone of the project will be created in your computer +A clone of the project will be created in your computer. -* Go into a project, directory or file to work in it +### Go into a project, directory or file to work in it ``` cd NAME-OF-PROJECT-OR-FILE ``` -* Go back one directory or file +### Go back one directory or file ``` cd ../ ``` -* To see what’s in the directory that you are in +### View what’s in the directory that you are in ``` ls ``` -* Create a directory +### Create a directory ``` mkdir NAME-OF-YOUR-DIRECTORY ``` -* Create a README.md or file in directory +### Create a README.md or file in directory ``` touch README.md nano README.md @@ -51,22 +52,23 @@ nano README.md #### Press: enter ``` -* Remove a file +### Remove a file ``` rm NAME-OF-FILE ``` -* Remove a directory and all of its contents +### Remove a directory and all of its contents ``` rm -rf NAME-OF-DIRECTORY ``` -* View history in the command line +### View history in the command line ``` history ``` -* Carry out commands for which the account you are using lacks authority. (You will be asked for an administrator’s password) +### Carry out commands for which the account you are using lacks authority +You will be asked for an administrator’s password. ``` sudo ``` diff --git a/doc/gitlab-basics/create-branch.md b/doc/gitlab-basics/create-branch.md new file mode 100644 index 0000000000000000000000000000000000000000..7556b0f663ebb14b6ae409e1c0100de35b9ddfd2 --- /dev/null +++ b/doc/gitlab-basics/create-branch.md @@ -0,0 +1,39 @@ +# How to create a branch + +A branch is an independent line of development. + +New commits are recorded in the history for the current branch, which results in taking the source from someone’s repository (the place where the history of your work is stored) at certain point in time, and apply your own changes to it in the history of the project. + +To add changes to your GitLab project, you should create a branch. You can do it in your [shell](basic-git-commands.md) or in GitLab. + +To create a new branch in GitLab, sign in and then select a project on the right side of your screen: + + + +Click on "commits" on the menu on the left side of your screen: + + + +Click on the "branches" tab: + + + +Click on the "new branch" button on the right side of the screen: + + + +Fill out the information required: + +1. Add a name for your new branch (you can't add spaces, so you can use hyphens or underscores) + +1. On the "create from" space, add the the name of the branch you want to branch off from + +1. Click on the button "create branch" + + + +### Note: + +You will be able to find and select the name of your branch in the white box next to a project's name: + + diff --git a/doc/gitlab-basics/create-project.md b/doc/gitlab-basics/create-project.md index e3963f660106fbadeb3eb02ca34cc419a474b5cf..90d40cb6c511979818971060efcfe6d94a13e5e3 100644 --- a/doc/gitlab-basics/create-project.md +++ b/doc/gitlab-basics/create-project.md @@ -1,14 +1,12 @@ # How to create a project in GitLab -## Create a project +To create a new project, sign in to [GitLab.com](https://gitlab.com). -* Sign in to [GitLab.com](https://gitlab.com) - -* Go to your Dashboard and click on "new project" on the right side of your screen +Go to your Dashboard and click on "new project" on the right side of your screen.  -* Fill out the required information +Fill out the required information: 1. Project path or the name of your project (you can't add spaces, so you can use hyphens or underscores) diff --git a/doc/gitlab-basics/create-your-ssh-keys.md b/doc/gitlab-basics/create-your-ssh-keys.md index e1acc2bd65b1bc6431c26fddae0b7ad33043c2f7..dcd3e6ffb317d6ce9e17f1c0041a405a61666a44 100644 --- a/doc/gitlab-basics/create-your-ssh-keys.md +++ b/doc/gitlab-basics/create-your-ssh-keys.md @@ -4,34 +4,34 @@ You need to connect your computer to your GitLab account through SSH Keys. They ## Generate your SSH Key -* Create an account on GitLab. Sign up and check your email for your confirmation link +Create an account on GitLab. Sign up and check your email for your confirmation link. -* After you confirm, go to [GitLab.com](https://about.gitlab.com/) and sign in to your account +After you confirm, go to [GitLab.com](https://about.gitlab.com/) and sign in to your account. ## Add your SSH Key -* At the top right corner, click on "profile settings" +At the top right corner, click on "profile settings":  -* On the left side menu click on "SSH Keys" +On the left side menu click on "SSH Keys":  -* Then click on the green button "Add SSH Key" +Then click on the green button "Add SSH Key":  -* There, you should paste the SSH Key that your commandline will generate for you. Below you'll find the steps to generate it +There, you should paste the SSH Key that your command line will generate for you. Below you'll find the steps to generate it:  -## To generate an SSH Key on your commandline +## To generate an SSH Key on your command line -* Go to your [commandline](start-using-git.md) and follow the [instructions](https://gitlab.com/help/ssh/README) to generate it +Go to your [command line](start-using-git.md) and follow the [instructions](../ssh/README.md) to generate it. -* Copy the SSH Key that your commandline created and paste it on the "Key" box on the GitLab page. The title will be added automatically +Copy the SSH Key that your command line created and paste it on the "Key" box on the GitLab page. The title will be added automatically.  -* Now, you'll be able to use Git over SSH, instead of Git over HTTP. +Now, you'll be able to use Git over SSH, instead of Git over HTTP. diff --git a/doc/gitlab-basics/fork-project.md b/doc/gitlab-basics/fork-project.md new file mode 100644 index 0000000000000000000000000000000000000000..5173aae2c0f6ffbb8d2e9b43368f97781e982503 --- /dev/null +++ b/doc/gitlab-basics/fork-project.md @@ -0,0 +1,19 @@ +# How to fork a project + +A fork is a copy of an original repository that you can put somewhere else +or where you can experiment and apply changes that you can later decide if +publishing or not, without affecting your original project. + +It takes just a few steps to fork a project in GitLab. + +Sign in to [gitlab.com](https://gitlab.com). + +Select a project on the right side of your screen: + + + +Click on the "fork" button on the right side of your screen: + + + +Click on the user or group to where you'd like to add the forked project. diff --git a/doc/gitlab-basics/start-using-git.md b/doc/gitlab-basics/start-using-git.md index 21d93ed2e4d0cca6375e317d7f5b75714ed2f643..5b1c6c1cd460bbd9944dbbac7722ef0f2e1d814a 100644 --- a/doc/gitlab-basics/start-using-git.md +++ b/doc/gitlab-basics/start-using-git.md @@ -1,10 +1,10 @@ -# Start using Git on the commandline +# Start using Git on the command line -If you want to start using a Git and GitLab, make sure that you have created an account on [GitLab.com](https://about.gitlab.com/) +If you want to start using a Git and GitLab, make sure that you have created an account on [GitLab.com](https://about.gitlab.com/). ## Open a shell -* Depending on your operating system, find the shell of your preference. Here are some suggestions +Depending on your operating system, find the shell of your preference. Here are some suggestions. - [Terminal](http://blog.teamtreehouse.com/introduction-to-the-mac-os-x-command-line) on Mac OSX @@ -14,54 +14,48 @@ If you want to start using a Git and GitLab, make sure that you have created an ## Check if Git has already been installed -* Git is usually preinstalled on Mac and Linux - -* Type the following command and then press enter +Git is usually preinstalled on Mac and Linux. +Type the following command and then press enter: ``` git --version ``` -* You should receive a message that will tell you which Git version you have in your computer. If you don’t receive a "Git version" message, it means that you need to [download Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +You should receive a message that will tell you which Git version you have in your computer. If you don’t receive a "Git version" message, it means that you need to [download Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git). -* If Git doesn't automatically download, there's an option on the website to [download manually](https://git-scm.com/downloads). Then follow the steps on the installation window +If Git doesn't automatically download, there's an option on the website to [download manually](https://git-scm.com/downloads). Then follow the steps on the installation window. -* After you finished installing, open a new shell and type "git --version" again to verify that it was correctly installed +After you finished installing, open a new shell and type "git --version" again to verify that it was correctly installed. ## Add your Git username and set your email -* It is important because every Git commit that you create will use this information - -* On your shell, type the following command to add your username +It is important because every Git commit that you create will use this information. +On your shell, type the following command to add your username: ``` git config --global user.name ADD YOUR USERNAME ``` -* Then verify that you have the correct username - +Then verify that you have the correct username: ``` git config --global user.name ``` -* To set your email address, type the following command - +To set your email address, type the following command: ``` git config --global user.email ADD YOUR EMAIL ``` -* To verify that you entered your email correctly, type - +To verify that you entered your email correctly, type: ``` git config --global user.email ``` -* You'll need to do this only once because you are using the "--global" option. It tells Git to always use this information for anything you do on that system. If you want to override this with a different username or email address for specific projects, you can run the command without the "--global" option when you’re in that project +You'll need to do this only once because you are using the "--global" option. It tells Git to always use this information for anything you do on that system. If you want to override this with a different username or email address for specific projects, you can run the command without the "--global" option when you’re in that project. ## Check your information -* To view the information that you entered, type - +To view the information that you entered, type: ``` git config --global --list ``` diff --git a/doc/install/installation.md b/doc/install/installation.md index a3c0505e2a43b187624d9a5cf5986cb45a73205f..d8303a905a95a8f8985f8d0074610e020752b746 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -404,7 +404,7 @@ NOTE: Supply `SANITIZE=true` environment variable to `gitlab:check` to omit proj Visit YOUR_SERVER in your web browser for your first GitLab login. The setup has created a default admin account for you. You can use it to log in: root - password + 5iveL!fe **Important Note:** On login you'll be prompted to change the password. diff --git a/doc/profile/two_factor_authentication.md b/doc/profile/two_factor_authentication.md index fb215c8b269e41ded51f28d8c83ed718efcc51a7..f60ce35d3e2f6ab953e5250eaf714e6b537dd8ce 100644 --- a/doc/profile/two_factor_authentication.md +++ b/doc/profile/two_factor_authentication.md @@ -63,5 +63,10 @@ your phone's application or a recovery code to log in. 1. Go to **Account**. 1. Click **Disable Two-factor Authentication**. +## Note to GitLab administrators + +You need to take special care to that 2FA keeps working after +[restoring a GitLab backup](../raketasks/backup_restore.md). + [Google Authenticator]: https://support.google.com/accounts/answer/1066447?hl=en [FreeOTP]: https://fedorahosted.org/freeotp/ diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md index 39a13b14fba2667214d15e90ec483187217bb338..36ab2b919591cc9926c43c629fc04dc4c86e1d1b 100644 --- a/doc/raketasks/backup_restore.md +++ b/doc/raketasks/backup_restore.md @@ -9,6 +9,13 @@ This archive will be saved in backup_path (see `config/gitlab.yml`). The filename will be `[TIMESTAMP]_gitlab_backup.tar`. This timestamp can be used to restore an specific backup. You can only restore a backup to exactly the same version of GitLab that you created it on, for example 7.2.1. +You need to keep a separate copy of `/etc/gitlab/gitlab-secrets.json` +(for omnibus packages) or `/home/git/gitlab/.secret` (for installations +from source). This file contains the database encryption key used +for two-factor authentication. If you restore a GitLab backup without +restoring the database encryption key, users who have two-factor +authentication enabled will loose access to your GitLab server. + If you are interested in GitLab CI backup please follow to the [CI backup documentation](https://gitlab.com/gitlab-org/gitlab-ci/blob/master/doc/raketasks/backup_restore.md)* ``` @@ -143,15 +150,39 @@ with the name of your bucket: ## Storing configuration files -Please be informed that a backup does not store your configuration files. +Please be informed that a backup does not store your configuration +files. One reason for this is that your database contains encrypted +information for two-factor authentication. Storing encrypted +information along with its key in the same place defeats the purpose +of using encryption in the first place! + If you use an Omnibus package please see the [instructions in the readme to backup your configuration](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/README.md#backup-and-restore-omnibus-gitlab-configuration). If you have a cookbook installation there should be a copy of your configuration in Chef. -If you have an installation from source, please consider backing up your `gitlab.yml` file, any SSL keys and certificates, and your [SSH host keys](https://superuser.com/questions/532040/copy-ssh-keys-from-one-server-to-another-server/532079#532079). +If you have an installation from source, please consider backing up your `.secret` file, `gitlab.yml` file, any SSL keys and certificates, and your [SSH host keys](https://superuser.com/questions/532040/copy-ssh-keys-from-one-server-to-another-server/532079#532079). + +At the very **minimum** you should backup `/etc/gitlab/gitlab-secrets.json` +(Omnibus) or `/home/git/gitlab/.secret` (source) to preserve your +database encryption key. ## Restore a previously created backup You can only restore a backup to exactly the same version of GitLab that you created it on, for example 7.2.1. +### Prerequisites + +You need to have a working GitLab installation before you can perform +a restore. This is mainly because the system user performing the +restore actions ('git') is usually not allowed to create or delete +the SQL database it needs to import data into ('gitlabhq_production'). +All existing data will be either erased (SQL) or moved to a separate +directory (repositories, uploads). + +If some or all of your GitLab users are using two-factor authentication +(2FA) then you must also make sure to restore +`/etc/gitlab/gitlab-secrets.json` (Omnibus) or `/home/git/gitlab/.secret` +(installations from source). Note that you need to run `gitlab-ctl +reconfigure` after changing `gitlab-secrets.json`. + ### Installation from source ``` diff --git a/doc/release/monthly.md b/doc/release/monthly.md index 3bc92187218816b9e9009d4a0f9649dd4d042474..ca9696e957e628d98708d7234a6149d5fbd9797b 100644 --- a/doc/release/monthly.md +++ b/doc/release/monthly.md @@ -6,6 +6,7 @@ It starts 7 working days before the release. The release manager doesn't have to perform all the work but must ensure someone is assigned. The current release manager must schedule the appointment of the next release manager. The new release manager should create overall issue to track the progress. +The release manager should be the only person pushing/merging commits to the x-y-stable branches. ## Release Manager diff --git a/doc/workflow/README.md b/doc/workflow/README.md index e45697a617c1abc1dc8fcd9da50588caecdb4223..a61d38d3fee563d52e6cd86aa4aa0449d7c96807 100644 --- a/doc/workflow/README.md +++ b/doc/workflow/README.md @@ -10,7 +10,7 @@ - [Labels](labels.md) - [Manage large binaries with git annex](git_annex.md) - [Merge Request Approvals](merge_request_approvals.md) -- [Notifications](notifications.md) +- [Notification emails](notifications.md) - [Project Features](project_features.md) - [Project forking workflow](forking_workflow.md) - [Protected branches](protected_branches.md) diff --git a/doc/workflow/notifications.md b/doc/workflow/notifications.md index 17215de677e153d2aedfae3f91f7f51b0a57f2f1..2b5f06dd1fa337de0ae98db315e64f90189cd548 100644 --- a/doc/workflow/notifications.md +++ b/doc/workflow/notifications.md @@ -1,6 +1,6 @@ -# GitLab Notifications +# GitLab Notification Emails -GitLab has notifications system in place to notify a user of events important for the workflow. +GitLab has a notification system in place to notify a user of events that are important for the workflow. ## Notification settings @@ -67,5 +67,3 @@ Below is the table of events users can be notified of: | Reopen merge request | Project members [1] | [1] higher than participating | | Merge merge request | MR author [1], MR assignee [2], project members [3] | [1] [2] not disabled, [3] higher than participating | | New comment | Mentioned users [1], users participating [2], project members [3] | [1] [2] not disabled, [3] higher than participating | - - diff --git a/docker/Dockerfile b/docker/Dockerfile index 86f6c896a6d8c50f19e4ea32244165e140c4f906..05521af696354b1a8cffad2cfb23cbe56e13a315 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -30,7 +30,9 @@ RUN ( \ echo "" && \ echo "# Docker options" && \ echo "# Prevent Postgres from trying to allocate 25% of total memory" && \ - echo "postgresql['shared_buffers'] = '1MB'" ) >> /etc/gitlab/gitlab.rb + echo "postgresql['shared_buffers'] = '1MB'" ) >> /etc/gitlab/gitlab.rb && \ + mkdir -p /assets/ && \ + cp /etc/gitlab/gitlab.rb /assets/gitlab.rb # Expose web & ssh EXPOSE 443 80 22 diff --git a/docker/README.md b/docker/README.md index 293ca79f915ded71f144820e1bcac6e9af289995..e4d56cdb33676b50b4154eb49359e666757ff857 100644 --- a/docker/README.md +++ b/docker/README.md @@ -10,7 +10,7 @@ It might take a while before the docker container is responding to queries. You can check the status with something like `sudo docker logs -f gitlab`. -You can login to the web interface with username `root` and password `password`. +You can login to the web interface with username `root` and password `5iveL!fe`. Next time, you can just use docker start and stop to run the container. @@ -165,3 +165,5 @@ sudo docker push gitlab/gitlab-ce:latest Please see the [troubleshooting](troubleshooting.md) file in this directory. Note: We use `fig.yml` to have compatibility with fig and because docker-compose also supports it. + +Our docker image runs chef at every start to generate GitLab configuration. diff --git a/docker/assets/wrapper b/docker/assets/wrapper index 966b2cab4a13f2727aa61731ad2f0c07f46bc42f..8bc8370fbc941b120c66f393528d7731c91a1dab 100755 --- a/docker/assets/wrapper +++ b/docker/assets/wrapper @@ -13,4 +13,9 @@ function entrypoint() { gitlab-ctl tail # tail all logs } +if [[ ! -e /etc/gitlab/gitlab.rb ]]; then + cp /assets/gitlab.rb /etc/gitlab/gitlab.rb + chmod 0600 /etc/gitlab/gitlab.rb +fi + entrypoint diff --git a/features/project/issues/milestones.feature b/features/project/issues/milestones.feature index 9ac65b1257cf6e4f960375515a8f9a174582ab27..bfbaaec5a35a57a457d937098392e3ca157d812a 100644 --- a/features/project/issues/milestones.feature +++ b/features/project/issues/milestones.feature @@ -17,6 +17,10 @@ Feature: Project Issues Milestones And I submit new milestone "v2.3" Then I should see milestone "v2.3" + Scenario: I delete new milestone + Given I click link to remove milestone "v2.2" + And I should see no milestones + @javascript Scenario: Listing closed issues Given the milestone has open and closed issues diff --git a/features/steps/project/issues/milestones.rb b/features/steps/project/issues/milestones.rb index 708c5243947fc271590275208eb489148f4c1b1a..61e62c2adbd1227729b1ea890953a3798600b111 100644 --- a/features/steps/project/issues/milestones.rb +++ b/features/steps/project/issues/milestones.rb @@ -56,4 +56,12 @@ class Spinach::Features::ProjectIssuesMilestones < Spinach::FeatureSteps step 'I should see 3 issues' do expect(page).to have_selector('#tab-issues li.issue-row', count: 4) end + + step 'I click link to remove milestone "v2.2"' do + click_link 'Remove' + end + + step 'I should see no milestones' do + expect(page).to have_content('No milestones to show') + end end diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 9c15dc9b9fa33297270989229f3a817a7c7d5f73..b96a122743aa084eb9cbcb0756e3a6ccc0e98db5 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -181,6 +181,7 @@ class MergeRequest < ProjectEntity expose :source_project_id, :target_project_id expose :label_names, as: :labels expose :description + expose :work_in_progress?, as: :work_in_progress expose :milestone, using: Entities::Milestone end diff --git a/lib/backup/database.rb b/lib/backup/database.rb index b8aa6b9ff2f58c2bff1161bf1a8541167751336a..c5a5396cbbf925e7d353d081cd79cf337a9156d6 100644 --- a/lib/backup/database.rb +++ b/lib/backup/database.rb @@ -25,6 +25,7 @@ def dump abort 'Backup failed' unless success $progress.print 'Compressing database ... ' + FileUtils.rm_f db_file_name_gz success = system('gzip', db_file_name) report_success(success) abort 'Backup failed: compress error' unless success diff --git a/lib/gitlab/google_code_import/importer.rb b/lib/gitlab/google_code_import/importer.rb index 70bfe0597760c7141b2ff81ee3647216d0da7b20..03c410726a577fa98f4d95fd624642bf033b7c8e 100644 --- a/lib/gitlab/google_code_import/importer.rb +++ b/lib/gitlab/google_code_import/importer.rb @@ -327,7 +327,7 @@ def format_attachments(issue_id, comment_id, raw_attachments) link = "https://storage.googleapis.com/google-code-attachments/#{@repo.name}/issue-#{issue_id}/comment-#{comment_id}/#{filename}" text = "[#{filename}](#{link})" - text = "!#{text}" if filename =~ /\.(png|jpg|jpeg|gif|bmp|tiff)\z/ + text = "!#{text}" if filename =~ /\.(png|jpg|jpeg|gif|bmp|tiff)\z/i text end.compact end diff --git a/lib/gitlab/satellite/merge_action.rb b/lib/gitlab/satellite/merge_action.rb index a4e113fa1b9f926a1b0e903401e199d450540678..df9790981c65b0a2235184b64b62d2a298b55862 100644 --- a/lib/gitlab/satellite/merge_action.rb +++ b/lib/gitlab/satellite/merge_action.rb @@ -187,7 +187,7 @@ def update_satellite_source_and_target!(repo) end def remove_source_branch(repo) - if merge_request.should_remove_source_branch && !project.root_ref?(merge_request.source_branch) && !merge_request.for_fork? + if merge_request.remove_source_branch? # will raise CommandFailed when push fails repo.git.push(default_options, :origin, ":#{merge_request.source_branch}") end diff --git a/lib/repository_cache.rb b/lib/repository_cache.rb index fa016a170cd69e3ff3758c52321524bd03f7ec8a..8ddc3511293e5dfd7419bb368dd022cbbd604eda 100644 --- a/lib/repository_cache.rb +++ b/lib/repository_cache.rb @@ -18,4 +18,12 @@ def expire(key) def fetch(key, &block) backend.fetch(cache_key(key), &block) end + + def exist?(key) + backend.exist?(cache_key(key)) + end + + def read(key) + backend.read(cache_key(key)) + end end diff --git a/lib/support/init.d/gitlab b/lib/support/init.d/gitlab index 946902e2f6d625c721b86023a75730c758ac7736..a3455728a94050cb43465f085cb9e4d17edecc00 100755 --- a/lib/support/init.d/gitlab +++ b/lib/support/init.d/gitlab @@ -41,7 +41,7 @@ shell_path="/bin/bash" test -f /etc/default/gitlab && . /etc/default/gitlab # Switch to the app_user if it is not he/she who is running the script. -if [ "$USER" != "$app_user" ]; then +if [ `whoami` != "$app_user" ]; then eval su - "$app_user" -s $shell_path -c $(echo \")$0 "$@"$(echo \"); exit; fi diff --git a/spec/controllers/projects/milestones_controller_spec.rb b/spec/controllers/projects/milestones_controller_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..d3868c1320211c99659164afd4010f0efc3e83a3 --- /dev/null +++ b/spec/controllers/projects/milestones_controller_spec.rb @@ -0,0 +1,28 @@ +require 'spec_helper' + +describe Projects::MilestonesController do + let(:project) { create(:project) } + let(:user) { create(:user) } + let(:milestone) { create(:milestone, project: project) } + let(:issue) { create(:issue, project: project, milestone: milestone) } + + before do + sign_in(user) + project.team << [user, :master] + controller.instance_variable_set(:@project, project) + end + + describe "#destroy" do + it "should remove milestone" do + expect(issue.milestone_id).to eq(milestone.id) + delete :destroy, namespace_id: project.namespace.id, project_id: project.id, id: milestone.id, format: :js + expect(response).to be_success + expect { Milestone.find(milestone.id) }.to raise_exception(ActiveRecord::RecordNotFound) + issue.reload + expect(issue.milestone_id).to eq(nil) + # Check system note left for milestone removal + last_note = project.issues.find(issue.id).notes[-1].note + expect(last_note).to eq('Milestone removed') + end + end +end diff --git a/spec/controllers/tree_controller_spec.rb b/spec/controllers/projects/tree_controller_spec.rb similarity index 69% rename from spec/controllers/tree_controller_spec.rb rename to spec/controllers/projects/tree_controller_spec.rb index e09caf5df13cd477c94dd5db39ec87d6516d9f6b..53915856357570e992a65547d3bcc0d6c3ee88d6 100644 --- a/spec/controllers/tree_controller_spec.rb +++ b/spec/controllers/projects/tree_controller_spec.rb @@ -8,9 +8,6 @@ sign_in(user) project.team << [user, :master] - - allow(project).to receive(:branches).and_return(['master', 'foo/bar/baz']) - allow(project).to receive(:tags).and_return(['v1.0.0', 'v2.0.0']) controller.instance_variable_set(:@project, project) end @@ -44,6 +41,32 @@ let(:id) { 'invalid-branch/encoding/' } it { is_expected.to respond_with(:not_found) } end + + context "valid empty branch, invalid path" do + let(:id) { 'empty-branch/invalid-path/' } + it { is_expected.to respond_with(:not_found) } + end + + context "valid empty branch" do + let(:id) { 'empty-branch' } + it { is_expected.to respond_with(:success) } + end + + context "invalid SHA commit ID" do + let(:id) { 'ff39438/.gitignore' } + it { is_expected.to respond_with(:not_found) } + end + + context "valid SHA commit ID" do + let(:id) { '6d39438' } + it { is_expected.to respond_with(:success) } + end + + context "valid SHA commit ID with path" do + let(:id) { '6d39438/.gitignore' } + it { expect(response.status).to eq(302) } + end + end describe 'GET show with blob path' do diff --git a/spec/features/admin/security_spec.rb b/spec/features/security/admin_access_spec.rb similarity index 96% rename from spec/features/admin/security_spec.rb rename to spec/features/security/admin_access_spec.rb index 175fa9d4647fea1de2cbf57b327d7113efef3cc3..fe8cd7b7602380afaecdc79f1ec53123d9584134 100644 --- a/spec/features/admin/security_spec.rb +++ b/spec/features/security/admin_access_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe "Admin::Projects", feature: true do + include AccessMatchers + describe "GET /admin/projects" do subject { admin_namespaces_projects_path } diff --git a/spec/features/security/dashboard_access_spec.rb b/spec/features/security/dashboard_access_spec.rb index 67238e3ab76029f1994bb9ddffeed26bb65fc291..c38cddbb90444a03b6183511305dd83e7e1c8d33 100644 --- a/spec/features/security/dashboard_access_spec.rb +++ b/spec/features/security/dashboard_access_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe "Dashboard access", feature: true do + include AccessMatchers + describe "GET /dashboard" do subject { dashboard_path } diff --git a/spec/features/security/group/group_access_spec.rb b/spec/features/security/group/group_access_spec.rb deleted file mode 100644 index 63793149459ab2b4a01a164628f443ced51d0f06..0000000000000000000000000000000000000000 --- a/spec/features/security/group/group_access_spec.rb +++ /dev/null @@ -1,98 +0,0 @@ -require 'spec_helper' - -describe "Group access", feature: true do - describe "GET /projects/new" do - it { expect(new_group_path).to be_allowed_for :admin } - it { expect(new_group_path).to be_allowed_for :user } - it { expect(new_group_path).to be_denied_for :visitor } - end - - describe "Group" do - let(:group) { create(:group) } - - let(:owner) { create(:owner) } - let(:master) { create(:user) } - let(:reporter) { create(:user) } - let(:guest) { create(:user) } - let(:nonmember) { create(:user) } - - before do - group.add_user(owner, Gitlab::Access::OWNER) - group.add_user(master, Gitlab::Access::MASTER) - group.add_user(reporter, Gitlab::Access::REPORTER) - group.add_user(guest, Gitlab::Access::GUEST) - end - - describe "GET /groups/:path" do - subject { group_path(group) } - - it { is_expected.to be_allowed_for owner } - it { is_expected.to be_allowed_for master } - it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for guest } - it { is_expected.to be_denied_for :user } - it { is_expected.to be_denied_for :visitor } - end - - describe "GET /groups/:path/issues" do - subject { issues_group_path(group) } - - it { is_expected.to be_allowed_for owner } - it { is_expected.to be_allowed_for master } - it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for guest } - it { is_expected.to be_denied_for :user } - it { is_expected.to be_denied_for :visitor } - end - - describe "GET /groups/:path/merge_requests" do - subject { merge_requests_group_path(group) } - - it { is_expected.to be_allowed_for owner } - it { is_expected.to be_allowed_for master } - it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for guest } - it { is_expected.to be_denied_for :user } - it { is_expected.to be_denied_for :visitor } - end - - describe "GET /groups/:path/group_members" do - subject { group_group_members_path(group) } - - it { is_expected.to be_allowed_for owner } - it { is_expected.to be_allowed_for master } - it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for guest } - it { is_expected.to be_denied_for :user } - it { is_expected.to be_denied_for :visitor } - end - - describe "GET /groups/:path/edit" do - subject { edit_group_path(group) } - - it { is_expected.to be_allowed_for owner } - it { is_expected.to be_denied_for master } - it { is_expected.to be_denied_for reporter } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_denied_for guest } - it { is_expected.to be_denied_for :user } - it { is_expected.to be_denied_for :visitor } - end - - describe "GET /groups/:path/projects" do - subject { projects_group_path(group) } - - it { is_expected.to be_allowed_for owner } - it { is_expected.to be_denied_for master } - it { is_expected.to be_denied_for reporter } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_denied_for guest } - it { is_expected.to be_denied_for :user } - it { is_expected.to be_denied_for :visitor } - end - end -end diff --git a/spec/features/security/group/internal_group_access_spec.rb b/spec/features/security/group/internal_group_access_spec.rb deleted file mode 100644 index d17a7412e43874c3ea826e3520958d20f154b7b7..0000000000000000000000000000000000000000 --- a/spec/features/security/group/internal_group_access_spec.rb +++ /dev/null @@ -1,82 +0,0 @@ -require 'spec_helper' - -describe "Group with internal project access", feature: true do - describe "Group" do - let(:group) { create(:group) } - - let(:owner) { create(:owner) } - let(:master) { create(:user) } - let(:reporter) { create(:user) } - let(:guest) { create(:user) } - let(:nonmember) { create(:user) } - - before do - group.add_user(owner, Gitlab::Access::OWNER) - group.add_user(master, Gitlab::Access::MASTER) - group.add_user(reporter, Gitlab::Access::REPORTER) - group.add_user(guest, Gitlab::Access::GUEST) - - create(:project, :internal, group: group) - end - - describe "GET /groups/:path" do - subject { group_path(group) } - - it { is_expected.to be_allowed_for owner } - it { is_expected.to be_allowed_for master } - it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for guest } - it { is_expected.to be_allowed_for :user } - it { is_expected.to be_denied_for :visitor } - end - - describe "GET /groups/:path/issues" do - subject { issues_group_path(group) } - - it { is_expected.to be_allowed_for owner } - it { is_expected.to be_allowed_for master } - it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for guest } - it { is_expected.to be_allowed_for :user } - it { is_expected.to be_denied_for :visitor } - end - - describe "GET /groups/:path/merge_requests" do - subject { merge_requests_group_path(group) } - - it { is_expected.to be_allowed_for owner } - it { is_expected.to be_allowed_for master } - it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for guest } - it { is_expected.to be_allowed_for :user } - it { is_expected.to be_denied_for :visitor } - end - - describe "GET /groups/:path/group_members" do - subject { group_group_members_path(group) } - - it { is_expected.to be_allowed_for owner } - it { is_expected.to be_allowed_for master } - it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for guest } - it { is_expected.to be_allowed_for :user } - it { is_expected.to be_denied_for :visitor } - end - - describe "GET /groups/:path/edit" do - subject { edit_group_path(group) } - - it { is_expected.to be_allowed_for owner } - it { is_expected.to be_denied_for master } - it { is_expected.to be_denied_for reporter } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_denied_for guest } - it { is_expected.to be_denied_for :user } - it { is_expected.to be_denied_for :visitor } - end - end -end diff --git a/spec/features/security/group/mixed_group_access_spec.rb b/spec/features/security/group/mixed_group_access_spec.rb deleted file mode 100644 index b3db7b5dea4404337e9eba20df6d6a85f9395efd..0000000000000000000000000000000000000000 --- a/spec/features/security/group/mixed_group_access_spec.rb +++ /dev/null @@ -1,83 +0,0 @@ -require 'spec_helper' - -describe "Group access", feature: true do - describe "Group" do - let(:group) { create(:group) } - - let(:owner) { create(:owner) } - let(:master) { create(:user) } - let(:reporter) { create(:user) } - let(:guest) { create(:user) } - let(:nonmember) { create(:user) } - - before do - group.add_user(owner, Gitlab::Access::OWNER) - group.add_user(master, Gitlab::Access::MASTER) - group.add_user(reporter, Gitlab::Access::REPORTER) - group.add_user(guest, Gitlab::Access::GUEST) - - create(:project, :internal, path: "internal_project", group: group) - create(:project, :public, path: "public_project", group: group) - end - - describe "GET /groups/:path" do - subject { group_path(group) } - - it { is_expected.to be_allowed_for owner } - it { is_expected.to be_allowed_for master } - it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for guest } - it { is_expected.to be_allowed_for :user } - it { is_expected.to be_allowed_for :visitor } - end - - describe "GET /groups/:path/issues" do - subject { issues_group_path(group) } - - it { is_expected.to be_allowed_for owner } - it { is_expected.to be_allowed_for master } - it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for guest } - it { is_expected.to be_allowed_for :user } - it { is_expected.to be_allowed_for :visitor } - end - - describe "GET /groups/:path/merge_requests" do - subject { merge_requests_group_path(group) } - - it { is_expected.to be_allowed_for owner } - it { is_expected.to be_allowed_for master } - it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for guest } - it { is_expected.to be_allowed_for :user } - it { is_expected.to be_allowed_for :visitor } - end - - describe "GET /groups/:path/group_members" do - subject { group_group_members_path(group) } - - it { is_expected.to be_allowed_for owner } - it { is_expected.to be_allowed_for master } - it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for guest } - it { is_expected.to be_allowed_for :user } - it { is_expected.to be_allowed_for :visitor } - end - - describe "GET /groups/:path/edit" do - subject { edit_group_path(group) } - - it { is_expected.to be_allowed_for owner } - it { is_expected.to be_denied_for master } - it { is_expected.to be_denied_for reporter } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_denied_for guest } - it { is_expected.to be_denied_for :user } - it { is_expected.to be_denied_for :visitor } - end - end -end diff --git a/spec/features/security/group/public_group_access_spec.rb b/spec/features/security/group/public_group_access_spec.rb deleted file mode 100644 index c16f0c0d1e1d004c7276c426a9f49b01d2b381fb..0000000000000000000000000000000000000000 --- a/spec/features/security/group/public_group_access_spec.rb +++ /dev/null @@ -1,82 +0,0 @@ -require 'spec_helper' - -describe "Group with public project access", feature: true do - describe "Group" do - let(:group) { create(:group) } - - let(:owner) { create(:owner) } - let(:master) { create(:user) } - let(:reporter) { create(:user) } - let(:guest) { create(:user) } - let(:nonmember) { create(:user) } - - before do - group.add_user(owner, Gitlab::Access::OWNER) - group.add_user(master, Gitlab::Access::MASTER) - group.add_user(reporter, Gitlab::Access::REPORTER) - group.add_user(guest, Gitlab::Access::GUEST) - - create(:project, :public, group: group) - end - - describe "GET /groups/:path" do - subject { group_path(group) } - - it { is_expected.to be_allowed_for owner } - it { is_expected.to be_allowed_for master } - it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for guest } - it { is_expected.to be_allowed_for :user } - it { is_expected.to be_allowed_for :visitor } - end - - describe "GET /groups/:path/issues" do - subject { issues_group_path(group) } - - it { is_expected.to be_allowed_for owner } - it { is_expected.to be_allowed_for master } - it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for guest } - it { is_expected.to be_allowed_for :user } - it { is_expected.to be_allowed_for :visitor } - end - - describe "GET /groups/:path/merge_requests" do - subject { merge_requests_group_path(group) } - - it { is_expected.to be_allowed_for owner } - it { is_expected.to be_allowed_for master } - it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for guest } - it { is_expected.to be_allowed_for :user } - it { is_expected.to be_allowed_for :visitor } - end - - describe "GET /groups/:path/group_members" do - subject { group_group_members_path(group) } - - it { is_expected.to be_allowed_for owner } - it { is_expected.to be_allowed_for master } - it { is_expected.to be_allowed_for reporter } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_allowed_for guest } - it { is_expected.to be_allowed_for :user } - it { is_expected.to be_allowed_for :visitor } - end - - describe "GET /groups/:path/edit" do - subject { edit_group_path(group) } - - it { is_expected.to be_allowed_for owner } - it { is_expected.to be_denied_for master } - it { is_expected.to be_denied_for reporter } - it { is_expected.to be_allowed_for :admin } - it { is_expected.to be_denied_for guest } - it { is_expected.to be_denied_for :user } - it { is_expected.to be_denied_for :visitor } - end - end -end diff --git a/spec/features/security/group_access_spec.rb b/spec/features/security/group_access_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..0f36e47498960f306ae54c15abaf5601669c14f7 --- /dev/null +++ b/spec/features/security/group_access_spec.rb @@ -0,0 +1,284 @@ +require 'rails_helper' + +describe 'Group access', feature: true do + include AccessMatchers + + def group + @group ||= create(:group) + end + + def create_project(access_level) + if access_level == :mixed + create(:empty_project, :public, group: group) + create(:empty_project, :internal, group: group) + else + create(:empty_project, access_level, group: group) + end + end + + def group_member(access_level, group = group) + level = Object.const_get("Gitlab::Access::#{access_level.upcase}") + + create(:user).tap do |user| + group.add_user(user, level) + end + end + + describe 'GET /groups/new' do + subject { new_group_path } + + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :visitor } + end + + describe 'GET /groups/:path' do + subject { group_path(group) } + + context 'with public projects' do + before(:all) { create_project(:public) } + + it { is_expected.to be_allowed_for group_member(:owner) } + it { is_expected.to be_allowed_for group_member(:master) } + it { is_expected.to be_allowed_for group_member(:reporter) } + it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :visitor } + end + + context 'with mixed projects' do + before(:all) { create_project(:mixed) } + + it { is_expected.to be_allowed_for group_member(:owner) } + it { is_expected.to be_allowed_for group_member(:master) } + it { is_expected.to be_allowed_for group_member(:reporter) } + it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :visitor } + end + + context 'with internal projects' do + before(:all) { create_project(:internal) } + + it { is_expected.to be_allowed_for group_member(:owner) } + it { is_expected.to be_allowed_for group_member(:master) } + it { is_expected.to be_allowed_for group_member(:reporter) } + it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :visitor } + end + + context 'with no projects' do + it { is_expected.to be_allowed_for group_member(:owner) } + it { is_expected.to be_allowed_for group_member(:master) } + it { is_expected.to be_allowed_for group_member(:reporter) } + it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } + end + end + + describe 'GET /groups/:path/issues' do + subject { issues_group_path(group) } + + context 'with public projects' do + before(:all) { create_project(:public) } + + it { is_expected.to be_allowed_for group_member(:owner) } + it { is_expected.to be_allowed_for group_member(:master) } + it { is_expected.to be_allowed_for group_member(:reporter) } + it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :visitor } + end + + context 'with mixed projects' do + before(:all) { create_project(:mixed) } + + it { is_expected.to be_allowed_for group_member(:owner) } + it { is_expected.to be_allowed_for group_member(:master) } + it { is_expected.to be_allowed_for group_member(:reporter) } + it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :visitor } + end + + context 'with internal projects' do + before(:all) { create_project(:internal) } + + it { is_expected.to be_allowed_for group_member(:owner) } + it { is_expected.to be_allowed_for group_member(:master) } + it { is_expected.to be_allowed_for group_member(:reporter) } + it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :visitor } + end + + context 'with no projects' do + it { is_expected.to be_allowed_for group_member(:owner) } + it { is_expected.to be_allowed_for group_member(:master) } + it { is_expected.to be_allowed_for group_member(:reporter) } + it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } + end + end + + describe 'GET /groups/:path/merge_requests' do + subject { merge_requests_group_path(group) } + + context 'with public projects' do + before(:all) { create_project(:public) } + + it { is_expected.to be_allowed_for group_member(:owner) } + it { is_expected.to be_allowed_for group_member(:master) } + it { is_expected.to be_allowed_for group_member(:reporter) } + it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :visitor } + end + + context 'with mixed projects' do + before(:all) { create_project(:mixed) } + + it { is_expected.to be_allowed_for group_member(:owner) } + it { is_expected.to be_allowed_for group_member(:master) } + it { is_expected.to be_allowed_for group_member(:reporter) } + it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :visitor } + end + + context 'with internal projects' do + before(:all) { create_project(:internal) } + + it { is_expected.to be_allowed_for group_member(:owner) } + it { is_expected.to be_allowed_for group_member(:master) } + it { is_expected.to be_allowed_for group_member(:reporter) } + it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :visitor } + end + + context 'with no projects' do + it { is_expected.to be_allowed_for group_member(:owner) } + it { is_expected.to be_allowed_for group_member(:master) } + it { is_expected.to be_allowed_for group_member(:reporter) } + it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } + end + end + + describe 'GET /groups/:path/group_members' do + subject { group_group_members_path(group) } + + context 'with public projects' do + before(:all) { create_project(:public) } + + it { is_expected.to be_allowed_for group_member(:owner) } + it { is_expected.to be_allowed_for group_member(:master) } + it { is_expected.to be_allowed_for group_member(:reporter) } + it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :visitor } + end + + context 'with mixed projects' do + before(:all) { create_project(:mixed) } + + it { is_expected.to be_allowed_for group_member(:owner) } + it { is_expected.to be_allowed_for group_member(:master) } + it { is_expected.to be_allowed_for group_member(:reporter) } + it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :visitor } + end + + context 'with internal projects' do + before(:all) { create_project(:internal) } + + it { is_expected.to be_allowed_for group_member(:owner) } + it { is_expected.to be_allowed_for group_member(:master) } + it { is_expected.to be_allowed_for group_member(:reporter) } + it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_denied_for :visitor } + end + + context 'with no projects' do + it { is_expected.to be_allowed_for group_member(:owner) } + it { is_expected.to be_allowed_for group_member(:master) } + it { is_expected.to be_allowed_for group_member(:reporter) } + it { is_expected.to be_allowed_for group_member(:guest) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } + end + end + + describe 'GET /groups/:path/edit' do + subject { edit_group_path(group) } + + context 'with public projects' do + before(:all) { create_project(:public) } + + it { is_expected.to be_allowed_for group_member(:owner) } + it { is_expected.to be_denied_for group_member(:master) } + it { is_expected.to be_denied_for group_member(:reporter) } + it { is_expected.to be_denied_for group_member(:guest) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } + end + + context 'with mixed projects' do + before(:all) { create_project(:mixed) } + + it { is_expected.to be_allowed_for group_member(:owner) } + it { is_expected.to be_denied_for group_member(:master) } + it { is_expected.to be_denied_for group_member(:reporter) } + it { is_expected.to be_denied_for group_member(:guest) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } + end + + context 'with internal projects' do + before(:all) { create_project(:internal) } + + it { is_expected.to be_allowed_for group_member(:owner) } + it { is_expected.to be_denied_for group_member(:master) } + it { is_expected.to be_denied_for group_member(:reporter) } + it { is_expected.to be_denied_for group_member(:guest) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } + end + + context 'with no projects' do + it { is_expected.to be_allowed_for group_member(:owner) } + it { is_expected.to be_denied_for group_member(:master) } + it { is_expected.to be_denied_for group_member(:reporter) } + it { is_expected.to be_denied_for group_member(:guest) } + it { is_expected.to be_allowed_for :admin } + it { is_expected.to be_denied_for :user } + it { is_expected.to be_denied_for :visitor } + end + end +end diff --git a/spec/features/security/profile_access_spec.rb b/spec/features/security/profile_access_spec.rb index bcabc2d53ac9d200cb0d0b1ba8f92579cd2cbb9f..c19678ab381334bad24144b2a1ace31a07613098 100644 --- a/spec/features/security/profile_access_spec.rb +++ b/spec/features/security/profile_access_spec.rb @@ -1,18 +1,11 @@ require 'spec_helper' describe "Profile access", feature: true do - before do - @u1 = create(:user) - end - - describe "GET /login" do - it { expect(new_user_session_path).not_to be_not_found_for :visitor } - end + include AccessMatchers describe "GET /profile/keys" do subject { profile_keys_path } - it { is_expected.to be_allowed_for @u1 } it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for :user } it { is_expected.to be_denied_for :visitor } @@ -21,7 +14,6 @@ describe "GET /profile" do subject { profile_path } - it { is_expected.to be_allowed_for @u1 } it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for :user } it { is_expected.to be_denied_for :visitor } @@ -30,7 +22,6 @@ describe "GET /profile/account" do subject { profile_account_path } - it { is_expected.to be_allowed_for @u1 } it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for :user } it { is_expected.to be_denied_for :visitor } @@ -39,7 +30,6 @@ describe "GET /profile/preferences" do subject { profile_preferences_path } - it { is_expected.to be_allowed_for @u1 } it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for :user } it { is_expected.to be_denied_for :visitor } @@ -48,7 +38,6 @@ describe "GET /profile/audit_log" do subject { audit_log_profile_path } - it { is_expected.to be_allowed_for @u1 } it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for :user } it { is_expected.to be_denied_for :visitor } @@ -57,7 +46,6 @@ describe "GET /profile/notifications" do subject { profile_notifications_path } - it { is_expected.to be_allowed_for @u1 } it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for :user } it { is_expected.to be_denied_for :visitor } diff --git a/spec/features/security/project/internal_access_spec.rb b/spec/features/security/project/internal_access_spec.rb index 54a09131b52d1280e51046062726cbd2cad2c986..a82374d8e8b7506749a88a400aeaaf402ccdd5e9 100644 --- a/spec/features/security/project/internal_access_spec.rb +++ b/spec/features/security/project/internal_access_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe "Internal Project Access", feature: true do + include AccessMatchers + let(:project) { create(:project, :internal) } let(:master) { create(:user) } diff --git a/spec/features/security/project/private_access_spec.rb b/spec/features/security/project/private_access_spec.rb index 45363c5564b5885ae42da41ae67f0464d332fe76..10e69ad89b585fa7558ea8fb0f98df1ab4acf7bd 100644 --- a/spec/features/security/project/private_access_spec.rb +++ b/spec/features/security/project/private_access_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe "Private Project Access", feature: true do + include AccessMatchers + let(:project) { create(:project) } let(:master) { create(:user) } diff --git a/spec/features/security/project/public_access_spec.rb b/spec/features/security/project/public_access_spec.rb index fb42d478c69622af05bad58aa75dfcd2c84c03ef..d8ee44dc2830b6b1b29dfe2c962c45566a61b994 100644 --- a/spec/features/security/project/public_access_spec.rb +++ b/spec/features/security/project/public_access_spec.rb @@ -1,6 +1,8 @@ require 'spec_helper' describe "Public Project Access", feature: true do + include AccessMatchers + let(:project) { create(:project) } let(:master) { create(:user) } @@ -17,7 +19,6 @@ # readonly project.team << [reporter, :reporter] - end describe "Project should be public" do diff --git a/spec/fixtures/GoogleCodeProjectHosting.json b/spec/fixtures/GoogleCodeProjectHosting.json index d05e77271ae0be5bf96dee120a9ea69e0c776ad8..67bb3bae5b7a68b1c664f72f6c350d91bd4184a3 100644 --- a/spec/fixtures/GoogleCodeProjectHosting.json +++ b/spec/fixtures/GoogleCodeProjectHosting.json @@ -382,6 +382,11 @@ "fileName" : "screenshot.png", "fileSize" : 0, "mimetype" : "image/png" + }, { + "attachmentId" : "001", + "fileName" : "screenshot1.PNG", + "fileSize" : 0, + "mimetype" : "image/x-png" } ] }, { "id" : 1, diff --git a/spec/javascripts/fixtures/line_highlighter.html.haml b/spec/javascripts/fixtures/line_highlighter.html.haml index 15ad1d8968fe5ed961688b687766bd24b6313110..da1ebcdb23cacb744c7824b612e5c496f15ce086 100644 --- a/spec/javascripts/fixtures/line_highlighter.html.haml +++ b/spec/javascripts/fixtures/line_highlighter.html.haml @@ -2,7 +2,9 @@ .file-content .line-numbers - 1.upto(25) do |i| - %a{href: "#L#{i}", id: "L#{i}", 'data-line-number' => i}= i + %a{href: "#L#{i}", id: "L#{i}", 'data-line-number' => i} + %i.fa.fa-link + = i %pre.code.highlight %code - 1.upto(25) do |i| diff --git a/spec/javascripts/line_highlighter_spec.js.coffee b/spec/javascripts/line_highlighter_spec.js.coffee index 14fa487ff7f42a959d0a0bb70a947c383282dbbe..57453c716a5df44f2e7df4268beab377a993d9fb 100644 --- a/spec/javascripts/line_highlighter_spec.js.coffee +++ b/spec/javascripts/line_highlighter_spec.js.coffee @@ -48,6 +48,14 @@ describe 'LineHighlighter', -> clickLine(13) expect(spy).toHaveBeenPrevented() + it 'handles clicking on a child icon element', -> + spy = spyOn(@class, 'setHash').and.callThrough() + + $('#L13 i').mousedown().click() + + expect(spy).toHaveBeenCalledWith(13) + expect($('#LC13')).toHaveClass(@css) + describe 'without shiftKey', -> it 'highlights one line when clicked', -> clickLine(13) diff --git a/spec/lib/gitlab/google_code_import/importer_spec.rb b/spec/lib/gitlab/google_code_import/importer_spec.rb index c53ddeb87b58ca5988865b0299c71a8138cc1db0..f49cbb7f532ae79cd44a6718bc9eba4e87d5144b 100644 --- a/spec/lib/gitlab/google_code_import/importer_spec.rb +++ b/spec/lib/gitlab/google_code_import/importer_spec.rb @@ -65,6 +65,7 @@ expect(issue.description).to include('all the best!') expect(issue.description).to include('[tint2_task_scrolling.diff](https://storage.googleapis.com/google-code-attachments/tint2/issue-169/comment-0/tint2_task_scrolling.diff)') expect(issue.description).to include('') + expect(issue.description).to include('') end it "imports issue comments" do diff --git a/spec/models/project_services/gitlab_ci_service_spec.rb b/spec/models/project_services/gitlab_ci_service_spec.rb index fedc37c9b94e4743987fcc97aeaed9bedc772a30..a14384c87b462a56fa0979701354a9d5c568ff09 100644 --- a/spec/models/project_services/gitlab_ci_service_spec.rb +++ b/spec/models/project_services/gitlab_ci_service_spec.rb @@ -26,6 +26,33 @@ it { is_expected.to have_one(:service_hook) } end + describe 'validations' do + context 'active' do + before { allow(subject).to receive(:activated?).and_return(true) } + + it { is_expected.to validate_presence_of(:token) } + it { is_expected.to validate_presence_of(:project_url) } + it { is_expected.to allow_value('ewf9843kdnfdfs89234n').for(:token) } + it { is_expected.to allow_value('http://ci.example.com/project/1').for(:project_url) } + it { is_expected.not_to allow_value('token with spaces').for(:token) } + it { is_expected.not_to allow_value('token/with%spaces').for(:token) } + it { is_expected.not_to allow_value('this is not url').for(:project_url) } + it { is_expected.not_to allow_value('http//noturl').for(:project_url) } + it { is_expected.not_to allow_value('ftp://ci.example.com/projects/3').for(:project_url) } + end + + context 'inactive' do + before { allow(subject).to receive(:activated?).and_return(false) } + + it { is_expected.not_to validate_presence_of(:token) } + it { is_expected.not_to validate_presence_of(:project_url) } + it { is_expected.to allow_value('ewf9843kdnfdfs89234n').for(:token) } + it { is_expected.to allow_value('http://ci.example.com/project/1').for(:project_url) } + it { is_expected.to allow_value('token with spaces').for(:token) } + it { is_expected.to allow_value('ftp://ci.example.com/projects/3').for(:project_url) } + end + end + describe 'commits methods' do before do @service = GitlabCiService.new diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb index cb6e5e89625d2898bf44aa13cde600ae40e7f265..5c1b58535cc3b636122791e49e27a0e309b3950a 100644 --- a/spec/requests/api/branches_spec.rb +++ b/spec/requests/api/branches_spec.rb @@ -14,10 +14,13 @@ describe "GET /projects/:id/repository/branches" do it "should return an array of project branches" do + project.repository.expire_cache + get api("/projects/#{project.id}/repository/branches", user) expect(response.status).to eq(200) expect(json_response).to be_an Array - expect(json_response.first['name']).to eq(project.repository.branch_names.first) + branch_names = json_response.map { |x| x['name'] } + expect(branch_names).to match_array(project.repository.branch_names) end end diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index bdb8b3d700ba73915532f76022e0a79c85856d86..8bfd761ab25ee34eedc1f7f63721af9693e2971e 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -89,7 +89,7 @@ it 'returns projects in the correct order when ci_enabled_first parameter is passed' do [project, project2, project3].each{ |project| project.build_missing_services } - project2.gitlab_ci_service.update(active: true, token: "token", project_url: "url") + project2.gitlab_ci_service.update(active: true, token: "token", project_url: "http://ci.example.com/projects/1") get api('/projects', user), { ci_enabled_first: 'true' } expect(response.status).to eq(200) expect(json_response).to be_an Array diff --git a/spec/requests/api/services_spec.rb b/spec/requests/api/services_spec.rb index 51c543578df747fec3bb0a5e5d3a7103248137a5..6d29a28580a7a849d9686876ceca09c2a3bc1284 100644 --- a/spec/requests/api/services_spec.rb +++ b/spec/requests/api/services_spec.rb @@ -7,7 +7,7 @@ describe "POST /projects/:id/services/gitlab-ci" do it "should update gitlab-ci settings" do - put api("/projects/#{project.id}/services/gitlab-ci", user), token: 'secret-token', project_url: "http://ci.example.com/projects/1" + put api("/projects/#{project.id}/services/gitlab-ci", user), token: 'secrettoken', project_url: "http://ci.example.com/projects/1" expect(response.status).to eq(200) end @@ -17,6 +17,18 @@ expect(response.status).to eq(400) end + + it "should return if the format of token is invalid" do + put api("/projects/#{project.id}/services/gitlab-ci", user), token: 'token-with dashes and spaces%', project_url: "http://ci.example.com/projects/1", active: true + + expect(response.status).to eq(404) + end + + it "should return if the format of token is invalid" do + put api("/projects/#{project.id}/services/gitlab-ci", user), token: 'token-with dashes and spaces%', project_url: "ftp://ci.example/projects/1", active: true + + expect(response.status).to eq(404) + end end describe "DELETE /projects/:id/services/gitlab-ci" do diff --git a/spec/support/matchers.rb b/spec/support/matchers.rb deleted file mode 100644 index a2f853e3e70123e5bce936f678c5c2e9e2494fbf..0000000000000000000000000000000000000000 --- a/spec/support/matchers.rb +++ /dev/null @@ -1,66 +0,0 @@ -RSpec::Matchers.define :be_valid_commit do - match do |actual| - actual && - actual.id == ValidCommit::ID && - actual.message == ValidCommit::MESSAGE && - actual.author_name == ValidCommit::AUTHOR_FULL_NAME - end -end - -def emulate_user(user) - user = case user - when :user then create(:user) - when :visitor then nil - when :admin then create(:admin) - else user - end - login_with(user) if user -end - -RSpec::Matchers.define :be_allowed_for do |user| - match do |url| - emulate_user(user) - visit url - status_code != 404 && current_path != new_user_session_path - end -end - -RSpec::Matchers.define :be_denied_for do |user| - match do |url| - emulate_user(user) - visit url - status_code == 404 || current_path == new_user_session_path - end -end - -RSpec::Matchers.define :be_not_found_for do |user| - match do |url| - emulate_user(user) - visit url - status_code == 404 - end -end - -RSpec::Matchers.define :include_module do |expected| - match do - described_class.included_modules.include?(expected) - end - - description do - "includes the #{expected} module" - end - - failure_message do - "expected #{described_class} to include the #{expected} module" - end -end - -# Extend shoulda-matchers -module Shoulda::Matchers::ActiveModel - class ValidateLengthOfMatcher - # Shortcut for is_at_least and is_at_most - def is_within(range) - is_at_least(range.min) && is_at_most(range.max) - end - end -end diff --git a/spec/support/matchers/access_matchers.rb b/spec/support/matchers/access_matchers.rb new file mode 100644 index 0000000000000000000000000000000000000000..558e8b1612f406c75ebdf6e0f7e7ef962c4225aa --- /dev/null +++ b/spec/support/matchers/access_matchers.rb @@ -0,0 +1,54 @@ +# AccessMatchers +# +# The custom matchers contained in this module are used to test a user's access +# to a URL by emulating a specific user or type of user account, visiting the +# URL, and then checking the response status code and resulting path. +module AccessMatchers + extend RSpec::Matchers::DSL + include Warden::Test::Helpers + + def emulate_user(user) + case user + when :user + login_as(create(:user)) + when :visitor + logout + when :admin + login_as(create(:admin)) + when User + login_as(user) + else + raise ArgumentError, "cannot emulate user #{user}" + end + end + + def description_for(user, type) + if user.kind_of?(User) + # User#inspect displays too much information for RSpec's description + # messages + "be #{type} for supplied User" + else + "be #{type} for #{user}" + end + end + + matcher :be_allowed_for do |user| + match do |url| + emulate_user(user) + visit url + status_code != 404 && current_path != new_user_session_path + end + + description { description_for(user, 'allowed') } + end + + matcher :be_denied_for do |user| + match do |url| + emulate_user(user) + visit url + status_code == 404 || current_path == new_user_session_path + end + + description { description_for(user, 'denied') } + end +end diff --git a/spec/support/matchers/include_module.rb b/spec/support/matchers/include_module.rb new file mode 100644 index 0000000000000000000000000000000000000000..0a78af1e90e2f06a330c8901d7eef7a799b0e63a --- /dev/null +++ b/spec/support/matchers/include_module.rb @@ -0,0 +1,13 @@ +RSpec::Matchers.define :include_module do |expected| + match do + described_class.included_modules.include?(expected) + end + + description do + "includes the #{expected} module" + end + + failure_message do + "expected #{described_class} to include the #{expected} module" + end +end diff --git a/spec/support/matchers/is_within.rb b/spec/support/matchers/is_within.rb new file mode 100644 index 0000000000000000000000000000000000000000..0c35fc7e899b71e01613aaa2dd04427280773107 --- /dev/null +++ b/spec/support/matchers/is_within.rb @@ -0,0 +1,9 @@ +# Extend shoulda-matchers +module Shoulda::Matchers::ActiveModel + class ValidateLengthOfMatcher + # Shortcut for is_at_least and is_at_most + def is_within(range) + is_at_least(range.min) && is_at_most(range.max) + end + end +end diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb index 8bdd6b43cdd96812961d52a851ca0a8b750bc502..dcf2a9e2ce5abf9ec463f2871e6e57e684e2c0c5 100644 --- a/spec/support/test_env.rb +++ b/spec/support/test_env.rb @@ -5,6 +5,7 @@ module TestEnv # When developing the seed repository, comment out the branch you will modify. BRANCH_SHA = { + 'empty-branch' => '7efb185', 'flatten-dir' => 'e56497b', 'feature' => '0b4bc9a', 'feature_conflict' => 'bb5206f', @@ -14,9 +15,13 @@ module TestEnv 'master' => '5937ac0' } - FORKED_BRANCH_SHA = BRANCH_SHA.merge({ - 'add-submodule-version-bump' => '3f547c08' - }) + # gitlab-test-fork is a fork of gitlab-fork, but we don't necessarily + # need to keep all the branches in sync. + # We currently only need a subset of the branches + FORKED_BRANCH_SHA = { + 'add-submodule-version-bump' => '3f547c08', + 'master' => '5937ac0' + } # Test environment #