diff options
author | Jan-Willem van der Meer <mail@jewilmeer.nl> | 2014-09-11 11:46:08 +0200 |
---|---|---|
committer | Jan-Willem van der Meer <mail@jewilmeer.nl> | 2014-09-11 11:46:08 +0200 |
commit | bf0de1a500e7a9aecc7c8bbf623ad39b75c6433b (patch) | |
tree | 382df65e2b17c91e03278d58bce8dce0129a6a7b | |
parent | b18d1c2786c2a385d6b797734a1afad7a01ddf35 (diff) | |
parent | 78ec7d9c9d156fe556d165c1c096bf5534d62d25 (diff) | |
download | gitlab-ce-bf0de1a500e7a9aecc7c8bbf623ad39b75c6433b.tar.gz |
Merge remote-tracking branch 'origin/master' into feature-oauth-refactoring
167 files changed, 2250 insertions, 909 deletions
diff --git a/.pkgr.yml b/.pkgr.yml index 97d78b6ef69..cf96e7916d8 100644 --- a/.pkgr.yml +++ b/.pkgr.yml @@ -5,6 +5,8 @@ targets: debian-7: &wheezy build_dependencies: - libicu-dev + - cmake + - pkg-config dependencies: - libicu48 - libpcre3 @@ -13,6 +15,8 @@ targets: ubuntu-14.04: build_dependencies: - libicu-dev + - cmake + - pkg-config dependencies: - libicu52 - libpcre3 @@ -20,6 +24,8 @@ targets: centos-6: build_dependencies: - libicu-devel + - cmake + - pkgconfig dependencies: - libicu - pcre diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 51076237fba..00000000000 --- a/.travis.yml +++ /dev/null @@ -1,42 +0,0 @@ -language: ruby -cache: - directories: - - vendor/bundle -env: - global: - - TRAVIS=true - matrix: - - TASK=spinach_project DB=mysql - - TASK=spinach_other DB=mysql - - TASK=spec:api DB=mysql - - TASK=spec:feature DB=mysql - - TASK=spec:other DB=mysql - - TASK=jasmine:ci DB=mysql - - TASK=spinach_project DB=postgresql - - TASK=spinach_other DB=postgresql - - TASK=spec:api DB=postgresql - - TASK=spec:feature DB=postgresql - - TASK=spec:other DB=postgresql - - TASK=jasmine:ci DB=postgresql -before_install: - - sudo apt-get install libicu-dev -y -install: - - "travis_retry bundle config build.nokogiri --use-system-libraries" - - "travis_retry bundle install --deployment --without production --retry 5" -branches: - only: - - 'master' -rvm: - - 2.0.0 -services: - - redis-server -before_script: - - "cp config/database.yml.$DB config/database.yml" - - "cp config/gitlab.yml.example config/gitlab.yml" - - "bundle exec rake db:setup" - - "bundle exec rake db:seed_fu" -script: "bundle exec rake $TASK --trace" -notifications: - email: false -git: - depth: 10 diff --git a/CHANGELOG b/CHANGELOG index c1531cb2ff2..6021da42422 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -10,9 +10,26 @@ v 7.3.0 - Support Unix domain sockets for Redis - Store session Redis keys in 'session:gitlab:' namespace - Deprecate LDAP account takeover based on partial LDAP email / GitLab username match + - Use /bin/sh instead of Bash in bin/web, bin/background_jobs (Pavel Novitskiy) - Keyboard shortcuts for productivity (Robert Schilling) - API: filter issues by state (Julien Bianchi) + - API: filter issues by labels (Julien Bianchi) - Add system hook for ssh key changes + - Add blob permalink link (Ciro Santilli) + - Create annotated tags through UI and API (Sean Edge) + - Snippets search (Charles Bushong) + - Comment new push to existing MR + - Add 'ci' to the blacklist of forbidden names + - Improve text filtering on issues page + - Comment & Close button + - Process git push --all much faster + - Don't allow edit of system notes + - Project wiki search (Ralf Seidler) + - Enabled Shibboleth authentication support (Matus Banas) + +v 7.2.1 + - Delete orphaned labels during label migration (James Brooks) + - Security: prevent XSS with stricter MIME types for raw repo files v 7.2.0 - Explore page @@ -27,6 +27,7 @@ gem 'omniauth', "~> 1.1.3" gem 'omniauth-google-oauth2' gem 'omniauth-twitter' gem 'omniauth-github' +gem 'omniauth-shibboleth' # Extracting information from a git repository # Provide access to Gitlab::Git library @@ -36,7 +37,7 @@ gem "gitlab_git", '~> 6.0' gem 'gitlab-grack', '~> 2.0.0.pre', require: 'grack' # LDAP Auth -gem 'gitlab_omniauth-ldap', '1.0.4', require: "omniauth-ldap" +gem 'gitlab_omniauth-ldap', '1.1.0', require: "omniauth-ldap" # Git Wiki gem 'gollum-lib', '~> 3.0.0' diff --git a/Gemfile.lock b/Gemfile.lock index b20f4169174..e8636fd7ac5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -168,7 +168,7 @@ GEM multi_json gitlab-grack (2.0.0.pre) rack (~> 1.5.1) - gitlab-grit (2.6.10) + gitlab-grit (2.6.11) charlock_holmes (~> 0.6) diff-lcs (~> 1.1) mime-types (~> 1.15) @@ -186,8 +186,8 @@ GEM gitlab-linguist (~> 3.0) rugged (~> 0.21.0) gitlab_meta (7.0) - gitlab_omniauth-ldap (1.0.4) - net-ldap (~> 0.3.1) + gitlab_omniauth-ldap (1.1.0) + net-ldap (~> 0.7.0) omniauth (~> 1.0) pyu-ruby-sasl (~> 0.0.3.1) rubyntlm (~> 0.1.1) @@ -292,7 +292,7 @@ GEM multi_xml (0.5.5) multipart-post (1.2.0) mysql2 (0.3.16) - net-ldap (0.3.1) + net-ldap (0.7.0) net-scp (1.1.2) net-ssh (>= 2.6.5) net-ssh (2.8.0) @@ -321,6 +321,8 @@ GEM omniauth-oauth2 (1.1.1) oauth2 (~> 0.8.0) omniauth (~> 1.0) + omniauth-shibboleth (1.1.1) + omniauth (>= 1.0.0) omniauth-twitter (1.0.1) multi_json (~> 1.3) omniauth-oauth (~> 1.0) @@ -616,7 +618,7 @@ DEPENDENCIES gitlab_emoji (~> 0.0.1.1) gitlab_git (~> 6.0) gitlab_meta (= 7.0) - gitlab_omniauth-ldap (= 1.0.4) + gitlab_omniauth-ldap (= 1.1.0) gollum-lib (~> 3.0.0) gon (~> 5.0.0) grape (~> 0.6.1) @@ -644,6 +646,7 @@ DEPENDENCIES omniauth-github omniauth-google-oauth2 omniauth-twitter + omniauth-shibboleth org-ruby pg poltergeist (~> 1.5.1) diff --git a/PROCESS.md b/PROCESS.md index c986013e2f2..c3a787662f7 100644 --- a/PROCESS.md +++ b/PROCESS.md @@ -87,7 +87,7 @@ Please use ``` to format console output, logs, and code as it's very hard to rea ### Issue fixed in newer version -Thanks for the issue report. This issue has already been fixed in newer versions of GitLab. Due to the size of this project and our limited resources we are only able to support the latest stable release as outlined in our \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker). In order to get this bug fix and enjoy many new features please \[upgrade\]\(https://github.com/gitlabhq/gitlabhq/tree/master/doc/update). If you still experience issues at that time please open a new issue following our issue tracker guidelines found in the \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker-guidelines). +Thanks for the issue report. This issue has already been fixed in newer versions of GitLab. Due to the size of this project and our limited resources we are only able to support the latest stable release as outlined in our \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker). In order to get this bug fix and enjoy many new features please \[upgrade\]\(https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update). If you still experience issues at that time please open a new issue following our issue tracker guidelines found in the \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker-guidelines). ### Improperly formatted merge request diff --git a/README.md b/README.md index 6d87f314720..fbcde347be6 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,8 @@ -# GitLab +#  GitLab ## Open source software to collaborate on code - - - + - Manage Git repositories with fine grained access controls that keep your code secure - Perform code reviews and enhance collaboration with merge requests @@ -21,6 +19,8 @@ - [](https://ci.gitlab.org/projects/1?ref=master) on ci.gitlab.org (master branch) +- [](https://semaphoreapp.com/gitlabhq/gitlabhq) + - [](https://codeclimate.com/github/gitlabhq/gitlabhq) - [](https://coveralls.io/r/gitlabhq/gitlabhq) @@ -29,14 +29,14 @@ ## Website -On [www.gitlab.com](https://www.gitlab.com/) you can find more information about: +On [about.gitlab.com](https://about.gitlab.com/) you can find more information about: -- [Subscriptions](https://www.gitlab.com/subscription/) -- [Consultancy](https://www.gitlab.com/consultancy/) -- [Community](https://www.gitlab.com/community/) -- [Hosted GitLab.com](https://www.gitlab.com/gitlab-com/) use GitLab as a free service -- [GitLab Enterprise Edition](https://www.gitlab.com/gitlab-ee/) with additional features aimed at larger organizations. -- [GitLab CI](https://www.gitlab.com/gitlab-ci/) a continuous integration (CI) server that is easy to integrate with GitLab. +- [Subscriptions](https://about.gitlab.com/subscription/) +- [Consultancy](https://about.gitlab.com/consultancy/) +- [Community](https://about.gitlab.com/community/) +- [Hosted GitLab.com](https://about.gitlab.com/gitlab-com/) use GitLab as a free service +- [GitLab Enterprise Edition](https://about.gitlab.com/gitlab-ee/) with additional features aimed at larger organizations. +- [GitLab CI](https://about.gitlab.com/gitlab-ci/) a continuous integration (CI) server that is easy to integrate with GitLab. ## Third-party applications @@ -61,11 +61,11 @@ These applications are maintained by contributors, GitLab B.V. does not offer su ## Installation -Please see [the installation page on the GitLab website](https://www.gitlab.com/installation/). +Please see [the installation page on the GitLab website](https://about.gitlab.com/installation/). ### New versions -Since 2011 a minor or major version of GitLab is released on the 22nd of every month. Patch and security releases come out when needed. New features are detailed on the [blog](https://www.gitlab.com/blog/) and in the [changelog](CHANGELOG). For more information about the release process see the release [documentation](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/release). Features that will likely be in the next releases can be found on the [feature request forum](http://feedback.gitlab.com/forums/176466-general) with the status [started](http://feedback.gitlab.com/forums/176466-general/status/796456) and [completed](http://feedback.gitlab.com/forums/176466-general/status/796457). +Since 2011 a minor or major version of GitLab is released on the 22nd of every month. Patch and security releases come out when needed. New features are detailed on the [blog](https://about.gitlab.com/blog/) and in the [changelog](CHANGELOG). For more information about the release process see the release [documentation](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/release). Features that will likely be in the next releases can be found on the [feature request forum](http://feedback.gitlab.com/forums/176466-general) with the status [started](http://feedback.gitlab.com/forums/176466-general/status/796456) and [completed](http://feedback.gitlab.com/forums/176466-general/status/796457). ### Upgrading @@ -85,7 +85,8 @@ Please login with `root` / `5iveL!fe` ## Install a development environment -We recommend setting up your development environment with [the cookbook](https://gitlab.com/gitlab-org/cookbook-gitlab/blob/master/README.md#installation). If you do not use the cookbook you might need to copy the example development unicorn configuration file +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 development kit you might need to copy the example development unicorn configuration file cp config/unicorn.rb.example.development config/unicorn.rb @@ -126,7 +127,7 @@ All documentation can be found on [doc.gitlab.com/ce/](http://doc.gitlab.com/ce/ ## Getting help -Please see [Getting help for GitLab](https://www.gitlab.com/getting-help/) on our website for the many options to get help. +Please see [Getting help for GitLab](https://about.gitlab.com/getting-help/) on our website for the many options to get help. ## Is it any good? diff --git a/app/assets/javascripts/issues.js.coffee b/app/assets/javascripts/issues.js.coffee index 54de93a4e04..2499ad5ad80 100644 --- a/app/assets/javascripts/issues.js.coffee +++ b/app/assets/javascripts/issues.js.coffee @@ -43,25 +43,31 @@ $(".selected_issue").bind "change", Issues.checkChanged - + # Make sure we trigger ajax request only after user stop typing initSearch: -> - form = $("#issue_search_form") - last_terms = "" + @timer = null $("#issue_search").keyup -> - terms = $(this).val() - unless terms is last_terms - last_terms = terms - if terms.length >= 2 or terms.length is 0 - $.ajax - type: "GET" - url: location.href - data: "issue_search=" + terms - complete: -> - $(".loading").hide() - success: (data) -> - $('.issues-holder').html(data.html) - Issues.reload() - dataType: "json" + clearTimeout(@timer); + @timer = setTimeout(Issues.filterResults, 500) + + filterResults: => + form = $("#issue_search_form") + search = $("#issue_search").val() + $('.issues-holder').css("opacity", '0.5') + issues_url = form.attr('action') + '? '+ form.serialize() + + $.ajax + type: "GET" + url: form.attr('action') + data: form.serialize() + complete: -> + $('.issues-holder').css("opacity", '1.0') + success: (data) -> + $('.issues-holder').html(data.html) + # Change url so if user reload a page - search results are saved + History.replaceState {page: issues_url}, document.title, issues_url + Issues.reload() + dataType: "json" checkChanged: -> checked_issues = $(".selected_issue:checked") diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index 51c617bd584..597d6d26b69 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -26,6 +26,7 @@ class Notes # Reopen and close actions for Issue/MR combined with note form submit $(document).on "click", ".js-note-target-reopen", @targetReopen $(document).on "click", ".js-note-target-close", @targetClose + $(document).on "click", ".js-comment-button", @updateCloseButton $(document).on "keyup", ".js-note-text", @updateTargetButtons # remove a note (in general) @@ -496,6 +497,11 @@ class Notes if noteText.trim().length > 0 form.submit() + updateCloseButton: (e) => + textarea = $(e.target) + form = textarea.parents('form') + form.find('.js-note-target-close').text('Close') + updateTargetButtons: (e) => textarea = $(e.target) form = textarea.parents('form') diff --git a/app/assets/javascripts/shortcuts_navigation.coffee b/app/assets/javascripts/shortcuts_navigation.coffee index e24a74ea9b6..e592b700e7c 100644 --- a/app/assets/javascripts/shortcuts_navigation.coffee +++ b/app/assets/javascripts/shortcuts_navigation.coffee @@ -3,7 +3,7 @@ class @ShortcutsNavigation extends Shortcuts constructor: -> super() - Mousetrap.bind('g a', -> ShortcutsNavigation.findAndollowLink('.shortcuts-activity')) + Mousetrap.bind('g p', -> ShortcutsNavigation.findAndollowLink('.shortcuts-project')) Mousetrap.bind('g f', -> ShortcutsNavigation.findAndollowLink('.shortcuts-tree')) Mousetrap.bind('g c', -> ShortcutsNavigation.findAndollowLink('.shortcuts-commits')) Mousetrap.bind('g n', -> ShortcutsNavigation.findAndollowLink('.shortcuts-network')) diff --git a/app/assets/stylesheets/generic/lists.scss b/app/assets/stylesheets/generic/lists.scss index 0cab6c44c91..d347ab2c2e4 100644 --- a/app/assets/stylesheets/generic/lists.scss +++ b/app/assets/stylesheets/generic/lists.scss @@ -39,7 +39,7 @@ &:hover { background: $hover; - border-bottom: 1px solid #ADF; + border-bottom: 1px solid darken($hover, 10%); } &:last-child { diff --git a/app/assets/stylesheets/generic/typography.scss b/app/assets/stylesheets/generic/typography.scss index 9aa819d40fc..385a627b4be 100644 --- a/app/assets/stylesheets/generic/typography.scss +++ b/app/assets/stylesheets/generic/typography.scss @@ -40,7 +40,7 @@ a { outline: none; color: $link_color; &:hover { - text-decoration: none; + text-decoration: underline; color: $link_hover_color; } @@ -89,6 +89,8 @@ a:focus { .wiki { @include md-typography; + word-wrap: break-word; + /* Link to current header. */ h1, h2, h3, h4, h5, h6 { position: relative; diff --git a/app/assets/stylesheets/main/variables.scss b/app/assets/stylesheets/main/variables.scss index 02ce2c8338f..72d84226fe7 100644 --- a/app/assets/stylesheets/main/variables.scss +++ b/app/assets/stylesheets/main/variables.scss @@ -2,13 +2,13 @@ * General Colors */ $style_color: #474D57; -$hover: #D9EDF7; +$hover: #FFECDB; /* * Link colors */ $link_color: #446e9b; -$link_hover_color: #2FA0BB; +$link_hover_color: darken($link-color, 10%); $btn-border: 1px solid #ccc; diff --git a/app/assets/stylesheets/sections/dashboard.scss b/app/assets/stylesheets/sections/dashboard.scss index 327e7aaa0e9..d181d83e857 100644 --- a/app/assets/stylesheets/sections/dashboard.scss +++ b/app/assets/stylesheets/sections/dashboard.scss @@ -100,14 +100,9 @@ margin-right: 15px; font-size: 20px; margin-bottom: 15px; - border: 1px solid #EEE; - padding: 8px 12px; - border-radius: 50px; - background: #f5f5f5; - text-align: center; i { - color: #BBB; + color: #888; } } diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb index 3c8e7ec73f6..6845fc5e6e6 100644 --- a/app/controllers/projects/branches_controller.rb +++ b/app/controllers/projects/branches_controller.rb @@ -17,9 +17,17 @@ class Projects::BranchesController < Projects::ApplicationController end def create - @branch = CreateBranchService.new.execute(project, params[:branch_name], params[:ref], current_user) - - redirect_to project_tree_path(@project, @branch.name) + result = CreateBranchService.new.execute(project, + params[:branch_name], + params[:ref], + current_user) + if result[:status] == :success + @branch = result[:branch] + redirect_to project_tree_path(@project, @branch.name) + else + @error = result[:message] + render action: 'new' + end end def destroy diff --git a/app/controllers/projects/edit_tree_controller.rb b/app/controllers/projects/edit_tree_controller.rb index ca83b21f429..72a41f771c0 100644 --- a/app/controllers/projects/edit_tree_controller.rb +++ b/app/controllers/projects/edit_tree_controller.rb @@ -31,7 +31,7 @@ class Projects::EditTreeController < Projects::BaseTreeController diffy = Diffy::Diff.new(@blob.data, @content, diff: '-U 3', include_diff_info: true) - @diff = Gitlab::DiffParser.new(diffy.diff.scan(/.*\n/)) + @diff_lines = Gitlab::Diff::Parser.new.parse(diffy.diff.scan(/.*\n/)) render layout: false end diff --git a/app/controllers/projects/labels_controller.rb b/app/controllers/projects/labels_controller.rb index 87d1c942034..6c7bde9c5d5 100644 --- a/app/controllers/projects/labels_controller.rb +++ b/app/controllers/projects/labels_controller.rb @@ -52,7 +52,7 @@ class Projects::LabelsController < Projects::ApplicationController respond_to do |format| format.html { redirect_to project_labels_path(@project), notice: 'Label was removed' } - format.js { render nothing: true } + format.js end end diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb index e03a9f4d66d..86788b9963b 100644 --- a/app/controllers/projects/tags_controller.rb +++ b/app/controllers/projects/tags_controller.rb @@ -13,10 +13,16 @@ class Projects::TagsController < Projects::ApplicationController end def create - @tag = CreateTagService.new.execute(@project, params[:tag_name], - params[:ref], current_user) - - redirect_to project_tags_path(@project) + result = CreateTagService.new.execute(@project, params[:tag_name], + params[:ref], params[:message], + current_user) + if result[:status] == :success + @tag = result[:tag] + redirect_to project_tags_path(@project) + else + @error = result[:message] + render action: 'new' + end end def destroy diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index f23afaf28fa..b3380a6ff23 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -103,7 +103,15 @@ class ProjectsController < ApplicationController ::Projects::DestroyService.new(@project, current_user, {}).execute respond_to do |format| - format.html { redirect_to root_path } + format.html do + flash[:alert] = "Project deleted." + + if request.referer.include?("/admin") + redirect_to admin_projects_path + else + redirect_to projects_dashboard_path + end + end end end diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index a58b24de643..55926a1ed22 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -5,15 +5,23 @@ class SearchController < ApplicationController @project = Project.find_by(id: params[:project_id]) if params[:project_id].present? @group = Group.find_by(id: params[:group_id]) if params[:group_id].present? @scope = params[:scope] + @show_snippets = params[:snippets].eql? 'true' @search_results = if @project return access_denied! unless can?(current_user, :download_code, @project) - unless %w(blobs notes issues merge_requests).include?(@scope) + unless %w(blobs notes issues merge_requests wiki_blobs). + include?(@scope) @scope = 'blobs' end Search::ProjectService.new(@project, current_user, params).execute + elsif @show_snippets + unless %w(snippet_blobs snippet_titles).include?(@scope) + @scope = 'snippet_blobs' + end + + Search::SnippetService.new(current_user, params).execute else unless %w(projects issues merge_requests).include?(@scope) @scope = 'projects' diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index e6d50bea4d1..c2c9301cc17 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -178,6 +178,8 @@ module ApplicationHelper def search_placeholder if @project && @project.persisted? "Search in this project" + elsif @snippet || @snippets || @show_snippets + 'Search snippets' elsif @group && @group.persisted? "Search in this group" else diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index f61aa259154..cab2984a4c4 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -16,38 +16,6 @@ module CommitsHelper commit_person_link(commit, options.merge(source: :committer)) end - def each_diff_line(diff, index) - Gitlab::DiffParser.new(diff.diff.lines.to_a, diff.new_path) - .each do |full_line, type, line_code, line_new, line_old| - yield(full_line, type, line_code, line_new, line_old) - end - end - - def each_diff_line_near(diff, index, expected_line_code) - max_number_of_lines = 16 - - prev_match_line = nil - prev_lines = [] - - each_diff_line(diff, index) do |full_line, type, line_code, line_new, line_old| - line = [full_line, type, line_code, line_new, line_old] - if line_code != expected_line_code - if type == "match" - prev_lines.clear - prev_match_line = line - else - prev_lines.push(line) - prev_lines.shift if prev_lines.length >= max_number_of_lines - end - else - yield(prev_match_line) if !prev_match_line.nil? - prev_lines.each { |ln| yield(ln) } - yield(line) - break - end - end - end - def image_diff_class(diff) if diff.deleted_file "deleted" @@ -63,14 +31,6 @@ module CommitsHelper escape_javascript(render "projects/commits/#{template}", commit: commit, project: project) unless commit.nil? end - def diff_line_content(line) - if line.blank? - " " - else - line - end - end - # Breadcrumb links for a Project and, if applicable, a tree path def commits_breadcrumbs return unless @project && @ref @@ -105,82 +65,6 @@ module CommitsHelper branches.sort.map { |branch| link_to(branch, project_tree_path(project, branch)) }.join(", ").html_safe end - def parallel_diff_lines(project, commit, diff, file) - old_file = project.repository.blob_at(commit.parent_id, diff.old_path) if commit.parent_id - deleted_lines = {} - added_lines = {} - each_diff_line(diff, 0) do |line, type, line_code, line_new, line_old| - if type == "old" - deleted_lines[line_old] = { line_code: line_code, type: type, line: line } - elsif type == "new" - added_lines[line_new] = { line_code: line_code, type: type, line: line } - end - end - max_length = old_file ? [old_file.loc, file.loc].max : file.loc - - offset1 = 0 - offset2 = 0 - old_lines = [] - new_lines = [] - - max_length.times do |line_index| - line_index1 = line_index - offset1 - line_index2 = line_index - offset2 - deleted_line = deleted_lines[line_index1 + 1] - added_line = added_lines[line_index2 + 1] - old_line = old_file.lines[line_index1] if old_file - new_line = file.lines[line_index2] - - if deleted_line && added_line - elsif deleted_line - new_line = nil - offset2 += 1 - elsif added_line - old_line = nil - offset1 += 1 - end - - old_lines[line_index] = DiffLine.new - new_lines[line_index] = DiffLine.new - - # old - if line_index == 0 && diff.new_file - old_lines[line_index].type = :file_created - old_lines[line_index].content = 'File was created' - elsif deleted_line - old_lines[line_index].type = :deleted - old_lines[line_index].content = old_line - old_lines[line_index].num = line_index1 + 1 - old_lines[line_index].code = deleted_line[:line_code] - elsif old_line - old_lines[line_index].type = :no_change - old_lines[line_index].content = old_line - old_lines[line_index].num = line_index1 + 1 - else - old_lines[line_index].type = :added - end - - # new - if line_index == 0 && diff.deleted_file - new_lines[line_index].type = :file_deleted - new_lines[line_index].content = "File was deleted" - elsif added_line - new_lines[line_index].type = :added - new_lines[line_index].num = line_index2 + 1 - new_lines[line_index].content = new_line - new_lines[line_index].code = added_line[:line_code] - elsif new_line - new_lines[line_index].type = :no_change - new_lines[line_index].num = line_index2 + 1 - new_lines[line_index].content = new_line - else - new_lines[line_index].type = :deleted - end - end - - return old_lines, new_lines - end - def link_to_browse_code(project, commit) if current_controller?(:projects, :commits) if @repo.blob_at(commit.id, @path) @@ -229,14 +113,6 @@ module CommitsHelper end end - def diff_file_mode_changed?(diff) - diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode - end - - def unfold_bottom_class(bottom) - (bottom) ? 'js-unfold-bottom' : '' - end - def view_file_btn(commit_sha, diff, project) link_to project_blob_path(project, tree_join(commit_sha, diff.new_path)), class: 'btn btn-small view-file js-view-file' do diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb index ee4d4fbdff5..afe7447d4e2 100644 --- a/app/helpers/diff_helper.rb +++ b/app/helpers/diff_helper.rb @@ -1,14 +1,20 @@ module DiffHelper - def safe_diff_files(diffs) + def allowed_diff_size if diff_hard_limit_enabled? - diffs.first(Commit::DIFF_HARD_LIMIT_FILES) + Commit::DIFF_HARD_LIMIT_FILES else - diffs.first(Commit::DIFF_SAFE_FILES) + Commit::DIFF_SAFE_FILES + end + end + + def safe_diff_files(diffs) + diffs.first(allowed_diff_size).map do |diff| + Gitlab::Diff::File.new(diff) end end - def show_diff_size_warninig?(diffs) - safe_diff_files(diffs).size < diffs.size + def show_diff_size_warning?(diffs) + diffs.size > allowed_diff_size end def diff_hard_limit_enabled? @@ -19,4 +25,76 @@ module DiffHelper false end end + + def generate_line_code(file_path, line) + Gitlab::Diff::LineCode.generate(file_path, line.new_pos, line.old_pos) + end + + def parallel_diff(diff_file, index) + lines = [] + skip_next = false + + # Building array of lines + # + # [left_type, left_line_number, left_line_content, line_code, right_line_type, right_line_number, right_line_content] + # + diff_file.diff_lines.each do |line| + + full_line = line.text + type = line.type + line_code = generate_line_code(diff_file.file_path, line) + line_new = line.new_pos + line_old = line.old_pos + + next_line = diff_file.next_line(line.index) + + if next_line + next_type = next_line.type + next_line = next_line.text + end + + line = [type, line_old, full_line, line_code, next_type, line_new] + if type == 'match' || type.nil? + # line in the right panel is the same as in the left one + line = [type, line_old, full_line, line_code, type, line_new, full_line] + lines.push(line) + elsif type == 'old' + if next_type == 'new' + # Left side has text removed, right side has text added + line.push(next_line) + lines.push(line) + skip_next = true + elsif next_type == 'old' || next_type.nil? + # Left side has text removed, right side doesn't have any change + line.pop # remove the newline + line.push(nil) # no line number on the right panel + line.push(" ") # empty line on the right panel + lines.push(line) + end + elsif type == 'new' + if skip_next + # Change has been already included in previous line so no need to do it again + skip_next = false + next + else + # Change is only on the right side, left side has no change + line = [nil, nil, " ", line_code, type, line_new, full_line] + lines.push(line) + end + end + end + lines + end + + def unfold_bottom_class(bottom) + (bottom) ? 'js-unfold-bottom' : '' + end + + def diff_line_content(line) + if line.blank? + " " + else + line + end + end end diff --git a/app/models/network/graph.rb b/app/models/network/graph.rb index 424819f3501..9c95470beb1 100644 --- a/app/models/network/graph.rb +++ b/app/models/network/graph.rb @@ -178,12 +178,6 @@ module Network space = find_free_space(time_range, 2, space_base) leaves.each do |l| l.spaces << space - # Also add space to parent - l.parents(@map).each do |parent| - if 0 < parent.space && parent.space < space - parent.spaces << space - end - end end # and mark it as reserved diff --git a/app/models/note.rb b/app/models/note.rb index 0fa1a7ab615..fa5fdea4eb0 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -117,6 +117,25 @@ class Note < ActiveRecord::Base }) end + def create_new_commits_note(noteable, project, author, commits) + commits_text = ActionController::Base.helpers.pluralize(commits.size, 'new commit') + body = "Added #{commits_text}:\n\n" + + commits.each do |commit| + message = "* #{commit.short_id} - #{commit.title}" + body << message + body << "\n" + end + + create( + noteable: noteable, + project: project, + author: author, + note: body, + system: true + ) + end + def discussions_from_notes(notes) discussion_ids = [] discussions = [] @@ -190,9 +209,10 @@ class Note < ActiveRecord::Base noteable.diffs.each do |mr_diff| next unless mr_diff.new_path == self.diff.new_path - Gitlab::DiffParser.new(mr_diff.diff.lines.to_a, mr_diff.new_path). - each do |full_line, type, line_code, line_new, line_old| - if full_line == diff_line + lines = Gitlab::Diff::Parser.new.parse(mr_diff.diff.lines.to_a) + + lines.each do |line| + if line.text == diff_line return true end end @@ -213,6 +233,14 @@ class Note < ActiveRecord::Base diff.new_path if diff end + def file_path + if diff.new_path.present? + diff.new_path + elsif diff.old_path.present? + diff.old_path + end + end + def diff_old_line line_code.split('_')[1].to_i end @@ -221,19 +249,49 @@ class Note < ActiveRecord::Base line_code.split('_')[2].to_i end + def generate_line_code(line) + Gitlab::Diff::LineCode.generate(file_path, line.new_pos, line.old_pos) + end + def diff_line return @diff_line if @diff_line if diff - Gitlab::DiffParser.new(diff.diff.lines.to_a, diff.new_path) - .each do |full_line, type, line_code, line_new, line_old| - @diff_line = full_line if line_code == self.line_code + diff_lines.each do |line| + if generate_line_code(line) == self.line_code + @diff_line = line.text end + end end @diff_line end + def truncated_diff_lines + max_number_of_lines = 16 + prev_match_line = nil + prev_lines = [] + + diff_lines.each do |line| + if generate_line_code(line) != self.line_code + if line.type == "match" + prev_lines.clear + prev_match_line = line + else + prev_lines.push(line) + prev_lines.shift if prev_lines.length >= max_number_of_lines + end + else + prev_lines << line + return prev_lines + end + end + end + + def diff_lines + @diff_lines ||= Gitlab::Diff::Parser.new.parse(diff.diff.lines.to_a) + end + def discussion_id @discussion_id ||= Note.build_discussion_id(noteable_type, noteable_id || commit_id, line_code) end diff --git a/app/models/project.rb b/app/models/project.rb index 5cc35f20cad..114e40983f8 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -70,7 +70,7 @@ class Project < ActiveRecord::Base has_many :merge_requests, dependent: :destroy, foreign_key: "target_project_id" # Merge requests from source project should be kept when source project was removed has_many :fork_merge_requests, foreign_key: "source_project_id", class_name: MergeRequest - has_many :issues, -> { order "state DESC, created_at DESC" }, dependent: :destroy + has_many :issues, -> { order 'issues.state DESC, issues.created_at DESC' }, dependent: :destroy has_many :labels, dependent: :destroy has_many :services, dependent: :destroy has_many :events, dependent: :destroy @@ -400,18 +400,35 @@ class Project < ActiveRecord::Base def update_merge_requests(oldrev, newrev, ref, user) return true unless ref =~ /heads/ branch_name = ref.gsub("refs/heads/", "") - c_ids = self.repository.commits_between(oldrev, newrev).map(&:id) + commits = self.repository.commits_between(oldrev, newrev) + c_ids = commits.map(&:id) # Close merge requests mrs = self.merge_requests.opened.where(target_branch: branch_name).to_a mrs = mrs.select(&:last_commit).select { |mr| c_ids.include?(mr.last_commit.id) } - mrs.each { |merge_request| MergeRequests::MergeService.new.execute(merge_request, user, nil) } + + mrs.uniq.each do |merge_request| + MergeRequests::MergeService.new.execute(merge_request, user, nil) + end # Update code for merge requests into project between project branches mrs = self.merge_requests.opened.by_branch(branch_name).to_a # Update code for merge requests between project and project fork mrs += self.fork_merge_requests.opened.by_branch(branch_name).to_a - mrs.each { |merge_request| merge_request.reload_code; merge_request.mark_as_unchecked } + + mrs.uniq.each do |merge_request| + merge_request.reload_code + merge_request.mark_as_unchecked + end + + # Add comment about pushing new commits to merge requests + mrs = self.merge_requests.opened.where(source_branch: branch_name).to_a + mrs += self.fork_merge_requests.opened.where(source_branch: branch_name).to_a + + mrs.uniq.each do |merge_request| + Note.create_new_commits_note(merge_request, merge_request.project, + user, commits) + end true end diff --git a/app/models/project_services/assembla_service.rb b/app/models/project_services/assembla_service.rb index 9a8cbb32ac1..3421a0330aa 100644 --- a/app/models/project_services/assembla_service.rb +++ b/app/models/project_services/assembla_service.rb @@ -5,21 +5,17 @@ # id :integer not null, primary key # type :string(255) # title :string(255) -# token :string(255) # project_id :integer not null # created_at :datetime # updated_at :datetime # active :boolean default(FALSE), not null -# project_url :string(255) -# subdomain :string(255) -# room :string(255) -# recipients :text -# api_key :string(255) +# properties :text # class AssemblaService < Service include HTTParty + prop_accessor :token, :subdomain validates :token, presence: true, if: :activated? def title diff --git a/app/models/project_services/campfire_service.rb b/app/models/project_services/campfire_service.rb index 83e1bac1ef2..2d8950db491 100644 --- a/app/models/project_services/campfire_service.rb +++ b/app/models/project_services/campfire_service.rb @@ -5,19 +5,15 @@ # id :integer not null, primary key # type :string(255) # title :string(255) -# token :string(255) # project_id :integer not null # created_at :datetime # updated_at :datetime # active :boolean default(FALSE), not null -# project_url :string(255) -# subdomain :string(255) -# room :string(255) -# recipients :text -# api_key :string(255) +# properties :text # class CampfireService < Service + prop_accessor :token, :subdomain, :room validates :token, presence: true, if: :activated? def title diff --git a/app/models/project_services/ci_service.rb b/app/models/project_services/ci_service.rb index 1a107f92c93..829f495abc6 100644 --- a/app/models/project_services/ci_service.rb +++ b/app/models/project_services/ci_service.rb @@ -5,16 +5,11 @@ # id :integer not null, primary key # type :string(255) # title :string(255) -# token :string(255) # project_id :integer not null # created_at :datetime # updated_at :datetime # active :boolean default(FALSE), not null -# project_url :string(255) -# subdomain :string(255) -# room :string(255) -# recipients :text -# api_key :string(255) +# properties :text # # Base class for CI services diff --git a/app/models/project_services/emails_on_push_service.rb b/app/models/project_services/emails_on_push_service.rb index be5bab4ec32..5c4537cfca5 100644 --- a/app/models/project_services/emails_on_push_service.rb +++ b/app/models/project_services/emails_on_push_service.rb @@ -5,19 +5,15 @@ # id :integer not null, primary key # type :string(255) # title :string(255) -# token :string(255) # project_id :integer not null # created_at :datetime # updated_at :datetime # active :boolean default(FALSE), not null -# project_url :string(255) -# subdomain :string(255) -# room :string(255) -# recipients :text -# api_key :string(255) +# properties :text # class EmailsOnPushService < Service + prop_accessor :recipients validates :recipients, presence: true, if: :activated? def title diff --git a/app/models/project_services/flowdock_service.rb b/app/models/project_services/flowdock_service.rb index 6cdd04a8648..4d11b00c192 100644 --- a/app/models/project_services/flowdock_service.rb +++ b/app/models/project_services/flowdock_service.rb @@ -5,21 +5,17 @@ # id :integer not null, primary key # type :string(255) # title :string(255) -# token :string(255) # project_id :integer not null # created_at :datetime # updated_at :datetime # active :boolean default(FALSE), not null -# project_url :string(255) -# subdomain :string(255) -# room :string(255) -# recipients :text -# api_key :string(255) +# properties :text # require "flowdock-git-hook" class FlowdockService < Service + prop_accessor :token validates :token, presence: true, if: :activated? def title diff --git a/app/models/project_services/gemnasium_service.rb b/app/models/project_services/gemnasium_service.rb index b363d7f57d2..7b6c87e4cec 100644 --- a/app/models/project_services/gemnasium_service.rb +++ b/app/models/project_services/gemnasium_service.rb @@ -5,21 +5,17 @@ # id :integer not null, primary key # type :string(255) # title :string(255) -# token :string(255) # project_id :integer not null # created_at :datetime # updated_at :datetime # active :boolean default(FALSE), not null -# project_url :string(255) -# subdomain :string(255) -# room :string(255) -# recipients :text -# api_key :string(255) +# properties :text # require "gemnasium/gitlab_service" class GemnasiumService < Service + prop_accessor :token, :api_key validates :token, :api_key, presence: true, if: :activated? def title diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb index 58ddce45288..0f327e75289 100644 --- a/app/models/project_services/gitlab_ci_service.rb +++ b/app/models/project_services/gitlab_ci_service.rb @@ -5,19 +5,15 @@ # id :integer not null, primary key # type :string(255) # title :string(255) -# token :string(255) # project_id :integer not null # created_at :datetime # updated_at :datetime # active :boolean default(FALSE), not null -# project_url :string(255) -# subdomain :string(255) -# room :string(255) -# recipients :text -# api_key :string(255) +# property :text # class GitlabCiService < CiService + prop_accessor :project_url, :token validates :project_url, presence: true, if: :activated? validates :token, presence: true, if: :activated? diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb index 256debffc51..3a1ba168e6a 100644 --- a/app/models/project_services/hipchat_service.rb +++ b/app/models/project_services/hipchat_service.rb @@ -5,21 +5,17 @@ # id :integer not null, primary key # type :string(255) # title :string(255) -# token :string(255) # project_id :integer not null # created_at :datetime # updated_at :datetime # active :boolean default(FALSE), not null -# project_url :string(255) -# subdomain :string(255) -# room :string(255) -# recipients :text -# api_key :string(255) +# properties :text # class HipchatService < Service MAX_COMMITS = 3 + prop_accessor :token, :room validates :token, presence: true, if: :activated? def title diff --git a/app/models/project_services/pivotaltracker_service.rb b/app/models/project_services/pivotaltracker_service.rb index aa2bcc5def7..3aa928b92a0 100644 --- a/app/models/project_services/pivotaltracker_service.rb +++ b/app/models/project_services/pivotaltracker_service.rb @@ -5,21 +5,17 @@ # id :integer not null, primary key # type :string(255) # title :string(255) -# token :string(255) # project_id :integer not null # created_at :datetime # updated_at :datetime # active :boolean default(FALSE), not null -# project_url :string(255) -# subdomain :string(255) -# room :string(255) -# recipients :text -# api_key :string(255) +# properties :text # class PivotaltrackerService < Service include HTTParty + prop_accessor :token validates :token, presence: true, if: :activated? def title diff --git a/app/models/project_services/slack_service.rb b/app/models/project_services/slack_service.rb index 7e54188abf7..4bda93f6006 100644 --- a/app/models/project_services/slack_service.rb +++ b/app/models/project_services/slack_service.rb @@ -5,19 +5,15 @@ # id :integer not null, primary key # type :string(255) # title :string(255) -# token :string(255) # project_id :integer not null # created_at :datetime # updated_at :datetime # active :boolean default(FALSE), not null -# project_url :string(255) -# subdomain :string(255) -# room :string(255) -# recipients :text -# api_key :string(255) +# properties :text # class SlackService < Service + prop_accessor :room, :subdomain, :token validates :room, presence: true, if: :activated? validates :subdomain, presence: true, if: :activated? validates :token, presence: true, if: :activated? diff --git a/app/models/repository.rb b/app/models/repository.rb index e970c449a73..9dd8603621f 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -64,10 +64,10 @@ class Repository gitlab_shell.add_branch(path_with_namespace, branch_name, ref) end - def add_tag(tag_name, ref) + def add_tag(tag_name, ref, message = nil) Rails.cache.delete(cache_key(:tag_names)) - gitlab_shell.add_tag(path_with_namespace, tag_name, ref) + gitlab_shell.add_tag(path_with_namespace, tag_name, ref, message) end def rm_branch(branch_name) diff --git a/app/models/service.rb b/app/models/service.rb index 0dc6d514b46..edfb31cbe08 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -5,22 +5,19 @@ # id :integer not null, primary key # type :string(255) # title :string(255) -# token :string(255) # project_id :integer not null # created_at :datetime # updated_at :datetime # active :boolean default(FALSE), not null -# project_url :string(255) -# subdomain :string(255) -# room :string(255) -# recipients :text -# api_key :string(255) -# +# properties :text # To add new service you should build a class inherited from Service # and implement a set of methods class Service < ActiveRecord::Base + serialize :properties, JSON + default_value_for :active, false + default_value_for :properties, {} belongs_to :project has_one :service_hook @@ -63,4 +60,20 @@ class Service < ActiveRecord::Base def can_test? !project.empty_repo? end + + # Provide convenient accessor methods + # for each serialized property. + def self.prop_accessor(*args) + args.each do |arg| + class_eval %{ + def #{arg} + properties['#{arg}'] + end + + def #{arg}=(value) + self.properties['#{arg}'] = value + end + } + end + end end diff --git a/app/models/snippet.rb b/app/models/snippet.rb index 2c38e7939bd..80c1af8f337 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -65,4 +65,18 @@ class Snippet < ActiveRecord::Base def expired? expires_at && expires_at < Time.current end + + class << self + def search(query) + where('(title LIKE :query OR file_name LIKE :query)', query: "%#{query}%") + end + + def search_code(query) + where('(content LIKE :query)', query: "%#{query}%") + end + + def accessible_to(user) + where('private = ? OR author_id = ?', false, user) + end + end end diff --git a/app/services/create_branch_service.rb b/app/services/create_branch_service.rb index 98beeee8354..79b8239602e 100644 --- a/app/services/create_branch_service.rb +++ b/app/services/create_branch_service.rb @@ -1,13 +1,38 @@ class CreateBranchService def execute(project, branch_name, ref, current_user) + valid_branch = Gitlab::GitRefValidator.validate(branch_name) + if valid_branch == false + return error('Branch name invalid') + end + repository = project.repository + existing_branch = repository.find_branch(branch_name) + if existing_branch + return error('Branch already exists') + end + repository.add_branch(branch_name, ref) new_branch = repository.find_branch(branch_name) if new_branch Event.create_ref_event(project, current_user, new_branch, 'add') + return success(new_branch) + else + return error('Invalid reference name') end + end + + def error(message) + { + message: message, + status: :error + } + end - new_branch + def success(branch) + { + branch: branch, + status: :success + } end end diff --git a/app/services/create_tag_service.rb b/app/services/create_tag_service.rb index 97766677405..3716abd4b2b 100644 --- a/app/services/create_tag_service.rb +++ b/app/services/create_tag_service.rb @@ -1,13 +1,42 @@ class CreateTagService - def execute(project, tag_name, ref, current_user) + def execute(project, tag_name, ref, message, current_user) + valid_tag = Gitlab::GitRefValidator.validate(tag_name) + if valid_tag == false + return error('Tag name invalid') + end + repository = project.repository - repository.add_tag(tag_name, ref) + existing_tag = repository.find_tag(tag_name) + if existing_tag + return error('Tag already exists') + end + + if message + message.gsub!(/^\s+|\s+$/, '') + end + + repository.add_tag(tag_name, ref, message) new_tag = repository.find_tag(tag_name) if new_tag Event.create_ref_event(project, current_user, new_tag, 'add', 'refs/tags') + return success(new_tag) + else + return error('Invalid reference name') end + end + + def error(message) + { + message: message, + status: :error + } + end - new_tag + def success(branch) + { + tag: branch, + status: :success + } end end diff --git a/app/services/delete_branch_service.rb b/app/services/delete_branch_service.rb index ce2d8093dff..a94dabcdfc0 100644 --- a/app/services/delete_branch_service.rb +++ b/app/services/delete_branch_service.rb @@ -5,21 +5,21 @@ class DeleteBranchService # No such branch unless branch - return error('No such branch') + return error('No such branch', 404) end if branch_name == repository.root_ref - return error('Cannot remove HEAD branch') + return error('Cannot remove HEAD branch', 405) end # Dont allow remove of protected branch if project.protected_branch?(branch_name) - return error('Protected branch cant be removed') + return error('Protected branch cant be removed', 405) end # Dont allow user to remove branch if he is not allowed to push unless current_user.can?(:push_code, project) - return error('You dont have push access to repo') + return error('You dont have push access to repo', 405) end if repository.rm_branch(branch_name) @@ -30,9 +30,10 @@ class DeleteBranchService end end - def error(message) + def error(message, return_code = 400) { message: message, + return_code: return_code, state: :error } end diff --git a/app/services/search/snippet_service.rb b/app/services/search/snippet_service.rb new file mode 100644 index 00000000000..8ca0877321d --- /dev/null +++ b/app/services/search/snippet_service.rb @@ -0,0 +1,14 @@ +module Search + class SnippetService + attr_accessor :current_user, :params + + def initialize(user, params) + @current_user, @params = user, params.dup + end + + def execute + snippet_ids = Snippet.accessible_to(current_user).pluck(:id) + Gitlab::SnippetSearchResults.new(snippet_ids, params[:search]) + end + end +end diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml index 3c30ccd78b3..f60d40b5334 100644 --- a/app/views/admin/users/show.html.haml +++ b/app/views/admin/users/show.html.haml @@ -71,6 +71,14 @@ No %li + %span.light Current sign-in at: + %strong + - if @user.current_sign_in_at + = @user.current_sign_in_at.stamp("Nov 12, 2031") + - else + never + + %li %span.light Last sign-in at: %strong - if @user.last_sign_in_at diff --git a/app/views/explore/projects/_project.html.haml b/app/views/explore/projects/_project.html.haml index 0b4be2ef5cd..fd5aacbfdb4 100644 --- a/app/views/explore/projects/_project.html.haml +++ b/app/views/explore/projects/_project.html.haml @@ -1,15 +1,14 @@ %li - .project-access-icon - = visibility_level_icon(project.visibility_level) + %h4.project-title + .project-access-icon + = visibility_level_icon(project.visibility_level) + = link_to project.name_with_namespace, project - .project-description - %h4.project-title - = link_to project.name_with_namespace, project - - - if current_page?(starred_explore_projects_path) - %strong.pull-right - = pluralize project.star_count, 'star' + - if current_page?(starred_explore_projects_path) + %strong.pull-right + = pluralize project.star_count, 'star' + .project-info - if project.description.present? %p.project-description.str-truncated = project.description diff --git a/app/views/help/_shortcuts.html.haml b/app/views/help/_shortcuts.html.haml index 4301a6eafc1..467f003b333 100644 --- a/app/views/help/_shortcuts.html.haml +++ b/app/views/help/_shortcuts.html.haml @@ -78,9 +78,9 @@ %tr %td.shortcut .key g - .key a + .key p %td - Go to the activity feed + Go to the project's activity feed %tr %td.shortcut .key g diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml index f485aee1e1a..5ab82122ad7 100644 --- a/app/views/layouts/_search.html.haml +++ b/app/views/layouts/_search.html.haml @@ -5,6 +5,8 @@ - if @project && @project.persisted? = hidden_field_tag :project_id, @project.id = hidden_field_tag :search_code, true + - if @snippet || @snippets + = hidden_field_tag :snippets, true = hidden_field_tag :repository_ref, @ref = submit_tag 'Go' if ENV['RAILS_ENV'] == 'test' .search-autocomplete-opts.hide{:'data-autocomplete-path' => search_autocomplete_path, :'data-autocomplete-project-id' => @project.try(:id), :'data-autocomplete-project-ref' => @ref } diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml index aadbb31dc96..6cb2a82bac8 100644 --- a/app/views/layouts/nav/_project.html.haml +++ b/app/views/layouts/nav/_project.html.haml @@ -1,6 +1,6 @@ %ul.project-navigation = nav_link(path: 'projects#show', html_options: {class: "home"}) do - = link_to project_path(@project), title: 'Project', class: 'shortcuts-activity' do + = link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do Project - if project_nav_tab? :files = nav_link(controller: %w(tree blob blame edit_tree new_tree)) do diff --git a/app/views/notify/_reassigned_issuable_email.html.haml b/app/views/notify/_reassigned_issuable_email.html.haml new file mode 100644 index 00000000000..56d81b2ed2e --- /dev/null +++ b/app/views/notify/_reassigned_issuable_email.html.haml @@ -0,0 +1,10 @@ +%p + Assignee changed + - if @previous_assignee + from + %strong #{@previous_assignee.name} + to + - if issuable.assignee_id + %strong #{issuable.assignee_name} + - else + %strong Unassigned diff --git a/app/views/notify/_reassigned_issuable_email.text.erb b/app/views/notify/_reassigned_issuable_email.text.erb new file mode 100644 index 00000000000..817d030c362 --- /dev/null +++ b/app/views/notify/_reassigned_issuable_email.text.erb @@ -0,0 +1,6 @@ +Reassigned <%= issuable.class.model_name.human.titleize %> <%= issuable.iid %> + +<%= url_for([issuable.project, issuable, {only_path: false}]) %> + +Assignee changed <%= "from #{@previous_assignee.name}" if @previous_assignee -%> + to <%= "#{issuable.assignee_id ? issuable.assignee_name : 'Unassigned'}" %> diff --git a/app/views/notify/reassigned_issue_email.html.haml b/app/views/notify/reassigned_issue_email.html.haml index f1458df5c7b..498ba8b8365 100644 --- a/app/views/notify/reassigned_issue_email.html.haml +++ b/app/views/notify/reassigned_issue_email.html.haml @@ -1,11 +1 @@ -%p - Assignee changed - - if @previous_assignee - from - %strong #{@previous_assignee.name} - to - - if @issue.assignee_id - %strong #{@issue.assignee_name} - - else - %strong Unassigned - += render 'reassigned_issuable_email', issuable: @issue diff --git a/app/views/notify/reassigned_issue_email.text.erb b/app/views/notify/reassigned_issue_email.text.erb index 4becac2749c..710253be984 100644 --- a/app/views/notify/reassigned_issue_email.text.erb +++ b/app/views/notify/reassigned_issue_email.text.erb @@ -1,5 +1 @@ -Reassigned Issue <%= @issue.iid %> - -<%= url_for(project_issue_url(@issue.project, @issue)) %> - -Assignee changed <%= "from #{@previous_assignee.name}" if @previous_assignee %> to <%= "#{@issue.assignee_id ? @issue.assignee_name : 'Unassigned'}" %> +<%= render 'reassigned_issuable_email', issuable: @issue %> diff --git a/app/views/notify/reassigned_merge_request_email.html.haml b/app/views/notify/reassigned_merge_request_email.html.haml index 00aee6bc952..2a650130f59 100644 --- a/app/views/notify/reassigned_merge_request_email.html.haml +++ b/app/views/notify/reassigned_merge_request_email.html.haml @@ -1,7 +1 @@ -%p - Assignee changed - - if @previous_assignee - from - %strong #{@previous_assignee.name} - to - %strong #{@merge_request.assignee_name} += render 'reassigned_issuable_email', issuable: @merge_request diff --git a/app/views/notify/reassigned_merge_request_email.text.erb b/app/views/notify/reassigned_merge_request_email.text.erb index 87a7847e06d..b5b4f1ff99a 100644 --- a/app/views/notify/reassigned_merge_request_email.text.erb +++ b/app/views/notify/reassigned_merge_request_email.text.erb @@ -1,7 +1 @@ -Reassigned Merge Request #<%= @merge_request.iid %> - -<%= url_for(project_merge_request_url(@merge_request.target_project, @merge_request)) %> - - -Assignee changed <%= "from #{@previous_assignee.name}" if @previous_assignee %> to <%= @merge_request.assignee_name %> - +<%= render 'reassigned_issuable_email', issuable: @merge_request %> diff --git a/app/views/projects/_issuable_form.html.haml b/app/views/projects/_issuable_form.html.haml new file mode 100644 index 00000000000..f7c4673b52d --- /dev/null +++ b/app/views/projects/_issuable_form.html.haml @@ -0,0 +1,39 @@ +.form-group + = f.label :title, class: 'control-label' do + %strong= 'Title *' + .col-sm-10 + = f.text_field :title, maxlength: 255, autofocus: true, + class: 'form-control pad js-gfm-input', required: true +.form-group + = f.label :description, 'Description', class: 'control-label' + .col-sm-10 + = f.text_area :description, rows: 14, + class: 'form-control js-gfm-input markdown-area' + .col-sm-12.hint + .pull-left + Parsed with + #{link_to 'GitLab Flavored Markdown', help_page_path('markdown', 'markdown'), target: '_blank'}. + .pull-right + Attach images (JPG, PNG, GIF) by dragging & dropping + or #{link_to 'selecting them', '#', class: 'markdown-selector' }. + .clearfix + .error-alert +%hr +.form-group + .issue-assignee + = f.label :assignee_id, class: 'control-label' do + %i.icon-user + Assign to + .col-sm-10 + = project_users_select_tag("#{issuable.class.model_name.param_key}[assignee_id]", + placeholder: 'Select a user', class: 'custom-form-control', + selected: issuable.assignee_id) + + = link_to 'Assign to me', '#', class: 'btn assign-to-me-link' +.form-group + .issue-milestone + = f.label :milestone_id, class: 'control-label' do + %i.icon-time + Milestone + .col-sm-10= f.select(:milestone_id, milestone_options(issuable), + { include_blank: 'Select milestone' }, { class: 'select2' }) diff --git a/app/views/projects/blob/_actions.html.haml b/app/views/projects/blob/_actions.html.haml index cabef3c19fe..8587dc4bc6d 100644 --- a/app/views/projects/blob/_actions.html.haml +++ b/app/views/projects/blob/_actions.html.haml @@ -13,6 +13,9 @@ - else = link_to "blame", project_blame_path(@project, @id), class: "btn btn-small" unless @blob.empty? = link_to "history", project_commits_path(@project, @id), class: "btn btn-small" + - if @ref != @commit.sha + = link_to 'permalink', project_blob_path(@project, + tree_join(@commit.sha, @path)), class: 'btn btn-small' - if allowed_tree_edit? = link_to '#modal-remove-blob', class: "remove-blob btn btn-small btn-remove", "data-toggle" => "modal" do diff --git a/app/views/projects/blob/diff.html.haml b/app/views/projects/blob/diff.html.haml index cfb91d6568a..5c79d0ef11f 100644 --- a/app/views/projects/blob/diff.html.haml +++ b/app/views/projects/blob/diff.html.haml @@ -1,7 +1,7 @@ - if @lines.present? - if @form.unfold? && @form.since != 1 && !@form.bottom? %tr.line_holder{ id: @form.since } - = render "projects/commits/diffs/match_line", {line: @match_line, + = render "projects/diffs/match_line", {line: @match_line, line_old: @form.since, line_new: @form.since, bottom: false} - @lines.each_with_index do |line, index| @@ -15,5 +15,5 @@ - if @form.unfold? && @form.bottom? && @form.to < @blob.loc %tr.line_holder{ id: @form.to } - = render "projects/commits/diffs/match_line", {line: @match_line, + = render "projects/diffs/match_line", {line: @match_line, line_old: @form.to, line_new: @form.to, bottom: true} diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml index 54a7b934dd7..08a537e0541 100644 --- a/app/views/projects/branches/_branch.html.haml +++ b/app/views/projects/branches/_branch.html.haml @@ -2,7 +2,7 @@ %li(class="js-branch-#{branch.name}") %h4 = link_to project_tree_path(@project, branch.name) do - %strong= truncate(branch.name, length: 60) + %strong.str-truncated= branch.name - if branch.name == @repository.root_ref %span.label.label-info default - if @project.protected_branch? branch.name diff --git a/app/views/projects/branches/new.html.haml b/app/views/projects/branches/new.html.haml index 5da2ede2937..3f202f7ea6b 100644 --- a/app/views/projects/branches/new.html.haml +++ b/app/views/projects/branches/new.html.haml @@ -1,3 +1,7 @@ +- if @error + .alert.alert-danger + %button{ type: "button", class: "close", "data-dismiss" => "alert"} × + = @error %h3.page-title %i.icon-code-fork New branch @@ -5,11 +9,11 @@ .form-group = label_tag :branch_name, 'Name for new branch', class: 'control-label' .col-sm-10 - = text_field_tag :branch_name, nil, placeholder: 'enter new branch name', required: true, tabindex: 1, class: 'form-control' + = text_field_tag :branch_name, params[:branch_name], placeholder: 'enter new branch name', required: true, tabindex: 1, class: 'form-control' .form-group = label_tag :ref, 'Create from', class: 'control-label' .col-sm-10 - = text_field_tag :ref, nil, placeholder: 'existing branch name, tag or commit SHA', required: true, tabindex: 2, class: 'form-control' + = text_field_tag :ref, params[:ref], placeholder: 'existing branch name, tag or commit SHA', required: true, tabindex: 2, class: 'form-control' .form-actions = submit_tag 'Create branch', class: 'btn btn-create', tabindex: 3 = link_to 'Cancel', project_branches_path(@project), class: 'btn btn-cancel' diff --git a/app/views/projects/commit/show.html.haml b/app/views/projects/commit/show.html.haml index 0a15aef6cb7..fc721067ed4 100644 --- a/app/views/projects/commit/show.html.haml +++ b/app/views/projects/commit/show.html.haml @@ -1,3 +1,3 @@ = render "commit_box" -= render "projects/commits/diffs", diffs: @diffs, project: @project += render "projects/diffs/diffs", diffs: @diffs, project: @project = render "projects/notes/notes_with_form" diff --git a/app/views/projects/commits/_diff_file.html.haml b/app/views/projects/commits/_diff_file.html.haml deleted file mode 100644 index 31208a227ce..00000000000 --- a/app/views/projects/commits/_diff_file.html.haml +++ /dev/null @@ -1,48 +0,0 @@ -- file = project.repository.blob_for_diff(@commit, diff) -- return unless file -- blob_diff_path = diff_project_blob_path(project, - tree_join(@commit.id, diff.new_path)) -.diff-file{id: "diff-#{i}", data: {blob_diff_path: blob_diff_path }} - .diff-header{id: "file-path-#{hexdigest(diff.new_path || diff.old_path)}"} - - if diff.deleted_file - %span= diff.old_path - - .diff-btn-group - - if @commit.parent_ids.present? - = view_file_btn(@commit.parent_id, diff, project) - - else - %span= diff.new_path - - if diff_file_mode_changed?(diff) - %span.file-mode= "#{diff.a_mode} → #{diff.b_mode}" - - .diff-btn-group - %label - = check_box_tag nil, 1, false, class: "js-toggle-diff-line-wrap" - Wrap text - - = link_to "#", class: "js-toggle-diff-comments btn btn-small" do - %i.icon-chevron-down - Diff comments - - - - if @merge_request && @merge_request.source_project - = link_to project_edit_tree_path(@merge_request.source_project, tree_join(@merge_request.source_branch, diff.new_path), from_merge_request_id: @merge_request.id), { class: 'btn btn-small' } do - Edit - - - = view_file_btn(@commit.id, diff, project) - - .diff-content - -# Skipp all non non-supported blobs - - return unless file.respond_to?('text?') - - if file.text? - - if params[:view] == 'parallel' - = render "projects/commits/parallel_view", diff: diff, project: project, file: file, index: i - - else - = render "projects/commits/text_file", diff: diff, index: i - - elsif file.image? - - old_file = project.repository.prev_blob_for_diff(@commit, diff) - = render "projects/commits/image", diff: diff, old_file: old_file, file: file, index: i - - else - .nothing-here-block No preview for this file type - diff --git a/app/views/projects/commits/_parallel_view.html.haml b/app/views/projects/commits/_parallel_view.html.haml deleted file mode 100644 index 80f5be98f2f..00000000000 --- a/app/views/projects/commits/_parallel_view.html.haml +++ /dev/null @@ -1,38 +0,0 @@ -/ Side-by-side diff view -- old_lines, new_lines = parallel_diff_lines(project, @commit, diff, file) -- num_lines = old_lines.length - -%div.text-file - %table - - num_lines.times do |index| - - new_line = new_lines[index] - - old_line = old_lines[index] - %tr.line_holder.parallel - -# For old line - - if old_line.type == :file_created - %td.old_line= old_line.num - %td.line_content.parallel= "File was created" - - elsif old_line.type == :deleted - %td.old_line.old= old_line.num - %td.line_content{class: "parallel noteable_line old #{old_line.code}", "line_code" => old_line.code}= old_line.content - - else old_line.type == :no_change - %td.old_line= old_line.num - %td.line_content.parallel= old_line.content - - -# For new line - - if new_line.type == :file_deleted - %td.new_line= new_line.num - %td.line_content.parallel= "File was deleted" - - elsif new_line.type == :added - %td.new_line.new= new_line.num - %td.line_content{class: "parallel noteable_line new #{new_line.code}", "line_code" => new_line.code}= new_line.content - - else new_line.type == :no_change - %td.new_line= new_line.num - %td.line_content.parallel= new_line.content - - - if @reply_allowed - - comments1 = @line_notes.select { |n| n.line_code == old_line.code }.sort_by(&:created_at) - - comments2 = @line_notes.select { |n| n.line_code == new_line.code }.sort_by(&:created_at) - - unless comments1.empty? and comments2.empty? - = render "projects/notes/diff_notes_with_reply_parallel", notes1: comments1, notes2: comments2 - diff --git a/app/views/projects/compare/show.html.haml b/app/views/projects/compare/show.html.haml index aa79d08509b..45269e662cd 100644 --- a/app/views/projects/compare/show.html.haml +++ b/app/views/projects/compare/show.html.haml @@ -18,7 +18,7 @@ - else %ul.well-list= render Commit.decorate(@commits), project: @project - = render "projects/commits/diffs", diffs: @diffs, project: @project + = render "projects/diffs/diffs", diffs: @diffs, project: @project - else .light-well diff --git a/app/views/projects/commits/_diffs.html.haml b/app/views/projects/diffs/_diffs.html.haml index 17efa8debe1..334ea1ba82f 100644 --- a/app/views/projects/commits/_diffs.html.haml +++ b/app/views/projects/diffs/_diffs.html.haml @@ -1,6 +1,6 @@ .row .col-md-8 - = render 'projects/commits/diff_stats', diffs: diffs + = render 'projects/diffs/stats', diffs: diffs .col-md-4 %ul.nav.nav-tabs %li.pull-right{class: params[:view] == 'parallel' ? 'active' : ''} @@ -11,16 +11,17 @@ - params_copy[:view] = 'inline' = link_to "Inline Diff", url_for(params_copy), {id: "commit-diff-viewtype"} -- if show_diff_size_warninig?(diffs) - = render 'projects/commits/diff_warning', diffs: diffs + +- if show_diff_size_warning?(diffs) + = render 'projects/diffs/warning', diffs: diffs .files - - safe_diff_files(diffs).each_with_index do |diff, i| - = render 'projects/commits/diff_file', diff: diff, i: i, project: project + - safe_diff_files(diffs).each_with_index do |diff_file, index| + = render 'projects/diffs/file', diff_file: diff_file, i: index, project: project - if @diff_timeout .alert.alert-danger %h4 Failed to collect changes %p - Maybe diff is really big and operation failed with timeout. Try to get diff localy + Maybe diff is really big and operation failed with timeout. Try to get diff locally diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml new file mode 100644 index 00000000000..f2a8d148cc6 --- /dev/null +++ b/app/views/projects/diffs/_file.html.haml @@ -0,0 +1,48 @@ +- blob = project.repository.blob_for_diff(@commit, diff_file.diff) +- return unless blob +- blob_diff_path = diff_project_blob_path(project, tree_join(@commit.id, diff_file.file_path)) +.diff-file{id: "diff-#{i}", data: {blob_diff_path: blob_diff_path }} + .diff-header{id: "file-path-#{hexdigest(diff_file.new_path || diff_file.old_path)}"} + - if diff_file.deleted_file + %span= diff_file.old_path + + .diff-btn-group + - if @commit.parent_ids.present? + = view_file_btn(@commit.parent_id, diff_file, project) + - else + %span= diff_file.new_path + - if diff_file.mode_changed? + %span.file-mode= "#{diff.a_mode} → #{diff.b_mode}" + + .diff-btn-group + - unless params[:view] == 'parallel' + %label + = check_box_tag nil, 1, false, class: "js-toggle-diff-line-wrap" + Wrap text + + = link_to "#", class: "js-toggle-diff-comments btn btn-small" do + %i.icon-chevron-down + Diff comments + + + - if @merge_request && @merge_request.source_project + = link_to project_edit_tree_path(@merge_request.source_project, tree_join(@merge_request.source_branch, diff_file.new_path), from_merge_request_id: @merge_request.id), { class: 'btn btn-small' } do + Edit + + + = view_file_btn(@commit.id, diff_file, project) + + .diff-content + -# Skipp all non non-supported blobs + - return unless blob.respond_to?('text?') + - if blob.text? + - if params[:view] == 'parallel' + = render "projects/diffs/parallel_view", diff_file: diff_file, project: project, blob: blob, index: i + - else + = render "projects/diffs/text_file", diff_file: diff_file, index: i + - elsif blob.image? + - old_file = project.repository.prev_blob_for_diff(@commit, diff_file) + = render "projects/diffs/image", diff_file: diff_file, old_file: old_file, file: blob, index: i + - else + .nothing-here-block No preview for this file type + diff --git a/app/views/projects/commits/_image.html.haml b/app/views/projects/diffs/_image.html.haml index 6d9ef5964d9..900646dd0a4 100644 --- a/app/views/projects/commits/_image.html.haml +++ b/app/views/projects/diffs/_image.html.haml @@ -1,3 +1,4 @@ +- diff = diff_file.diff - if diff.renamed_file || diff.new_file || diff.deleted_file .image %span.wrap diff --git a/app/views/projects/commits/diffs/_match_line.html.haml b/app/views/projects/diffs/_match_line.html.haml index 4ebe3379733..4ebe3379733 100644 --- a/app/views/projects/commits/diffs/_match_line.html.haml +++ b/app/views/projects/diffs/_match_line.html.haml diff --git a/app/views/projects/diffs/_match_line_parallel.html.haml b/app/views/projects/diffs/_match_line_parallel.html.haml new file mode 100644 index 00000000000..815df16aa4a --- /dev/null +++ b/app/views/projects/diffs/_match_line_parallel.html.haml @@ -0,0 +1,4 @@ +%td.old_line + %td.line_content.parallel.matched= line +%td.new_line + %td.line_content.parallel.matched= line diff --git a/app/views/projects/diffs/_parallel_view.html.haml b/app/views/projects/diffs/_parallel_view.html.haml new file mode 100644 index 00000000000..3ec769e0b83 --- /dev/null +++ b/app/views/projects/diffs/_parallel_view.html.haml @@ -0,0 +1,27 @@ +/ Side-by-side diff view +%div.text-file.diff-wrap-lines + %table + - parallel_diff(diff_file, index).each do |line| + - type_left = line[0] + - line_number_left = line[1] + - line_content_left = line[2] + - line_code = line[3] + - type_right = line[4] + - line_number_right = line[5] + - line_content_right = line[6] + + %tr.line_holder.parallel{id: line_code} + - if type_left == 'match' + = render "projects/diffs/match_line_parallel", { line: line_content_left, + line_old: line_number_left, line_new: line_number_right } + - elsif type_left == 'old' || type_left.nil? + %td.old_line{class: "#{type_left}"} + = link_to raw(line_number_left), "##{line_code}", id: line_code + %td.line_content{class: "parallel noteable_line #{type_left} #{line_code}", "line_code" => line_code }= raw line_content_left + %td.new_line{ class: "#{type_right == 'new' ? 'new' : nil}", data: { linenumber: line_number_right }} + = link_to raw(line_number_right), "##{line_code}", id: line_code + %td.line_content.parallel{class: "noteable_line #{type_right == 'new' ? 'new' : nil} #{line_code}", "line_code" => line_code}= raw line_content_right + +- if diff_file.diff.diff.blank? && diff_file.mode_changed? + .file-mode-changed + File mode changed diff --git a/app/views/projects/commits/_diff_stats.html.haml b/app/views/projects/diffs/_stats.html.haml index 8ef7cc6e086..8ef7cc6e086 100644 --- a/app/views/projects/commits/_diff_stats.html.haml +++ b/app/views/projects/diffs/_stats.html.haml diff --git a/app/views/projects/commits/_text_file.html.haml b/app/views/projects/diffs/_text_file.html.haml index 756481c1b21..b1c987563f1 100644 --- a/app/views/projects/commits/_text_file.html.haml +++ b/app/views/projects/diffs/_text_file.html.haml @@ -1,33 +1,36 @@ -- too_big = diff.diff.lines.count > Commit::DIFF_SAFE_LINES +- too_big = diff_file.diff_lines.count > Commit::DIFF_SAFE_LINES - if too_big %a.supp_diff_link Changes suppressed. Click to show %table.text-file{class: "#{'hide' if too_big}"} - last_line = 0 - - each_diff_line(diff, index) do |line, type, line_code, line_new, line_old, raw_line| - - last_line = line_new + - diff_file.diff_lines.each_with_index do |line, index| + - type = line.type + - last_line = line.new_pos + - line_code = generate_line_code(diff_file.file_path, line) + - line_old = line.old_pos %tr.line_holder{ id: line_code, class: "#{type}" } - if type == "match" - = render "projects/commits/diffs/match_line", {line: line, - line_old: line_old, line_new: line_new, bottom: false} + = render "projects/diffs/match_line", {line: line.text, + line_old: line_old, line_new: line.new_pos, bottom: false} - else %td.old_line = link_to raw(type == "new" ? " " : line_old), "##{line_code}", id: line_code - if @comments_allowed = link_to_new_diff_note(line_code) - %td.new_line{data: {linenumber: line_new}} - = link_to raw(type == "old" ? " " : line_new) , "##{line_code}", id: line_code - %td.line_content{class: "noteable_line #{type} #{line_code}", "line_code" => line_code}= raw diff_line_content(line) + %td.new_line{data: {linenumber: line.new_pos}} + = link_to raw(type == "old" ? " " : line.new_pos) , "##{line_code}", id: line_code + %td.line_content{class: "noteable_line #{type} #{line_code}", "line_code" => line_code}= raw diff_line_content(line.text) - if @reply_allowed - comments = @line_notes.select { |n| n.line_code == line_code }.sort_by(&:created_at) - unless comments.empty? - = render "projects/notes/diff_notes_with_reply", notes: comments, line: line + = render "projects/notes/diff_notes_with_reply", notes: comments, line: line.text - if last_line > 0 - = render "projects/commits/diffs/match_line", {line: "", + = render "projects/diffs/match_line", {line: "", line_old: last_line, line_new: last_line, bottom: true} -- if diff.diff.blank? && diff_file_mode_changed?(diff) +- if diff_file.diff.blank? && diff_file.mode_changed? .file-mode-changed File mode changed diff --git a/app/views/projects/commits/_diff_warning.html.haml b/app/views/projects/diffs/_warning.html.haml index 05d516efa11..86ed6bbeaa2 100644 --- a/app/views/projects/commits/_diff_warning.html.haml +++ b/app/views/projects/diffs/_warning.html.haml @@ -14,6 +14,6 @@ = link_to "Email patch", project_merge_request_path(@project, @merge_request, format: :patch), class: "btn btn-warning btn-small" %p To preserve performance only - %strong #{safe_diff_files(diffs).size} of #{diffs.size} + %strong #{allowed_diff_size} of #{diffs.size} files displayed. diff --git a/app/views/projects/edit_tree/_diff.html.haml b/app/views/projects/edit_tree/_diff.html.haml deleted file mode 100644 index cf044feb9a4..00000000000 --- a/app/views/projects/edit_tree/_diff.html.haml +++ /dev/null @@ -1,13 +0,0 @@ -%table.text-file - - each_diff_line(diff, 1) do |line, type, line_code, line_new, line_old, raw_line| - %tr.line_holder{ id: line_code, class: "#{type}" } - - if type == "match" - %td.old_line= "..." - %td.new_line= "..." - %td.line_content.matched= line - - else - %td.old_line - = link_to raw(type == "new" ? " " : line_old), "##{line_code}", id: line_code - %td.new_line= link_to raw(type == "old" ? " " : line_new) , "##{line_code}", id: line_code - %td.line_content{class: "noteable_line #{type} #{line_code}", "line_code" => line_code}= raw diff_line_content(line) - diff --git a/app/views/projects/edit_tree/preview.html.haml b/app/views/projects/edit_tree/preview.html.haml index 87ce5dc31d3..e7c3460ad78 100644 --- a/app/views/projects/edit_tree/preview.html.haml +++ b/app/views/projects/edit_tree/preview.html.haml @@ -9,18 +9,17 @@ = raw render_markup(@blob.name, @content) - else .file-content.code - - unless @diff.empty? + - unless @diff_lines.empty? %table.text-file - - @diff.each do |line, type, line_code, line_new, line_old, raw_line| - %tr.line_holder{ id: line_code, class: "#{type}" } - - if type == "match" + - @diff_lines.each do |line| + %tr.line_holder{ class: "#{line.type}" } + - if line.type == "match" %td.old_line= "..." %td.new_line= "..." - %td.line_content.matched= line + %td.line_content.matched= line.text - else %td.old_line - = link_to raw(type == "new" ? " " : line_old), "##{line_code}", id: line_code - %td.new_line= link_to raw(type == "old" ? " " : line_new) , "##{line_code}", id: line_code - %td.line_content{class: "noteable_line #{type} #{line_code}", "line_code" => line_code}= raw diff_line_content(line) + %td.new_line + %td.line_content{class: "#{line.type}"}= raw diff_line_content(line.text) - else .nothing-here-block No changes. diff --git a/app/views/projects/import.html.haml b/app/views/projects/import.html.haml index 649dd56a8d9..2b907748486 100644 --- a/app/views/projects/import.html.haml +++ b/app/views/projects/import.html.haml @@ -23,7 +23,7 @@ .col-sm-10 = f.text_field :import_url, class: 'form-control', placeholder: 'https://github.com/randx/six.git' .bs-callout.bs-callout-info - This url must be publicly accessible or you can add a username and password like this: https://username:password@gitlab.com/company/project.git. + This URL must be publicly accessible or you can add a username and password like this: https://username:password@gitlab.com/company/project.git. %br The import will time out after 4 minutes. For big repositories, use a clone/push combination. .form-actions diff --git a/app/views/projects/issues/_form.html.haml b/app/views/projects/issues/_form.html.haml index b2a8e8e091e..d063f92e87f 100644 --- a/app/views/projects/issues/_form.html.haml +++ b/app/views/projects/issues/_form.html.haml @@ -16,37 +16,7 @@ - @issue.errors.full_messages.each do |msg| %span= msg %br - .form-group - = f.label :title, class: 'control-label' do - %strong= 'Title *' - .col-sm-10 - = f.text_field :title, maxlength: 255, class: "form-control js-gfm-input", autofocus: true, required: true - .form-group - = f.label :description, 'Description', class: 'control-label' - .col-sm-10 - = f.text_area :description, class: 'form-control js-gfm-input markdown-area', rows: 14 - .col-sm-12.hint - .pull-left Issues are parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"), target: '_blank'}. - .pull-right Attach images (JPG, PNG, GIF) by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }. - .clearfix - .error-alert - %hr - .form-group - .issue-assignee - = f.label :assignee_id, class: 'control-label' do - %i.icon-user - Assign to - .col-sm-10 - = project_users_select_tag('issue[assignee_id]', placeholder: 'Select a user', class: 'custom-form-control', selected: @issue.assignee_id) - - = link_to 'Assign to me', '#', class: 'btn assign-to-me-link' - .form-group - .issue-milestone - = f.label :milestone_id, class: 'control-label' do - %i.icon-time - Milestone - .col-sm-10= f.select(:milestone_id, milestone_options(@issue), { include_blank: "Select milestone" }, {class: 'select2'}) - + = render 'projects/issuable_form', f: f, issuable: @issue .form-group = f.label :label_ids, class: 'control-label' do %i.icon-tag diff --git a/app/views/projects/issues/_head.html.haml b/app/views/projects/issues/_head.html.haml index dad547d4ebc..82cde14e05d 100644 --- a/app/views/projects/issues/_head.html.haml +++ b/app/views/projects/issues/_head.html.haml @@ -24,7 +24,7 @@ %i.icon.icon-list = form_tag project_issues_path(@project), method: :get, id: "issue_search_form", class: 'pull-left issue-search-form' do .append-right-10.hidden-xs.hidden-sm - = search_field_tag :issue_search, nil, { placeholder: 'Filter by title or description', class: 'form-control issue_search search-text-input input-mn-300' } + = search_field_tag :issue_search, params[:issue_search], { placeholder: 'Filter by title or description', class: 'form-control issue_search search-text-input input-mn-300' } = hidden_field_tag :state, params['state'] = hidden_field_tag :scope, params['scope'] = hidden_field_tag :assignee_id, params['assignee_id'] diff --git a/app/views/projects/labels/destroy.js.haml b/app/views/projects/labels/destroy.js.haml new file mode 100644 index 00000000000..1b4c83ab097 --- /dev/null +++ b/app/views/projects/labels/destroy.js.haml @@ -0,0 +1,2 @@ +- if @project.labels.size == 0 + $('.labels').load(document.URL + ' .light-well').hide().fadeIn(1000) diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml index 075779a9c88..06568278de8 100644 --- a/app/views/projects/labels/index.html.haml +++ b/app/views/projects/labels/index.html.haml @@ -7,11 +7,11 @@ Labels %hr -- if @labels.present? - %ul.bordered-list.manage-labels-list - = render @labels - = paginate @labels, theme: 'gitlab' - -- else - .light-well - .nothing-here-block Create first label or #{link_to 'generate', generate_project_labels_path(@project), method: :post} default set of labels +.labels + - if @labels.present? + %ul.bordered-list.manage-labels-list + = render @labels + = paginate @labels, theme: 'gitlab' + - else + .light-well + .nothing-here-block Create first label or #{link_to 'generate', generate_project_labels_path(@project), method: :post} default set of labels diff --git a/app/views/projects/merge_requests/_form.html.haml b/app/views/projects/merge_requests/_form.html.haml index 0af89b6e376..a97547aabec 100644 --- a/app/views/projects/merge_requests/_form.html.haml +++ b/app/views/projects/merge_requests/_form.html.haml @@ -15,37 +15,7 @@ %div= msg .merge-request-form-info - .form-group - = f.label :title, class: 'control-label' do - %strong= "Title *" - .col-sm-10= f.text_field :title, class: "form-control pad js-gfm-input", maxlength: 255, rows: 5, required: true - .form-group - = f.label :description, "Description", class: 'control-label' - .col-sm-10 - = f.text_area :description, class: "form-control js-gfm-input markdown-area", rows: 14 - .col-sm-12.hint - .pull-left Description is parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"), target: '_blank'}. - .pull-right Attach images (JPG, PNG, GIF) by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }. - .clearfix - .error-alert - %hr - .form-group - .issue-assignee - = f.label :assignee_id, class: 'control-label' do - %i.icon-user - Assign to - .col-sm-10 - = project_users_select_tag('merge_request[assignee_id]', placeholder: 'Select a user', class: 'custom-form-control', selected: @merge_request.assignee_id) - - = link_to 'Assign to me', '#', class: 'btn assign-to-me-link' - .form-group - .issue-milestone - = f.label :milestone_id, class: 'control-label' do - %i.icon-time - Milestone - .col-sm-10= f.select(:milestone_id, milestone_options(@merge_request), { include_blank: "Select milestone" }, {class: 'select2'}) - - + = render 'projects/issuable_form', f: f, issuable: @merge_request .form-group = f.label :label_ids, class: 'control-label' do %i.icon-tag diff --git a/app/views/projects/merge_requests/_new_submit.html.haml b/app/views/projects/merge_requests/_new_submit.html.haml index dc3f9d592f6..e013fd6d1ce 100644 --- a/app/views/projects/merge_requests/_new_submit.html.haml +++ b/app/views/projects/merge_requests/_new_submit.html.haml @@ -75,7 +75,7 @@ %h4 Changes - if @diffs.present? - = render "projects/commits/diffs", diffs: @diffs, project: @project + = render "projects/diffs/diffs", diffs: @diffs, project: @project - elsif @commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE .bs-callout.bs-callout-danger %h4 This comparison includes more than #{MergeRequestDiff::COMMITS_SAFE_SIZE} commits. diff --git a/app/views/projects/merge_requests/show/_diffs.html.haml b/app/views/projects/merge_requests/show/_diffs.html.haml index eb63b68106e..d361c5f579a 100644 --- a/app/views/projects/merge_requests/show/_diffs.html.haml +++ b/app/views/projects/merge_requests/show/_diffs.html.haml @@ -1,5 +1,5 @@ - if @merge_request_diff.collected? - = render "projects/commits/diffs", diffs: @merge_request.diffs, project: @merge_request.source_project + = render "projects/diffs/diffs", diffs: @merge_request.diffs, project: @merge_request.source_project - elsif @merge_request_diff.empty? .nothing-here-block Nothing to merge from #{@merge_request.source_branch} into #{@merge_request.target_branch} - else diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index 7efaf5a087b..949b72356d7 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -48,9 +48,9 @@ .col-sm-10 = f.text_field :import_url, class: 'form-control', placeholder: 'https://github.com/randx/six.git' .bs-callout.bs-callout-info - This url must be publicly accessible or you can add a username and password like this: https://username:password@gitlab.com/company/project.git. + This URL must be publicly accessible or you can add a username and password like this: https://username:password@gitlab.com/company/project.git. %br - The import will time out after 2 minutes. For big repositories, use a clone/push combination. + The import will time out after 4 minutes. For big repositories, use a clone/push combination. %hr .form-group diff --git a/app/views/projects/notes/_diff_note_link.html.haml b/app/views/projects/notes/_diff_note_link.html.haml deleted file mode 100644 index 377c926a204..00000000000 --- a/app/views/projects/notes/_diff_note_link.html.haml +++ /dev/null @@ -1,10 +0,0 @@ -- note = @project.notes.new(@comments_target.merge({ line_code: line_code })) -= link_to "", - "javascript:;", - class: "add-diff-note js-add-diff-note-button", - data: { noteable_type: note.noteable_type, - noteable_id: note.noteable_id, - commit_id: note.commit_id, - line_code: note.line_code, - discussion_id: note.discussion_id }, - title: "Add a comment to this line" diff --git a/app/views/projects/notes/discussions/_diff.html.haml b/app/views/projects/notes/discussions/_diff.html.haml index 26c5494f466..da71220af17 100644 --- a/app/views/projects/notes/discussions/_diff.html.haml +++ b/app/views/projects/notes/discussions/_diff.html.haml @@ -11,16 +11,17 @@ %br/ .diff-content %table - - each_diff_line_near(diff, note.diff_file_index, note.line_code) do |line, type, line_code, line_new, line_old| + - note.truncated_diff_lines.each do |line| + - line_code = generate_line_code(note.file_path, line) %tr.line_holder{ id: line_code } - - if type == "match" + - if line.type == "match" %td.old_line= "..." %td.new_line= "..." - %td.line_content.matched= line + %td.line_content.matched= line.text - else - %td.old_line= raw(type == "new" ? " " : line_old) - %td.new_line= raw(type == "old" ? " " : line_new) - %td.line_content{class: "noteable_line #{type} #{line_code}", "line_code" => line_code}= raw "#{line} " + %td.old_line= raw(line.type == "new" ? " " : line.old_pos) + %td.new_line= raw(line.type == "old" ? " " : line.new_pos) + %td.line_content{class: "noteable_line #{line.type} #{line_code}", "line_code" => line_code}= raw "#{line.text} " - if line_code == note.line_code = render "projects/notes/diff_notes_with_reply", notes: discussion_notes diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml index a9fd97f8915..45ee61caf68 100644 --- a/app/views/projects/tags/new.html.haml +++ b/app/views/projects/tags/new.html.haml @@ -1,3 +1,7 @@ +- if @error + .alert.alert-danger + %button{ type: "button", class: "close", "data-dismiss" => "alert"} × + = @error %h3.page-title %i.icon-code-fork New tag @@ -5,12 +9,17 @@ .form-group = label_tag :tag_name, 'Name for new tag', class: 'control-label' .col-sm-10 - = text_field_tag :tag_name, nil, placeholder: 'v3.0.1', required: true, tabindex: 1, class: 'form-control' + = text_field_tag :tag_name, params[:tag_name], placeholder: 'v3.0.1', required: true, tabindex: 1, class: 'form-control' .form-group = label_tag :ref, 'Create from', class: 'control-label' .col-sm-10 - = text_field_tag :ref, nil, placeholder: 'master', required: true, tabindex: 2, class: 'form-control' + = text_field_tag :ref, params[:ref], placeholder: 'master', required: true, tabindex: 2, class: 'form-control' .light Branch name or commit SHA + .form-group + = label_tag :message, 'Message', class: 'control-label' + .col-sm-10 + = text_field_tag :message, nil, placeholder: 'Enter message.', required: false, tabindex: 3, class: 'form-control' + .light (Optional) Entering a message will create an annotated tag. .form-actions = submit_tag 'Create tag', class: 'btn btn-create', tabindex: 3 = link_to 'Cancel', project_tags_path(@project), class: 'btn btn-cancel' diff --git a/app/views/search/_project_filter.html.haml b/app/views/search/_project_filter.html.haml index 36947675d18..57a45c9acb6 100644 --- a/app/views/search/_project_filter.html.haml +++ b/app/views/search/_project_filter.html.haml @@ -23,3 +23,9 @@ Comments .pull-right = @search_results.notes_count + %li{class: ("active" if @scope == 'wiki_blobs')} + = link_to search_filter_path(scope: 'wiki_blobs') do + Wiki + .pull-right + = @search_results.wiki_blobs_count + diff --git a/app/views/search/_results.html.haml b/app/views/search/_results.html.haml index f9c0a6d61ff..58bcff9dbe3 100644 --- a/app/views/search/_results.html.haml +++ b/app/views/search/_results.html.haml @@ -1,9 +1,10 @@ %h4 #{@search_results.total_count} results found - - if @project - for #{link_to @project.name_with_namespace, @project} - - elsif @group - for #{link_to @group.name, @group} + - unless @show_snippets + - if @project + for #{link_to @project.name_with_namespace, @project} + - elsif @group + for #{link_to @group.name, @group} %hr @@ -11,6 +12,8 @@ .col-sm-3 - if @project = render "project_filter" + - elsif @show_snippets + = render 'snippet_filter' - else = render "global_filter" .col-sm-9 diff --git a/app/views/search/_snippet_filter.html.haml b/app/views/search/_snippet_filter.html.haml new file mode 100644 index 00000000000..0d1984a0d78 --- /dev/null +++ b/app/views/search/_snippet_filter.html.haml @@ -0,0 +1,13 @@ +%ul.nav.nav-pills.nav-stacked.search-filter + %li{class: ("active" if @scope == 'snippet_blobs')} + = link_to search_filter_path(scope: 'snippet_blobs', snippets: true, group_id: nil, project_id: nil) do + %i.icon-code + Snippet Contents + .pull-right + = @search_results.snippet_blobs_count + %li{class: ("active" if @scope == 'snippet_titles')} + = link_to search_filter_path(scope: 'snippet_titles', snippets: true, group_id: nil, project_id: nil) do + %i.icon-book + Titles and Filenames + .pull-right + = @search_results.snippet_titles_count diff --git a/app/views/search/results/_snippet_blob.html.haml b/app/views/search/results/_snippet_blob.html.haml new file mode 100644 index 00000000000..a3d909d44dc --- /dev/null +++ b/app/views/search/results/_snippet_blob.html.haml @@ -0,0 +1,65 @@ +.search-result-row + %span + = snippet_blob[:snippet_object].title + by + = link_to user_snippets_path(snippet_blob[:snippet_object].author) do + = image_tag avatar_icon(snippet_blob[:snippet_object].author_email), class: "avatar avatar-inline s16", alt: '' + = snippet_blob[:snippet_object].author_name + %span.light #{time_ago_with_tooltip(snippet_blob[:snippet_object].created_at)} + %h4.snippet-title + - snippet_path = reliable_snippet_path(snippet_blob[:snippet_object]) + = link_to snippet_path do + .file-holder + .file-title + %i.icon-file + %strong= snippet_blob[:snippet_object].file_name + %span.options + .btn-group.tree-btn-group.pull-right + - if snippet_blob[:snippet_object].author == current_user + = link_to "Edit", edit_snippet_path(snippet_blob[:snippet_object]), class: "btn btn-tiny", title: 'Edit Snippet' + = link_to "Delete", snippet_path(snippet_blob[:snippet_object]), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-tiny", title: 'Delete Snippet' + = link_to "Raw", raw_snippet_path(snippet_blob[:snippet_object]), class: "btn btn-tiny", target: "_blank" + - if gitlab_markdown?(snippet_blob[:snippet_object].file_name) + .file-content.wiki + - snippet_blob[:snippet_chunks].each do |snippet| + - unless snippet[:data].empty? + = preserve do + = markdown(snippet[:data]) + - else + .file-content.code + .nothing-here-block Empty file + - elsif markup?(snippet_blob[:snippet_object].file_name) + .file-content.wiki + - snippet_blob[:snippet_chunks].each do |snippet| + - unless snippet[:data].empty? + = render_markup(snippet_blob[:snippet_object].file_name, snippet[:data]) + - else + .file-content.code + .nothing-here-block Empty file + - else + .file-content.code + %div.highlighted-data{class: user_color_scheme_class} + .line-numbers + - snippet_blob[:snippet_chunks].each do |snippet| + - unless snippet[:data].empty? + - snippet[:data].lines.to_a.size.times do |index| + - offset = defined?(snippet[:start_line]) ? snippet[:start_line] : 1 + - i = index + offset + = link_to snippet_path+"#L#{i}", id: "L#{i}", rel: "#L#{i}" do + %i.icon-link + = i + - unless snippet == snippet_blob[:snippet_chunks].last + %a + = "." + .highlight.term + %pre + %code + - snippet_blob[:snippet_chunks].each do |snippet| + - unless snippet[:data].empty? + = snippet[:data] + - unless snippet == snippet_blob[:snippet_chunks].last + %a + = "..." + - else + .file-content.code + .nothing-here-block Empty file diff --git a/app/views/search/results/_snippet_title.html.haml b/app/views/search/results/_snippet_title.html.haml new file mode 100644 index 00000000000..84abb9293b2 --- /dev/null +++ b/app/views/search/results/_snippet_title.html.haml @@ -0,0 +1,23 @@ +.search-result-row + %h4.snippet-title.term + = link_to reliable_snippet_path(snippet_title) do + = truncate(snippet_title.title, length: 60) + - if snippet_title.private? + %span.label.label-gray + %i.icon-lock + private + %span.cgray.monospace.tiny.pull-right.term + = snippet_title.file_name + + %small.pull-right.cgray + - if snippet_title.project_id? + = link_to snippet_title.project.name_with_namespace, project_path(snippet_title.project) + + .snippet-info + = "##{snippet_title.id}" + %span + by + = link_to user_snippets_path(snippet_title.author) do + = image_tag avatar_icon(snippet_title.author_email), class: "avatar avatar-inline s16", alt: '' + = snippet_title.author_name + %span.light #{time_ago_with_tooltip(snippet_title.created_at)} diff --git a/app/views/search/results/_wiki_blob.html.haml b/app/views/search/results/_wiki_blob.html.haml new file mode 100644 index 00000000000..75414d73b0c --- /dev/null +++ b/app/views/search/results/_wiki_blob.html.haml @@ -0,0 +1,9 @@ +.blob-result + .file-holder + .file-title + = link_to project_wiki_path(@project, wiki_blob.filename) do + %i.icon-file + %strong + = wiki_blob.filename + .file-content.code.term + = render 'shared/file_hljs', blob: wiki_blob, first_line_number: wiki_blob.startline diff --git a/app/views/search/show.html.haml b/app/views/search/show.html.haml index 8d1614bfbd4..bae57917a4c 100644 --- a/app/views/search/show.html.haml +++ b/app/views/search/show.html.haml @@ -9,10 +9,12 @@ = submit_tag 'Search', class: "btn btn-create" .form-group .col-sm-2 - .col-sm-10 - = render 'filter', f: f + - unless params[:snippets].eql? 'true' + .col-sm-10 + = render 'filter', f: f = hidden_field_tag :project_id, params[:project_id] = hidden_field_tag :group_id, params[:group_id] + = hidden_field_tag :snippets, params[:snippets] = hidden_field_tag :scope, params[:scope] .results.prepend-top-10 diff --git a/bin/background_jobs b/bin/background_jobs index c7ba4398cfb..59a51c5c868 100755 --- a/bin/background_jobs +++ b/bin/background_jobs @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/sh cd $(dirname $0)/.. app_root=$(pwd) @@ -6,22 +6,22 @@ sidekiq_pidfile="$app_root/tmp/pids/sidekiq.pid" sidekiq_logfile="$app_root/log/sidekiq.log" gitlab_user=$(ls -l config.ru | awk '{print $3}') -function warn +warn() { echo "$@" 1>&2 } -function stop +stop() { bundle exec sidekiqctl stop $sidekiq_pidfile >> $sidekiq_logfile 2>&1 } -function killall +killall() { pkill -u $gitlab_user -f 'sidekiq [0-9]' } -function restart +restart() { if [ -f $sidekiq_pidfile ]; then stop @@ -30,20 +30,20 @@ function restart start_sidekiq -d -L $sidekiq_logfile } -function start_no_deamonize +start_no_deamonize() { start_sidekiq } -function start_sidekiq +start_sidekiq() { bundle exec sidekiq -q post_receive -q mailer -q system_hook -q project_web_hook -q gitlab_shell -q common -q default -e $RAILS_ENV -P $sidekiq_pidfile $@ >> $sidekiq_logfile 2>&1 } -function load_ok +load_ok() { sidekiq_pid=$(cat $sidekiq_pidfile) - if [[ -z $sidekiq_pid ]] ; then + if [ -z "$sidekiq_pid" ] ; then warn "Could not find a PID in $sidekiq_pidfile" exit 0 fi @@ -1,4 +1,4 @@ -#!/usr/bin/env bash +#!/bin/sh cd $(dirname $0)/.. app_root=$(pwd) @@ -6,28 +6,28 @@ app_root=$(pwd) unicorn_pidfile="$app_root/tmp/pids/unicorn.pid" unicorn_config="$app_root/config/unicorn.rb" -function get_unicorn_pid +get_unicorn_pid() { local pid=$(cat $unicorn_pidfile) - if [ -z $pid ] ; then + if [ -z "$pid" ] ; then echo "Could not find a PID in $unicorn_pidfile" exit 1 fi unicorn_pid=$pid } -function start +start() { bundle exec unicorn_rails -D -c $unicorn_config -E $RAILS_ENV } -function stop +stop() { get_unicorn_pid kill -QUIT $unicorn_pid } -function reload +reload() { get_unicorn_pid kill -USR2 $unicorn_pid diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 0a0d9241e27..8e85634d054 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -3,9 +3,11 @@ # # # # # # # # # # # # # # # # # # # # How to use: -# 1. copy file as gitlab.yml -# 2. Replace gitlab -> host with your domain -# 3. Replace gitlab -> email_from +# 1. Copy file as gitlab.yml +# 2. Update gitlab -> host with your fully qualified domain name +# 3. Update gitlab -> email_from +# 4. If you installed Git from source, change git -> bin_path to /usr/local/bin/git +# 5. Review this configuration file for other settings you may want to adjust production: &base # @@ -16,8 +18,8 @@ production: &base gitlab: ## Web server settings (note: host is the FQDN, do not include http://) host: localhost - port: 80 - https: false + port: 80 # Set to 443 if using HTTPS, see installation.md#using-https for additional HTTPS configuration details + https: false # Set to true if using HTTPS, see installation.md#using-https for additional HTTPS configuration details # Uncommment this line below if your ssh host is different from HTTP/HTTPS one # (you'd obviously need to replace ssh.host_example.com with your own host). diff --git a/db/fixtures/development/04_project.rb b/db/fixtures/development/04_project.rb index b93229a0609..fef9666c6cb 100644 --- a/db/fixtures/development/04_project.rb +++ b/db/fixtures/development/04_project.rb @@ -4,10 +4,10 @@ Sidekiq::Testing.inline! do Gitlab::Seeder.quiet do project_urls = [ 'https://github.com/documentcloud/underscore.git', - 'https://github.com/gitlabhq/gitlabhq.git', - 'https://github.com/gitlabhq/gitlab-ci.git', - 'https://github.com/gitlabhq/gitlab-shell.git', - 'https://github.com/gitlabhq/testme.git', + 'https://gitlab.com/gitlab-org/gitlab-ce.git', + 'https://gitlab.com/gitlab-org/gitlab-ci.git', + 'https://gitlab.com/gitlab-org/gitlab-shell.git', + 'https://gitlab.com/gitlab-org/testme.git', 'https://github.com/twitter/flight.git', 'https://github.com/twitter/typeahead.js.git', 'https://github.com/h5bp/html5-boilerplate.git', diff --git a/db/fixtures/production/001_admin.rb b/db/fixtures/production/001_admin.rb index c00ba3c10ba..21c10f31923 100644 --- a/db/fixtures/production/001_admin.rb +++ b/db/fixtures/production/001_admin.rb @@ -1,9 +1,15 @@ +password = if ENV['GITLAB_ROOT_PASSWORD'].nil? || ENV['GITLAB_ROOT_PASSWORD'].empty? + "5iveL!fe" + else + ENV['GITLAB_ROOT_PASSWORD'] + end + admin = User.create( email: "admin@example.com", name: "Administrator", username: 'root', - password: "5iveL!fe", - password_confirmation: "5iveL!fe", + password: password, + password_confirmation: password, password_expires_at: Time.now, theme_id: Gitlab::Theme::MARS @@ -19,6 +25,6 @@ puts %q[ Administrator account created: login.........root -password......5iveL!fe +password......#{password} ] end diff --git a/db/migrate/20140903115954_migrate_to_new_shell.rb b/db/migrate/20140903115954_migrate_to_new_shell.rb new file mode 100644 index 00000000000..2d832109513 --- /dev/null +++ b/db/migrate/20140903115954_migrate_to_new_shell.rb @@ -0,0 +1,10 @@ +class MigrateToNewShell < ActiveRecord::Migration + def change + gitlab_shell_path = Gitlab.config.gitlab_shell.path + if system("#{gitlab_shell_path}/bin/create-hooks") + puts 'Repositories updated with new hooks' + else + raise 'Failed to rewrite gitlab-shell hooks in repositories' + end + end +end diff --git a/db/migrate/20140907220153_serialize_service_properties.rb b/db/migrate/20140907220153_serialize_service_properties.rb new file mode 100644 index 00000000000..2326fd0aebf --- /dev/null +++ b/db/migrate/20140907220153_serialize_service_properties.rb @@ -0,0 +1,35 @@ +class SerializeServiceProperties < ActiveRecord::Migration + def change + add_column :services, :properties, :text + + associations = + { + AssemblaService: [:token, :subdomain], + CampfireService: [:token, :subdomain, :room], + EmailsOnPushService: [:recipients], + FlowdockService: [:token], + GemnasiumService: [:api_key, :token], + GitlabCiService: [:token, :project_url], + HipchatService: [:token, :room], + PivotaltrackerService: [:token], + SlackService: [:subdomain, :token, :room], + JenkinsService: [:token, :subdomain], + JiraService: [:project_url, :username, :password, + :api_version, :jira_issue_transition_id], + } + + Service.all.each do |service| + associations[service.type.to_sym].each do |attribute| + service.send("#{attribute}=", service.attributes[attribute.to_s]) + end + service.save! + end + + remove_column :services, :project_url, :string + remove_column :services, :subdomain, :string + remove_column :services, :room, :string + remove_column :services, :recipients, :text + remove_column :services, :api_key, :string + remove_column :services, :token, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 9159556ac72..e9b3713557d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20140730111702) do +ActiveRecord::Schema.define(version: 20140907220153) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -265,16 +265,11 @@ ActiveRecord::Schema.define(version: 20140730111702) do create_table "services", force: true do |t| t.string "type" t.string "title" - t.string "token" - t.integer "project_id", null: false + t.integer "project_id", null: false t.datetime "created_at" t.datetime "updated_at" - t.boolean "active", default: false, null: false - t.string "project_url" - t.string "subdomain" - t.string "room" - t.text "recipients" - t.string "api_key" + t.boolean "active", default: false, null: false + t.text "properties" end add_index "services", ["project_id"], name: "index_services_on_project_id", using: :btree diff --git a/doc/api/branches.md b/doc/api/branches.md index 31469b6fe97..74386615545 100644 --- a/doc/api/branches.md +++ b/doc/api/branches.md @@ -196,6 +196,8 @@ Parameters: } ``` +It return 200 if succeed or 400 if failed with error message explaining reason. + ## Delete repository branch ``` @@ -207,4 +209,5 @@ Parameters: - `id` (required) - The ID of a project - `branch` (required) - The name of the branch -It return 200 if succeed or 405 if failed with error message explaining reason. +It return 200 if succeed, 404 if the branch to be deleted does not exist +or 400 for other reasons. In case of an error, an explaining message is provided. diff --git a/doc/api/issues.md b/doc/api/issues.md index c12d4528546..a935b146d37 100644 --- a/doc/api/issues.md +++ b/doc/api/issues.md @@ -9,11 +9,15 @@ Get all issues created by authenticated user. This function takes pagination par GET /issues GET /issues?state=opened GET /issues?state=closed +GET /issues?labels=foo +GET /issues?labels=foo,bar +GET /issues?labels=foo,bar&state=opened ``` Parameters: - `state` (optional) - Return `all` issues or just those that are `opened` or `closed` +- `labels` (optional) - Comma-separated list of label names ```json [ @@ -88,12 +92,16 @@ to return the list of project issues. GET /projects/:id/issues GET /projects/:id/issues?state=opened GET /projects/:id/issues?state=closed +GET /projects/:id/issues?labels=foo +GET /projects/:id/issues?labels=foo,bar +GET /projects/:id/issues?labels=foo,bar&state=opened ``` Parameters: - `id` (required) - The ID of a project - `state` (optional) - Return `all` issues or just those that are `opened` or `closed` +- `labels` (optional) - Comma-separated list of label names ## Single issue diff --git a/doc/api/projects.md b/doc/api/projects.md index 8995551b9ea..9f6f6741093 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -248,6 +248,7 @@ POST /projects Parameters: - `name` (required) - new project name +- `path` (optional) - custom repository name for new project. By default generated based on name - `namespace_id` (optional) - namespace for the new project (defaults to user) - `description` (optional) - short project description - `issues_enabled` (optional) diff --git a/doc/api/repositories.md b/doc/api/repositories.md index 1074b78fd73..a412f60c0d9 100644 --- a/doc/api/repositories.md +++ b/doc/api/repositories.md @@ -50,6 +50,7 @@ Parameters: - `id` (required) - The ID of a project - `tag_name` (required) - The name of a tag - `ref` (required) - Create tag using commit SHA, another tag name, or branch name. +- `message` (optional) - Creates annotated tag. ```json [ @@ -71,6 +72,9 @@ Parameters: ] ``` +It returns 200 if the operation succeed. In case of an error, +405 with an explaining error message is returned. + ## List repository tree Get a list of repository files and directories in a project. diff --git a/doc/install/database_mysql.md b/doc/install/database_mysql.md index 270ad3b0b86..ae68fd007ab 100644 --- a/doc/install/database_mysql.md +++ b/doc/install/database_mysql.md @@ -1,4 +1,4 @@ -# Database Mysql +# Database MySQL ## Note @@ -12,16 +12,16 @@ We do not recommend using MySQL due to various issues. For example, case [(in)se # Ensure you have MySQL version 5.5.14 or later mysql --version - # Pick a database root password (can be anything), type it and press enter - # Retype the database root password and press enter + # Pick a MySQL root password (can be anything), type it and press enter + # Retype the MySQL root password and press enter - # Secure your installation. + # Secure your installation sudo mysql_secure_installation # Login to MySQL mysql -u root -p - # Type the database root password + # Type the MySQL root password # Create a user for GitLab # do not type the 'mysql>', this is part of the prompt diff --git a/doc/install/installation.md b/doc/install/installation.md index 423a5f0cb19..5ad8392fb63 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -76,7 +76,7 @@ Is the system packaged Git too old? Remove it and compile from source. # Install into /usr/local/bin sudo make prefix=/usr/local install - # When editing config/gitlab.yml (Step 5), change the git bin_path to /usr/local/bin/git + # When editing config/gitlab.yml (Step 5), change the git -> bin_path to /usr/local/bin/git **Note:** In order to receive mail notifications, make sure to install a mail server. By default, Debian is shipped with exim4 but this [has problems](https://github.com/gitlabhq/gitlabhq/issues/4866#issuecomment-32726573) while Ubuntu does not ship with one. The recommended mail server is postfix and you can install it with: @@ -153,12 +153,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da # Copy the example GitLab config sudo -u git -H cp config/gitlab.yml.example config/gitlab.yml - # Make sure to change "localhost" to the fully-qualified domain name of your - # host serving GitLab where necessary - # - # If you want to use https make sure that you set `https` to `true`. See #using-https for all necessary details. - # - # If you installed Git from source, change the git bin_path to /usr/local/bin/git + # Update GitLab config file, follow the directions at top of file sudo -u git -H editor config/gitlab.yml # Make sure GitLab can write to the log/ and tmp/ directories @@ -196,6 +191,8 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da **Important Note:** Make sure to edit both `gitlab.yml` and `unicorn.rb` to match your setup. +**Note:** If you want to use HTTPS, see [Using HTTPS](#using-https) for the additional steps. + ### Configure GitLab DB Settings # PostgreSQL only: @@ -233,17 +230,12 @@ GitLab Shell is an SSH access and repository management software developed speci # Run the installation task for gitlab-shell (replace `REDIS_URL` if needed): sudo -u git -H bundle exec rake gitlab:shell:install[v1.9.7] REDIS_URL=redis://localhost:6379 RAILS_ENV=production - # By default, the gitlab-shell config is generated from your main gitlab config. - # - # Note: When using GitLab with HTTPS please change the following: - # - Provide paths to the certificates under `ca_file` and `ca_path` options. - # - The `gitlab_url` option must point to the https endpoint of GitLab. - # - In case you are using self signed certificate set `self_signed_cert` to `true`. - # See #using-https for all necessary details. - # + # By default, the gitlab-shell config is generated from your main GitLab config. # You can review (and modify) the gitlab-shell config as follows: sudo -u git -H editor /home/git/gitlab-shell/config.yml +**Note:** If you want to use HTTPS, see [Using HTTPS](#using-https) for the additional steps. + ### Initialize Database and Activate Advanced Features sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production @@ -252,6 +244,10 @@ GitLab Shell is an SSH access and repository management software developed speci # When done you see 'Administrator account created:' +**Note:** You can set the Administrator password by supplying it in environmental variable `GITLAB_ROOT_PASSWORD`, eg.: + + sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production GITLAB_ROOT_PASSWORD=newpassword + ### Install Init Script Download the init script (will be `/etc/init.d/gitlab`): @@ -309,14 +305,14 @@ Make sure to edit the config file to match your setup: # domain name of your host serving GitLab. sudo editor /etc/nginx/sites-available/gitlab -**Note:** If you want to use HTTPS, replace the `gitlab` Nginx config with `gitlab-ssl`. See [Using HTTPS](#using-https) for all necessary details. +**Note:** If you want to use HTTPS, replace the `gitlab` Nginx config with `gitlab-ssl`. See [Using HTTPS](#using-https) for HTTPS configuration details. ### Test Configuration Validate your `gitlab` or `gitlab-ssl` Nginx config file with the following command: sudo nginx -t - + You should receive `syntax is okay` and `test is successful` messages. If you receive errors check your `gitlab` or `gitlab-ssl` Nginx config file for typos, etc. as indiciated in the error message given. ### Restart @@ -350,11 +346,30 @@ Visit YOUR_SERVER in your web browser for your first GitLab login. The setup has ### Using HTTPS -To recapitulate what is needed to use GitLab with HTTPS: +To use GitLab with HTTPS: + +1. In `gitlab.yml`: + 1. Set the `port` option in section 1 to `443`. + 1. Set the `https` option in section 1 to `true`. +1. In the `config.yml` of gitlab-shell: + 1. Set `gitlab_url` option to the HTTPS endpoint of GitLab (e.g. `https://git.example.com`). + 1. Set the certificates using either the `ca_file` or `ca_path` option. +1. Use the `gitlab-ssl` Nginx example config instead of the `gitlab` config. + 1. Update `YOUR_SERVER_FQDN`. + 1. Update `ssl_certificate` and `ssl_certificate_key`. + 1. Review the configuration file and consider applying other security and performance enhancing features. + +Using a self-signed certificate is discouraged but if you must use it follow the normal directions then: -1. In `gitlab.yml` set the `https` option to `true` -1. In the `config.yml` of gitlab-shell set the relevant options (see the [install GitLab Shell section](#install-gitlab-shell) of this document). -1. Use the `gitlab-ssl` nginx example config instead of the `gitlab` config. +1. Generate a self-signed SSL certificate: + + ``` + mkdir -p /etc/nginx/ssl/ + cd /etc/nginx/ssl/ + sudo openssl req -newkey rsa:2048 -x509 -nodes -days 3560 -out gitlab.crt -keyout gitlab.key + sudo chmod o-r gitlab.key + ``` +1. In the `config.yml` of gitlab-shell set `self_signed_cert` to `true`. ### Additional Markup Styles @@ -390,38 +405,4 @@ You can configure LDAP authentication in `config/gitlab.yml`. Please restart Git ### Using Custom Omniauth Providers -GitLab uses [Omniauth](http://www.omniauth.org/) for authentication and already ships with a few providers preinstalled (e.g. LDAP, GitHub, Twitter). But sometimes that is not enough and you need to integrate with other authentication solutions. For these cases you can use the Omniauth provider. - -#### Steps - -These steps are fairly general and you will need to figure out the exact details from the Omniauth provider's documentation. - -- Stop GitLab: - - sudo service gitlab stop - -- Add the gem to your [Gemfile](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/Gemfile): - - gem "omniauth-your-auth-provider" - -- If you're using MySQL, install the new Omniauth provider gem by running the following command: - - sudo -u git -H bundle install --without development test postgres --path vendor/bundle --no-deployment - -- If you're using PostgreSQL, install the new Omniauth provider gem by running the following command: - - sudo -u git -H bundle install --without development test mysql --path vendor/bundle --no-deployment - - > These are the same commands you used in the [Install Gems section](#install-gems) with `--path vendor/bundle --no-deployment` instead of `--deployment`. - -- Start GitLab: - - sudo service gitlab start - -#### Examples - -If you have successfully set up a provider that is not shipped with GitLab itself, please let us know. - -You can help others by reporting successful configurations and probably share a few insights or provide warnings for common errors or pitfalls by sharing your experience [in the public Wiki](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Custom-omniauth-provider-configurations). - -While we can't officially support every possible authentication mechanism out there, we'd like to at least help those with special needs. +See the [omniauth integration document](doc/integration/omniauth.md) diff --git a/doc/install/requirements.md b/doc/install/requirements.md index 53f6ccc8c34..fd2e29d3c52 100644 --- a/doc/install/requirements.md +++ b/doc/install/requirements.md @@ -7,9 +7,9 @@ - Ubuntu - Debian - CentOS -- RedHat Enterprise Linux -- Scientific Linux -- Oracle Linux +- RedHat Enterprise Linux (please use the CentOS packages and instructions) +- Scientific Linux (please use the CentOS packages and instructions) +- Oracle Linux (please use the CentOS packages and instructions) For the installations options please see [the installation page on the GitLab website](https://about.gitlab.com/installation/). diff --git a/doc/integration/omniauth.md b/doc/integration/omniauth.md index 1b0bf9c5f64..00adae58dfa 100644 --- a/doc/integration/omniauth.md +++ b/doc/integration/omniauth.md @@ -50,6 +50,13 @@ Before configuring individual OmniAuth providers there are a few global settings # - { name: 'github', app_id: 'YOUR APP ID', # app_secret: 'YOUR APP SECRET', # args: { scope: 'user:email' } } + # - {"name": 'shibboleth', + # args: { shib_session_id_field: "HTTP_SHIB_SESSION_ID", + # shib_application_id_field: "HTTP_SHIB_APPLICATION_ID", + # uid_field: "HTTP_EPPN", + # name_field: "HTTP_CN", + # info_fields: {"email": "HTTP_MAIL" } } } + ``` 1. Change `enabled` to `true`. @@ -69,6 +76,7 @@ Before configuring individual OmniAuth providers there are a few global settings - [GitHub](github.md) - [Google](google.md) +- [Shibboleth](shibboleth.md) - [Twitter](twitter.md) ## Enable OmniAuth for an Existing User @@ -82,3 +90,41 @@ Existing users can enable OmniAuth for specific providers after the account is c 1. The user will be redirected to the provider. Once the user authorized GitLab they will be redirected back to GitLab. The chosen OmniAuth provider is now active and can be used to sign in to GitLab from then on. + +## Using Custom Omniauth Providers + +GitLab uses [Omniauth](http://www.omniauth.org/) for authentication and already ships with a few providers preinstalled (e.g. LDAP, GitHub, Twitter). But sometimes that is not enough and you need to integrate with other authentication solutions. For these cases you can use the Omniauth provider. + +### Steps + +These steps are fairly general and you will need to figure out the exact details from the Omniauth provider's documentation. + +- Stop GitLab: + + sudo service gitlab stop + +- Add the gem to your [Gemfile](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/Gemfile): + + gem "omniauth-your-auth-provider" + +- If you're using MySQL, install the new Omniauth provider gem by running the following command: + + sudo -u git -H bundle install --without development test postgres --path vendor/bundle --no-deployment + +- If you're using PostgreSQL, install the new Omniauth provider gem by running the following command: + + sudo -u git -H bundle install --without development test mysql --path vendor/bundle --no-deployment + + > These are the same commands you used in the [Install Gems section](#install-gems) with `--path vendor/bundle --no-deployment` instead of `--deployment`. + +- Start GitLab: + + sudo service gitlab start + +### Examples + +If you have successfully set up a provider that is not shipped with GitLab itself, please let us know. + +You can help others by reporting successful configurations and probably share a few insights or provide warnings for common errors or pitfalls by sharing your experience [in the public Wiki](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Custom-omniauth-provider-configurations). + +While we can't officially support every possible authentication mechanism out there, we'd like to at least help those with specific needs. diff --git a/doc/integration/shibboleth.md b/doc/integration/shibboleth.md new file mode 100644 index 00000000000..78317a5c0f2 --- /dev/null +++ b/doc/integration/shibboleth.md @@ -0,0 +1,78 @@ +# Shibboleth OmniAuth Provider + +This documentation is for enabling shibboleth with gitlab-omnibus package. + +In order to enable Shibboleth support in gitlab we need to use Apache instead of Nginx (It may be possible to use Nginx, however I did not found way to easily configure nginx that is bundled in gitlab-omnibus package). Apache uses mod_shib2 module for shibboleth authentication and can pass attributes as headers to omniauth-shibboleth provider. + + +To enable the Shibboleth OmniAuth provider you must: + +1. Configure Apache shibboleth module. Installation and configuration of module it self is out of scope of this document. +Check https://wiki.shibboleth.net/ for more info. + +1. You can find Apache config in gitlab-reciepes (https://github.com/gitlabhq/gitlab-recipes/blob/master/web-server/apache/gitlab-ssl.conf) + +Following changes are needed to enable shibboleth: + +protect omniauth-shibboleth callback url: +``` + <Location /users/auth/shibboleth/callback> + AuthType shibboleth + ShibRequestSetting requireSession 1 + ShibUseHeaders On + require valid-user + </Location> + + Alias /shibboleth-sp /usr/share/shibboleth + <Location /shibboleth-sp> + Satisfy any + </Location> + + <Location /Shibboleth.sso> + SetHandler shib + </Location> +``` +exclude shibboleth urls from rewriting, add "RewriteCond %{REQUEST_URI} !/Shibboleth.sso" and "RewriteCond %{REQUEST_URI} !/shibboleth-sp", config should look like this: +``` + #apache equivalent of nginx try files + RewriteEngine on + RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_URI} !/Shibboleth.sso + RewriteCond %{REQUEST_URI} !/shibboleth-sp + RewriteRule .* http://127.0.0.1:8080%{REQUEST_URI} [P,QSA] + RequestHeader set X_FORWARDED_PROTO 'https' +``` + +1. Edit /etc/gitlab/gitlab.rb configuration file, your shibboleth attributes should be in form of "HTTP_ATTRIBUTE" and you should addjust them to your need and environment. Add any other configuration you need. + +File it should look like this: +``` +external_url 'https://gitlab.example.com' +gitlab_rails['internal_api_url'] = 'https://gitlab.example.com' + +# disable nginx +nginx['enable'] = false + +gitlab_rails['omniauth_allow_single_sign_on'] = true +gitlab_rails['omniauth_block_auto_created_users'] = false +gitlab_rails['omniauth_enabled'] = true +gitlab_rails['omniauth_providers'] = [ + { + "name" => 'shibboleth', + "args" => { + "shib_session_id_field" => "HTTP_SHIB_SESSION_ID", + "shib_application_id_field" => "HTTP_SHIB_APPLICATION_ID", + "uid_field" => 'HTTP_EPPN', + "name_field" => 'HTTP_CN', + "info_fields" => { "email" => 'HTTP_MAIL'} + } + } +] + +``` +1. Save changes and reconfigure gitlab: +``` +sudo gitlab-ctl reconfigure +``` + +On the sign in page there should now be a "Sign in with: Shibboleth" icon below the regular sign in form. Click the icon to begin the authentication process. You will be redirected to IdP server (Depends on your Shibboleth module configuration). If everything goes well the user will be returned to GitLab and will be signed in. diff --git a/doc/raketasks/maintenance.md b/doc/raketasks/maintenance.md index a0901cc4070..f6bd7565799 100644 --- a/doc/raketasks/maintenance.md +++ b/doc/raketasks/maintenance.md @@ -115,8 +115,10 @@ Checking GitLab ... Finished This will create satellite repositories for all your projects. -If necessary, remove the `tmp/repo_satellites` directory and rerun the command below. +If necessary, remove the `repo_satellites` directory and rerun the commands below. ``` -bundle exec rake gitlab:satellites:create RAILS_ENV=production +sudo -u git -H mkdir -p /home/git/gitlab-satellites +sudo -u git -H bundle exec rake gitlab:satellites:create RAILS_ENV=production +sudo chmod u+rwx,g=rx,o-rwx /home/git/gitlab-satellites ``` diff --git a/doc/release/monthly.md b/doc/release/monthly.md index 2e19fc50890..2c26a90589d 100644 --- a/doc/release/monthly.md +++ b/doc/release/monthly.md @@ -107,11 +107,13 @@ List any major changes here, so the user is aware of them before starting to upg Check if any of these changed since last release: - <https://gitlab.com/gitlab-org/gitlab-ce/commits/master/lib/support/nginx/gitlab> +- <https://gitlab.com/gitlab-org/gitlab-ce/commits/master/lib/support/nginx/gitlab-ssl> - <https://gitlab.com/gitlab-org/gitlab-shell/commits/master/config.yml.example> - <https://gitlab.com/gitlab-org/gitlab-ce/commits/master/config/gitlab.yml.example> - <https://gitlab.com/gitlab-org/gitlab-ce/commits/master/config/unicorn.rb.example> - <https://gitlab.com/gitlab-org/gitlab-ce/commits/master/config/database.yml.mysql> - <https://gitlab.com/gitlab-org/gitlab-ce/commits/master/config/database.yml.postgresql> +- <https://gitlab.com/gitlab-org/gitlab-ce/blob/master/config/initializers/rack_attack.rb.example> #### 8. Need to update init script? diff --git a/doc/update/4.2-to-5.0.md b/doc/update/4.2-to-5.0.md index 6ec153f6245..897cd0b91fa 100644 --- a/doc/update/4.2-to-5.0.md +++ b/doc/update/4.2-to-5.0.md @@ -10,7 +10,7 @@ GitLab 5.0 is affected by critical security vulnerability CVE-2013-4490. - Self signed SSL certificates are not supported until GitLab 5.1 - **requires ruby1.9.3** -## 0. Stop gitlab +## 0. Stop GitLab sudo service gitlab stop @@ -41,7 +41,7 @@ git checkout v1.1.0 # copy config cp config.yml.example config.yml -# change url to gitlab instance +# change url to GitLab instance # ! make sure url end with '/' like 'https://gitlab.example/' vim config.yml @@ -49,14 +49,14 @@ vim config.yml ./support/rewrite-hooks.sh # check ruby version for git user ( 1.9 required!! ) -# gitlab shell requires system ruby 1.9 +# GitLab shell requires system ruby 1.9 ruby -v # exit from git user exit ``` -## 4. Copy gitlab instance to git user +## 4. Copy GitLab instance to git user ```bash sudo cp -R /home/gitlab/gitlab /home/git/gitlab @@ -162,8 +162,43 @@ sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production ``` -**P.S. If everything works as expected you can remove gitlab user from system** +## 9. Cleanup + +**If everything works as expected you can cleanup some old things** +Recommend you wait a bit and do a backup before completing the following. ```bash +# remove GitLab user from system sudo userdel -r gitlab + +cd /home/git + +# cleanup .profile +## remove text from .profile added during gitolite installation: +## PATH=\$PATH:/home/git/bin +## export PATH +## to see what a clean .profile for new users on your system would look like see /etc/skel/.profile +sudo -u git -H vim .profile + +# remove gitolite +sudo rm -R bin +sudo rm -Rf gitolite +sudo rm -R .gitolite +sudo rm .gitolite.rc +sudo rm -f gitlab.pub +sudo rm projects.list + +# reset tmp folders +sudo service gitlab stop +cd /home/git/gitlab +sudo rm -R tmp +sudo -u git -H mkdir tmp +sudo chmod -R u+rwX tmp/ + +# reboot system +sudo reboot + +# login, check that GitLab is running fine +cd /home/git/gitlab +sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production ``` diff --git a/doc/update/6.0-to-7.2.md b/doc/update/6.0-to-7.2.md index 770519a46e0..8dfcbcdd056 100644 --- a/doc/update/6.0-to-7.2.md +++ b/doc/update/6.0-to-7.2.md @@ -135,7 +135,8 @@ git diff 6-0-stable:config/gitlab.yml.example 7-2-stable:config/gitlab.yml.examp * Make `/home/git/gitlab/config/gitlab.yml` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-2-stable/config/gitlab.yml.example but with your settings. * Make `/home/git/gitlab/config/unicorn.rb` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-2-stable/config/unicorn.rb.example but with your settings. * Make `/home/git/gitlab-shell/config.yml` the same as https://gitlab.com/gitlab-org/gitlab-shell/blob/v1.9.7/config.yml.example but with your settings. -* Make `/etc/nginx/sites-available/nginx` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-2-stable/lib/support/nginx/gitlab but with your settings. +* HTTP setups: Make `/etc/nginx/sites-available/nginx` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-2-stable/lib/support/nginx/gitlab but with your settings. +* HTTPS setups: Make `/etc/nginx/sites-available/nginx-ssl` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-2-stable/lib/support/nginx/gitlab-ssl but with your settings. * Copy rack attack middleware config ```bash diff --git a/doc/update/6.9-to-7.0.md b/doc/update/6.9-to-7.0.md index bbb3b2617a7..1f3421a799b 100644 --- a/doc/update/6.9-to-7.0.md +++ b/doc/update/6.9-to-7.0.md @@ -105,6 +105,9 @@ There are new configuration options available for gitlab.yml. View them with the git diff origin/6-9-stable:config/gitlab.yml.example origin/7-0-stable:config/gitlab.yml.example ``` +* HTTP setups: Make `/etc/nginx/sites-available/nginx` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-0-stable/lib/support/nginx/gitlab but with your settings. +* HTTPS setups: Make `/etc/nginx/sites-available/nginx-ssl` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-0-stable/lib/support/nginx/gitlab-ssl but with your setting + ### 7. Start application sudo service gitlab start diff --git a/doc/update/7.1-to-7.2.md b/doc/update/7.1-to-7.2.md index b06f62aeb03..ff5574114a8 100644 --- a/doc/update/7.1-to-7.2.md +++ b/doc/update/7.1-to-7.2.md @@ -89,6 +89,9 @@ There are new configuration options available for gitlab.yml. View them with the git diff 7-1-stable:config/gitlab.yml.example 7-2-stable:config/gitlab.yml.example ``` +* HTTP setups: Make `/etc/nginx/sites-available/nginx` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-0-stable/lib/support/nginx/gitlab but with your settings. +* HTTPS setups: Make `/etc/nginx/sites-available/nginx-ssl` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-0-stable/lib/support/nginx/gitlab-ssl but with your setting + Update rack attack middleware config ``` diff --git a/doc/update/7.2-to-7.3.md b/doc/update/7.2-to-7.3.md new file mode 100644 index 00000000000..7cc8f8e2ede --- /dev/null +++ b/doc/update/7.2-to-7.3.md @@ -0,0 +1,10 @@ +# From 7.2 to 7.3 + +# GitLab 7.3 has not been released yet! + +This document currently just serves as a place to keep track of updates that will be needed for the 7.3 update. + +### Update config files + +* HTTP setups: Make `/etc/nginx/sites-available/nginx` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-0-stable/lib/support/nginx/gitlab but with your settings. +* HTTPS setups: Make `/etc/nginx/sites-available/nginx-ssl` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-0-stable/lib/support/nginx/gitlab-ssl but with your setting
\ No newline at end of file diff --git a/doc/update/mysql_to_postgresql.md b/doc/update/mysql_to_postgresql.md index ed72e156efe..695c083d361 100644 --- a/doc/update/mysql_to_postgresql.md +++ b/doc/update/mysql_to_postgresql.md @@ -21,6 +21,9 @@ sudo -u git psql -f databasename.psql -d gitlabhq_production # Rebuild indexes (see below) +# Install gems for PostgreSQL (note: the line below states '--without ... mysql') +sudo -u git -H bundle install --without development test mysql --deployment + sudo service gitlab start ``` diff --git a/features/project/commits/branches.feature b/features/project/commits/branches.feature index d657bd4951f..d124cb7eecd 100644 --- a/features/project/commits/branches.feature +++ b/features/project/commits/branches.feature @@ -15,7 +15,7 @@ Feature: Project Browse branches Scenario: I create a branch Given I visit project branches page And I click new branch link - When I submit new branch form + And I submit new branch form Then I should see new branch created @javascript @@ -23,3 +23,21 @@ Feature: Project Browse branches Given I visit project branches page And I click branch 'improve/awesome' delete link Then I should not see branch 'improve/awesome' + + Scenario: I create a branch with invalid name + Given I visit project branches page + And I click new branch link + And I submit new branch form with invalid name + Then I should see new an error that branch is invalid + + Scenario: I create a branch with invalid reference + Given I visit project branches page + And I click new branch link + And I submit new branch form with invalid reference + Then I should see new an error that ref is invalid + + Scenario: I create a branch that already exists + Given I visit project branches page + And I click new branch link + And I submit new branch form with branch that already exists + Then I should see new an error that branch already exists diff --git a/features/project/commits/tags.feature b/features/project/commits/tags.feature index 1ac0f8bfa45..36c7a6492ff 100644 --- a/features/project/commits/tags.feature +++ b/features/project/commits/tags.feature @@ -7,5 +7,25 @@ Feature: Project Browse tags Scenario: I can see all git tags Then I should see "Shop" all tags list + Scenario: I create a tag + And I click new tag link + And I submit new tag form + Then I should see new tag created + + Scenario: I create a tag with invalid name + And I click new tag link + And I submit new tag form with invalid name + Then I should see new an error that tag is invalid + + Scenario: I create a tag with invalid reference + And I click new tag link + And I submit new tag form with invalid reference + Then I should see new an error that tag ref is invalid + + Scenario: I create a tag that already exists + And I click new tag link + And I submit new tag form with tag that already exists + Then I should see new an error that tag already exists + # @wip # Scenario: I can download project by tag diff --git a/features/project/issues/labels.feature b/features/project/issues/labels.feature index 29cf5307271..77ee5d8a686 100644 --- a/features/project/issues/labels.feature +++ b/features/project/issues/labels.feature @@ -24,6 +24,11 @@ Feature: Project Labels When I remove label 'bug' Then I should not see label 'bug' + @javascript + Scenario: I remove all labels + When I delete all labels + Then I should see labels help message + Scenario: I create a label with invalid color Given I visit project "Shop" new label page When I submit new label with invalid color diff --git a/features/project/shortcuts.feature b/features/project/shortcuts.feature index 16882fded8e..e5f9c103fb1 100644 --- a/features/project/shortcuts.feature +++ b/features/project/shortcuts.feature @@ -44,3 +44,9 @@ Feature: Project shortcuts Scenario: Navigate to wiki tab Given I press "g" and "w" Then the active main tab should be Wiki + + @javascript + Scenario: Navigate to project feed + Given I visit my project's files page + Given I press "g" and "p" + Then the active main tab should be Home diff --git a/features/project/source/browse_files.feature b/features/project/source/browse_files.feature index f8934da8de5..a674800ccb8 100644 --- a/features/project/source/browse_files.feature +++ b/features/project/source/browse_files.feature @@ -51,3 +51,15 @@ Feature: Project Browse files Scenario: I can browse code with Browse Code Given I click on history link Then I see Browse code link + + # Permalink + + Scenario: I click on the permalink link from a branch ref + Given I click on ".gitignore" file in repo + And I click on permalink + Then I am redirected to the permalink URL + + Scenario: I don't see the permalink link from a SHA ref + Given I visit project source page for "6d394385cf567f80a8fd85055db1ab4c5295806f" + And I click on ".gitignore" file in repo + Then I don't see the permalink link diff --git a/features/snippet_search.feature b/features/snippet_search.feature new file mode 100644 index 00000000000..834bd3b2376 --- /dev/null +++ b/features/snippet_search.feature @@ -0,0 +1,20 @@ +@dashboard +Feature: Snippet Search + Background: + Given I sign in as a user + And I have public "Personal snippet one" snippet + And I have private "Personal snippet private" snippet + And I have a public many lined snippet + + Scenario: I should see my public and private snippets + When I search for "snippet" in snippet titles + Then I should see "Personal snippet one" in results + And I should see "Personal snippet private" in results + + Scenario: I should see three surrounding lines on either side of a matching snippet line + When I search for "line seven" in snippet contents + Then I should see "line four" in results + And I should see "line seven" in results + And I should see "line ten" in results + And I should not see "line three" in results + And I should not see "line eleven" in results diff --git a/features/steps/project/browse_branches.rb b/features/steps/project/browse_branches.rb index c00a95a62fd..cfc88bdad22 100644 --- a/features/steps/project/browse_branches.rb +++ b/features/steps/project/browse_branches.rb @@ -38,10 +38,38 @@ class ProjectBrowseBranches < Spinach::FeatureSteps click_button 'Create branch' end + step 'I submit new branch form with invalid name' do + fill_in 'branch_name', with: '1.0 stable' + fill_in 'ref', with: 'master' + click_button 'Create branch' + end + + step 'I submit new branch form with invalid reference' do + fill_in 'branch_name', with: 'foo' + fill_in 'ref', with: 'foo' + click_button 'Create branch' + end + + step 'I submit new branch form with branch that already exists' do + fill_in 'branch_name', with: 'master' + fill_in 'ref', with: 'master' + click_button 'Create branch' + end + step 'I should see new branch created' do - within '.tree-ref-holder' do - page.should have_content 'deploy_keys' - end + page.should have_content 'deploy_keys' + end + + step 'I should see new an error that branch is invalid' do + page.should have_content 'Branch name invalid' + end + + step 'I should see new an error that ref is invalid' do + page.should have_content 'Invalid reference name' + end + + step 'I should see new an error that branch already exists' do + page.should have_content 'Branch already exists' end step "I click branch 'improve/awesome' delete link" do diff --git a/features/steps/project/browse_files.rb b/features/steps/project/browse_files.rb index 6fd0c2c2ded..bd395a0d26e 100644 --- a/features/steps/project/browse_files.rb +++ b/features/steps/project/browse_files.rb @@ -90,4 +90,17 @@ class ProjectBrowseFiles < Spinach::FeatureSteps page.should_not have_link 'Browse File »' page.should_not have_link 'Browse Dir »' end + + step 'I click on permalink' do + click_link 'permalink' + end + + step 'I am redirected to the permalink URL' do + expect(current_path).to eq(project_blob_path( + @project, @project.repository.commit.sha + '/.gitignore')) + end + + step "I don't see the permalink link" do + expect(page).not_to have_link('permalink') + end end diff --git a/features/steps/project/browse_tags.rb b/features/steps/project/browse_tags.rb index 7c679911e00..64c0c284f6c 100644 --- a/features/steps/project/browse_tags.rb +++ b/features/steps/project/browse_tags.rb @@ -3,8 +3,52 @@ class ProjectBrowseTags < Spinach::FeatureSteps include SharedProject include SharedPaths - Then 'I should see "Shop" all tags list' do + step 'I should see "Shop" all tags list' do page.should have_content "Tags" page.should have_content "v1.0.0" end + + step 'I click new tag link' do + click_link 'New tag' + end + + step 'I submit new tag form' do + fill_in 'tag_name', with: 'v7.0' + fill_in 'ref', with: 'master' + click_button 'Create tag' + end + + step 'I submit new tag form with invalid name' do + fill_in 'tag_name', with: 'v 1.0' + fill_in 'ref', with: 'master' + click_button 'Create tag' + end + + step 'I submit new tag form with invalid reference' do + fill_in 'tag_name', with: 'foo' + fill_in 'ref', with: 'foo' + click_button 'Create tag' + end + + step 'I submit new tag form with tag that already exists' do + fill_in 'tag_name', with: 'v1.0.0' + fill_in 'ref', with: 'master' + click_button 'Create tag' + end + + step 'I should see new tag created' do + page.should have_content 'v7.0' + end + + step 'I should see new an error that tag is invalid' do + page.should have_content 'Tag name invalid' + end + + step 'I should see new an error that tag ref is invalid' do + page.should have_content 'Invalid reference name' + end + + step 'I should see new an error that tag already exists' do + page.should have_content 'Tag already exists' + end end diff --git a/features/steps/project/issues.rb b/features/steps/project/issues.rb index ab2d7cee2e3..32a3a0d3f56 100644 --- a/features/steps/project/issues.rb +++ b/features/steps/project/issues.rb @@ -74,34 +74,34 @@ class ProjectIssues < Spinach::FeatureSteps end Given 'I fill in issue search with "Re"' do - fill_in 'issue_search', with: "Re" + filter_issue "Re" end Given 'I fill in issue search with "Bu"' do - fill_in 'issue_search', with: "Bu" + filter_issue "Bu" end And 'I fill in issue search with ".3"' do - fill_in 'issue_search', with: ".3" + filter_issue ".3" end And 'I fill in issue search with "Something"' do - fill_in 'issue_search', with: "Something" + filter_issue "Something" end And 'I fill in issue search with ""' do - fill_in 'issue_search', with: "" + filter_issue "" end Given 'project "Shop" has milestone "v2.2"' do - project = Project.find_by(name: "Shop") + milestone = create(:milestone, title: "v2.2", project: project) 3.times { create(:issue, project: project, milestone: milestone) } end And 'project "Shop" has milestone "v3.0"' do - project = Project.find_by(name: "Shop") + milestone = create(:milestone, title: "v3.0", project: project) 3.times { create(:issue, project: project, milestone: milestone) } @@ -117,20 +117,20 @@ class ProjectIssues < Spinach::FeatureSteps end When 'I select first assignee from "Shop" project' do - project = Project.find_by(name: "Shop") + first_assignee = project.users.first select first_assignee.name, from: "assignee_id" end Then 'I should see first assignee from "Shop" as selected assignee' do issues_assignee_selector = "#issue_assignee_id_chzn > a" - project = Project.find_by(name: "Shop") + assignee_name = project.users.first.name page.find(issues_assignee_selector).should have_content(assignee_name) end And 'project "Shop" have "Release 0.4" open issue' do - project = Project.find_by(name: "Shop") + create(:issue, title: "Release 0.4", project: project, @@ -140,7 +140,6 @@ class ProjectIssues < Spinach::FeatureSteps end And 'project "Shop" have "Tweet control" open issue' do - project = Project.find_by(name: "Shop") create(:issue, title: "Tweet control", project: project, @@ -148,7 +147,6 @@ class ProjectIssues < Spinach::FeatureSteps end And 'project "Shop" have "Release 0.3" closed issue' do - project = Project.find_by(name: "Shop") create(:closed_issue, title: "Release 0.3", project: project, @@ -189,25 +187,23 @@ class ProjectIssues < Spinach::FeatureSteps end step 'project \'Shop\' has issue \'Bugfix1\' with description: \'Description for issue1\'' do - project = Project.find_by(name: 'Shop') issue = create(:issue, title: 'Bugfix1', description: 'Description for issue1', project: project) end step 'project \'Shop\' has issue \'Feature1\' with description: \'Feature submitted for issue1\'' do - project = Project.find_by(name: 'Shop') issue = create(:issue, title: 'Feature1', description: 'Feature submitted for issue1', project: project) end step 'I fill in issue search with \'Description for issue1\'' do - fill_in 'issue_search', with: 'Description for issue' + filter_issue 'Description for issue' end step 'I fill in issue search with \'issue1\'' do - fill_in 'issue_search', with: 'issue1' + filter_issue 'issue1' end step 'I fill in issue search with \'Rock and roll\'' do - fill_in 'issue_search', with: 'Description for issue' + filter_issue 'Description for issue' end step 'I should see \'Bugfix1\' in issues' do @@ -221,4 +217,15 @@ class ProjectIssues < Spinach::FeatureSteps step 'I should not see \'Bugfix1\' in issues' do page.should_not have_content 'Bugfix1' end + + def filter_issue(text) + fill_in 'issue_search', with: text + + # make sure AJAX request finished + URI.parse(current_url).request_uri == project_issues_path(project, issue_search: text) + end + + def project + @project ||= Project.find_by(name: 'Shop') + end end diff --git a/features/steps/project/labels.rb b/features/steps/project/labels.rb index 8320405e096..6dd4df8a1ad 100644 --- a/features/steps/project/labels.rb +++ b/features/steps/project/labels.rb @@ -25,6 +25,22 @@ class ProjectLabels < Spinach::FeatureSteps end end + step 'I delete all labels' do + within '.labels' do + all('.btn-remove').each do |remove| + remove.click + sleep 0.05 + end + end + end + + step 'I should see labels help message' do + within '.labels' do + page.should have_content 'Create first label or generate default set of '\ + 'labels' + end + end + step 'I submit new label \'support\'' do fill_in 'Title', with: 'support' fill_in 'Background Color', with: '#F95610' diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb index 0d06383509f..276947dc060 100644 --- a/features/steps/shared/paths.rb +++ b/features/steps/shared/paths.rb @@ -269,6 +269,12 @@ module SharedPaths visit project_tree_path(@project, "6d39438") end + step 'I visit project source page for' \ + ' "6d394385cf567f80a8fd85055db1ab4c5295806f"' do + visit project_tree_path(@project, + '6d394385cf567f80a8fd85055db1ab4c5295806f') + end + step 'I visit project tags page' do visit project_tags_path(@project) end diff --git a/features/steps/shared/project_tab.rb b/features/steps/shared/project_tab.rb index 498a173e9a5..6aa4f1b20df 100644 --- a/features/steps/shared/project_tab.rb +++ b/features/steps/shared/project_tab.rb @@ -1,3 +1,5 @@ +require_relative 'active_tab' + module SharedProjectTab include Spinach::DSL include SharedActiveTab diff --git a/features/steps/shared/search.rb b/features/steps/shared/search.rb new file mode 100644 index 00000000000..6c3d601763d --- /dev/null +++ b/features/steps/shared/search.rb @@ -0,0 +1,11 @@ +module SharedSearch + include Spinach::DSL + + def search_snippet_contents(query) + visit "/search?search=#{URI::encode(query)}&snippets=true&scope=snippet_blobs" + end + + def search_snippet_titles(query) + visit "/search?search=#{URI::encode(query)}&snippets=true&scope=snippet_titles" + end +end diff --git a/features/steps/shared/snippet.rb b/features/steps/shared/snippet.rb index 543e43196a5..c64299ae6f3 100644 --- a/features/steps/shared/snippet.rb +++ b/features/steps/shared/snippet.rb @@ -18,4 +18,27 @@ module SharedSnippet private: true, author: current_user) end + And 'I have a public many lined snippet' do + create(:personal_snippet, + title: 'Many lined snippet', + content: <<-END.gsub(/^\s+\|/, ''), + |line one + |line two + |line three + |line four + |line five + |line six + |line seven + |line eight + |line nine + |line ten + |line eleven + |line twelve + |line thirteen + |line fourteen + END + file_name: 'many_lined_snippet.rb', + private: true, + author: current_user) + end end diff --git a/features/steps/snippet_search.rb b/features/steps/snippet_search.rb new file mode 100644 index 00000000000..fe03b847c56 --- /dev/null +++ b/features/steps/snippet_search.rb @@ -0,0 +1,56 @@ +class Spinach::Features::SnippetSearch < Spinach::FeatureSteps + include SharedAuthentication + include SharedPaths + include SharedSnippet + include SharedUser + include SharedSearch + + step 'I search for "snippet" in snippet titles' do + search_snippet_titles 'snippet' + end + + step 'I search for "snippet private" in snippet titles' do + search_snippet_titles 'snippet private' + end + + step 'I search for "line seven" in snippet contents' do + search_snippet_contents 'line seven' + end + + step 'I should see "line seven" in results' do + page.should have_content 'line seven' + end + + step 'I should see "line four" in results' do + page.should have_content 'line four' + end + + step 'I should see "line ten" in results' do + page.should have_content 'line ten' + end + + step 'I should not see "line eleven" in results' do + page.should_not have_content 'line eleven' + end + + step 'I should not see "line three" in results' do + page.should_not have_content 'line three' + end + + Then 'I should see "Personal snippet one" in results' do + page.should have_content 'Personal snippet one' + end + + And 'I should see "Personal snippet private" in results' do + page.should have_content 'Personal snippet private' + end + + Then 'I should not see "Personal snippet one" in results' do + page.should_not have_content 'Personal snippet one' + end + + And 'I should not see "Personal snippet private" in results' do + page.should_not have_content 'Personal snippet private' + end + +end diff --git a/lib/api/branches.rb b/lib/api/branches.rb index b32a4aa7bc2..4db5f61dd28 100644 --- a/lib/api/branches.rb +++ b/lib/api/branches.rb @@ -80,9 +80,17 @@ module API # POST /projects/:id/repository/branches post ":id/repository/branches" do authorize_push_project - @branch = CreateBranchService.new.execute(user_project, params[:branch_name], params[:ref], current_user) - - present @branch, with: Entities::RepoObject, project: user_project + result = CreateBranchService.new.execute(user_project, + params[:branch_name], + params[:ref], + current_user) + if result[:status] == :success + present result[:branch], + with: Entities::RepoObject, + project: user_project + else + render_api_error!(result[:message], 400) + end end # Delete branch @@ -99,7 +107,7 @@ module API if result[:state] == :success true else - render_api_error!(result[:message], 405) + render_api_error!(result[:message], result[:return_code]) end end end diff --git a/lib/api/issues.rb b/lib/api/issues.rb index 043ce04d321..5369149cdfc 100644 --- a/lib/api/issues.rb +++ b/lib/api/issues.rb @@ -11,6 +11,10 @@ module API else issues end end + + def filter_issues_labels(issues, labels) + issues.includes(:labels).where("labels.title" => labels.split(',')) + end end resource :issues do @@ -18,13 +22,22 @@ module API # # Parameters: # state (optional) - Return "opened" or "closed" issues - # + # labels (optional) - Comma-separated list of label names + # Example Requests: # GET /issues # GET /issues?state=opened # GET /issues?state=closed + # GET /issues?labels=foo + # GET /issues?labels=foo,bar + # GET /issues?labels=foo,bar&state=opened get do - present paginate(filter_issues_state(current_user.issues, params['state'])), with: Entities::Issue + issues = current_user.issues + issues = filter_issues_state(issues, params[:state]) unless params[:state].nil? + issues = filter_issues_labels(issues, params[:labels]) unless params[:labels].nil? + issues = issues.order('issues.id DESC') + + present paginate(issues), with: Entities::Issue end end @@ -34,13 +47,23 @@ module API # Parameters: # id (required) - The ID of a project # state (optional) - Return "opened" or "closed" issues + # labels (optional) - Comma-separated list of label names # # Example Requests: # GET /projects/:id/issues # GET /projects/:id/issues?state=opened # GET /projects/:id/issues?state=closed + # GET /projects/:id/issues + # GET /projects/:id/issues?labels=foo + # GET /projects/:id/issues?labels=foo,bar + # GET /projects/:id/issues?labels=foo,bar&state=opened get ":id/issues" do - present paginate(filter_issues_state(user_project.issues, params['state'])), with: Entities::Issue + issues = user_project.issues + issues = filter_issues_state(issues, params[:state]) unless params[:state].nil? + issues = filter_issues_labels(issues, params[:labels]) unless params[:labels].nil? + issues = issues.order('issues.id DESC') + + present paginate(issues), with: Entities::Issue end # Get a single project issue diff --git a/lib/api/repositories.rb b/lib/api/repositories.rb index 42068bb343d..07c29aa7b4c 100644 --- a/lib/api/repositories.rb +++ b/lib/api/repositories.rb @@ -32,14 +32,23 @@ module API # id (required) - The ID of a project # tag_name (required) - The name of the tag # ref (required) - Create tag from commit sha or branch + # message (optional) - Specifying a message creates an annotated tag. # Example Request: # POST /projects/:id/repository/tags post ':id/repository/tags' do authorize_push_project - @tag = CreateTagService.new.execute(user_project, params[:tag_name], - params[:ref], current_user) - - present @tag, with: Entities::RepoObject, project: user_project + message = params[:message] || nil + result = CreateTagService.new.execute(user_project, params[:tag_name], + params[:ref], message, + current_user) + + if result[:status] == :success + present result[:tag], + with: Entities::RepoObject, + project: user_project + else + render_api_error!(result[:message], 400) + end end # Get a project repository tree diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb index 6f7c4f7c909..ea05fa2c261 100644 --- a/lib/backup/repository.rb +++ b/lib/backup/repository.rb @@ -69,7 +69,7 @@ module Backup end print 'Put GitLab hooks in repositories dirs'.yellow - if system("#{Gitlab.config.gitlab_shell.path}/support/rewrite-hooks.sh", Gitlab.config.gitlab_shell.repos_path) + if system("#{Gitlab.config.gitlab_shell.path}/bin/create-hooks") puts " [DONE]".green else puts " [FAILED]".red diff --git a/lib/gitlab/backend/shell.rb b/lib/gitlab/backend/shell.rb index 53bff3037e5..907373ab991 100644 --- a/lib/gitlab/backend/shell.rb +++ b/lib/gitlab/backend/shell.rb @@ -107,12 +107,17 @@ module Gitlab # path - project path with namespace # tag_name - new tag name # ref - HEAD for new tag + # message - optional message for tag (annotated tag) # # Ex. # add_tag("gitlab/gitlab-ci", "v4.0", "master") + # add_tag("gitlab/gitlab-ci", "v4.0", "master", "message") # - def add_tag(path, tag_name, ref) - system "#{gitlab_shell_path}/bin/gitlab-projects", "create-tag", "#{path}.git", tag_name, ref + def add_tag(path, tag_name, ref, message = nil) + cmd = %W(#{gitlab_shell_path}/bin/gitlab-projects create-tag #{path}.git + #{tag_name} #{ref}) + cmd << message unless message.nil? || message.empty? + system *cmd end # Remove repository tag diff --git a/lib/gitlab/blacklist.rb b/lib/gitlab/blacklist.rb index 65efb6e4407..43145e0ee1b 100644 --- a/lib/gitlab/blacklist.rb +++ b/lib/gitlab/blacklist.rb @@ -27,6 +27,7 @@ module Gitlab notes unsubscribes all + ci ) end end diff --git a/lib/gitlab/diff/file.rb b/lib/gitlab/diff/file.rb new file mode 100644 index 00000000000..19a1198c68c --- /dev/null +++ b/lib/gitlab/diff/file.rb @@ -0,0 +1,49 @@ +module Gitlab + module Diff + class File + attr_reader :diff + + delegate :new_file, :deleted_file, :renamed_file, + :old_path, :new_path, to: :diff, prefix: false + + def initialize(diff) + @diff = diff + end + + # Array of Gitlab::DIff::Line objects + def diff_lines + @lines ||= parser.parse(raw_diff.lines) + end + + def mode_changed? + !!(diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode) + end + + def parser + Gitlab::Diff::Parser.new + end + + def raw_diff + diff.diff + end + + def next_line(index) + diff_lines[index + 1] + end + + def prev_line(index) + if index > 0 + diff_lines[index - 1] + end + end + + def file_path + if diff.new_path.present? + diff.new_path + elsif diff.old_path.present? + diff.old_path + end + end + end + end +end diff --git a/lib/gitlab/diff/line.rb b/lib/gitlab/diff/line.rb new file mode 100644 index 00000000000..8ac1b15e88a --- /dev/null +++ b/lib/gitlab/diff/line.rb @@ -0,0 +1,12 @@ +module Gitlab + module Diff + class Line + attr_reader :type, :text, :index, :old_pos, :new_pos + + def initialize(text, type, index, old_pos, new_pos) + @text, @type, @index = text, type, index + @old_pos, @new_pos = old_pos, new_pos + end + end + end +end diff --git a/lib/gitlab/diff/line_code.rb b/lib/gitlab/diff/line_code.rb new file mode 100644 index 00000000000..f3578ab3d35 --- /dev/null +++ b/lib/gitlab/diff/line_code.rb @@ -0,0 +1,9 @@ +module Gitlab + module Diff + class LineCode + def self.generate(file_path, new_line_position, old_line_position) + "#{Digest::SHA1.hexdigest(file_path)}_#{old_line_position}_#{new_line_position}" + end + end + end +end diff --git a/lib/gitlab/diff/parser.rb b/lib/gitlab/diff/parser.rb new file mode 100644 index 00000000000..9d6309954a4 --- /dev/null +++ b/lib/gitlab/diff/parser.rb @@ -0,0 +1,81 @@ +module Gitlab + module Diff + class Parser + include Enumerable + + def parse(lines) + @lines = lines, + lines_obj = [] + line_obj_index = 0 + line_old = 1 + line_new = 1 + type = nil + + lines_arr = ::Gitlab::InlineDiff.processing lines + + lines_arr.each do |line| + raw_line = line.dup + + next if filename?(line) + + full_line = html_escape(line.gsub(/\n/, '')) + full_line = ::Gitlab::InlineDiff.replace_markers full_line + + if line.match(/^@@ -/) + type = "match" + + line_old = line.match(/\-[0-9]*/)[0].to_i.abs rescue 0 + line_new = line.match(/\+[0-9]*/)[0].to_i.abs rescue 0 + + next if line_old == 1 && line_new == 1 #top of file + lines_obj << Gitlab::Diff::Line.new(full_line, type, line_obj_index, line_old, line_new) + line_obj_index += 1 + next + else + type = identification_type(line) + lines_obj << Gitlab::Diff::Line.new(full_line, type, line_obj_index, line_old, line_new) + line_obj_index += 1 + end + + + if line[0] == "+" + line_new += 1 + elsif line[0] == "-" + line_old += 1 + else + line_new += 1 + line_old += 1 + end + end + + lines_obj + end + + def empty? + @lines.empty? + end + + private + + def filename?(line) + line.start_with?('--- /dev/null', '+++ /dev/null', '--- a', '+++ b', + '--- /tmp/diffy', '+++ /tmp/diffy') + end + + def identification_type(line) + if line[0] == "+" + "new" + elsif line[0] == "-" + "old" + else + nil + end + end + + def html_escape str + replacements = { '&' => '&', '>' => '>', '<' => '<', '"' => '"', "'" => ''' } + str.gsub(/[&"'><]/, replacements) + end + end + end +end diff --git a/lib/gitlab/diff_parser.rb b/lib/gitlab/diff_parser.rb deleted file mode 100644 index b244295027e..00000000000 --- a/lib/gitlab/diff_parser.rb +++ /dev/null @@ -1,83 +0,0 @@ -module Gitlab - class DiffParser - include Enumerable - - attr_reader :lines, :new_path - - def initialize(lines, new_path = '') - @lines = lines - @new_path = new_path - end - - def each - line_old = 1 - line_new = 1 - type = nil - - lines_arr = ::Gitlab::InlineDiff.processing lines - lines_arr.each do |line| - raw_line = line.dup - - next if filename?(line) - - full_line = html_escape(line.gsub(/\n/, '')) - full_line = ::Gitlab::InlineDiff.replace_markers full_line - - if line.match(/^@@ -/) - type = "match" - - line_old = line.match(/\-[0-9]*/)[0].to_i.abs rescue 0 - line_new = line.match(/\+[0-9]*/)[0].to_i.abs rescue 0 - - next if line_old == 1 && line_new == 1 #top of file - yield(full_line, type, nil, line_new, line_old) - next - else - type = identification_type(line) - line_code = generate_line_code(new_path, line_new, line_old) - yield(full_line, type, line_code, line_new, line_old, raw_line) - end - - - if line[0] == "+" - line_new += 1 - elsif line[0] == "-" - line_old += 1 - else - line_new += 1 - line_old += 1 - end - end - end - - def empty? - @lines.empty? - end - - private - - def filename?(line) - line.start_with?('--- /dev/null', '+++ /dev/null', '--- a', '+++ b', - '--- /tmp/diffy', '+++ /tmp/diffy') - end - - def identification_type(line) - if line[0] == "+" - "new" - elsif line[0] == "-" - "old" - else - nil - end - end - - def generate_line_code(path, line_new, line_old) - "#{Digest::SHA1.hexdigest(path)}_#{line_old}_#{line_new}" - end - - def html_escape str - replacements = { '&' => '&', '>' => '>', '<' => '<', '"' => '"', "'" => ''' } - str.gsub(/[&"'><]/, replacements) - end - end -end diff --git a/lib/gitlab/git_ref_validator.rb b/lib/gitlab/git_ref_validator.rb new file mode 100644 index 00000000000..13cb08948bb --- /dev/null +++ b/lib/gitlab/git_ref_validator.rb @@ -0,0 +1,11 @@ +module Gitlab + module GitRefValidator + extend self + # Validates a given name against the git reference specification + # + # Returns true for a valid reference name, false otherwise + def validate(ref_name) + system *%W(git check-ref-format refs/#{ref_name}) + end + end +end diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb index b248d8f9436..6017a4c86c1 100644 --- a/lib/gitlab/markdown.rb +++ b/lib/gitlab/markdown.rb @@ -192,8 +192,12 @@ module Gitlab link_to("##{identifier}", url, options) end - elsif project.issues_tracker == 'jira' - reference_jira_issue(identifier, project) + else + config = Gitlab.config + external_issue_tracker = config.issues_tracker[project.issues_tracker] + if external_issue_tracker.present? + reference_external_issue(identifier, external_issue_tracker, project) + end end end @@ -229,15 +233,15 @@ module Gitlab end end - def reference_jira_issue(identifier, project = @project) - url = url_for_issue(identifier) - title = Gitlab.config.issues_tracker[project.issues_tracker]["title"] + def reference_external_issue(identifier, issue_tracker, project = @project) + url = url_for_issue(identifier, project) + title = issue_tracker['title'] options = html_options.merge( title: "Issue in #{title}", class: "gfm gfm-issue #{html_options[:class]}" ) - link_to("#{identifier}", url, options) + link_to("##{identifier}", url, options) end end end diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb index 90511662b20..9dc8b34d9c7 100644 --- a/lib/gitlab/project_search_results.rb +++ b/lib/gitlab/project_search_results.rb @@ -14,13 +14,16 @@ module Gitlab notes.page(page).per(per_page) when 'blobs' Kaminari.paginate_array(blobs).page(page).per(per_page) + when 'wiki_blobs' + Kaminari.paginate_array(wiki_blobs).page(page).per(per_page) else super end end def total_count - @total_count ||= issues_count + merge_requests_count + blobs_count + notes_count + @total_count ||= issues_count + merge_requests_count + blobs_count + + notes_count + wiki_blobs_count end def blobs_count @@ -31,6 +34,10 @@ module Gitlab @notes_count ||= notes.count end + def wiki_blobs_count + @wiki_blobs_count ||= wiki_blobs.count + end + private def blobs @@ -41,6 +48,20 @@ module Gitlab end end + def wiki_blobs + if project.wiki_enabled? + wiki_repo = Repository.new(ProjectWiki.new(project).path_with_namespace) + + if wiki_repo.exists? + wiki_repo.search_files(query) + else + [] + end + else + [] + end + end + def notes Note.where(project_id: limit_project_ids).search(query).order('updated_at DESC') end diff --git a/lib/gitlab/snippet_search_results.rb b/lib/gitlab/snippet_search_results.rb new file mode 100644 index 00000000000..938219efdb2 --- /dev/null +++ b/lib/gitlab/snippet_search_results.rb @@ -0,0 +1,131 @@ +module Gitlab + class SnippetSearchResults < SearchResults + attr_reader :limit_snippet_ids + + def initialize(limit_snippet_ids, query) + @limit_snippet_ids = limit_snippet_ids + @query = query + end + + def objects(scope, page = nil) + case scope + when 'snippet_titles' + Kaminari.paginate_array(snippet_titles).page(page).per(per_page) + when 'snippet_blobs' + Kaminari.paginate_array(snippet_blobs).page(page).per(per_page) + else + super + end + end + + def total_count + @total_count ||= snippet_titles_count + snippet_blobs_count + end + + def snippet_titles_count + @snippet_titles_count ||= snippet_titles.count + end + + def snippet_blobs_count + @snippet_blobs_count ||= snippet_blobs.count + end + + private + + def snippet_titles + Snippet.where(id: limit_snippet_ids).search(query).order('updated_at DESC') + end + + def snippet_blobs + search = Snippet.where(id: limit_snippet_ids).search_code(query) + search = search.order('updated_at DESC').to_a + snippets = [] + search.each { |e| snippets << chunk_snippet(e) } + snippets + end + + def default_scope + 'snippet_blobs' + end + + # Get an array of line numbers surrounding a matching + # line, bounded by min/max. + # + # @returns Array of line numbers + def bounded_line_numbers(line, min, max) + lower = line - surrounding_lines > min ? line - surrounding_lines : min + upper = line + surrounding_lines < max ? line + surrounding_lines : max + (lower..upper).to_a + end + + # Returns a sorted set of lines to be included in a snippet preview. + # This ensures matching adjacent lines do not display duplicated + # surrounding code. + # + # @returns Array, unique and sorted. + def matching_lines(lined_content) + used_lines = [] + lined_content.each_with_index do |line, line_number| + used_lines.concat bounded_line_numbers( + line_number, + 0, + lined_content.size + ) if line.include?(query) + end + + used_lines.uniq.sort + end + + # 'Chunkify' entire snippet. Splits the snippet data into matching lines + + # surrounding_lines() worth of unmatching lines. + # + # @returns a hash with {snippet_object, snippet_chunks:{data,start_line}} + def chunk_snippet(snippet) + lined_content = snippet.content.split("\n") + used_lines = matching_lines(lined_content) + + snippet_chunk = [] + snippet_chunks = [] + snippet_start_line = 0 + last_line = -1 + + # Go through each used line, and add consecutive lines as a single chunk + # to the snippet chunk array. + used_lines.each do |line_number| + if last_line < 0 + # Start a new chunk. + snippet_start_line = line_number + snippet_chunk << lined_content[line_number] + elsif last_line == line_number - 1 + # Consecutive line, continue chunk. + snippet_chunk << lined_content[line_number] + else + # Non-consecutive line, add chunk to chunk array. + snippet_chunks << { + data: snippet_chunk.join("\n"), + start_line: snippet_start_line + 1 + } + + # Start a new chunk. + snippet_chunk = [lined_content[line_number]] + snippet_start_line = line_number + end + last_line = line_number + end + # Add final chunk to chunk array + snippet_chunks << { + data: snippet_chunk.join("\n"), + start_line: snippet_start_line + 1 + } + + # Return snippet with chunk array + { snippet_object: snippet, snippet_chunks: snippet_chunks } + end + + # Defines how many unmatching lines should be + # included around the matching lines in a snippet + def surrounding_lines + 3 + end + end +end diff --git a/lib/gitlab/upgrader.rb b/lib/gitlab/upgrader.rb index 0846359f9b1..74b049b5143 100644 --- a/lib/gitlab/upgrader.rb +++ b/lib/gitlab/upgrader.rb @@ -43,7 +43,7 @@ module Gitlab end def latest_version_raw - remote_tags, _ = Gitlab::Popen.popen(%W(git ls-remote --tags origin)) + remote_tags, _ = Gitlab::Popen.popen(%W(git ls-remote --tags https://gitlab.com/gitlab-org/gitlab-ce.git)) git_tags = remote_tags.split("\n").grep(/tags\/v#{current_version.major}/) git_tags = git_tags.select { |version| version =~ /v\d\.\d\.\d\Z/ } last_tag = git_tags.last.match(/v\d\.\d\.\d/).to_s diff --git a/lib/support/nginx/gitlab-ssl b/lib/support/nginx/gitlab-ssl index 9ab228b46d7..d2aa06fe7f6 100644 --- a/lib/support/nginx/gitlab-ssl +++ b/lib/support/nginx/gitlab-ssl @@ -26,23 +26,12 @@ ## [1] https://github.com/agentzh/chunkin-nginx-module#status ## [2] https://github.com/agentzh/chunkin-nginx-module ## -################################### -## SSL file editing ## -################################### -## -## Edit `gitlab-shell/config.yml`: -## 1) Set "gitlab_url" param in `gitlab-shell/config.yml` to `https://git.example.com` -## 2) Set "ca_file" to `/etc/nginx/ssl/gitlab.crt` -## 3) Set "self_signed_cert" to `true` -## Edit `gitlab/config/gitlab.yml`: -## 1) Define port for http "port: 443" -## 2) Enable https "https: true" -## 3) Update ssl for gravatar "ssl_url: https://secure.gravatar.com/avatar/%{hash}?s=%{size}&d=mm" ## ################################### ## SSL configuration ## ################################### ## +## See installation.md#using-https for additional HTTPS configuration details. upstream gitlab { server unix:/home/git/gitlab/tmp/sockets/gitlab.socket; @@ -76,7 +65,7 @@ server { ssl_certificate /etc/nginx/ssl/gitlab.crt; ssl_certificate_key /etc/nginx/ssl/gitlab.key; - ssl_ciphers 'ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES256-GCM-SHA384:AES128-GCM-SHA256:AES256-SHA256:AES128-SHA256:AES256-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4'; + ssl_ciphers 'AES256+EECDH:AES256+EDH'; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_session_cache builtin:1000 shared:SSL:10m; @@ -87,6 +76,23 @@ server { add_header X-Frame-Options SAMEORIGIN; add_header X-Content-Type-Options nosniff; + ## [Optional] If your certficate has OCSP, enable OCSP stapling to reduce the overhead and latency of running SSL. + ## Replace with your ssl_trusted_certificate. For more info see: + ## - https://medium.com/devops-programming/4445f4862461 + ## - https://www.ruby-forum.com/topic/4419319 + ## - https://www.digitalocean.com/community/tutorials/how-to-configure-ocsp-stapling-on-apache-and-nginx + # ssl_stapling on; + # ssl_stapling_verify on; + # ssl_trusted_certificate /etc/nginx/ssl/stapling.trusted.crt; + # resolver 208.67.222.222 208.67.222.220 valid=300s; # Can change to your DNS resolver if desired + # resolver_timeout 10s; + + ## [Optional] Generate a stronger DHE parameter: + ## cd /etc/ssl/certs + ## sudo openssl dhparam -out dhparam.pem 4096 + ## + # ssl_dhparam /etc/ssl/certs/dhparam.pem; + ## Individual nginx logs for this GitLab vhost access_log /var/log/nginx/gitlab_access.log; error_log /var/log/nginx/gitlab_error.log; diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake index 032ed5ee370..9ea5c55abd6 100644 --- a/lib/tasks/gitlab/check.rake +++ b/lib/tasks/gitlab/check.rake @@ -322,7 +322,7 @@ namespace :gitlab do "core.autocrlf" => "input" } correct_options = options.map do |name, value| - run(%W(git config --global --get #{name})).try(:squish) == value + run(%W(#{Gitlab.config.git.bin_path} config --global --get #{name})).try(:squish) == value end if correct_options.all? @@ -330,9 +330,9 @@ namespace :gitlab do else puts "no".red try_fixing_it( - sudo_gitlab("git config --global user.name \"#{options["user.name"]}\""), - sudo_gitlab("git config --global user.email \"#{options["user.email"]}\""), - sudo_gitlab("git config --global core.autocrlf \"#{options["core.autocrlf"]}\"") + sudo_gitlab("\"#{Gitlab.config.git.bin_path}\" config --global user.name \"#{options["user.name"]}\""), + sudo_gitlab("\"#{Gitlab.config.git.bin_path}\" config --global user.email \"#{options["user.email"]}\""), + sudo_gitlab("\"#{Gitlab.config.git.bin_path}\" config --global core.autocrlf \"#{options["core.autocrlf"]}\"") ) for_more_information( see_installation_guide_section "GitLab" @@ -541,7 +541,7 @@ namespace :gitlab do "sudo -u #{gitlab_shell_ssh_user} ln -sf #{gitlab_shell_hook_file} #{project_hook_file}" ) for_more_information( - "#{gitlab_shell_path}/support/rewrite-hooks.sh" + "#{gitlab_shell_path}/bin/create-hooks" ) fix_and_rerun next @@ -556,7 +556,7 @@ namespace :gitlab do "sudo -u #{gitlab_shell_ssh_user} ln -sf #{gitlab_shell_hook_file} #{project_hook_file}" ) for_more_information( - "lib/support/rewrite-hooks.sh" + "#{gitlab_shell_path}/bin/create-hooks" ) fix_and_rerun end diff --git a/spec/factories.rb b/spec/factories.rb index 03c87fcc6c5..f7f65bffb8b 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -165,7 +165,6 @@ FactoryGirl.define do factory :service do type "" title "GitLab CI" - token "x56olispAND34ng" project end diff --git a/spec/helpers/diff_helper_spec.rb b/spec/helpers/diff_helper_spec.rb new file mode 100644 index 00000000000..4ab415b4ef3 --- /dev/null +++ b/spec/helpers/diff_helper_spec.rb @@ -0,0 +1,98 @@ +require 'spec_helper' + +describe DiffHelper do + include RepoHelpers + + let(:project) { create(:project) } + let(:commit) { project.repository.commit(sample_commit.id) } + let(:diff) { commit.diffs.first } + let(:diff_file) { Gitlab::Diff::File.new(diff) } + + describe 'diff_hard_limit_enabled?' do + it 'should return true if param is provided' do + controller.stub(:params).and_return { { :force_show_diff => true } } + diff_hard_limit_enabled?.should be_true + end + + it 'should return false if param is not provided' do + diff_hard_limit_enabled?.should be_false + end + end + + describe 'allowed_diff_size' do + it 'should return hard limit for a diff if force diff is true' do + controller.stub(:params).and_return { { :force_show_diff => true } } + allowed_diff_size.should eq(1000) + end + + it 'should return safe limit for a diff if force diff is false' do + allowed_diff_size.should eq(100) + end + end + + describe 'parallel_diff' do + it 'should return an array of arrays containing the parsed diff' do + parallel_diff(diff_file, 0).should match_array(parallel_diff_result_array) + end + end + + describe 'generate_line_code' do + it 'should generate correct line code' do + generate_line_code(diff_file.file_path, diff_file.diff_lines.first).should == '2f6fcd96b88b36ce98c38da085c795a27d92a3dd_6_6' + end + end + + describe 'unfold_bottom_class' do + it 'should return empty string when bottom line shouldnt be unfolded' do + unfold_bottom_class(false).should == '' + end + + it 'should return js class when bottom lines should be unfolded' do + unfold_bottom_class(true).should == 'js-unfold-bottom' + end + end + + describe 'diff_line_content' do + + it 'should return non breaking space when line is empty' do + diff_line_content(nil).should eq(" ") + end + + it 'should return the line itself' do + diff_line_content(diff_file.diff_lines.first.text).should eq("@@ -6,12 +6,18 @@ module Popen") + diff_line_content(diff_file.diff_lines.first.type).should eq("match") + diff_line_content(diff_file.diff_lines.first.new_pos).should eq(6) + end + end + + def parallel_diff_result_array + [ + ["match", 6, "@@ -6,12 +6,18 @@ module Popen", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_6_6", "match", 6, "@@ -6,12 +6,18 @@ module Popen"], + [nil, 6, " ", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_6_6", nil, 6, " "], + [nil, 7, " def popen(cmd, path=nil)", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_7_7", nil, 7, " def popen(cmd, path=nil)"], + [nil, 8, " unless cmd.is_a?(Array)", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_8_8", nil, 8, " unless cmd.is_a?(Array)"], + ["old", 9, "- raise <span class='idiff'></span>"System commands must be given as an array of strings"", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_9_9", "new", 9, "+ raise <span class='idiff'>RuntimeError, </span>"System commands must be given as an array of strings""], + [nil, 10, " end", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_10", nil, 10, " end"], [nil, 11, " ", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_11_11", nil, 11, " "], + [nil, 12, " path ||= Dir.pwd", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_12_12", nil, 12, " path ||= Dir.pwd"], + ["old", 13, "- vars = { "PWD" => path }", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_13_13", "old", nil, " "], + ["old", 14, "- options = { chdir: path }", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_14_13", "new", 13, "+"], + [nil, nil, " ", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_14", "new", 14, "+ vars = {"], + [nil, nil, " ", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_15", "new", 15, "+ "PWD" => path"], + [nil, nil, " ", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_16", "new", 16, "+ }"], + [nil, nil, " ", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_17", "new", 17, "+"], + [nil, nil, " ", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_18", "new", 18, "+ options = {"], + [nil, nil, " ", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_19", "new", 19, "+ chdir: path"], + [nil, nil, " ", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_20", "new", 20, "+ }"], + [nil, 15, " ", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_21", nil, 21, " "], + [nil, 16, " unless File.directory?(path)", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_16_22", nil, 22, " unless File.directory?(path)"], + [nil, 17, " FileUtils.mkdir_p(path)", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_17_23", nil, 23, " FileUtils.mkdir_p(path)"], + ["match", 19, "@@ -19,6 +25,7 @@ module Popen", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_19_25", "match", 25, "@@ -19,6 +25,7 @@ module Popen"], + [nil, 19, " ", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_19_25", nil, 25, " "], [nil, 20, " @cmd_output = """, "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_20_26", nil, 26, " @cmd_output = """], + [nil, 21, " @cmd_status = 0", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_21_27", nil, 27, " @cmd_status = 0"], + [nil, nil, " ", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_22_28", "new", 28, "+"], + [nil, 22, " Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_22_29", nil, 29, " Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|"], + [nil, 23, " @cmd_output << stdout.read", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_23_30", nil, 30, " @cmd_output << stdout.read"], + [nil, 24, " @cmd_output << stderr.read", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_24_31", nil, 31, " @cmd_output << stderr.read"] + ] + end +end diff --git a/spec/lib/git_ref_validator_spec.rb b/spec/lib/git_ref_validator_spec.rb new file mode 100644 index 00000000000..b2469c18395 --- /dev/null +++ b/spec/lib/git_ref_validator_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe Gitlab::GitRefValidator do + it { Gitlab::GitRefValidator.validate('feature/new').should be_true } + it { Gitlab::GitRefValidator.validate('implement_@all').should be_true } + it { Gitlab::GitRefValidator.validate('my_new_feature').should be_true } + it { Gitlab::GitRefValidator.validate('#1').should be_true } + it { Gitlab::GitRefValidator.validate('feature/~new/').should be_false } + it { Gitlab::GitRefValidator.validate('feature/^new/').should be_false } + it { Gitlab::GitRefValidator.validate('feature/:new/').should be_false } + it { Gitlab::GitRefValidator.validate('feature/?new/').should be_false } + it { Gitlab::GitRefValidator.validate('feature/*new/').should be_false } + it { Gitlab::GitRefValidator.validate('feature/[new/').should be_false } + it { Gitlab::GitRefValidator.validate('feature/new/').should be_false } + it { Gitlab::GitRefValidator.validate('feature/new.').should be_false } + it { Gitlab::GitRefValidator.validate('feature\@{').should be_false } + it { Gitlab::GitRefValidator.validate('feature\new').should be_false } + it { Gitlab::GitRefValidator.validate('feature//new').should be_false } + it { Gitlab::GitRefValidator.validate('feature new').should be_false } +end diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb new file mode 100644 index 00000000000..cf0b5c282c1 --- /dev/null +++ b/spec/lib/gitlab/diff/file_spec.rb @@ -0,0 +1,21 @@ +require 'spec_helper' + +describe Gitlab::Diff::File do + include RepoHelpers + + let(:project) { create(:project) } + let(:commit) { project.repository.commit(sample_commit.id) } + let(:diff) { commit.diffs.first } + let(:diff_file) { Gitlab::Diff::File.new(diff) } + + describe :diff_lines do + let(:diff_lines) { diff_file.diff_lines } + + it { diff_lines.size.should == 30 } + it { diff_lines.first.should be_kind_of(Gitlab::Diff::Line) } + end + + describe :mode_changed? do + it { diff_file.mode_changed?.should be_false } + end +end diff --git a/spec/lib/gitlab/diff/parser_spec.rb b/spec/lib/gitlab/diff/parser_spec.rb new file mode 100644 index 00000000000..35b78260acd --- /dev/null +++ b/spec/lib/gitlab/diff/parser_spec.rb @@ -0,0 +1,93 @@ +require 'spec_helper' + +describe Gitlab::Diff::Parser do + include RepoHelpers + + let(:project) { create(:project) } + let(:commit) { project.repository.commit(sample_commit.id) } + let(:diff) { commit.diffs.first } + let(:parser) { Gitlab::Diff::Parser.new } + + describe :parse do + let(:diff) do + <<eos +--- a/files/ruby/popen.rb ++++ b/files/ruby/popen.rb +@@ -6,12 +6,18 @@ module Popen + + def popen(cmd, path=nil) + unless cmd.is_a?(Array) +- raise "System commands must be given as an array of strings" ++ raise RuntimeError, "System commands must be given as an array of strings" + end + + path ||= Dir.pwd +- vars = { "PWD" => path } +- options = { chdir: path } ++ ++ vars = { ++ "PWD" => path ++ } ++ ++ options = { ++ chdir: path ++ } + + unless File.directory?(path) + FileUtils.mkdir_p(path) +@@ -19,6 +25,7 @@ module Popen + + @cmd_output = "" + @cmd_status = 0 ++ + Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr| + @cmd_output << stdout.read + @cmd_output << stderr.read +eos + end + + before do + @lines = parser.parse(diff.lines) + end + + it { @lines.size.should == 30 } + + describe 'lines' do + describe 'first line' do + let(:line) { @lines.first } + + it { line.type.should == 'match' } + it { line.old_pos.should == 6 } + it { line.new_pos.should == 6 } + it { line.text.should == '@@ -6,12 +6,18 @@ module Popen' } + end + + describe 'removal line' do + let(:line) { @lines[10] } + + it { line.type.should == 'old' } + it { line.old_pos.should == 14 } + it { line.new_pos.should == 13 } + it { line.text.should == '- options = { chdir: path }' } + end + + describe 'addition line' do + let(:line) { @lines[16] } + + it { line.type.should == 'new' } + it { line.old_pos.should == 15 } + it { line.new_pos.should == 18 } + it { line.text.should == '+ options = {' } + end + + describe 'unchanged line' do + let(:line) { @lines.last } + + it { line.type.should == nil } + it { line.old_pos.should == 24 } + it { line.new_pos.should == 31 } + it { line.text.should == ' @cmd_output << stderr.read' } + end + end + end +end diff --git a/spec/models/assembla_service_spec.rb b/spec/models/assembla_service_spec.rb index acc08fc4d69..0ef475b87c3 100644 --- a/spec/models/assembla_service_spec.rb +++ b/spec/models/assembla_service_spec.rb @@ -5,16 +5,11 @@ # id :integer not null, primary key # type :string(255) # title :string(255) -# token :string(255) # project_id :integer not null # created_at :datetime # updated_at :datetime # active :boolean default(FALSE), not null -# project_url :string(255) -# subdomain :string(255) -# room :string(255) -# recipients :text -# api_key :string(255) +# properties :text # require 'spec_helper' diff --git a/spec/models/flowdock_service_spec.rb b/spec/models/flowdock_service_spec.rb index 25ad133e122..710b8cba502 100644 --- a/spec/models/flowdock_service_spec.rb +++ b/spec/models/flowdock_service_spec.rb @@ -5,16 +5,11 @@ # id :integer not null, primary key # type :string(255) # title :string(255) -# token :string(255) # project_id :integer not null # created_at :datetime # updated_at :datetime # active :boolean default(FALSE), not null -# project_url :string(255) -# subdomain :string(255) -# room :string(255) -# recipients :text -# api_key :string(255) +# properties :text # require 'spec_helper' diff --git a/spec/models/gemnasium_service_spec.rb b/spec/models/gemnasium_service_spec.rb index efdf0dc891b..5de645cdf33 100644 --- a/spec/models/gemnasium_service_spec.rb +++ b/spec/models/gemnasium_service_spec.rb @@ -5,16 +5,11 @@ # id :integer not null, primary key # type :string(255) # title :string(255) -# token :string(255) # project_id :integer not null # created_at :datetime # updated_at :datetime # active :boolean default(FALSE), not null -# project_url :string(255) -# subdomain :string(255) -# room :string(255) -# recipients :text -# api_key :string(255) +# properties :text # require 'spec_helper' diff --git a/spec/models/gitlab_ci_service_spec.rb b/spec/models/gitlab_ci_service_spec.rb index 439a30869bb..e4cd8bb90c3 100644 --- a/spec/models/gitlab_ci_service_spec.rb +++ b/spec/models/gitlab_ci_service_spec.rb @@ -5,16 +5,11 @@ # id :integer not null, primary key # type :string(255) # title :string(255) -# token :string(255) # project_id :integer not null # created_at :datetime # updated_at :datetime # active :boolean default(FALSE), not null -# project_url :string(255) -# subdomain :string(255) -# room :string(255) -# recipients :text -# api_key :string(255) +# properties :text # require 'spec_helper' diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb index adeeac115c1..480aeabf67f 100644 --- a/spec/models/service_spec.rb +++ b/spec/models/service_spec.rb @@ -5,16 +5,11 @@ # id :integer not null, primary key # type :string(255) # title :string(255) -# token :string(255) # project_id :integer not null # created_at :datetime # updated_at :datetime # active :boolean default(FALSE), not null -# project_url :string(255) -# subdomain :string(255) -# room :string(255) -# recipients :text -# api_key :string(255) +# properties :text # require 'spec_helper' diff --git a/spec/models/slack_service_spec.rb b/spec/models/slack_service_spec.rb index b00eb30569b..4576913b473 100644 --- a/spec/models/slack_service_spec.rb +++ b/spec/models/slack_service_spec.rb @@ -5,16 +5,11 @@ # id :integer not null, primary key # type :string(255) # title :string(255) -# token :string(255) # project_id :integer not null # created_at :datetime # updated_at :datetime # active :boolean default(FALSE), not null -# project_url :string(255) -# subdomain :string(255) -# room :string(255) -# recipients :text -# api_key :string(255) +# properties :text # require 'spec_helper' diff --git a/spec/requests/api/branches_spec.rb b/spec/requests/api/branches_spec.rb index f3d7ca2ed21..e7f91c5e46e 100644 --- a/spec/requests/api/branches_spec.rb +++ b/spec/requests/api/branches_spec.rb @@ -94,22 +94,50 @@ describe API::API, api: true do describe "POST /projects/:id/repository/branches" do it "should create a new branch" do post api("/projects/#{project.id}/repository/branches", user), - branch_name: branch_name, - ref: branch_sha + branch_name: 'feature1', + ref: branch_sha response.status.should == 201 - json_response['name'].should == branch_name + json_response['name'].should == 'feature1' json_response['commit']['id'].should == branch_sha end it "should deny for user without push access" do post api("/projects/#{project.id}/repository/branches", user2), - branch_name: branch_name, - ref: branch_sha - + branch_name: branch_name, + ref: branch_sha response.status.should == 403 end + + it 'should return 400 if branch name is invalid' do + post api("/projects/#{project.id}/repository/branches", user), + branch_name: 'new design', + ref: branch_sha + response.status.should == 400 + json_response['message'].should == 'Branch name invalid' + end + + it 'should return 400 if branch already exists' do + post api("/projects/#{project.id}/repository/branches", user), + branch_name: 'new_design1', + ref: branch_sha + response.status.should == 201 + + post api("/projects/#{project.id}/repository/branches", user), + branch_name: 'new_design1', + ref: branch_sha + response.status.should == 400 + json_response['message'].should == 'Branch already exists' + end + + it 'should return 400 if ref name is invalid' do + post api("/projects/#{project.id}/repository/branches", user), + branch_name: 'new_design3', + ref: 'foo' + response.status.should == 400 + json_response['message'].should == 'Invalid reference name' + end end describe "DELETE /projects/:id/repository/branches/:branch" do @@ -120,6 +148,11 @@ describe API::API, api: true do response.status.should == 200 end + it 'should return 404 if branch not exists' do + delete api("/projects/#{project.id}/repository/branches/foobar", user) + response.status.should == 404 + end + it "should remove protected branch" do project.protected_branches.create(name: branch_name) delete api("/projects/#{project.id}/repository/branches/#{branch_name}", user) diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb index f70b56b194f..e8eebda95b4 100644 --- a/spec/requests/api/issues_spec.rb +++ b/spec/requests/api/issues_spec.rb @@ -9,6 +9,7 @@ describe API::API, api: true do let!(:label) do create(:label, title: 'label', color: '#FFAABB', project: project) end + let!(:label_link) { create(:label_link, label: label, target: issue) } before { project.team << [user, :reporter] } @@ -58,6 +59,45 @@ describe API::API, api: true do json_response.first['id'].should == issue.id json_response.second['id'].should == closed_issue.id end + + it 'should return an array of labeled issues' do + get api("/issues?labels=#{label.title}", user) + response.status.should == 200 + json_response.should be_an Array + json_response.length.should == 1 + json_response.first['labels'].should == [label.title] + end + + it 'should return an array of labeled issues when at least one label matches' do + get api("/issues?labels=#{label.title},foo,bar", user) + response.status.should == 200 + json_response.should be_an Array + json_response.length.should == 1 + json_response.first['labels'].should == [label.title] + end + + it 'should return an empty array if no issue matches labels' do + get api('/issues?labels=foo,bar', user) + response.status.should == 200 + json_response.should be_an Array + json_response.length.should == 0 + end + + it 'should return an array of labeled issues matching given state' do + get api("/issues?labels=#{label.title}&state=opened", user) + response.status.should == 200 + json_response.should be_an Array + json_response.length.should == 1 + json_response.first['labels'].should == [label.title] + json_response.first['state'].should == 'opened' + end + + it 'should return an empty array if no issue matches labels and state filters' do + get api("/issues?labels=#{label.title}&state=closed", user) + response.status.should == 200 + json_response.should be_an Array + json_response.length.should == 0 + end end end @@ -68,6 +108,29 @@ describe API::API, api: true do json_response.should be_an Array json_response.first['title'].should == issue.title end + + it 'should return an array of labeled project issues' do + get api("/projects/#{project.id}/issues?labels=#{label.title}", user) + response.status.should == 200 + json_response.should be_an Array + json_response.length.should == 1 + json_response.first['labels'].should == [label.title] + end + + it 'should return an array of labeled project issues when at least one label matches' do + get api("/projects/#{project.id}/issues?labels=#{label.title},foo,bar", user) + response.status.should == 200 + json_response.should be_an Array + json_response.length.should == 1 + json_response.first['labels'].should == [label.title] + end + + it 'should return an empty array if no project issue matches labels' do + get api("/projects/#{project.id}/issues?labels=foo,bar", user) + response.status.should == 200 + json_response.should be_an Array + json_response.length.should == 0 + end end describe "GET /projects/:id/issues/:issue_id" do @@ -182,7 +245,7 @@ describe API::API, api: true do labels: 'label2', state_event: "close" response.status.should == 200 - json_response['labels'].should == ['label2'] + json_response['labels'].should include 'label2' json_response['state'].should eq "closed" end end diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 12a3a07ff76..058b831e783 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -114,6 +114,7 @@ describe API::API, api: true do it "should assign attributes to project" do project = attributes_for(:project, { + path: 'camelCasePath', description: Faker::Lorem.sentence, issues_enabled: false, merge_requests_enabled: false, @@ -123,7 +124,6 @@ describe API::API, api: true do post api("/projects", user), project project.each_pair do |k,v| - next if k == :path json_response[k.to_s].should == v end end diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb index f8603e11a04..17173aaeeac 100644 --- a/spec/requests/api/repositories_spec.rb +++ b/spec/requests/api/repositories_spec.rb @@ -23,22 +23,67 @@ describe API::API, api: true do end describe 'POST /projects/:id/repository/tags' do - it 'should create a new tag' do - post api("/projects/#{project.id}/repository/tags", user), - tag_name: 'v1.0.0', - ref: 'master' - - response.status.should == 201 - json_response['name'].should == 'v1.0.0' + context 'lightweight tags' do + it 'should create a new tag' do + post api("/projects/#{project.id}/repository/tags", user), + tag_name: 'v7.0.1', + ref: 'master' + + response.status.should == 201 + json_response['name'].should == 'v7.0.1' + end end + # TODO: fix this test for CI + #context 'annotated tag' do + #it 'should create a new annotated tag' do + #post api("/projects/#{project.id}/repository/tags", user), + #tag_name: 'v7.1.0', + #ref: 'master', + #message: 'tag message' + + #response.status.should == 201 + #json_response['name'].should == 'v7.1.0' + # The message is not part of the JSON response. + # Additional changes to the gitlab_git gem may be required. + # json_response['message'].should == 'tag message' + #end + #end + it 'should deny for user without push access' do post api("/projects/#{project.id}/repository/tags", user2), - tag_name: 'v1.0.0', + tag_name: 'v1.9.0', ref: '621491c677087aa243f165eab467bfdfbee00be1' - response.status.should == 403 end + + it 'should return 400 if tag name is invalid' do + post api("/projects/#{project.id}/repository/tags", user), + tag_name: 'v 1.0.0', + ref: 'master' + response.status.should == 400 + json_response['message'].should == 'Tag name invalid' + end + + it 'should return 400 if tag already exists' do + post api("/projects/#{project.id}/repository/tags", user), + tag_name: 'v8.0.0', + ref: 'master' + response.status.should == 201 + post api("/projects/#{project.id}/repository/tags", user), + tag_name: 'v8.0.0', + ref: 'master' + response.status.should == 400 + json_response['message'].should == 'Tag already exists' + end + + it 'should return 400 if ref name is invalid' do + post api("/projects/#{project.id}/repository/tags", user), + tag_name: 'mytag', + ref: 'foo' + response.status.should == 400 + json_response['message'].should == 'Invalid reference name' + end end describe "GET /projects/:id/repository/tree" do |