Commit 91a91557 authored by Achilleas Pipinellis's avatar Achilleas Pipinellis

Merge branch 'master' into 'fix_oauth_doc'

# Conflicts:
#   doc/api/oauth2.md
parents 5838d1ba b9daced7
*.erb *.erb
lib/gitlab/sanitizers/svg/whitelist.rb lib/gitlab/sanitizers/svg/whitelist.rb
lib/gitlab/diff/position_tracer.rb
...@@ -209,9 +209,6 @@ rubocop: *exec ...@@ -209,9 +209,6 @@ rubocop: *exec
rake haml_lint: *exec rake haml_lint: *exec
rake scss_lint: *exec rake scss_lint: *exec
rake brakeman: *exec rake brakeman: *exec
rake flog:
<<: *exec
allow_failure: yes
rake flay: rake flay:
<<: *exec <<: *exec
allow_failure: yes allow_failure: yes
......
...@@ -2,17 +2,37 @@ Please view this file on the master branch, on stable branches it's out of date. ...@@ -2,17 +2,37 @@ Please view this file on the master branch, on stable branches it's out of date.
v 8.13.0 (unreleased) v 8.13.0 (unreleased)
- Use gitlab-shell v3.6.2 (GIT TRACE logging) - Use gitlab-shell v3.6.2 (GIT TRACE logging)
- AbstractReferenceFilter caches project_refs on RequestStore when active
- Speed-up group milestones show page - Speed-up group milestones show page
- Log LDAP lookup errors and don't swallow unrelated exceptions. !6103 (Markus Koller) - Log LDAP lookup errors and don't swallow unrelated exceptions. !6103 (Markus Koller)
- Add more tests for calendar contribution (ClemMakesApps) - Add more tests for calendar contribution (ClemMakesApps)
- Avoid database queries on Banzai::ReferenceParser::BaseParser for nodes without references
- Fix permission for setting an issue's due date
- Expose expires_at field when sharing project on API
- Allow the Koding integration to be configured through the API
- Fix robots.txt disallowing access to groups starting with "s" (Matt Harrison) - Fix robots.txt disallowing access to groups starting with "s" (Matt Harrison)
- Use a ConnectionPool for Rails.cache on Sidekiq servers
- Replace `alias_method_chain` with `Module#prepend`
- Only update issuable labels if they have been changed - Only update issuable labels if they have been changed
- Take filters in account in issuable counters. !6496
- Revoke button in Applications Settings underlines on hover. - Revoke button in Applications Settings underlines on hover.
- Fix Long commit messages overflow viewport in file tree
- Revert avoid touching file system on Build#artifacts?
- Update ruby-prof to 0.16.2. !6026 (Elan Ruusamäe) - Update ruby-prof to 0.16.2. !6026 (Elan Ruusamäe)
- Fix unnecessary escaping of reserved HTML characters in milestone title. !6533
- Add organization field to user profile - Add organization field to user profile
- Fix resolved discussion display in side-by-side diff view !6575
- Optimize GitHub importing for speed and memory - Optimize GitHub importing for speed and memory
- API: expose pipeline data in builds API (!6502, Guilherme Salazar)
- Fix broken repository 500 errors in project list
- Close todos when accepting merge requests via the API !6486 (tonygambone)
v 8.12.2 (unreleased) v 8.12.4 (unreleased)
v 8.12.3
- Update Gitlab Shell to support low IO priority for storage moves
v 8.12.2
- Fix Import/Export not recognising correctly the imported services. - Fix Import/Export not recognising correctly the imported services.
- Fix snippets pagination - Fix snippets pagination
- Fix List-Unsubscribe header in emails - Fix List-Unsubscribe header in emails
...@@ -20,11 +40,15 @@ v 8.12.2 (unreleased) ...@@ -20,11 +40,15 @@ v 8.12.2 (unreleased)
- Fix an issue with the "Commits" section of the cycle analytics summary. !6513 - Fix an issue with the "Commits" section of the cycle analytics summary. !6513
- Fix errors importing project feature and milestone models using GitLab project import - Fix errors importing project feature and milestone models using GitLab project import
- Make JWT messages Docker-compatible - Make JWT messages Docker-compatible
- Fix duplicate branch entry in the merge request version compare dropdown
- Respect the fork_project permission when forking projects
- Only update issuable labels if they have been changed
- Fix bug where 'Search results' repeated many times when a search in the emoji search form is cleared (Xavier Bick) (@zeiv)
- Fix resolve discussion buttons endpoint path
v 8.12.1 v 8.12.1
- Fix a memory leak in HTML::Pipeline::SanitizationFilter::WHITELIST - Fix a memory leak in HTML::Pipeline::SanitizationFilter::WHITELIST
- Fix issue with search filter labels not displaying - Fix issue with search filter labels not displaying
- Fix bug where 'Search results' repeated many times when a search in the emoji search form is cleared (Xavier Bick) (@zeiv)
v 8.12.0 v 8.12.0
- Update the rouge gem to 2.0.6, which adds highlighting support for JSX, Prometheus, and others. !6251 - Update the rouge gem to 2.0.6, which adds highlighting support for JSX, Prometheus, and others. !6251
...@@ -34,13 +58,13 @@ v 8.12.0 ...@@ -34,13 +58,13 @@ v 8.12.0
- Allow to set request_access_enabled for groups and projects - Allow to set request_access_enabled for groups and projects
- Cleanup misalignments in Issue list view !6206 - Cleanup misalignments in Issue list view !6206
- Only create a protected branch upon a push to a new branch if a rule for that branch doesn't exist - Only create a protected branch upon a push to a new branch if a rule for that branch doesn't exist
- Add Pipelines for Commit
- Prune events older than 12 months. (ritave) - Prune events older than 12 months. (ritave)
- Prepend blank line to `Closes` message on merge request linked to issue (lukehowell) - Prepend blank line to `Closes` message on merge request linked to issue (lukehowell)
- Fix issues/merge-request templates dropdown for forked projects - Fix issues/merge-request templates dropdown for forked projects
- Filter tags by name !6121 - Filter tags by name !6121
- Update gitlab shell secret file also when it is empty. !3774 (glensc) - Update gitlab shell secret file also when it is empty. !3774 (glensc)
- Give project selection dropdowns responsive width, make non-wrapping. - Give project selection dropdowns responsive width, make non-wrapping.
- Fix resolve discussion buttons endpoint path
- Fix note form hint showing slash commands supported for commits. - Fix note form hint showing slash commands supported for commits.
- Make push events have equal vertical spacing. - Make push events have equal vertical spacing.
- API: Ensure invitees are not returned in Members API. - API: Ensure invitees are not returned in Members API.
...@@ -211,6 +235,12 @@ v 8.12.0 ...@@ -211,6 +235,12 @@ v 8.12.0
- Fix non-master branch readme display in tree view - Fix non-master branch readme display in tree view
- Add UX improvements for merge request version diffs - Add UX improvements for merge request version diffs
v 8.11.8
- Respect the fork_project permission when forking projects
- Set a restrictive CORS policy on the API for credentialed requests
- API: disable rails session auth for non-GET/HEAD requests
- Escape HTML nodes in builds commands in CI linter
v 8.11.7 v 8.11.7
- Avoid conflict with admin labels when importing GitHub labels. !6158 - Avoid conflict with admin labels when importing GitHub labels. !6158
- Restores `fieldName` to allow only string values in `gl_dropdown.js`. !6234 - Restores `fieldName` to allow only string values in `gl_dropdown.js`. !6234
...@@ -430,6 +460,12 @@ v 8.11.0 ...@@ -430,6 +460,12 @@ v 8.11.0
- Update gitlab_git gem to 10.4.7 - Update gitlab_git gem to 10.4.7
- Simplify SQL queries of marking a todo as done - Simplify SQL queries of marking a todo as done
v 8.10.11
- Respect the fork_project permission when forking projects
- Set a restrictive CORS policy on the API for credentialed requests
- API: disable rails session auth for non-GET/HEAD requests
- Escape HTML nodes in builds commands in CI linter
v 8.10.10 v 8.10.10
- Allow the Rails cookie to be used for API authentication. - Allow the Rails cookie to be used for API authentication.
...@@ -666,6 +702,12 @@ v 8.10.0 ...@@ -666,6 +702,12 @@ v 8.10.0
- Show tooltip on GitLab export link in new project page - Show tooltip on GitLab export link in new project page
- Fix import_data wrongly saved as a result of an invalid import_url !5206 - Fix import_data wrongly saved as a result of an invalid import_url !5206
v 8.9.11
- Respect the fork_project permission when forking projects
- Set a restrictive CORS policy on the API for credentialed requests
- API: disable rails session auth for non-GET/HEAD requests
- Escape HTML nodes in builds commands in CI linter
v 8.9.10 v 8.9.10
- Allow the Rails cookie to be used for API authentication. - Allow the Rails cookie to be used for API authentication.
......
...@@ -17,7 +17,7 @@ gem 'mysql2', '~> 0.3.16', group: :mysql ...@@ -17,7 +17,7 @@ gem 'mysql2', '~> 0.3.16', group: :mysql
gem 'pg', '~> 0.18.2', group: :postgres gem 'pg', '~> 0.18.2', group: :postgres
# Authentication libraries # Authentication libraries
gem 'devise', '~> 4.0' gem 'devise', '~> 4.2'
gem 'doorkeeper', '~> 4.2.0' gem 'doorkeeper', '~> 4.2.0'
gem 'omniauth', '~> 1.3.1' gem 'omniauth', '~> 1.3.1'
gem 'omniauth-auth0', '~> 1.4.1' gem 'omniauth-auth0', '~> 1.4.1'
...@@ -51,7 +51,7 @@ gem 'browser', '~> 2.2' ...@@ -51,7 +51,7 @@ gem 'browser', '~> 2.2'
# Extracting information from a git repository # Extracting information from a git repository
# Provide access to Gitlab::Git library # Provide access to Gitlab::Git library
gem 'gitlab_git', '~> 10.6.6' gem 'gitlab_git', '~> 10.6.7'
# LDAP Auth # LDAP Auth
# GitLab fork with several improvements to original library. For full list of changes # GitLab fork with several improvements to original library. For full list of changes
...@@ -300,7 +300,6 @@ group :development, :test do ...@@ -300,7 +300,6 @@ group :development, :test do
gem 'scss_lint', '~> 0.47.0', require: false gem 'scss_lint', '~> 0.47.0', require: false
gem 'haml_lint', '~> 0.18.2', require: false gem 'haml_lint', '~> 0.18.2', require: false
gem 'simplecov', '0.12.0', require: false gem 'simplecov', '0.12.0', require: false
gem 'flog', '~> 4.3.2', require: false
gem 'flay', '~> 2.6.1', require: false gem 'flay', '~> 2.6.1', require: false
gem 'bundler-audit', '~> 0.5.0', require: false gem 'bundler-audit', '~> 0.5.0', require: false
......
...@@ -161,7 +161,7 @@ GEM ...@@ -161,7 +161,7 @@ GEM
activerecord (>= 3.2.0, < 5.1) activerecord (>= 3.2.0, < 5.1)
descendants_tracker (0.0.4) descendants_tracker (0.0.4)
thread_safe (~> 0.3, >= 0.3.1) thread_safe (~> 0.3, >= 0.3.1)
devise (4.1.1) devise (4.2.0)
bcrypt (~> 3.0) bcrypt (~> 3.0)
orm_adapter (~> 0.1) orm_adapter (~> 0.1)
railties (>= 4.1.0, < 5.1) railties (>= 4.1.0, < 5.1)
...@@ -209,9 +209,6 @@ GEM ...@@ -209,9 +209,6 @@ GEM
flay (2.6.1) flay (2.6.1)
ruby_parser (~> 3.0) ruby_parser (~> 3.0)
sexp_processor (~> 4.0) sexp_processor (~> 4.0)
flog (4.3.2)
ruby_parser (~> 3.1, > 3.1.0)
sexp_processor (~> 4.4)
flowdock (0.7.1) flowdock (0.7.1)
httparty (~> 0.7) httparty (~> 0.7)
multi_json multi_json
...@@ -279,7 +276,7 @@ GEM ...@@ -279,7 +276,7 @@ GEM
diff-lcs (~> 1.1) diff-lcs (~> 1.1)
mime-types (>= 1.16, < 3) mime-types (>= 1.16, < 3)
posix-spawn (~> 0.3) posix-spawn (~> 0.3)
gitlab_git (10.6.6) gitlab_git (10.6.7)
activesupport (~> 4.0) activesupport (~> 4.0)
charlock_holmes (~> 0.7.3) charlock_holmes (~> 0.7.3)
github-linguist (~> 4.7.0) github-linguist (~> 4.7.0)
...@@ -587,7 +584,7 @@ GEM ...@@ -587,7 +584,7 @@ GEM
request_store (1.3.1) request_store (1.3.1)
rerun (0.11.0) rerun (0.11.0)
listen (~> 3.0) listen (~> 3.0)
responders (2.1.1) responders (2.3.0)
railties (>= 4.2.0, < 5.1) railties (>= 4.2.0, < 5.1)
rinku (2.0.0) rinku (2.0.0)
rotp (2.1.2) rotp (2.1.2)
...@@ -835,7 +832,7 @@ DEPENDENCIES ...@@ -835,7 +832,7 @@ DEPENDENCIES
d3_rails (~> 3.5.0) d3_rails (~> 3.5.0)
database_cleaner (~> 1.5.0) database_cleaner (~> 1.5.0)
default_value_for (~> 3.0.0) default_value_for (~> 3.0.0)
devise (~> 4.0) devise (~> 4.2)
devise-two-factor (~> 3.0.0) devise-two-factor (~> 3.0.0)
diffy (~> 3.0.3) diffy (~> 3.0.3)
doorkeeper (~> 4.2.0) doorkeeper (~> 4.2.0)
...@@ -845,7 +842,6 @@ DEPENDENCIES ...@@ -845,7 +842,6 @@ DEPENDENCIES
factory_girl_rails (~> 4.6.0) factory_girl_rails (~> 4.6.0)
ffaker (~> 2.0.0) ffaker (~> 2.0.0)
flay (~> 2.6.1) flay (~> 2.6.1)
flog (~> 4.3.2)
fog-aws (~> 0.9) fog-aws (~> 0.9)
fog-azure (~> 0.0) fog-azure (~> 0.0)
fog-core (~> 1.40) fog-core (~> 1.40)
...@@ -861,7 +857,7 @@ DEPENDENCIES ...@@ -861,7 +857,7 @@ DEPENDENCIES
github-linguist (~> 4.7.0) github-linguist (~> 4.7.0)
github-markup (~> 1.4) github-markup (~> 1.4)
gitlab-flowdock-git-hook (~> 1.0.1) gitlab-flowdock-git-hook (~> 1.0.1)
gitlab_git (~> 10.6.6) gitlab_git (~> 10.6.7)
gitlab_omniauth-ldap (~> 1.2.1) gitlab_omniauth-ldap (~> 1.2.1)
gollum-lib (~> 4.2) gollum-lib (~> 4.2)
gollum-rugged_adapter (~> 0.4.2) gollum-rugged_adapter (~> 0.4.2)
......
...@@ -247,7 +247,7 @@ ...@@ -247,7 +247,7 @@
$this.toggleClass('active'); $this.toggleClass('active');
var notesHolders = $this.closest('.diff-file').find('.notes_holder'); var notesHolders = $this.closest('.diff-file').find('.notes_holder');
if ($this.hasClass('active')) { if ($this.hasClass('active')) {
notesHolders.show(); notesHolders.show().find('.hide').show();
} else { } else {
notesHolders.hide(); notesHolders.hide();
} }
......
...@@ -27,7 +27,12 @@ ...@@ -27,7 +27,12 @@
} }
.last-commit { .last-commit {
@include str-truncated(60%); @include str-truncated(506px);
@media (min-width: $screen-sm-max) and (max-width: $screen-md-max) {
@include str-truncated(450px);
}
} }
.commit-history-link-spacer { .commit-history-link-spacer {
......
...@@ -10,7 +10,7 @@ class Admin::GroupsController < Admin::ApplicationController ...@@ -10,7 +10,7 @@ class Admin::GroupsController < Admin::ApplicationController
def show def show
@members = @group.members.order("access_level DESC").page(params[:members_page]) @members = @group.members.order("access_level DESC").page(params[:members_page])
@requesters = @group.requesters @requesters = AccessRequestsFinder.new(@group).execute(current_user)
@projects = @group.projects.page(params[:projects_page]) @projects = @group.projects.page(params[:projects_page])
end end
......
...@@ -22,7 +22,7 @@ class Admin::ProjectsController < Admin::ApplicationController ...@@ -22,7 +22,7 @@ class Admin::ProjectsController < Admin::ApplicationController
end end
@project_members = @project.members.page(params[:project_members_page]) @project_members = @project.members.page(params[:project_members_page])
@requesters = @project.requesters @requesters = AccessRequestsFinder.new(@project).execute(current_user)
end end
def transfer def transfer
......
...@@ -15,7 +15,7 @@ class Groups::GroupMembersController < Groups::ApplicationController ...@@ -15,7 +15,7 @@ class Groups::GroupMembersController < Groups::ApplicationController
end end
@members = @members.order('access_level DESC').page(params[:page]).per(50) @members = @members.order('access_level DESC').page(params[:page]).per(50)
@requesters = @group.requesters if can?(current_user, :admin_group, @group) @requesters = AccessRequestsFinder.new(@group).execute(current_user)
@group_member = @group.group_members.new @group_member = @group.group_members.new
end end
......
...@@ -33,7 +33,7 @@ module Projects ...@@ -33,7 +33,7 @@ module Projects
def issue def issue
@issue ||= @issue ||=
IssuesFinder.new(current_user, project_id: project.id, state: 'all') IssuesFinder.new(current_user, project_id: project.id)
.execute .execute
.where(iid: params[:id]) .where(iid: params[:id])
.first! .first!
......
...@@ -10,10 +10,11 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -10,10 +10,11 @@ class Projects::CommitController < Projects::ApplicationController
before_action :require_non_empty_project before_action :require_non_empty_project
before_action :authorize_download_code!, except: [:cancel_builds, :retry_builds] before_action :authorize_download_code!, except: [:cancel_builds, :retry_builds]
before_action :authorize_update_build!, only: [:cancel_builds, :retry_builds] before_action :authorize_update_build!, only: [:cancel_builds, :retry_builds]
before_action :authorize_read_pipeline!, only: [:pipelines]
before_action :authorize_read_commit_status!, only: [:builds] before_action :authorize_read_commit_status!, only: [:builds]
before_action :commit before_action :commit
before_action :define_commit_vars, only: [:show, :diff_for_path, :builds] before_action :define_commit_vars, only: [:show, :diff_for_path, :builds, :pipelines]
before_action :define_status_vars, only: [:show, :builds] before_action :define_status_vars, only: [:show, :builds, :pipelines]
before_action :define_note_vars, only: [:show, :diff_for_path] before_action :define_note_vars, only: [:show, :diff_for_path]
before_action :authorize_edit_tree!, only: [:revert, :cherry_pick] before_action :authorize_edit_tree!, only: [:revert, :cherry_pick]
...@@ -31,6 +32,9 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -31,6 +32,9 @@ class Projects::CommitController < Projects::ApplicationController
render_diff_for_path(@commit.diffs(diff_options)) render_diff_for_path(@commit.diffs(diff_options))
end end
def pipelines
end
def builds def builds
end end
...@@ -96,10 +100,6 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -96,10 +100,6 @@ class Projects::CommitController < Projects::ApplicationController
@noteable = @commit ||= @project.commit(params[:id]) @noteable = @commit ||= @project.commit(params[:id])
end end
def pipelines
@pipelines ||= project.pipelines.where(sha: commit.sha)
end
def ci_builds def ci_builds
@ci_builds ||= Ci::Build.where(pipeline: pipelines) @ci_builds ||= Ci::Build.where(pipeline: pipelines)
end end
...@@ -134,8 +134,9 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -134,8 +134,9 @@ class Projects::CommitController < Projects::ApplicationController
end end
def define_status_vars def define_status_vars
@statuses = CommitStatus.where(pipeline: pipelines).relevant @ci_pipelines = project.pipelines.where(sha: commit.sha)
@builds = Ci::Build.where(pipeline: pipelines).relevant @statuses = CommitStatus.where(pipeline: @ci_pipelines).relevant
@builds = Ci::Build.where(pipeline: @ci_pipelines).relevant
end end
def assign_change_commit_vars(mr_source_branch) def assign_change_commit_vars(mr_source_branch)
......
...@@ -308,8 +308,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -308,8 +308,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController
return return
end end
TodoService.new.merge_merge_request(merge_request, current_user)
@merge_request.update(merge_error: nil) @merge_request.update(merge_error: nil)
if params[:merge_when_build_succeeds].present? if params[:merge_when_build_succeeds].present?
......
...@@ -29,7 +29,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController ...@@ -29,7 +29,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController
@group_members = @group_members.order('access_level DESC') @group_members = @group_members.order('access_level DESC')
end end
@requesters = @project.requesters if can?(current_user, :admin_project, @project) @requesters = AccessRequestsFinder.new(@project).execute(current_user)
@project_member = @project.project_members.new @project_member = @project.project_members.new
@project_group_links = @project.project_group_links @project_group_links = @project.project_group_links
......
...@@ -137,10 +137,10 @@ class ProjectsController < Projects::ApplicationController ...@@ -137,10 +137,10 @@ class ProjectsController < Projects::ApplicationController
noteable = noteable =
case params[:type] case params[:type]
when 'Issue' when 'Issue'
IssuesFinder.new(current_user, project_id: @project.id, state: 'all'). IssuesFinder.new(current_user, project_id: @project.id).
execute.find_by(iid: params[:type_id]) execute.find_by(iid: params[:type_id])
when 'MergeRequest' when 'MergeRequest'
MergeRequestsFinder.new(current_user, project_id: @project.id, state: 'all'). MergeRequestsFinder.new(current_user, project_id: @project.id).
execute.find_by(iid: params[:type_id]) execute.find_by(iid: params[:type_id])
when 'Commit' when 'Commit'
@project.commit(params[:type_id]) @project.commit(params[:type_id])
...@@ -324,7 +324,12 @@ class ProjectsController < Projects::ApplicationController ...@@ -324,7 +324,12 @@ class ProjectsController < Projects::ApplicationController
end end
def repo_exists? def repo_exists?
project.repository_exists? && !project.empty_repo? project.repository_exists? && !project.empty_repo? && project.repo
rescue Gitlab::Git::Repository::NoRepository
project.repository.expire_exists_cache
false
end end
def project_view_files? def project_view_files?
......
class AccessRequestsFinder
attr_accessor :source
# Arguments:
# source - a Group or Project
def initialize(source)
@source = source
end
def execute(*args)
execute!(*args)
rescue Gitlab::Access::AccessDeniedError
[]
end
def execute!(current_user)
raise Gitlab::Access::AccessDeniedError unless can_see_access_requests?(current_user)
source.requesters
end
private
def can_see_access_requests?(current_user)
source && Ability.allowed?(current_user, :"admin_#{source.class.to_s.underscore}", source)
end
end
...@@ -183,17 +183,12 @@ class IssuableFinder ...@@ -183,17 +183,12 @@ class IssuableFinder
end end
def by_state(items) def by_state(items)
case params[:state] params[:state] ||= 'all'
when 'closed'
items.closed if items.respond_to?(params[:state])
when 'merged' items.public_send(params[:state])
items.respond_to?(:merged) ? items.merged : items.closed
when 'all'
items
when 'opened'
items.opened
else else
raise 'You must specify default state' items
end end
end end
......
...@@ -280,32 +280,6 @@ module ApplicationHelper ...@@ -280,32 +280,6 @@ module ApplicationHelper
end end
end end
def state_filters_text_for(entity, project)
titles = {
opened: "Open"
}
entity_title = titles[entity] || entity.to_s.humanize
count =
if project.nil?
nil
elsif current_controller?(:issues)
project.issues.visible_to_user(current_user).send(entity).count
elsif current_controller?(:merge_requests)
project.merge_requests.send(entity).count
end
html = content_tag :span, entity_title
if count.present?
html += " "
html += content_tag :span, number_with_delimiter(count), class: 'badge'
end
html.html_safe
end
def truncate_first_line(message, length = 50) def truncate_first_line(message, length = 50)
truncate(message.each_line.first.chomp, length: length) if message truncate(message.each_line.first.chomp, length: length) if message
end end
......
...@@ -56,7 +56,7 @@ module CiStatusHelper ...@@ -56,7 +56,7 @@ module CiStatusHelper
def render_commit_status(commit, tooltip_placement: 'auto left') def render_commit_status(commit, tooltip_placement: 'auto left')
project = commit.project project = commit.project
path = builds_namespace_project_commit_path(project.namespace, project, commit) path = pipelines_namespace_project_commit_path(project.namespace, project, commit)
render_status_with_link('commit', commit.status, path, tooltip_placement: tooltip_placement) render_status_with_link('commit', commit.status, path, tooltip_placement: tooltip_placement)
end end
......
...@@ -94,6 +94,24 @@ module IssuablesHelper ...@@ -94,6 +94,24 @@ module IssuablesHelper
label_names.join(', ') label_names.join(', ')
end end
def issuables_state_counter_text(issuable_type, state)
titles = {
opened: "Open"
}
state_title = titles[state] || state.to_s.humanize
count =
Rails.cache.fetch(issuables_state_counter_cache_key(issuable_type, state), expires_in: 2.minutes) do
issuables_count_for_state(issuable_type, state)
end
html = content_tag(:span, state_title)
html << " " << content_tag(:span, number_with_delimiter(count), class: 'badge')
html.html_safe
end
private private
def sidebar_gutter_collapsed? def sidebar_gutter_collapsed?
...@@ -111,4 +129,22 @@ module IssuablesHelper ...@@ -111,4 +129,22 @@ module IssuablesHelper
issuable.open? ? :opened : :closed issuable.open? ? :opened : :closed
end end
end end
def issuables_count_for_state(issuable_type, state)
issuables_finder = public_send("#{issuable_type}_finder")
issuables_finder.params[:state] = state
issuables_finder.execute.page(1).total_count
end
IRRELEVANT_PARAMS_FOR_CACHE_KEY = %i[utf8 sort page]
private_constant :IRRELEVANT_PARAMS_FOR_CACHE_KEY
def issuables_state_counter_cache_key(issuable_type, state)
opts = params.with_indifferent_access
opts[:state] = state
opts.except!(*IRRELEVANT_PARAMS_FOR_CACHE_KEY)
hexdigest(['issuables_count', issuable_type, opts.sort].flatten.join('-'))
end
end end
...@@ -373,7 +373,7 @@ module Ci ...@@ -373,7 +373,7 @@ module Ci
end end
def artifacts? def artifacts?
!artifacts_expired? && self[:artifacts_file].present? !artifacts_expired? && artifacts_file.exists?
end end
def artifacts_metadata? def artifacts_metadata?
......
...@@ -158,7 +158,7 @@ class Milestone < ActiveRecord::Base ...@@ -158,7 +158,7 @@ class Milestone < ActiveRecord::Base
end end
def title=(value) def title=(value)
write_attribute(:title, Sanitize.clean(value.to_s)) if value.present? write_attribute(:title, sanitize_title(value)) if value.present?
end end
# Sorts the issues for the given IDs. # Sorts the issues for the given IDs.
...@@ -204,4 +204,8 @@ class Milestone < ActiveRecord::Base ...@@ -204,4 +204,8 @@ class Milestone < ActiveRecord::Base
iid iid
end end
end end
def sanitize_title(value)
CGI.unescape_html(Sanitize.clean(value.to_s))
end
end end
...@@ -50,6 +50,7 @@ class IssuableBaseService < BaseService ...@@ -50,6 +50,7 @@ class IssuableBaseService < BaseService
params.delete(:remove_label_ids) params.delete(:remove_label_ids)
params.delete(:label_ids) params.delete(:label_ids)
params.delete(:assignee_id) params.delete(:assignee_id)
params.delete(:due_date)
end end
end end
......
...@@ -7,6 +7,7 @@ module MergeRequests ...@@ -7,6 +7,7 @@ module MergeRequests
class PostMergeService < MergeRequests::BaseService class PostMergeService < MergeRequests::BaseService
def execute(merge_request) def execute(merge_request)
close_issues(merge_request) close_issues(merge_request)
todo_service.merge_merge_request(merge_request, current_user)
merge_request.mark_as_merged merge_request.mark_as_merged
create_merge_event(merge_request, current_user) create_merge_event(merge_request, current_user)
create_note(merge_request) create_note(merge_request)
......
...@@ -44,6 +44,11 @@ module Projects ...@@ -44,6 +44,11 @@ module Projects
begin begin
gitlab_shell.import_repository(project.repository_storage_path, project.path_with_namespace, project.import_url) gitlab_shell.import_repository(project.repository_storage_path, project.path_with_namespace, project.import_url)
rescue => e rescue => e
# Expire cache to prevent scenarios such as:
# 1. First import failed, but the repo was imported successfully, so +exists?+ returns true
# 2. Retried import, repo is broken or not imported but +exists?+ still returns true
project.repository.before_import if project.repository_exists?
raise Error, "Error importing repository #{project.import_url} into #{project.path_with_namespace} - #{e.message}" raise Error, "Error importing repository #{project.import_url} into #{project.path_with_namespace} - #{e.message}"
end end
end end
......
...@@ -195,7 +195,7 @@ module SlashCommands ...@@ -195,7 +195,7 @@ module SlashCommands
params '<in 2 days | this Friday | December 31st>' params '<in 2 days | this Friday | December 31st>'
condition do condition do
issuable.respond_to?(:due_date) && issuable.respond_to?(:due_date) &&
current_user.can?(:"update_#{issuable.to_ability_name}", issuable) current_user.can?(:"admin_#{issuable.to_ability_name}", project)
end end
command :due do |due_date_param| command :due do |due_date_param|
due_date = Chronic.parse(due_date_param).try(:to_date) due_date = Chronic.parse(due_date_param).try(:to_date)
...@@ -208,7 +208,7 @@ module SlashCommands ...@@ -208,7 +208,7 @@ module SlashCommands
issuable.persisted? && issuable.persisted? &&
issuable.respond_to?(:due_date) && issuable.respond_to?(:due_date) &&
issuable.due_date? && issuable.due_date? &&
current_user.can?(:"update_#{issuable.to_ability_name}", issuable) current_user.can?(:"admin_#{issuable.to_ability_name}", project)
end end
command :remove_due_date do command :remove_due_date do
@updates[:due_date] = nil @updates[:due_date] = nil
......
...@@ -5,7 +5,8 @@ ...@@ -5,7 +5,8 @@
# Values are checked for formatting and exclusion from a list of reserved path # Values are checked for formatting and exclusion from a list of reserved path
# names. # names.
class NamespaceValidator < ActiveModel::EachValidator class NamespaceValidator < ActiveModel::EachValidator
RESERVED = %w( RESERVED = %w[
.well-known
admin admin
all all
assets assets
...@@ -31,7 +32,7 @@ class NamespaceValidator < ActiveModel::EachValidator ...@@ -31,7 +32,7 @@ class NamespaceValidator < ActiveModel::EachValidator
u u
unsubscribes unsubscribes
users users
).freeze ].freeze
def validate_each(record, attribute, value) def validate_each(record, attribute, value)
unless value =~ Gitlab::Regex.namespace_regex unless value =~ Gitlab::Regex.namespace_regex
......
...@@ -18,11 +18,11 @@ ...@@ -18,11 +18,11 @@
.form-group.js-toggle-colors-container.hide .form-group.js-toggle-colors-container.hide
= f.label :color, "Background Color", class: 'control-label' = f.label :color, "Background Color", class: 'control-label'
.col-sm-10 .col-sm-10
= f.color_field :color, class: "form-control" = f.text_field :color, class: "form-control"
.form-group.js-toggle-colors-container.hide .form-group.js-toggle-colors-container.hide
= f.label :font, "Font Color", class: 'control-label' = f.label :font, "Font Color", class: 'control-label'
.col-sm-10 .col-sm-10
= f.color_field :font, class: "form-control" = f.text_field :font, class: "form-control"
.form-group .form-group
= f.label :starts_at, class: 'control-label' = f.label :starts_at, class: 'control-label'
.col-sm-10.datetime-controls .col-sm-10.datetime-controls
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
.col-sm-10 .col-sm-10
.input-group .input-group
.input-group-addon.label-color-preview &nbsp; .input-group-addon.label-color-preview &nbsp;
= f.color_field :color, class: "form-control" = f.text_field :color, class: "form-control"
.help-block .help-block
Choose any color. Choose any color.
%br %br
......
...@@ -5,8 +5,10 @@ ...@@ -5,8 +5,10 @@
%p.prepend-top-default %p.prepend-top-default
%span %span
To register a new runner you should enter the following registration token. To register a new Runner you should enter the following registration
With this token the runner will request a unique runner token and use that for future communication. token.
With this token the Runner will request a unique Runner token and use
that for future communication.
%br %br
Registration token is Registration token is
%code{ id: 'runners-token' } #{current_application_settings.runners_registration_token} %code{ id: 'runners-token' } #{current_application_settings.runners_registration_token}
...@@ -24,27 +26,27 @@ ...@@ -24,27 +26,27 @@
.bs-callout .bs-callout
%p %p
A 'runner' is a process which runs a build. A 'Runner' is a process which runs a build.
You can setup as many runners as you need. You can setup as many Runners as you need.
%br %br
Runners can be placed on separate users, servers, and even on your local machine. Runners can be placed on separate users, servers, even on your local machine.
%br %br
%div %div
%span Each runner can be in one of the following states: %span Each Runner can be in one of the following states:
%ul %ul
%li %li
%span.label.label-success shared %span.label.label-success shared
\- run builds from all unassigned projects \- Runner runs builds from all unassigned projects
%li %li
%span.label.label-info specific %span.label.label-info specific
\- run builds from assigned projects \- Runner runs builds from assigned projects
%li %li
%span.label.label-warning locked %span.label.label-warning locked
\- runner cannot be assigned to other projects \- Runner cannot be assigned to other projects
%li %li
%span.label.label-danger paused %span.label.label-danger paused
\- runner will not receive any new builds \- Runner will not receive any new builds
.append-bottom-20.clearfix .append-bottom-20.clearfix
.pull-left .pull-left
......
...@@ -11,14 +11,14 @@ ...@@ -11,14 +11,14 @@
- if @runner.shared? - if @runner.shared?
.bs-callout.bs-callout-success .bs-callout.bs-callout-success
%h4 This runner will process builds from ALL UNASSIGNED projects %h4 This Runner will process builds from ALL UNASSIGNED projects
%p %p
If you want runners to build only specific projects, enable them in the table below. If you want Runners to build only specific projects, enable them in the table below.
Keep in mind that this is a one way transition. Keep in mind that this is a one way transition.
- else - else
.bs-callout.bs-callout-info .bs-callout.bs-callout-info
%h4 This runner will process builds only from ASSIGNED projects %h4 This Runner will process builds only from ASSIGNED projects
%p You can't make this a shared runner. %p You can't make this a shared Runner.
%hr %hr
.append-bottom-20 .append-bottom-20
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
.row .row
.col-md-6 .col-md-6
%h4 Restrict projects for this runner %h4 Restrict projects for this Runner
- if @runner.projects.any? - if @runner.projects.any?
%table.table.assigned-projects %table.table.assigned-projects
%thead %thead
...@@ -70,7 +70,7 @@ ...@@ -70,7 +70,7 @@
= paginate @projects = paginate @projects
.col-md-6 .col-md-6
%h4 Recent builds served by this runner %h4 Recent builds served by this Runner
%table.table.builds.runner-builds %table.table.builds.runner-builds
%thead %thead
%tr %tr
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
- else - else
Any Any
%b.caret %b.caret
%ul.dropdown-menu %ul.dropdown-menu.dropdown-menu-align-right
%li %li
= link_to filter_projects_path(visibility_level: nil) do = link_to filter_projects_path(visibility_level: nil) do
Any Any
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
- else - else
Any Any
%b.caret %b.caret
%ul.dropdown-menu %ul.dropdown-menu.dropdown-menu-align-right
%li %li
= link_to filter_projects_path(tag: nil) do = link_to filter_projects_path(tag: nil) do
Any Any
......
- status = pipeline.status - status = pipeline.status
- show_commit = local_assigns.fetch(:show_commit, true)
- show_branch = local_assigns.fetch(:show_branch, true)
%tr.commit %tr.commit
%td.commit-link %td.commit-link
= link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id) do = link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id) do
...@@ -10,14 +13,14 @@ ...@@ -10,14 +13,14 @@
.branch-commit .branch-commit
= link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id) do = link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id) do
%span ##{pipeline.id} %span ##{pipeline.id}
- if pipeline.ref - if pipeline.ref && show_branch
- unless defined?(hide_branch) && hide_branch .icon-container
.icon-container = pipeline.tag? ? icon('tag') : icon('code-fork')
= pipeline.tag? ? icon('tag') : icon('code-fork') = link_to pipeline.ref, namespace_project_commits_path(pipeline.project.namespace, pipeline.project, pipeline.ref), class: "monospace branch-name"
= link_to pipeline.ref, namespace_project_commits_path(pipeline.project.namespace, pipeline.project, pipeline.ref), class: "monospace branch-name" - if show_commit
.icon-container .icon-container
= custom_icon("icon_commit") = custom_icon("icon_commit")
= link_to pipeline.short_sha, namespace_project_commit_path(pipeline.project.namespace, pipeline.project, pipeline.sha), class: "commit-id monospace" = link_to pipeline.short_sha, namespace_project_commit_path(pipeline.project.namespace, pipeline.project, pipeline.sha), class: "commit-id monospace"
- if pipeline.latest? - if pipeline.latest?
%span.label.label-success.has-tooltip{ title: 'Latest build for this branch' } latest %span.label.label-success.has-tooltip{ title: 'Latest build for this branch' } latest
- if pipeline.triggered? - if pipeline.triggered?
......
- @pipelines.each do |pipeline| - @ci_pipelines.each do |pipeline|
= render "pipeline", pipeline: pipeline, pipeline_details: true = render "pipeline", pipeline: pipeline, pipeline_details: true
...@@ -3,6 +3,11 @@ ...@@ -3,6 +3,11 @@
= link_to namespace_project_commit_path(@project.namespace, @project, @commit.id) do = link_to namespace_project_commit_path(@project.namespace, @project, @commit.id) do
Changes Changes
%span.badge= @diffs.size %span.badge= @diffs.size
- if can?(current_user, :read_pipeline, @project)
= nav_link(path: 'commit#pipelines') do
= link_to pipelines_namespace_project_commit_path(@project.namespace, @project, @commit.id) do
Pipelines
%span.badge= @ci_pipelines.count
= nav_link(path: 'commit#builds') do = nav_link(path: 'commit#builds') do
= link_to builds_namespace_project_commit_path(@project.namespace, @project, @commit.id) do = link_to builds_namespace_project_commit_path(@project.namespace, @project, @commit.id) do
Builds Builds
......
...@@ -7,8 +7,8 @@ ...@@ -7,8 +7,8 @@
%table.table.builds %table.table.builds
%tbody %tbody
%th Status %th Status
%th Commit %th Pipeline
%th Stages %th Stages
%th %th
%th %th
= render pipelines, commit_sha: true, stage: true, allow_retry: true, stages: pipelines.stages, status_icon_only: true, hide_branch: true = render pipelines, commit_sha: true, stage: true, allow_retry: true, stages: pipelines.stages, status_icon_only: true, show_commit: false
- page_title "Pipelines", "#{@commit.title} (#{@commit.short_id})", "Commits"
.prepend-top-default
= render "commit_box"
= render "ci_menu"
= render "pipelines_list", pipelines: @ci_pipelines
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
- unless diff_file.submodule? - unless diff_file.submodule?
.file-actions.hidden-xs .file-actions.hidden-xs
- if blob_text_viewable?(blob) - if blob_text_viewable?(blob)
= link_to '#', class: 'js-toggle-diff-comments btn active has-tooltip btn-file-option', title: "Toggle comments for this files", disabled: @diff_notes_disabled do = link_to '#', class: 'js-toggle-diff-comments btn active has-tooltip btn-file-option', title: "Toggle comments for this file", disabled: @diff_notes_disabled do
= icon('comment') = icon('comment')
\ \
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
.col-sm-10 .col-sm-10
.input-group .input-group
.input-group-addon.label-color-preview &nbsp; .input-group-addon.label-color-preview &nbsp;
= f.color_field :color, class: "form-control" = f.text_field :color, class: "form-control"
.help-block .help-block
Choose any color. Choose any color.
%br %br
......
...@@ -58,11 +58,11 @@ ...@@ -58,11 +58,11 @@
.monospace #{short_sha(merge_request_diff.head_commit_sha)} .monospace #{short_sha(merge_request_diff.head_commit_sha)}
%small %small
= time_ago_with_tooltip(merge_request_diff.created_at) = time_ago_with_tooltip(merge_request_diff.created_at)
%li %li
= link_to merge_request_version_path(@project, @merge_request, @merge_request_diff), class: ('is-active' unless @start_sha) do = link_to merge_request_version_path(@project, @merge_request, @merge_request_diff), class: ('is-active' unless @start_sha) do
%strong %strong
#{@merge_request.target_branch} (base) #{@merge_request.target_branch} (base)
.monospace #{short_sha(@merge_request_diff.base_commit_sha)} .monospace #{short_sha(@merge_request_diff.base_commit_sha)}
- unless @merge_request_diff.latest? && !@start_sha - unless @merge_request_diff.latest? && !@start_sha
.comments-disabled-notif.content-block .comments-disabled-notif.content-block
......
...@@ -46,7 +46,7 @@ ...@@ -46,7 +46,7 @@
%table.table.builds %table.table.builds
%tbody %tbody
%th Status %th Status
%th Commit %th Pipeline
%th Stages %th Stages
%th %th
%th %th
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
.col-sm-10 .col-sm-10
.checkbox .checkbox
= f.check_box :active = f.check_box :active
%span.light Paused runners don't accept new builds %span.light Paused Runners don't accept new builds
.form-group .form-group
= label :run_untagged, 'Run untagged jobs', class: 'control-label' = label :run_untagged, 'Run untagged jobs', class: 'control-label'
.col-sm-10 .col-sm-10
...@@ -33,6 +33,6 @@ ...@@ -33,6 +33,6 @@
Tags Tags
.col-sm-10 .col-sm-10
= f.text_field :tag_list, value: runner.tag_list.to_s, class: 'form-control' = f.text_field :tag_list, value: runner.tag_list.to_s, class: 'form-control'
.help-block You can setup jobs to only use runners with specific tags .help-block You can setup jobs to only use Runners with specific tags
.form-actions .form-actions
= f.submit 'Save changes', class: 'btn btn-save' = f.submit 'Save changes', class: 'btn btn-save'
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
.pull-right .pull-right
- if @project_runners.include?(runner) - if @project_runners.include?(runner)
- if runner.belongs_to_one_project? - if runner.belongs_to_one_project?
= link_to 'Remove runner', runner_path(runner), data: { confirm: "Are you sure?" }, method: :delete, class: 'btn btn-danger btn-sm' = link_to 'Remove Runner', runner_path(runner), data: { confirm: "Are you sure?" }, method: :delete, class: 'btn btn-danger btn-sm'
- else - else
- runner_project = @project.runner_projects.find_by(runner_id: runner) - runner_project = @project.runner_projects.find_by(runner_id: runner)
= link_to 'Disable for this project', namespace_project_runner_project_path(@project.namespace, @project, runner_project), data: { confirm: "Are you sure?" }, method: :delete, class: 'btn btn-danger btn-sm' = link_to 'Disable for this project', namespace_project_runner_project_path(@project.namespace, @project, runner_project), data: { confirm: "Are you sure?" }, method: :delete, class: 'btn btn-danger btn-sm'
......
%h3 Shared runners %h3 Shared Runners
.bs-callout.bs-callout-warning.shared-runners-description .bs-callout.bs-callout-warning.shared-runners-description
- if shared_runners_text.present? - if shared_runners_text.present?
= markdown(shared_runners_text, pipeline: 'plain_markdown') = markdown(shared_runners_text, pipeline: 'plain_markdown')
- else - else
Shared runners execute code of different projects on the same Runner unless you configure GitLab Runner Autoscale with MaxBuilds 1 (which it is on GitLab.com). GitLab Shared Runners execute code of different projects on the same Runner
unless you configure GitLab Runner Autoscale with MaxBuilds 1 (which it is
on GitLab.com).
%hr %hr
- if @project.shared_runners_enabled? - if @project.shared_runners_enabled?
= link_to toggle_shared_runners_namespace_project_runners_path(@project.namespace, @project), class: 'btn btn-warning', method: :post do = link_to toggle_shared_runners_namespace_project_runners_path(@project.namespace, @project), class: 'btn btn-warning', method: :post do
Disable shared runners Disable shared Runners
- else - else
= link_to toggle_shared_runners_namespace_project_runners_path(@project.namespace, @project), class: 'btn btn-success', method: :post do = link_to toggle_shared_runners_namespace_project_runners_path(@project.namespace, @project), class: 'btn btn-success', method: :post do
Enable shared runners Enable shared Runners
&nbsp; for this project &nbsp; for this project
- if @shared_runners_count.zero? - if @shared_runners_count.zero?
This GitLab server does not provide any shared runners yet. This GitLab server does not provide any shared Runners yet.
Please use specific runners or ask the administrator to create one. Please use the specific Runners or ask your administrator to create one.
- else - else
%h4.underlined-title Available shared runners - #{@shared_runners_count} %h4.underlined-title Available shared Runners : #{@shared_runners_count}
%ul.bordered-list.available-shared-runners %ul.bordered-list.available-shared-runners
= render partial: 'runner', collection: @shared_runners, as: :runner = render partial: 'runner', collection: @shared_runners, as: :runner
- if @shared_runners_count > 10 - if @shared_runners_count > 10
......
%h3 Specific runners %h3 Specific Runners
.bs-callout.help-callout .bs-callout.help-callout
%h4 How to setup a new project specific runner %h4 How to setup a specific Runner for a new project
%ol %ol
%li %li
Install GitLab Runner software. Install a Runner compatible with GitLab CI
Checkout the #{link_to 'GitLab Runner section', 'https://about.gitlab.com/gitlab-ci/#gitlab-runner', target: '_blank'} to install it (checkout the #{link_to 'GitLab Runner section', 'https://about.gitlab.com/gitlab-ci/#gitlab-runner', target: '_blank'} for information on how to install it).
%li %li
Specify the following URL during runner setup: Specify the following URL during the Runner setup:
%code #{ci_root_url(only_path: false)} %code #{ci_root_url(only_path: false)}
%li %li
Use the following registration token during setup: Use the following registration token during setup:
%code #{@project.runners_token} %code #{@project.runners_token}
%li %li
Start runner! Start the Runner!
- if @project_runners.any? - if @project_runners.any?
......
...@@ -2,24 +2,24 @@ ...@@ -2,24 +2,24 @@
.light.prepend-top-default .light.prepend-top-default
%p %p
A 'runner' is a process which runs a build. A 'Runner' is a process which runs a build.
You can setup as many runners as you need. You can setup as many Runners as you need.
%br %br
Runners can be placed on separate users, servers, and even on your local machine. Runners can be placed on separate users, servers, and even on your local machine.
%p Each runner can be in one of the following states: %p Each Runner can be in one of the following states:
%div %div
%ul %ul
%li %li
%span.label.label-success active %span.label.label-success active
\- runner is active and can process any new build \- Runner is active and can process any new builds
%li %li
%span.label.label-danger paused %span.label.label-danger paused
\- runner is paused and will not receive any new build \- Runner is paused and will not receive any new builds
%hr %hr
%p.lead To start serving your builds you can either add specific runners to your project or use shared runners %p.lead To start serving your builds you can either add specific Runners to your project or use shared Runners
.row .row
.col-sm-6 .col-sm-6
= render 'specific_runners' = render 'specific_runners'
......
- type = local_assigns.fetch(:type, :issues)
- page_context_word = type.to_s.humanize(capitalize: false)
- issuables = @issues || @merge_requests
%ul.nav-links.issues-state-filters %ul.nav-links.issues-state-filters
- if defined?(type) && type == :merge_requests
- page_context_word = 'merge requests'
- else
- page_context_word = 'issues'
%li{class: ("active" if params[:state] == 'opened')} %li{class: ("active" if params[:state] == 'opened')}
= link_to page_filter_path(state: 'opened', label: true), title: "Filter by #{page_context_word} that are currently opened." do = link_to page_filter_path(state: 'opened', label: true), title: "Filter by #{page_context_word} that are currently opened." do
#{state_filters_text_for(:opened, @project)} #{issuables_state_counter_text(type, :opened)}
- if defined?(type) && type == :merge_requests - if type == :merge_requests
%li{class: ("active" if params[:state] == 'merged')} %li{class: ("active" if params[:state] == 'merged')}
= link_to page_filter_path(state: 'merged', label: true), title: 'Filter by merge requests that are currently merged.' do = link_to page_filter_path(state: 'merged', label: true), title: 'Filter by merge requests that are currently merged.' do
#{state_filters_text_for(:merged, @project)} #{issuables_state_counter_text(type, :merged)}
%li{class: ("active" if params[:state] == 'closed')} %li{class: ("active" if params[:state] == 'closed')}
= link_to page_filter_path(state: 'closed', label: true), title: 'Filter by merge requests that are currently closed and unmerged.' do = link_to page_filter_path(state: 'closed', label: true), title: 'Filter by merge requests that are currently closed and unmerged.' do
#{state_filters_text_for(:closed, @project)} #{issuables_state_counter_text(type, :closed)}
- else - else
%li{class: ("active" if params[:state] == 'closed')} %li{class: ("active" if params[:state] == 'closed')}
= link_to page_filter_path(state: 'closed', label: true), title: 'Filter by issues that are currently closed.' do = link_to page_filter_path(state: 'closed', label: true), title: 'Filter by issues that are currently closed.' do
#{state_filters_text_for(:closed, @project)} #{issuables_state_counter_text(type, :closed)}
%li{class: ("active" if params[:state] == 'all')} %li{class: ("active" if params[:state] == 'all')}
= link_to page_filter_path(state: 'all', label: true), title: "Show all #{page_context_word}." do = link_to page_filter_path(state: 'all', label: true), title: "Show all #{page_context_word}." do
#{state_filters_text_for(:all, @project)} #{issuables_state_counter_text(type, :all)}
...@@ -99,13 +99,24 @@ module Gitlab ...@@ -99,13 +99,24 @@ module Gitlab
config.action_view.sanitized_allowed_protocols = %w(smb) config.action_view.sanitized_allowed_protocols = %w(smb)
config.middleware.use Rack::Attack config.middleware.insert_before Warden::Manager, Rack::Attack
# Allow access to GitLab API from other domains # Allow access to GitLab API from other domains
config.middleware.use Rack::Cors do config.middleware.insert_before Warden::Manager, Rack::Cors do
allow do
origins Gitlab.config.gitlab.url
resource '/api/*',
credentials: true,
headers: :any,
methods: :any,
expose: ['Link']
end
# Cross-origin requests must not have the session cookie available
allow do allow do
origins '*' origins '*'
resource '/api/*', resource '/api/*',
credentials: false,
headers: :any, headers: :any,
methods: :any, methods: :any,
expose: ['Link'] expose: ['Link']
...@@ -116,6 +127,10 @@ module Gitlab ...@@ -116,6 +127,10 @@ module Gitlab
redis_config_hash = Gitlab::Redis.params redis_config_hash = Gitlab::Redis.params
redis_config_hash[:namespace] = Gitlab::Redis::CACHE_NAMESPACE redis_config_hash[:namespace] = Gitlab::Redis::CACHE_NAMESPACE
redis_config_hash[:expires_in] = 2.weeks # Cache should not grow forever redis_config_hash[:expires_in] = 2.weeks # Cache should not grow forever
if Sidekiq.server? # threaded context
redis_config_hash[:pool_size] = Sidekiq.options[:concurrency] + 5
redis_config_hash[:pool_timeout] = 1
end
config.cache_store = :redis_store, redis_config_hash config.cache_store = :redis_store, redis_config_hash
config.active_record.raise_in_transactional_callbacks = true config.active_record.raise_in_transactional_callbacks = true
......
# Make sure we initialize a Redis connection pool before Sidekiq starts
# multi-threaded execution.
Gitlab::Redis.with { nil }
module AttrEncrypted module AttrEncrypted
module Adapters module Adapters
module ActiveRecord module ActiveRecord
def attribute_instance_methods_as_symbols_with_no_db_connection module DBConnectionQuerier
# Use with_connection so the connection doesn't stay pinned to the thread. def attribute_instance_methods_as_symbols
connected = ::ActiveRecord::Base.connection_pool.with_connection(&:active?) rescue false # Use with_connection so the connection doesn't stay pinned to the thread.
connected = ::ActiveRecord::Base.connection_pool.with_connection(&:active?) rescue false
if connected
# Call version from AttrEncrypted::Adapters::ActiveRecord if connected
attribute_instance_methods_as_symbols_without_no_db_connection # Call version from AttrEncrypted::Adapters::ActiveRecord
else super
# Call version from AttrEncrypted, i.e., `super` with regards to AttrEncrypted::Adapters::ActiveRecord else
AttrEncrypted.instance_method(:attribute_instance_methods_as_symbols).bind(self).call # Call version from AttrEncrypted, i.e., `super` with regards to AttrEncrypted::Adapters::ActiveRecord
AttrEncrypted.instance_method(:attribute_instance_methods_as_symbols).bind(self).call
end
end end
end end
prepend DBConnectionQuerier
alias_method_chain :attribute_instance_methods_as_symbols, :no_db_connection
end end
end end
end end
...@@ -20,7 +20,7 @@ if defined?(ActiveRecord::ConnectionAdapters::Mysql2Adapter) ...@@ -20,7 +20,7 @@ if defined?(ActiveRecord::ConnectionAdapters::Mysql2Adapter)
execute_without_retry(*args) execute_without_retry(*args)
rescue ActiveRecord::StatementInvalid => e rescue ActiveRecord::StatementInvalid => e
if e.message =~ /server has gone away/i if e.message =~ /server has gone away/i
warn "Server timed out, retrying" warn "Lost connection to MySQL server during query"
reconnect! reconnect!
retry retry
else else
......
if defined?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter) if defined?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter)
class ActiveRecord::ConnectionAdapters::PostgreSQLAdapter class ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
module LimitFilter
def add_column(table_name, column_name, type, options = {})
options.delete(:limit) if type == :text
super(table_name, column_name, type, options)
end
def change_column(table_name, column_name, type, options = {})
options.delete(:limit) if type == :text
super(table_name, column_name, type, options)
end
end
prepend ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::LimitFilter
class TableDefinition class TableDefinition
def text(*args) def text(*args)
options = args.extract_options! options = args.extract_options!
...@@ -9,18 +23,5 @@ if defined?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter) ...@@ -9,18 +23,5 @@ if defined?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter)
column_names.each { |name| column(name, type, options) } column_names.each { |name| column(name, type, options) }
end end
end end
def add_column_with_limit_filter(table_name, column_name, type, options = {})
options.delete(:limit) if type == :text
add_column_without_limit_filter(table_name, column_name, type, options)
end
def change_column_with_limit_filter(table_name, column_name, type, options = {})
options.delete(:limit) if type == :text
change_column_without_limit_filter(table_name, column_name, type, options)
end
alias_method_chain :add_column, :limit_filter
alias_method_chain :change_column, :limit_filter
end end
end end
...@@ -635,6 +635,7 @@ Rails.application.routes.draw do ...@@ -635,6 +635,7 @@ Rails.application.routes.draw do
member do member do
get :branches get :branches
get :builds get :builds
get :pipelines
post :cancel_builds post :cancel_builds
post :retry_builds post :retry_builds
post :revert post :revert
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
- [SSH](ssh/README.md) Setup your ssh keys and deploy keys for secure access to your projects. - [SSH](ssh/README.md) Setup your ssh keys and deploy keys for secure access to your projects.
- [Webhooks](web_hooks/web_hooks.md) Let GitLab notify you when new code has been pushed to your project. - [Webhooks](web_hooks/web_hooks.md) Let GitLab notify you when new code has been pushed to your project.
- [Workflow](workflow/README.md) Using GitLab functionality and importing projects from GitHub and SVN. - [Workflow](workflow/README.md) Using GitLab functionality and importing projects from GitHub and SVN.
- [University](university/README.md) Learn Git and GitLab through videos and courses.
## Administrator documentation ## Administrator documentation
......
...@@ -40,6 +40,12 @@ Example of response ...@@ -40,6 +40,12 @@ Example of response
"finished_at": "2015-12-24T17:54:27.895Z", "finished_at": "2015-12-24T17:54:27.895Z",
"id": 7, "id": 7,
"name": "teaspoon", "name": "teaspoon",
"pipeline": {
"id": 6,
"ref": "master",
"sha": "0ff3ae198f8601a285adcf5c0fff204ee6fba5fd",
"status": "pending"
}
"ref": "master", "ref": "master",
"runner": null, "runner": null,
"stage": "test", "stage": "test",
...@@ -78,6 +84,12 @@ Example of response ...@@ -78,6 +84,12 @@ Example of response
"finished_at": "2015-12-24T17:54:24.921Z", "finished_at": "2015-12-24T17:54:24.921Z",
"id": 6, "id": 6,
"name": "spinach:other", "name": "spinach:other",
"pipeline": {
"id": 6,
"ref": "master",
"sha": "0ff3ae198f8601a285adcf5c0fff204ee6fba5fd",
"status": "pending"
}
"ref": "master", "ref": "master",
"runner": null, "runner": null,
"stage": "test", "stage": "test",
...@@ -146,6 +158,12 @@ Example of response ...@@ -146,6 +158,12 @@ Example of response
"finished_at": "2016-01-11T10:14:09.526Z", "finished_at": "2016-01-11T10:14:09.526Z",
"id": 69, "id": 69,
"name": "rubocop", "name": "rubocop",
"pipeline": {
"id": 6,
"ref": "master",
"sha": "0ff3ae198f8601a285adcf5c0fff204ee6fba5fd",
"status": "pending"
}
"ref": "master", "ref": "master",
"runner": null, "runner": null,
"stage": "test", "stage": "test",
...@@ -170,6 +188,12 @@ Example of response ...@@ -170,6 +188,12 @@ Example of response
"finished_at": "2015-12-24T17:54:33.913Z", "finished_at": "2015-12-24T17:54:33.913Z",
"id": 9, "id": 9,
"name": "brakeman", "name": "brakeman",
"pipeline": {
"id": 6,
"ref": "master",
"sha": "0ff3ae198f8601a285adcf5c0fff204ee6fba5fd",
"status": "pending"
}
"ref": "master", "ref": "master",
"runner": null, "runner": null,
"stage": "test", "stage": "test",
...@@ -231,6 +255,12 @@ Example of response ...@@ -231,6 +255,12 @@ Example of response
"finished_at": "2015-12-24T17:54:31.198Z", "finished_at": "2015-12-24T17:54:31.198Z",
"id": 8, "id": 8,
"name": "rubocop", "name": "rubocop",
"pipeline": {
"id": 6,
"ref": "master",
"sha": "0ff3ae198f8601a285adcf5c0fff204ee6fba5fd",
"status": "pending"
}
"ref": "master", "ref": "master",
"runner": null, "runner": null,
"stage": "test", "stage": "test",
......
...@@ -48,7 +48,7 @@ You should then use the `code` to request an access token. ...@@ -48,7 +48,7 @@ You should then use the `code` to request an access token.
>**Important:** >**Important:**
It is highly recommended that you send a `state` value with the request to `/oauth/authorize` and It is highly recommended that you send a `state` value with the request to `/oauth/authorize` and
validate that value is returned and matches in the redirect request. validate that value is returned and matches in the redirect request.
This is important to prevent [CSFR attacks](http://www.oauthsecurity.com/#user-content-authorization-code-flow), This is important to prevent [CSRF attacks](http://www.oauthsecurity.com/#user-content-authorization-code-flow),
`state` really should have been a requirement in the standard! `state` really should have been a requirement in the standard!
### 3. Requesting the access token ### 3. Requesting the access token
...@@ -134,4 +134,4 @@ access_token = client.password.get_token('user@example.com', 'secret') ...@@ -134,4 +134,4 @@ access_token = client.password.get_token('user@example.com', 'secret')
puts access_token.token puts access_token.token
``` ```
[personal access tokens]: ./README.md#personal-access-tokens [personal access tokens]: ./README.md#personal-access-tokens
\ No newline at end of file
...@@ -899,6 +899,7 @@ Parameters: ...@@ -899,6 +899,7 @@ Parameters:
- `id` (required) - The ID or NAMESPACE/PROJECT_NAME of the project to be forked - `id` (required) - The ID or NAMESPACE/PROJECT_NAME of the project to be forked
- `group_id` (required) - The ID of a group - `group_id` (required) - The ID of a group
- `group_access` (required) - Level of permissions for sharing - `group_access` (required) - Level of permissions for sharing
- `expires_at` - Share expiration date in ISO 8601 format: 2016-09-26
## Hooks ## Hooks
......
...@@ -41,7 +41,9 @@ Example response: ...@@ -41,7 +41,9 @@ Example response:
"gravatar_enabled" : true, "gravatar_enabled" : true,
"sign_in_text" : null, "sign_in_text" : null,
"container_registry_token_expire_delay": 5, "container_registry_token_expire_delay": 5,
"repository_storage": "default" "repository_storage": "default",
"koding_enabled": false,
"koding_url": null
} }
``` ```
...@@ -72,7 +74,9 @@ PUT /application/settings ...@@ -72,7 +74,9 @@ PUT /application/settings
| `after_sign_out_path` | string | no | Where to redirect users after logout | | `after_sign_out_path` | string | no | Where to redirect users after logout |
| `container_registry_token_expire_delay` | integer | no | Container Registry token duration in minutes | | `container_registry_token_expire_delay` | integer | no | Container Registry token duration in minutes |
| `repository_storage` | string | no | Storage path for new projects. The value should be the name of one of the repository storage paths defined in your gitlab.yml | | `repository_storage` | string | no | Storage path for new projects. The value should be the name of one of the repository storage paths defined in your gitlab.yml |
| `enabled_git_access_protocol` | string | no | Enabled protocols for Git access. Allowed values are: `ssh`, `http`, and `nil` to allow both protocols. | `enabled_git_access_protocol` | string | no | Enabled protocols for Git access. Allowed values are: `ssh`, `http`, and `nil` to allow both protocols. |
| `koding_enabled` | boolean | no | Enable Koding integration. Default is `false`. |
| `koding_url` | string | yes (if `koding_enabled` is `true`) | The Koding instance URL for integration. |
```bash ```bash
curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/application/settings?signup_enabled=false&default_project_visibility=1 curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/application/settings?signup_enabled=false&default_project_visibility=1
...@@ -103,6 +107,8 @@ Example response: ...@@ -103,6 +107,8 @@ Example response:
"user_oauth_applications": true, "user_oauth_applications": true,
"after_sign_out_path": "", "after_sign_out_path": "",
"container_registry_token_expire_delay": 5, "container_registry_token_expire_delay": 5,
"repository_storage": "default" "repository_storage": "default",
"koding_enabled": false,
"koding_url": null
} }
``` ```
...@@ -381,7 +381,7 @@ sudo usermod -aG redis git ...@@ -381,7 +381,7 @@ sudo usermod -aG redis git
GitLab Shell is an SSH access and repository management software developed specially for GitLab. GitLab Shell is an SSH access and repository management software developed specially for GitLab.
# Run the installation task for gitlab-shell (replace `REDIS_URL` if needed): # Run the installation task for gitlab-shell (replace `REDIS_URL` if needed):
sudo -u git -H bundle exec rake gitlab:shell:install REDIS_URL=unix:/var/run/redis/redis.sock RAILS_ENV=production sudo -u git -H bundle exec rake gitlab:shell:install REDIS_URL=unix:/var/run/redis/redis.sock RAILS_ENV=production SKIP_STORAGE_VALIDATION=true
# By default, the gitlab-shell config is generated from your main GitLab config. # By default, the gitlab-shell config is generated from your main GitLab config.
# You can review (and modify) the gitlab-shell config as follows: # You can review (and modify) the gitlab-shell config as follows:
......
## What is GitLab University
_GitLab University_ has as a goal to teach the fundamentals of **Version Control with Git and GitLab** through courses that cover topics which can be mastered in around 2 hours.
_University materials don't replace our [Documentation](http://docs.gitlab.com) or [Blog Articles](https://about.gitlab.com/blog/)._
---
### On this page
+ [GITx] Git
+ [OPSx] DevOps
+ [GLBx] GitLab Basics
+ [INTx] GitLab Integrations
+ [GLFx] GitLab Workflows
+ [GLEx] GitLab Enterprise Edition extra features
+ [GCIx] GitLab CI
+ [ECO] Ecosystem
+ [COM] Competition comparison
+ [SPTx] Support Bootcamp
+ [SLSx] Sales Bootcamp
+ [TRAx] Trainings
---
+ [GIT1] [Version Control Systems](https://docs.google.com/presentation/d/16sX7hUrCZyOFbpvnrAFrg6tVO5_yT98IgdAqOmXwBho/edit#slide=id.g72f2e4906_2_29)
+ [GIT2] [Operating Systems and How Git Works](https://drive.google.com/a/gitlab.com/file/d/0B41DBToSSIG_OVYxVFJDOGI3Vzg/view?usp=sharing)
+ [GIT3] [Intro to Git](https://www.codeschool.com/account/courses/try-git)
---
+ [OPS1] [What is Omnibus](https://www.youtube.com/watch?v=XTmpKudd-Oo)
+ [OPS2] [Installing GitLab](https://www.youtube.com/watch?v=Q69YaOjqNhg)
+ [OPS3] [Configuring an external PostgreSQL database](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/README.md#using-a-non-packaged-postgresql-database-management-server)
+ [OPS5] [Importing from Other Tools or SVN](http://doc.gitlab.com/ee/workflow/importing/)
+ [OPS6] [High Availability Documentation](https://about.gitlab.com/high-availability/)
+ [OPS7] [Managing LDAP, Active Directory](https://www.youtube.com/watch?v=HPMjM-14qa8)
+ [OPS8] [Scalability and High Availability](https://www.youtube.com/watch?v=cXRMJJb6sp4&list=PLFGfElNsQthbQu_IWlNOxul0TbS_2JH-e&index=2)
+ [OPS9] [High Availability on AWS](high-availability/aws/README.md)
---
+ [GLB1] [Terminology](glossary/README.md)
+ [GLB2] [GitLab Basics](http://doc.gitlab.com/ce/gitlab-basics/README.html)
+ [GLB3] [Demo of GitLab.com](https://www.youtube.com/watch?v=WaiL5DGEMR4)
+ [GLB4] [Create and Add your SSH key to GitLab](https://www.youtube.com/watch?v=54mxyLo3Mqk)
+ [GLB5] [Repositories, Projects and Groups](https://www.youtube.com/watch?v=4TWfh1aKHHw&index=1&list=PLFGfElNsQthbQu_IWlNOxul0TbS_2JH-e)
+ [GLB6] [Creating a Project in GitLab](https://www.youtube.com/watch?v=7p0hrpNaJ14)
+ [GLB7] [Issues and Merge Requests](https://www.youtube.com/watch?v=raXvuwet78M)
+ [GLB8] [Big files in Git (Git LFS, Annex)](https://gitlab.com/gitlab-org/University/blob/master/classes/git_lfs_and_annex.md)
---
+ [INT1] [JIRA and Jenkins integrations in GitLab](https://gitlabmeetings.webex.com/gitlabmeetings/ldr.php?RCID=44b548147a67ab4d8a62274047146415)
+ [INT2] [Integrating JIRA with GitLab](http://doc.gitlab.com/ee/integration/jira.html)
+ [INT3] [Integrating Jenkins with GitLab](http://doc.gitlab.com/ee/integration/jenkins.html)
+ [INT4] [Integrating Bamboo with GitLab](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/project_services/bamboo.md)
+ [INT5] [Documentation on Integrating Slack with GitLab](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/integration/slack.md)
---
+ [GLF1] [GitLab Flow](https://www.youtube.com/watch?v=UGotqAUACZA)
---
+ [GLE1] [Configuring an external MySQL database](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/README.md#using-a-mysql-database-management-server-enterprise-edition-only)
+ [GLE2] [Managing Permissions within EE](https://www.youtube.com/watch?v=DjUoIrkiNuM)
+ [GLE3] [Upcoming in EE and Big files in Git (Git LFS, Annex)](https://gitlab.com/gitlab-org/University/blob/master/classes/upcoming_in_ee.md)
---
+ [GCI1] [GitLab CI product page](https://about.gitlab.com/gitlab-ci/)
+ [GCI2] [Setting up GitLab Runner For Continuous Integration](https://about.gitlab.com/2016/03/01/gitlab-runner-with-docker/)
---
+ [COM1] [GitLab compared to other tools](https://about.gitlab.com/comparison/)
+ [COM2] [Compare GitLab versions](https://about.gitlab.com/features/#compare)
+ [COM3] [Innersourcing article](https://about.gitlab.com/2014/09/05/innersourcing-using-the-open-source-workflow-to-improve-collaboration-within-an-organization/)
---
+ [ECO1] [Ecosystem Overview](https://www.youtube.com/watch?v=sXlhgPK1NTY&list=PLFGfElNsQthbQu_IWlNOxul0TbS_2JH-e&index=6)
+ [ECO2] [Positioning FAQ](https://about.gitlab.com/handbook/positioning-faq)
+ [ECO3] [GitLab Ecosystem slides](https://docs.google.com/presentation/d/1vCU-NbZWz8NTNK8Vu3y4zGMAHb5DpC8PE5mHtw1PWfI/edit)
+ [ECO4] [Customer Use-Cases](https://about.gitlab.com/handbook/use-cases/)
---
+ [SPT1] [Support Path](support/README.md)
+ [SPT2] [End User Training Material](https://gitlab.com/gitlab-org/University/blob/master/training/user_training.md)
+ [SPT3] [Materials for Training Sessions](https://gitlab.com/gitlab-org/University/tree/master/training/topics)
---
+ [SLS1] [Sales Path (redirect to sales handbook)](https://about.gitlab.com/handbook/sales-onboarding/)
+ [SLS2] [GitLab Direction](https://about.gitlab.com/direction/)
---
+ [TRA1] [End User Training](training/end-user/README.md)
---
### External Resources
+ [DOC] GitLab Documentation
+ [Set up and use GitLab Pages](http://doc.gitlab.com/ee/pages/README.html)
+ [Markdown Reference](http://doc.gitlab.com/ce/markdown/markdown.html)
+ [GLW] GitLab Workshop (@ Platzi)
+ [GitLab Workshop Part 1: Basics of Git and GitLab](https://courses.platzi.com/classes/git-gitlab/)
+ [Create a GitLab Account](https://courses.platzi.com/classes/git-gitlab/concepto/first-steps/create-an-account-on-gitlab/material/)
+ [GLY] GitLab YouTube Videos
+ [Making GitLab Great for Everyone, our response to the Dear GitHub letter](https://www.youtube.com/watch?v=GGC40y4vMx0)
+ [Compared to Atlassian (Recorded on 2016-03-03) ](https://youtu.be/Nbzp1t45ERo)
+ [GLI] GitLab Team-Only Access
+ [GitLab architecture for noobs](https://dev.gitlab.org/gitlab/gitlabhq/blob/master/doc/development/architecture.md)
+ [Client Assessment of GitLab versus GitHub](https://docs.google.com/a/gitlab.com/spreadsheets/d/18cRF9Y5I6I7Z_ab6qhBEW55YpEMyU4PitZYjomVHM-M/edit?usp=sharing)
+ [KNT] Slides & Keynotes by GitLabbers & other individuals
+ [Why Git and GitLab slide deck](https://docs.google.com/a/gitlab.com/presentation/d/1RcZhFmn5VPvoFu6UMxhMOy7lAsToeBZRjLRn0LIdaNc/)
+ [Git Workshop](https://docs.google.com/presentation/d/1JzTYD8ij9slejV2-TO-NzjCvlvj6mVn9BORePXNJoMI/)
+ Others (not created by GitLab)
+ [Dev Ops terminology](https://xebialabs.com/glossary/)
+ [Continuous Delivery vs Continuous Deployment](https://www.youtube.com/watch?v=igwFj8PPSnw)
+ [Periodic Table of DevOps Tools](https://xebialabs.com/periodic-table-of-devops-tools/)
+ [State of Dev Ops 2015 Report by Puppet Labs](https://puppetlabs.com/sites/default/files/2015-state-of-devops-report.pdf) Insightful Chapters to understand the Impact of Continuous Delivery on Performance (Chapter 4), the Application Architecture (Chapter 5) and How IT Managers can help their teams win (Chapter 6).
+ [2011 WSJ article by Mark Andreeson - Software is Eating the World](http://www.wsj.com/articles/SB10001424053111903480904576512250915629460)
+ [2014 Blog post by Chris Dixon - Software eats software development](http://cdixon.org/2014/04/13/software-eats-software-development/)
+ [2015 Venture Beat article - Actually, Open Source is Eating the World](http://venturebeat.com/2015/12/06/its-actually-open-source-software-thats-eating-the-world/)
+ [Customer review of GitLab with talking points on why they prefer GitLab](https://www.enovate.co.uk/web-design-blog/2015/11/25/gitlab-review/)
+ [3rd party tool comparison](http://technologyconversations.com/2015/10/16/github-vs-gitlabs-vs-bitbucket-server-formerly-stash/)
+ [Amazon's transition to Continuous Delivery](https://www.youtube.com/watch?v=esEFaY0FDKc)
+ [Article on Continuous Integration from ThoughtWorks](https://www.thoughtworks.com/continuous-integration)
## What is the Glossary
This contains a simplified list and definitions of some of the terms that you will encounter in your day to day activities when working with GitLab.
Please add any terms that you discover that you think would be useful for others.
### 2FA
User authentication by combination of 2 different steps during login. This allows for more security.
### Access Levels
Process of selective restriction to create, view, modify or delete a resource based on a set of assigned permissions.
See, [GitLab's Permission Guidelines](http://doc.gitlab.com/ce/permissions/permissions.html)
### Active Directory (AD)
A Microsoft based directory service for windows domain networks. It uses LDAP technology under the hood
### Agile
Building and delivering software in phases/parts rather than trying to build everything at once then delivering to the user/client. The later is known as a WaterFall model
### Application Lifecycle Management (ALM)
Entire product lifecycle management process for an application. From requirements management, development and testing until deployment.
### Artifactory
Version control for binaries.
### Artifacts
objects (usually binary and large) created by a build process
### Atlassian
A company that develops software products for developers and project managers including Bitbucket, Jira, Hipchat, Confluence, Bamboo. See [Atlassian] (https://www.atlassian.com)
### Audit Log
*** Needs definition here
### Auto Defined User Group
User groups are a way of centralizing control over important management tasks, particularly access control and password policies.
A simple example of such groups are the users and the admins groups.
In most of the cases these groups are auto defined in terms of access, rules of usage, conditions to be part of, etc...
### Bamboo
Atlassian's CI tool similar to GitLab CI and Jenkins
### Basic Subscription
Entry level subscription for GitLab EE currently available in packs of 10 see [Basic subscription](https://about.gitlab.com/pricing/)
### Bitbucket
Atlassian's web hosting service for Git and Mercurial Projects i.e. GitLab.com competitor
### Branch
A branch is a parallel version of a repository. Allows you to work on the repository without you affecting the "master" branch. Allows you to make changes without affecting the current "live" version. When you have made all your changes to your branch you can then merge to the master and to make the changes fo "live".
### Branded Login
Having your own logo on your GitLab instance login page instead of the GitLab logo.
### CEPH
is a distributed object store and file system designed to provide excellent performance, reliability and scalability.
### Clone
A copy of a repository stored on your machine that allows you to use your own editor without being online, but still tracks the changes made remotely.
### Code Review
Examination of a progam's code. The main aim is to maintain high standards quality of code that is being shipped.
### Code Snippet
A small amount of code. Usually for the purpose of showing other developers how
to do something specific or reproduce a problem.
### Collaborator
Person with read and write access to a repository who has been invited by repository owner.
### Commit
Is a change (revision) to a file, and also creates an ID that allows you to see revision history and who made the changes.
### Community
Everyone who is using GitLab
### Confluence
Atlassian's product for collaboration of documents and projects.
### Continuous Deivery
Continuous delivery is a series of practices designed to ensure that code can be rapidly and safely deployed to production by delivering every change to a production-like environment and ensuring business applications and services function as expected through rigorous automated testing.
### Continuous Deployment
Continuous deployment is the next step of continuous delivery: Every change that passes the automated tests is deployed to production automatically.
### Continuous Integration
A process that involves adding new code commits to source code with the combined code being run on an automated test to ensure that the changes do not break the software.
### Contributor
Term used to a person contributing to an Open Source Project.
### Data Centre
Atlassian product for High Availability.
### Deploy Keys
An SSH key stored on the your server that grants access to a single GitLab repository. This is used by a GitLab runner to clone a project's code so that tests can be run against the checked out code.
### Developer
For us (GitLab) this means a software developer, i.e. someone who makes software. It is also one of the levels of access in our multi level approval system.
### Diff
Is the difference between two commits, or saved changes. This will also be shown visually after the changes.
### Docker
Containers wrap up a piece of software in a complete filesystem that contains everything it needs to run: code, runtime, system tools, system libraries – anything you can install on a server.
This guarantees that it will always run the same, regardless of the environment it is running in.
### Fork
Your own copy of a repository that allows you to make changes to the repository without affecting the original.
### Gerrit
A code review tool built on top of Git.
### Git Hooks
Are scripts you can use to trigger actions at certain points.
### GitHost.io
Is a single-tenant solution that provides GitLab CE or EE as a managed service. GitLab Inc. is responsible for
installing, updating, hosting, and backing up customers own private and secure GitLab instance.
### GitHub
A web-based Git repository hosting service with an enterprise offering. Its main features are: issue tracking, pull request with code review, abundancy of integrations and wiki. As of April 2016, the service has over 14 million users. It offers free public repos, private repos and enterprise services are paid.
### GitLab CE
Our free on Premise solution with >100,000 users
### GitLab CI
Our own Continuos Integration feature that is shipped with each instance
### GitLab EE
Our premium on premise solution that currently has Basic, Standard and Plus subscription packages with additional features and support.
### GitLab.com
Our free SaaS for public and private repositories.
### Gitolite
Is basically an access layer that sits on top of Git. Users are granted access to repos via a simple config file and you as an admin only needs the users public SSH key and a username from the user.
### Gitorious
A web based hosting service for projects using Git. It was acquired by GitLab and we discontinued the service. [Gitorious Acquisition Blog Post](https://about.gitlab.com/2015/03/03/gitlab-acquires-gitorious/)
### HADR
Sometimes written HA/DR. High Availability for Disaster Recovery. Usually refers to a strategy having a failover server in place in case the main server fails.
### Hip Chat
Atlassian's real time chat application for teams. Competitor to Slack, RocketChat and MatterMost.
### High Availability
Refers to a system or component that is continuously operational for a desirably long length of time. Availability can be measured relative to "100% operational" or "never failing."
### Issue Tracker
A tool used to manage, organize, and maintain a list of issues, making it easier for an organization to manage.
### Jenkins
An Open Source CI tool written using the Java programming language. Does the same job as GitLab CI, Bamboo, Travis CI. It is extremely popular. see [Jenkins](https://jenkins-ci.org/)
### Jira
Atlassian's project management software. i.e. a complex issue tracker. See[Jira](https://www.atlassian.com/software/jira)
### Kerberos
A network authentication protocol that uses secret-key cryptography for security.
### Kubernetes
An open source container cluster manager originally designed by Google. It's basically a platform for automating deployment, scaling, and operations of application containers over clusters of hosts.
### Labels
An identifier to describe a group of one or more specific file revisions
### LDAP
Lightweight Directory Access Protocol - basically its a directory (electronic address book) with user information e.g. name, phone_number etc
### LDAP User Authentication
Allowing GitLab to sign in people from an LDAP server i.e. Allow people whose names are on the electronic user directory server) to be able to use their LDAP accounts to login.
### LDAP Group Sync
Allows you to synchronize the members of a GitLab group with one or more LDAP groups.
### Git LFS
Git Large File Storage. A way to enable git to handle large binary files by using reference pointers within small text files to point to the large files.
### Linux
An operating system like Windows or OS X. It is mostly used by software developers and on servers.
### Markdown
Is a lightweight markup language with plain text formatting syntax designed so that it can be converted to HTML and many other formats using a tool by the same name.
Markdown is often used to format readme files, for writing messages in online discussion forums, and to create rich text using a plain text editor.
### Maria DB
A community developed fork/variation of MySQL. MySQL is owned by Oracle.
### Master
Name of the default branch in every git repository.
### Mercurial
A free distributed version control system like Git. Think of it as a competitor to Git.
### Merge
Takes changes from one branch, and applies them into another branch.
### Meteor
A hip platform for building javascript apps.[Meteor] (https://www.meteor.com)
### Milestones
Allows you to track the progress on issues, and merge requests, which allows you to get a snapshot of the progress made.
### Mirror Repositories
You can set up a project to automatically have its branches, tags, and commits updated from an upstream repository. This is useful when a repository you're interested in is located on a different server, and you want to be able to browse its content and its activity using the familiar GitLab interface.
### MIT License
A type of software license. It lets people do anything with your code with proper attribution and without warranty. It is the most common license for open source applications written in Ruby on Rails. GitLab CE is issued under this license.
This means, you can download the code, modify it as you want even build a new commercial product using the underlying code and its not illegal. The only condition is that there is no form of waranty provided by GitLab so whatever happens if you use the code is your own problem.
### Mondo
*** Needs definition here
### Multi LDAP Server
*** Needs definition here
### My SQL
A relational database. Currently only supported if you are using EE. It is owned by Oracle.
### Namespace
In computing, a namespace is a set of symbols that are used to organize objects of various kinds, so that these objects may be referred to by name.
Prominent examples include:
- file systems are namespaces that assign names to files;
- programming languages organize their variables and subroutines in namespaces;
- computer networks and distributed systems assign names to resources, such as computers, printers, websites, (remote) files, etc.
### Nginx
(pronounced "engine x") is a web server. It can act as a reverse proxy server for HTTP, HTTPS, SMTP, POP3, and IMAP protocols, as well as a load balancer and an HTTP cache.
### oAuth
Is an open standard for authorization, commonly used as a way for Internet users to log into third party websites using their Microsoft, Google, Facebook or Twitter accounts without exposing their password.
### Omnibus Packages
Omnibus is a way to package the different services and tools required to run GitLab, so that users can install it without as much work.
### On Premise
On your own server. In GitLab, this refers to the ability to download GitLab EE/GitLab CE and host it on your own server rather than using GitLab.com which is hosted by GitLab Inc's servers.
### Open Source Software
Software for which the original source code is freely available and may be redistributed and modified.
### Owner
This is the most powerful person on a GitLab project. He has the permissions of all the other users plus the additional permission of being able to destroy i.e. delete the project
### PaaS
Typically referred to in regards to application development, it is a model in which a cloud provider delivers hardware and software tools to its users as a service
### Perforce
The company that produces Helix. A commercial, proprietary, centralised VCS well known for it's ability to version files of any size and type. They OEM a re-branded version of GitLab called "GitSwarm" that is tightly integrated with their "GitFusion" product, which in turn represents a portion of a Helix repository (called a depot) as a git repo
### Phabricator
Is a suite of web-based software development collaboration tools, including the Differential code review tool, the Diffusion repository browser, the Herald change monitoring tool, the Maniphest bug tracker and the Phriction wiki. Phabricator integrates with Git, Mercurial, and Subversion.
### Piwik Analytics
An open source analytics software to help you analyze web traffic. It is similar to google analytics only that google analytics is not open source and information is stored by google while in Piwik the information is stored in your own server hence fully private.
### Plus Subscription
GitLab Premium EE subscription that includes training and dedicated Account Management and Service Engineer and complete support package [Plus subscription](https://about.gitlab.com/pricing/)
### PostgreSQL
A relational database. Touted as the most advanced open source database.
### Protected Branches
A feature that protects branches from unauthorized pushes, force pushing or deletion.
### Pull
Git command to synchronize the local repository with the remote repository, by fetching all remote changes and merging them into the local repository.
### Puppet
A popular devops automation tool
### Push
Git command to send commits from the local repository to the remote repository.
### RE Read Only
Permissions to see a file and it's contents, but not change it
### Rebase
Moves a branch from one commit to another. This allows you to re-write your project's history.
### Git Repository
Storage location of all files which are tracked by git.
### Requirements management
*** Needs definition here
### Revision
*** Needs definition here
### Revision Control
Also known as version control or source control, is the management of changes to documents, computer programs, large web sites, and other collections of information. Changes are usually identified by a number or letter code, termed the "revision number", "revision level", or simply "revision".
### RocketChat
An open source chat application for teams. Very similar to Slack only that is is open-source.
### Runners
Actual build machines/containers that run/execute tests you have specified to be run on GitLab CI
### SaaS
Software as a service. Software is hosted centrally and accessed on-demand i.e. when you want to. This refers to GitLab.com in our scenario
### SCM
Software Configuration Management. Often used by people when they mean Version Control
## Scrum
An Agile framework designed to help complete complex (typically) software projects. It's made up of several parts: product requirments backlog, sprint plannnig, sprint (development), sprint review, retrospec (analyzing the sprint). The goal is to end up with potentially shippable products.
### Scrum Board
The board used to track the status and progress of each of the sprint backlog items.
### Slack
Real time messaging app for teams. Used internally by GitLab
### Slave Servers
Also known as secondary servers. They help to spread the load over multiple machines, they also provide backups when the master/primary server crashes.
### Source Code
Program code as typed by a computer programmer. i.e. it has not yet been compiled/translated by the computer to machine language.
### SSH Key
A unique identifier of a computer. It is used to identify computers without the need for a password. e.g. On GitLab I have added the ssh key of all my work machines so that the GitLab instance knows that it can accept code pushes and pulls from this trusted machines whose keys are I have added.
### SSO
Single Sign On. An authentication process that allows you enter one username and password to access multiple applications.
### Standard Subscription
Our mid range EE subscription that includes 24/7 support, support for High Availability [Standard Subscription](https://about.gitlab.com/pricing/)
### Stash
Atlassian's Git On-Premises solution. Think of it as Atlassian's GitLab EE. It is now known as BitBucket Server.
### Subversion
Non-proprietary, centralized version control system.
### Sudo
A program that allows you to perform superuser/administrator actions on Unix Operating Systems e.g. Linux, OS X. It actually stands for 'superuser do'
### SVN
Abbreviation for Subversion.
### Tag
Represents a version of a particular branch at a moment in time.
### Tool Stack
Set of tools used in a process to achieve a common outcome. E.g. set of tools used in Application Lifecycle Management.
### Trac
An Open Source project management and bug tracking web application.
### User
Anyone interacting with the software.
### VCS
Version Control Software
### Waterfall
A model of building software that involves collecting all requirements from the customer, then building and refining all the requirements and finally delivering the COMPLETE software to the customer that meets all the requirements specified by the customer
### Webhooks
A way for for an app to provide other applications with real-time information. e.g. send a message to a slack channel when a commit is pushed
### Wiki
A website/system that allows for collaborative editing of its content by the users. In programming, they usually contain documentation of how to use the software
# High Availability on AWS
GitLab on AWS can leverage many of the services that are already
configurable with High Availability. These services have a lot of
flexibility and are able to adopt to most companies, best of all is the
ability to automate both vertical and horizontal scaling.
In this article we'll go through a basic HA setup where we'll start by
configuring our Virtual Private Cloud and subnets to later integrate
services such as RDS for our database server and ElastiCache as a Redis
cluster to finally manage them within an auto scaling group with custom
scaling policies.
***
## Where to Start
Login to your AWS account through the `My Account` dropdown on
`https://aws.amazon.com` or through the URI assigned to your team such as
`https://myteam.signin.aws.amazon.com/console/`. You'll start on the
Amazon Web Services console from where we can choose all of the services
we'll be using to configure our cloud infrastructure.
***
## Network
We'll start by creating a VPC for our GitLab cloud infrastructure, then
we can create subnets to have public and private instances in at least
two AZs. Public subnets will require a Route Table keep an associated
Internet Gateway.
### VPC
Start by looking for the VPC option on the web console. Now create a new
VPC. We can use `10.0.0.0/16` for the CIDR block and leave tenancy as
default if we don't require dedicated hardware.
![New VPC](img/new_vpc.png)
If you're setting up the Elastic File System service then select the VPC
and from the Actions dropdown choose Edit DNS Hostnames and select Yes.
### Subnet
Now let's create some subnets in different Availability Zones. Make sure
that each subnet is associated the the VPC we just created, that it has
a distinct VPC and lastly that CIDR blocks don't overlap. This will also
allow us to enable multi AZ for redundancy.
We will create private and public subnets to match load balancers and
RDS instances as well.
![Subnet Creation](img/subnet.png)
The subnets are listed with their name, AZ and CIDR block:
* gitlab-public-10.0.0.0 - us-west-2a - 10.0.0.0
* gitlab-private-10.0.1.0 - us-west-2a - 10.0.1.0
* gitlab-public-10.0.2.0 - us-west-2b - 10.0.2.0
* gitlab-private-10.0.3.0 - us-west-2b - 10.0.3.0
### Route Table
Up to now all our subnets are private. We need to create a Route Table
to associate an Internet Gateway. On the same VPC dashboard choose
Route Tables on the left column and give it a name and associate it to
our newly created VPC.
![Route Table](img/route_table.png)
### Internet Gateway
Now still on the same dashboard head over to Internet Gateways and
create a new one. After its created pres on the `Attach to VPC` button and
select our VPC.
![Internet Gateway](img/ig.png)
### Configure Subnets
Go back to the Router Tables screen and select the newly created one,
press the Routes tab on the bottom section and edit it. We need to add a
new target which will be our Internet Gateway and have it receive
traffic from any destination.
![Subnet Config](img/ig-rt.png)
Before leaving this screen select the next tab to the rgiht which is
Subnet Associations and add our public subnets. If you followed our
naming convention they should be easy to find.
***
## Database with RDS
For our database server we will use Amazon RDS which offers Multi AZ
for redundancy. Lets start by creating a subnet group and then we'll
create the actual RDS instance.
### Subnet Group
From the RDS dashboard select Subnet Groups. Lets select our VPC from
the VPC ID dropdown and at the bottom we can add our private subnets.
![Subnet Group](img/db-subnet-group.png)
### RDS
Select the RDS service from the Database section and create a new
PostgreSQL instance. After choosing between a Production or
Development instance we'll start with the actual configuration. On the
image bellow we have the settings for this article but note the
following two options which are of particular interest for HA:
1. Multi-AZ-Deployment is recommended as redundancy. Read more at
[High Availability (Multi-AZ)](http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.MultiAZ.html)
1. While we chose a General Purpose (SSD) for this article a Provisioned
IOPS (SSD) is best suited for HA. Read more about it at
[Storage for Amazon RDS](http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_Storage.html)
![RDS Instance Specs](img/instance_specs.png)
The rest of the setting on this page request a DB identifier, username
and a master password. We've chosen to use `gitlab-ha`, `gitlab` and a
very secure password respectively. Keep these in hand for later.
![Network and Security](img/rds-net-opt.png)
Make sure to choose our gitlab VPC, our subnet group, not have it public,
and to leave it to create a new security group. The only additional
change which will be helpful is the database name for which we can use
`gitlabhq_production`.
***
## ElastiCache
EC is an in-memory hosted caching solution. Redis maintains its own
persistance and is used for certain types of application.
Let's choose the ElastiCache service in the Database section from our
AWS console. Now lets create a cache subnet group which will be very
similar to the RDS subnet group. Make sure to select our VPC and its
private subnets.
![ElastiCache](img/ec-subnet.png)
Now press the Launch a Cache Cluster and choose Redis for our
DB engine. You'll be able to configure details such as replication,
Multi AZ and node types. The second section will allow us to choose our
subnet and security group and
![Redis Cluster details](img/redis-cluster-det.png)
![Redis Network](img/redis-net.png)
***
## Elastic File System
This new AWS offering allows us to create a file system accessible by

EC2 instances within a VPC. Choose our VPC and the subnets will be

automatically configured assuming we don't need to set explicit IPs.
The
next section allows us to add tags and choose between General
Purpose or
Max I/O which is a good option when being accessed by a
large number of
EC2 instances.


![Elastic File System](img/elastic-file-system.png)
To actually mount and install the NFS client we'll use the User Data
section when adding our Launch Configuration.
***
## Initiate AMI
We are going to launch an EC2 instance and bake an image so that we can
later use it for auto scaling. We'll also take this opportunity to add an
extension to our RDS through this temporary EC2 instance.
### EC2 Instance
Look for the EC2 option and choose to create an instance. We'll need at
least a t2.medium type and for this article we'll choose an Ubuntu 14.04
HVM 64-bit. In the Configure Instance section choose our GitLab VPC and
a public subnet. I'd choose at least 10GB of storage.
In the security group we'll create a new one considering that we need to
SSH into the instance and also try it out through http. So let's add the
http traffic from anywhere and name it something such as
`gitlab-ec2-security-group`.
While we wait for it to launch we can allocate an Elastic IP and
associate it with our new EC2 instance.
### RDS and Redis Security Group
After the instance is being created we will navigate to our EC2 security
groups and add a small change for our EC2 instances to be able to
connect to RDS. First copy the security group name we just defined,
namely `gitlab-ec2-security-group`, and edit select the RDS security
group and edit the inbound rules. Choose the rule type to be PostgreSQL
and paste the name under source.
![RDS security group](img/rds-sec-group.png)
Similar to the above we'll jump to the `gitlab-ec2-security-group` group
and add a custom TCP rule for port 6379 accessible within itself.
### Install GitLab
To connect through SSH you will need to have the `pem` file which you
chose available and with the correct permissions such as `400`.
After accessing your server don't forget to update and upgrade your
packages.
sudo apt-get update && sudo apt-get upgrade -y
Then follow installation instructions from
[GitLab](https://about.gitlab.com/downloads-ee/#ubuntu1404), but before
running reconfigure we need to make sure all our services are tied down
so just leave the reconfigure command until after we edit our gitlab.rb
file.
### Extension for PostgreSQL
Connect to your new RDS instance to verify access and to install
a required extension. We can find the host or endpoint by selecting the
instance and we just created and after the details drop down we'll find
it labeled as 'Endpoint'; do remember not to include the colon and port
number.
sudo /opt/gitlab/embedded/bin/psql -U gitlab -h <rds-endpoint> -d gitlabhq_production
psql (9.4.7)
Type "help" for help.
gitlab=# CREATE EXTENSION pg_trgm;
gitlab=# \q
### Configure GitLab
While connected to your server edit the `gitlab.rb` file at `/etc/gitlab/gitlab.rb`
find the `external_url 'http://gitlab.example.com'` option and change it
to the domain you will be using or the public IP address of the current
instance to test the configuration.
For a more detailed description about configuring GitLab read [Configuring GitLab for HA](http://docs.gitlab.com/ee/administration/high_availability/gitlab.html)
Now look for the GitLab database settings and uncomment as necessary. In
our current case we'll specify the adapter, encoding, host, db name,
username, and password.
gitlab_rails['db_adapter'] = "postgresql"
gitlab_rails['db_encoding'] = "unicode"
gitlab_rails['db_database'] = "gitlabhq_production"
gitlab_rails['db_username'] = "gitlab"
gitlab_rails['db_password'] = "mypassword"
gitlab_rails['db_host'] = "<rds-endpoint>"
Next we only need to configure the Redis section by adding the host and
uncommenting the port.
The last configuration step is to [change the default file locations ](http://docs.gitlab.com/ee/administration/high_availability/nfs.html)
to make the EFS integration easier to manage.
gitlab_rails['redis_host'] = "<redis-endpoint>"
gitlab_rails['redis_port'] = 6379
Finally run reconfigure, you might find it useful to run a check and
a service status to make sure everything has been setup correctly.
sudo gitlab-ctl reconfigure
sudo gitlab-rake gitlab:check
sudo gitlab-ctl status
If everything looks good copy the Elastic IP over to your browser and
test the instance manually.
### AMI
After you finish testing your EC2 instance go back to its dashboard and
while the instance is selected press on the Actions dropdown to choose
Image -> Create an Image. Give it a name and description and confirm.
***
## Load Balancer
On the same dashboard look for Load Balancer on the left column and press
the Create button. Choose a classic Load Balancer, our gitlab VPC, not
internal and make sure its listening for HTTP and HTTPS on port 80.
Here is a tricky part though, when adding subnets we need to associate
public subnets instead of the private ones where our instances will
actually live.
On the secruity group section let's create a new one named
`gitlab-loadbalancer-sec-group` and allow both HTTP ad HTTPS traffic
from anywhere.
The Load Balancer Health will allow us to indicate where to ping and what
makes up a healthy or unhealthy instance.
We won't add the instance on the next session because we'll destroy it
momentarily as we'll be using the image we where creating. We will keep
the Enable Cross-Zone and Enable Connection Draining active.
After we finish creating the Load Balancer we can re visit our Security
Groups to improve access only through the ELB and any other requirement
you might have.
***
## Auto Scaling Group
Our AMI should be done by now so we can start working on our Auto
Scaling Group.
This option is also available through the EC2 dashboard on the left
sidebar. Press on the create button. Select the new image on My AMIs and
give it a `t2.medium` size. To be able to use Elastic File System we need
to add a script to mount EFS automatically at launch. We'll do this at
the Advanced Details section where we have a [User Data](http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/user-data.html)
text area that allows us to add a lot of custom configurations which
allows you to add a custom script for when launching an instance. Let's
add the following script to the User Data section:
#cloud-config
package_upgrade: true
packages:
- nfs-common
runcmd:
- mkdir -p /gitlab-data
- chown ec2-user:ec2-user /gitlab-data
- echo "$(curl --silent http://169.254.169.254/latest/meta-data/placement/availability-zone).file-system-id.aws-region.amazonaws.com:/ /gitlab-data nfs defaults,vers=4.1 0 0" >> /etc/fstab
- mount -a -t nfs
- sudo gitlab-ctl reconfigure
On the security group section we can chosse our existing
`gitlab-ec2-security-group` group which has already been tested.
After this is launched we are able to start creating our Auto Scaling
Group. Start by giving it a name and assinging it our VPC and private
subnets. We also want to always start with two instances and if you
scroll down to Advanced Details we can choose to receive traffic from ELBs.
Lets enable that option and select our ELB. We also want to use the ELB's
health check.
![Auto scaling](img/auto-scaling-det.png)
### Policies
This is the really great part of Auto Scaling, we get to choose when AWS
launches new instances and when it removes them. For this group we'll
scale between 2 and 4 instances where one instance will be added if CPU
utilization is greater than 60% and one instance is removed if it falls
to less than 45%. Here are the complete policies:
![Policies](img/policies.png)
You'll notice that after we save this AWS starts launching our two
instances in different AZs and without a public IP which is exactly what
we where aiming for.
***
## Final Thoughts
After you're done with the policies section have some fun trying to break
instances. You should be able to see how the Auto Scaling Group and the
EC2 screen start bringing them up again.
High Availability is a very big area, we went mostly through scaling and
some redundancy options but it might also imply Geographic replication.
There is a lot of ground yet to cover so have a read through these other
resources and feel free to open an issue to request additional material.
* [GitLab High Availability](http://docs.gitlab.com/ce/administration/high_availability/README.html#sts=High Availability)
* [GitLab Geo](http://docs.gitlab.com/ee/gitlab-geo/README.html)
---
title: University | Process
---
## Suggesting improvements
If you would like to teach a class or participate or help in any way please
submit a merge request and assign it to [Job](https://gitlab.com/u/JobV).
If you have suggestions for additional courses you would like to see,
please submit a merge request to add an upcoming class, assign to
[Chad](https://gitlab.com/u/chadmalchow) and /cc [Job](https://gitlab.com/u/JobV).
## Adding classes
1. All training materials of any kind should be added to [GitLab CE](https://gitlab.com/gitlab-org/gitlab-ce/)
to ensure they are available to a broad audience (don't use any other repo or
storage for training materials).
1. Don't make materials that are needlessly specific to one group of people, try
to keep the wording broad and inclusive (don't make things for only GitLab Inc.
people, only interns, only customers, etc.).
1. To allow people to contribute all content should be in git.
1. The content can go in a subdirectory under `/doc/university/`.
1. To make, view or modify the slides of the classes use [Deckset](http://www.decksetapp.com/)
or [RevealJS](http://lab.hakim.se/reveal-js/). Do not use Powerpoint or Google
Slides since this prevents everyone from contributing.
1. Please upload any video recordings to our Youtube channel. We prefer them to
be public, if needed they can be unlisted but if so they should be linked from
this page.
1. Please create a merge request and assign to [SeanPackham](https://gitlab.com/u/SeanPackham).
## Support Boot Camp
**Goal:** Prepare new Service Engineers at GitLab
For each stage there are learning goals and content to support the learning of the engineer.
The goal of this boot camp is to have every Service Engineer prepared to help our customers
with whatever needs they might have and to also assist our awesome community with their
questions.
Always start with the [University Overview](../README.md) and then work
your way here for more advanced and specific training. Once you feel comfortable
with the topics of the current stage, move to the next.
### Stage 1
Follow the topics on the [University Overview](../README.md), concentrate on it
during your first Stage, but also:
- Perform the [first steps](https://about.gitlab.com/handbook/support/onboarding/#first-steps) of
the on-boarding process for new Service Engineers
#### Goals
Aim to have a good overview of the Product and main features, Git and the Company
### Stage 2
Continue to look over remaining portions of the [University Overview](../README.md) and continue on to these topics:
#### Set up your development machine
Get your development machine ready to familiarize yourself with the codebase, the components, and to be prepared to reproduce issues that our users encounter
- Install the [GDK](https://gitlab.com/gitlab-org/gitlab-development-kit)
- [Setup OpenLDAP as part of this](https://gitlab.com/gitlab-org/gitlab-development-kit#openldap)
#### Become comfortable with the Installation processes that we support
It's important to understand how to install GitLab in the same way that our users do. Try installing different versions and upgrading and downgrading between them. Installation from source will give you a greater understanding of the components that we employ and how everything fits together.
Sometimes we need to upgrade customers from old versions of GitLab to latest, so it's good to get some experience of doing that now.
- [Installation Methods](https://about.gitlab.com/installation/):
- [Omnibus](https://gitlab.com/gitlab-org/omnibus-gitlab/)
- [Docker](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/docker)
- [Source](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md)
- Get yourself a Digital Ocean droplet, where you can install and maintain your own instance of GitLab
- Ask in #infrastructure about this
- Populate with some test data
- Keep this up-to-date as patch and version releases become available, just like our customers would
- Try out the following installation path
- [Install GitLab 4.2 from source](https://gitlab.com/gitlab-org/gitlab-ce/blob/d67117b5a185cfb15a1d7e749588ff981ffbf779/doc/install/installation.md)
- External MySQL database
- External NGINX
- Create some test data
- Populated Repos
- Users
- Groups
- Projects
- [Backup using our Backup rake task](http://docs.gitlab.com/ce/raketasks/backup_restore.html#create-a-backup-of-the-gitlab-system)
- [Upgrade to 5.0 source using our Upgrade documentation](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/doc/update/4.2-to-5.0.md)
- [Upgrade to 5.1 source](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/doc/update/5.0-to-5.1.md)
- [Upgrade to 6.0 source](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/doc/update/5.1-to-6.0.md)
- [Upgrade to 7.14 source](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/doc/update/6.x-or-7.x-to-7.14.md)
- [Backup using our Backup rake task](http://docs.gitlab.com/ce/raketasks/backup_restore.html#create-a-backup-of-the-gitlab-system)
- [Perform the MySQL to PostgreSQL migration to convert your backup](http://docs.gitlab.com/ce/update/mysql_to_postgresql.html#converting-a-gitlab-backup-file-from-mysql-to-postgres)
- [Upgrade to Omnibus 7.14](http://doc.gitlab.com/omnibus/update/README.html#upgrading-from-a-non-omnibus-installation-to-an-omnibus-installation)
- [Restore backup using our Restore rake task](http://docs.gitlab.com/ce/raketasks/backup_restore.html#restore-a-previously-created-backup)
- [Upgrade to latest EE](https://about.gitlab.com/downloads-ee)
- (GitLab inc. only) Acquire and apply a license for the Enterprise Edition product, ask in #support
- Perform a downgrade from [EE to CE](http://doc.gitlab.com/ee/downgrade_ee_to_ce/README.html)
#### Start to learn about some of the integrations that we support
Our integrations add great value to GitLab. User questions often relate to integrating GitLab with existing external services and the configuration involved
- Learn about our Integrations (specially, not only):
- [LDAP](http://doc.gitlab.com/ee/integration/ldap.html)
- [JIRA](http://doc.gitlab.com/ee/project_services/jira.html)
- [Jenkins](http://doc.gitlab.com/ee/integration/jenkins.html)
- [SAML](http://doc.gitlab.com/ce/integration/saml.html)
#### Goals
- Aim to be comfortable with installation of the GitLab product and configuration of some of the major integrations
- Aim to have an installation available for reproducing customer reports
### Stage 3
#### Understand the gathering of diagnostics for GitLab instances
- Learn about the GitLab checks that are available
- [Environment Information and maintenance checks](http://docs.gitlab.com/ce/raketasks/maintenance.html)
- [GitLab check](http://docs.gitlab.com/ce/raketasks/check.html)
- Omnibus commands
- [Status](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/maintenance/README.md#get-service-status)
- [Starting and stopping services](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/maintenance/README.md#starting-and-stopping)
- [Starting a rails console](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/maintenance/README.md#invoking-rake-tasks)
#### Learn about the Support process
Zendesk is our Support Centre and our main communication line with our Customers. We communicate with customers through several other channels too
- Familiarize yourself with ZenDesk
- [UI Overview](https://support.zendesk.com/hc/en-us/articles/203661806-Introduction-to-the-Zendesk-agent-interface)
- [Updating Tickets](https://support.zendesk.com/hc/en-us/articles/212530318-Updating-and-solving-tickets)
- [Working w/ Tickets](https://support.zendesk.com/hc/en-us/articles/203690856-Working-with-tickets) *Read: avoiding agent collision.*
- Dive into our ZenDesk support process by reading how to [handle tickets](https://about.gitlab.com/handbook/support/onboarding/#handling-tickets)
- Start getting real world experience by handling real tickets, all the while gaining further experience with the Product.
- First, learn about our [Support Channels](https://about.gitlab.com/handbook/support/#support-channels)
- Ask other Service Engineers for help, when necessary, and to review your responses
- Start with [StackOverflow](https://about.gitlab.com/handbook/support/#stack-overflowa-namestack-overflowa) and the [GitLab forum](https://about.gitlab.com/handbook/support/#foruma-namegitlab-foruma)
- Here you will find a large variety of queries mainly from our Users who are self hosting GitLab CE
- Understand the questions that are asked and dig in to try to find a solution
- [Proceed on to the GitLab.com Support Forum](https://about.gitlab.com/handbook/support/#gitlabcom-support-trackera-namesupp-foruma)
- Here you will find queries regarding our own GitLab.com
- Helping Users here will give you an understanding of our Admin interface and other tools
- [Proceed on to the Twitter tickets in Zendesk](https://about.gitlab.com/handbook/support/#twitter)
- Here you will gain a great insight into our userbase
- Learn from any complaints and problems and feed them back to the team
- Tweets can range from help needed with GitLab installations, the API and just general queries
- [Proceed on to Regular email Support tickets](https://about.gitlab.com/handbook/support/#regular-zendesk-tickets-a-nameregulara)
- Here you will find tickets from our GitLab EE Customers and GitLab CE Users
- Tickets here are extremely varied and often very technical
- You should be prepared for these tickets, given the knowledge gained from previous tiers and your training
- Check out your colleagues' responses
- Hop on to the #support-live-feed channel in Slack and see the tickets as they come in and are updated
- Read through old tickets that your colleagues have worked on
- Start arranging to pair on calls with other Service Engineers. Aim to cover a few of each type of call
- [Learn about Cisco WebEx](https://about.gitlab.com/handbook/support/onboarding/#webexa-namewebexa)
- Training calls
- Information gathering calls
- It's good to find out how new and prospective customers are going to be using the product and how they will set up their infrastructure
- Diagnosis calls
- When email isn't enough we may need to hop on a call and do some debugging along side the customer
- These paired calls are a great learning experience
- Upgrade calls
- Emergency calls
#### Learn about the Escalation process for tickets
Some tickets need specific knowledge or a deep understanding of a particular component and will need to be escalated to a Senior Service Engineer or Developer
- Read about [Escalation](https://about.gitlab.com/handbook/support/onboarding/#create-issuesa-namecreate-issuea)
- Find the macros in Zendesk for ticket escalations
- Take a look at the [GitLab.com Team page](https://about.gitlab.com/team/) to find the resident experts in their fields
#### Learn about raising issues and fielding feature proposals
- Understand what's in the pipeline and proposed features at GitLab: [Direction Page](https://about.gitlab.com/direction/)
- Practice searching issues and filtering using [labels](https://gitlab.com/gitlab-org/gitlab-ce/labels) to find existing feature proposals and bugs
- If raising a new issue always provide a relevant label and a link to the relevant ticket in Zendesk
- Add [customer labels](https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name%5B%5D=customer) for those issues relevant to our subscribers
- Take a look at the [existing issue templates](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker) to see what is expected
- Raise issues for bugs in a manner that would make the issue easily reproducible. A Developer or a contributor may work on your issue
#### Goals
- Aim to have a good understanding of the problems that customers are facing
- Aim to have gained experience in scheduling and participating in calls with customers
- Aim to have a good understanding of ticket flow through Zendesk and how to interat with our various channels
### Stage 4
#### Advanced GitLab topics
Move on to understanding some of GitLab's more advanced features. You can make use of GitLab.com to understand the features from an end-user perspective and then use your own instance to understand setup and configuration of the feature from an Administrative perspective
- Set up and try [Git Annex](http://doc.gitlab.com/ee/workflow/git_annex.html)
- Set up and try [Git LFS](http://doc.gitlab.com/ee/workflow/lfs/manage_large_binaries_with_git_lfs.html)
- Get to know the [GitLab API](http://doc.gitlab.com/ee/api/README.html), its capabilities and shortcomings
- Learn how to [migrate from SVN to Git](http://doc.gitlab.com/ee/workflow/importing/migrating_from_svn.html)
- Set up [GitLab CI](http://doc.gitlab.com/ee/ci/quick_start/README.html)
- Create your first [GitLab Page](http://doc.gitlab.com/ee/pages/administration.html)
- Get to know the GitLab Codebase by reading through the source code:
- Find the differences between the [EE codebase](https://gitlab.com/gitlab-org/gitlab-ce)
and the [CE codebase](https://gitlab.com/gitlab-org/gitlab-ce)
- Ask as many questions as you can think of on the `#support` chat channel
#### Get initiated for on-call duty
- Read over the [public run-books to understand common tasks](https://gitlab.com/gitlab-com/runbooks)
- Create an issue on the internal Organization tracker to schedule time with the DevOps / Production team, so that you learn how to handle GitLab.com going down. Once you are trained for this, you are ready to be added to the on-call rotation.
#### Goals
- Aim to become a fully-fledged Service Engineer!
# Training
This training material is the markdown used to generate training slides
which can be found at [End User Slides](https://gitlab-org.gitlab.io/end-user-training-slides/#/)
through it's [RevealJS](https://gitlab.com/gitlab-org/end-user-training-slides)
project.
---
## Git Intro
---
### What is a Version Control System (VCS)
- Records changes to a file
- Maintains history of changes
- Disaster Recovery
- Types of VCS: Local, Centralized and Distributed
---
### Short Story of Git
- 1991-2002: The Linux kernel was being maintaned by sharing archived files
and patches.
- 2002: The Linux kernel project began using a DVCS called BitKeeper
- 2005: BitKeeper revoked the free-of-charge status and Git was created
---
### What is Git
- Distributed Version Control System
- Great branching model that adapts well to most workflows
- Fast and reliable
- Keeps a complete history
- Disaster recovery friendly
- Open Source
---
### Getting Help
- Use the tools at your disposal when you get stuck.
- Use `git help <command>` command
- Use Google (i.e. StackOverflow, Google groups)
- Read documentation at https://git-scm.com
---
## Git Setup
Workshop Time!
---
### Setup
- Windows: Install 'Git for Windows'
- https://git-for-windows.github.io
- Mac: Type `git` in the Terminal application.
- If it's not installed, it will prompt you to install it.
- Linux
- Debian: `sudo apt-get install git-all`
- Red Hat `sudo yum install git-all`
---
### Configure
- One-time configuration of the Git client:
```bash
git config --global user.name "Your Name"
git config --global user.email you@example.com
```
- If you don't use the global flag you can setup a different author for
each project
- Check settings with:
```bash
git config --global --list
```
- You might want or be required to use an SSH key.
- Instructions: [SSH](http://doc.gitlab.com/ce/ssh/README.html)
---
### Workspace
- Choose a directory on you machine easy to access
- Create a workspace or development directory
- This is where we'll be working and adding content
---
```bash
mkdir ~/development
cd ~/development
-or-
mkdir ~/workspace
cd ~/workspace
```
---
## Git Basics
---
### Git Workflow
- Untracked files
- New files that Git has not been told to track previously.
- Working area (Workspace)
- Files that have been modified but are not committed.
- Staging area (Index)
- Modified files that have been marked to go in the next commit.
- Upstream
- Hosted repository on a shared server
---
### GitLab
- GitLab is an application to code, test and deploy.
- Provides repository management with access controls, code reviews,
issue tracking, Merge Requests, and other features.
- The hosted version of GitLab is gitlab.com
---
### New Project
- Sign in into your gitlab.com account
- Create a project
- Choose to import from 'Any Repo by URL' and use https://gitlab.com/gitlab-org/training-examples.git
- On your machine clone the `training-examples` project
---
### Git and GitLab basics
1. Edit `edit_this_file.rb` in `training-examples`
2. See it listed as a changed file (working area)
3. View the differences
4. Stage the file
5. Commit
6. Push the commit to the remote
7. View the git log
---
```shell
# Edit `edit_this_file.rb`
git status
git diff
git add <file>
git commit -m 'My change'
git push origin master
git log
```
---
### Feature Branching
1. Create a new feature branch called `squash_some_bugs`
2. Edit `bugs.rb` and remove all the bugs.
3. Commit
4. Push
---
```shell
git checkout -b squash_some_bugs
# Edit `bugs.rb`
git status
git add bugs.rb
git commit -m 'Fix some buggy code'
git push origin squash_some_bugs
```
---
## Merge Request
---
### Merge requests
- When you want feedback create a merge request
- Target is the ‘default’ branch (usually master)
- Assign or mention the person you would like to review
- Add `WIP` to the title if it's a work in progress
- When accepting, always delete the branch
- Anyone can comment, not just the assignee
- Push corrections to the same branch
---
### Merge request example
- Create your first merge request
- Use the blue button in the activity feed
- View the diff (changes) and leave a comment
- Push a new commit to the same branch
- Review the changes again and notice the update
---
### Feedback and Collaboration
- Merge requests are a time for feedback and collaboration
- Giving feedback is hard
- Be as kind as possible
- Receiving feedback is hard
- Be as receptive as possible
- Feedback is about the best code, not the person. You are not your code
- Feedback and Collaboration
---
### Feedback and Collaboration
- Review the Thoughtbot code-review guide for suggestions to follow when reviewing merge requests:[Thoughtbot](https://github.com/thoughtbot/guides/tree/master/code-review)
- See GitLab merge requests for examples: [Merge Requests](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests)
---
## Merge Conflicts
---
### Merge Conflicts
* Happen often
* Learning to fix conflicts is hard
* Practice makes perfect
* Force push after fixing conflicts. Be careful!
---
### Example Plan
1. Checkout a new branch and edit conflicts.rb. Add 'Line4' and 'Line5'.
2. Commit and push
3. Checkout master and edit conflicts.rb. Add 'Line6' and 'Line7' below 'Line3'.
4. Commit and push to master
5. Create a merge request and watch it fail
6. Rebase our new branch with master
7. Fix conflicts on the conflicts.rb file.
8. Stage the file and continue rebasing
9. Force push the changes
10. Finally continue with the Merge Request
---
### Example 1/2
git checkout -b conflicts_branch
# vi conflicts.rb
# Add 'Line4' and 'Line5'
git commit -am "add line4 and line5"
git push origin conflicts_branch
git checkout master
# vi conflicts.rb
# Add 'Line6' and 'Line7'
git commit -am "add line6 and line7"
git push origin master
---
### Example 2/2
Create a merge request on the GitLab web UI. You'll see a conflict warning.
git checkout conflicts_branch
git fetch
git rebase master
# Fix conflicts by editing the files.
git add conflicts.rb
# No need to commit this file
git rebase --continue
# Remember that we have rewritten our commit history so we
# need to force push so that our remote branch is restructured
git push origin conflicts_branch -f
---
### Notes
* When to use `git merge` and when to use `git rebase`
* Rebase when updating your branch with master
* Merge when bringing changes from feature to master
* Reference: https://www.atlassian.com/git/tutorials/merging-vs-rebasing/
---
## Revert and Unstage
---
### Unstage
To remove files from stage use reset HEAD. Where HEAD is the last commit of the current branch:
git reset HEAD <file>
This will unstage the file but maintain the modifications. To revert the file back to the state it was in before the changes we can use:
git checkout -- <file>
To remove a file from disk and repo use 'git rm' and to rm a dir use the '-r' flag:
git rm '*.txt'
git rm -r <dirname>
If we want to remove a file from the repository but keep it on disk, say we forgot to add it to our .gitignore file then use `--cache`:
git rm <filename> --cache
---
### Undo Commits
Undo last commit putting everything back into the staging area:
git reset --soft HEAD^
Add files and change message with:
git commit --amend -m "New Message"
Undo last and remove changes
git reset --hard HEAD^
Same as last one but for two commits back:
git reset --hard HEAD^^
Don't reset after pushing
---
### Reset Workflow
1. Edit file again 'edit_this_file.rb'
2. Check status
3. Add and commit with wrong message
4. Check log
5. Amend commit
6. Check log
7. Soft reset
8. Check log
9. Pull for updates
10. Push changes
----
# Change file edit_this_file.rb
git status
git commit -am "kjkfjkg"
git log
git commit --amend -m "New comment added"
git log
git reset --soft HEAD^
git log
git pull origin master
git push origin master
---
### Note
git revert vs git reset
Reset removes the commit while revert removes the changes but leaves the commit
Revert is safer considering we can revert a revert
# Changed file
git commit -am "bug introduced"
git revert HEAD
# New commit created reverting changes
# Now we want to re apply the reverted commit
git log # take hash from the revert commit
git revert <rev commit hash>
# reverted commit is back (new commit created again)
---
## Questions
---
## Instructor Notes
---
### Version Control
- Local VCS was used with a filesystem or a simple db.
- Centralized VCS such as Subversion includes collaboration but
still is prone to data loss as the main server is the single point of
failure.
- Distributed VCS enables the team to have a complete copy of the project
and work with little dependency to the main server. In case of a main
server failing the project can be recovered by any of the latest copies
from the team
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
* [Horizontal Rule](#horizontal-rule) * [Horizontal Rule](#horizontal-rule)
* [Line Breaks](#line-breaks) * [Line Breaks](#line-breaks)
* [Tables](#tables) * [Tables](#tables)
* [Footnotes](#footnotes)
**[Wiki-Specific Markdown](#wiki-specific-markdown)** **[Wiki-Specific Markdown](#wiki-specific-markdown)**
...@@ -699,6 +700,15 @@ By including colons in the header row, you can align the text within that column ...@@ -699,6 +700,15 @@ By including colons in the header row, you can align the text within that column
| Cell 1 | Cell 2 | Cell 3 | Cell 4 | Cell 5 | Cell 6 | | Cell 1 | Cell 2 | Cell 3 | Cell 4 | Cell 5 | Cell 6 |
| Cell 7 | Cell 8 | Cell 9 | Cell 10 | Cell 11 | Cell 12 | | Cell 7 | Cell 8 | Cell 9 | Cell 10 | Cell 11 | Cell 12 |
## Footnotes
You can add footnotes to your text as follows.[^1]
[^1]: This is my awesome footnote.
```
You can add footnotes to your text as follows.[^1]
[^1]: This is my awesome footnote.
```
## Wiki-specific Markdown ## Wiki-specific Markdown
......
...@@ -16,9 +16,9 @@ module API ...@@ -16,9 +16,9 @@ module API
# GET /projects/:id/access_requests # GET /projects/:id/access_requests
get ":id/access_requests" do get ":id/access_requests" do
source = find_source(source_type, params[:id]) source = find_source(source_type, params[:id])
authorize_admin_source!(source_type, source)
access_requesters = paginate(source.requesters.includes(:user)) access_requesters = AccessRequestsFinder.new(source).execute!(current_user)
access_requesters = paginate(access_requesters.includes(:user))
present access_requesters.map(&:user), with: Entities::AccessRequester, source: source present access_requesters.map(&:user), with: Entities::AccessRequester, source: source
end end
......
...@@ -343,7 +343,7 @@ module API ...@@ -343,7 +343,7 @@ module API
end end
class ProjectGroupLink < Grape::Entity class ProjectGroupLink < Grape::Entity
expose :id, :project_id, :group_id, :group_access expose :id, :project_id, :group_id, :group_access, :expires_at
end end
class Todo < Grape::Entity class Todo < Grape::Entity
...@@ -494,6 +494,8 @@ module API ...@@ -494,6 +494,8 @@ module API
expose :after_sign_out_path expose :after_sign_out_path
expose :container_registry_token_expire_delay expose :container_registry_token_expire_delay
expose :repository_storage expose :repository_storage
expose :koding_enabled
expose :koding_url
end end
class Release < Grape::Entity class Release < Grape::Entity
...@@ -545,6 +547,10 @@ module API ...@@ -545,6 +547,10 @@ module API
expose :filename, :size expose :filename, :size
end end
class PipelineBasic < Grape::Entity
expose :id, :sha, :ref, :status
end
class Build < Grape::Entity class Build < Grape::Entity
expose :id, :status, :stage, :name, :ref, :tag, :coverage expose :id, :status, :stage, :name, :ref, :tag, :coverage
expose :created_at, :started_at, :finished_at expose :created_at, :started_at, :finished_at
...@@ -552,6 +558,7 @@ module API ...@@ -552,6 +558,7 @@ module API
expose :artifacts_file, using: BuildArtifactFile, if: -> (build, opts) { build.artifacts? } expose :artifacts_file, using: BuildArtifactFile, if: -> (build, opts) { build.artifacts? }
expose :commit, with: RepoCommit expose :commit, with: RepoCommit
expose :runner, with: Runner expose :runner, with: Runner
expose :pipeline, with: PipelineBasic
end end
class Trigger < Grape::Entity class Trigger < Grape::Entity
...@@ -562,8 +569,8 @@ module API ...@@ -562,8 +569,8 @@ module API
expose :key, :value expose :key, :value
end end
class Pipeline < Grape::Entity class Pipeline < PipelineBasic
expose :id, :status, :ref, :sha, :before_sha, :tag, :yaml_errors expose :before_sha, :tag, :yaml_errors
expose :user, with: Entities::UserBasic expose :user, with: Entities::UserBasic
expose :created_at, :updated_at, :started_at, :finished_at, :committed_at expose :created_at, :updated_at, :started_at, :finished_at, :committed_at
......
...@@ -90,7 +90,7 @@ module API ...@@ -90,7 +90,7 @@ module API
{ {
username: token_handler.actor_name, username: token_handler.actor_name,
lfs_token: token_handler.generate, lfs_token: token_handler.token,
repository_http_path: project.http_url_to_repo repository_http_path: project.http_url_to_repo
} }
end end
......
...@@ -108,8 +108,7 @@ module API ...@@ -108,8 +108,7 @@ module API
finder_params = { finder_params = {
project_id: user_project.id, project_id: user_project.id,
milestone_title: @milestone.title, milestone_title: @milestone.title
state: 'all'
} }
issues = IssuesFinder.new(current_user, finder_params).execute issues = IssuesFinder.new(current_user, finder_params).execute
......
...@@ -393,23 +393,24 @@ module API ...@@ -393,23 +393,24 @@ module API
# Share project with group # Share project with group
# #
# Parameters: # Parameters:
# id (required) - The ID of a project # id (required) - The ID of a project
# group_id (required) - The ID of a group # group_id (required) - The ID of a group
# group_access (required) - Level of permissions for sharing # group_access (required) - Level of permissions for sharing
# expires_at (optional) - Share expiration date
# #
# Example Request: # Example Request:
# POST /projects/:id/share # POST /projects/:id/share
post ":id/share" do post ":id/share" do
authorize! :admin_project, user_project authorize! :admin_project, user_project
required_attributes! [:group_id, :group_access] required_attributes! [:group_id, :group_access]
attrs = attributes_for_keys [:group_id, :group_access, :expires_at]
unless user_project.allowed_to_share_with_group? unless user_project.allowed_to_share_with_group?
return render_api_error!("The project sharing with group is disabled", 400) return render_api_error!("The project sharing with group is disabled", 400)
end end
link = user_project.project_group_links.new link = user_project.project_group_links.new(attrs)
link.group_id = params[:group_id]
link.group_access = params[:group_access]
if link.save if link.save
present link, with: Entities::ProjectGroupLink present link, with: Entities::ProjectGroupLink
else else
......
...@@ -64,7 +64,7 @@ module Banzai ...@@ -64,7 +64,7 @@ module Banzai
end end
end end
def project_from_ref_cache(ref) def project_from_ref_cached(ref)
if RequestStore.active? if RequestStore.active?
cache = project_refs_cache cache = project_refs_cache
...@@ -146,7 +146,7 @@ module Banzai ...@@ -146,7 +146,7 @@ module Banzai
# have `gfm` and `gfm-OBJECT_NAME` class names attached for styling. # have `gfm` and `gfm-OBJECT_NAME` class names attached for styling.
def object_link_filter(text, pattern, link_text: nil) def object_link_filter(text, pattern, link_text: nil)
references_in(text, pattern) do |match, id, project_ref, matches| references_in(text, pattern) do |match, id, project_ref, matches|
project = project_from_ref_cache(project_ref) project = project_from_ref_cached(project_ref)
if project && object = find_object_cached(project, id) if project && object = find_object_cached(project, id)
title = object_link_title(object) title = object_link_title(object)
...@@ -243,11 +243,27 @@ module Banzai ...@@ -243,11 +243,27 @@ module Banzai
end end
end end
# Returns the projects for the given paths. def projects_relation_for_paths(paths)
def find_projects_for_paths(paths)
Project.where_paths_in(paths).includes(:namespace) Project.where_paths_in(paths).includes(:namespace)
end end
# Returns projects for the given paths.
def find_projects_for_paths(paths)
if RequestStore.active?
to_query = paths - project_refs_cache.keys
unless to_query.empty?
projects_relation_for_paths(to_query).each do |project|
get_or_set_cache(project_refs_cache, project.path_with_namespace) { project }
end
end
project_refs_cache.slice(*paths).values
else
projects_relation_for_paths(paths)
end
end
def current_project_path def current_project_path
@current_project_path ||= project.path_with_namespace @current_project_path ||= project.path_with_namespace
end end
......
...@@ -66,7 +66,7 @@ module Banzai ...@@ -66,7 +66,7 @@ module Banzai
end end
end end
def find_projects_for_paths(paths) def projects_relation_for_paths(paths)
super(paths).includes(:gitlab_issue_tracker_service) super(paths).includes(:gitlab_issue_tracker_service)
end end
end end
......
...@@ -10,19 +10,21 @@ module Banzai ...@@ -10,19 +10,21 @@ module Banzai
# task_list gem. # task_list gem.
# #
# See https://github.com/github/task_list/pull/60 # See https://github.com/github/task_list/pull/60
class TaskListFilter < TaskList::Filter module ClassNamesFilter
def add_css_class_with_fix(node, *new_class_names) def add_css_class(node, *new_class_names)
if new_class_names.include?('task-list') if new_class_names.include?('task-list')
# Don't add class to all lists # Don't add class to all lists
return return
elsif new_class_names.include?('task-list-item') elsif new_class_names.include?('task-list-item')
add_css_class_without_fix(node.parent, 'task-list') super(node.parent, 'task-list')
end end
add_css_class_without_fix(node, *new_class_names) super(node, *new_class_names)
end end
end
alias_method_chain :add_css_class, :fix class TaskListFilter < TaskList::Filter
prepend ClassNamesFilter
end end
end end
end end
...@@ -79,7 +79,11 @@ module Banzai ...@@ -79,7 +79,11 @@ module Banzai
def referenced_by(nodes) def referenced_by(nodes)
ids = unique_attribute_values(nodes, self.class.data_attribute) ids = unique_attribute_values(nodes, self.class.data_attribute)
references_relation.where(id: ids) if ids.empty?
references_relation.none
else
references_relation.where(id: ids)
end
end end
# Returns the ActiveRecord::Relation to use for querying references in the # Returns the ActiveRecord::Relation to use for querying references in the
......
class VersionInfo
include Comparable
attr_reader :major, :minor, :patch
def self.parse(str)
if str && m = str.match(/(\d+)\.(\d+)\.(\d+)/)
VersionInfo.new(m[1].to_i, m[2].to_i, m[3].to_i)
else
VersionInfo.new
end
end
def initialize(major = 0, minor = 0, patch = 0)
@major = major
@minor = minor
@patch = patch
end
def <=>(other)
return unless other.is_a? VersionInfo
return unless valid? && other.valid?
if other.major < @major
1
elsif @major < other.major
-1
elsif other.minor < @minor
1
elsif @minor < other.minor
-1
elsif other.patch < @patch
1
elsif @patch < other.patch
-1
else
0
end
end
def to_s
if valid?
"%d.%d.%d" % [@major, @minor, @patch]
else
"Unknown"
end
end
def valid?
@major >= 0 && @minor >= 0 && @patch >= 0 && @major + @minor + @patch > 0
end
end
...@@ -124,7 +124,7 @@ module Gitlab ...@@ -124,7 +124,7 @@ module Gitlab
read_authentication_abilities read_authentication_abilities
end end
Result.new(actor, nil, token_handler.type, authentication_abilities) if Devise.secure_compare(token_handler.value, password) Result.new(actor, nil, token_handler.type, authentication_abilities) if Devise.secure_compare(token_handler.token, password)
end end
def build_access_token_check(login, password) def build_access_token_check(login, password)
......
...@@ -17,19 +17,13 @@ module Gitlab ...@@ -17,19 +17,13 @@ module Gitlab
end end
end end
def generate def token
token = Devise.friendly_token(TOKEN_LENGTH)
Gitlab::Redis.with do |redis| Gitlab::Redis.with do |redis|
token = redis.get(redis_key)
token ||= Devise.friendly_token(TOKEN_LENGTH)
redis.set(redis_key, token, ex: EXPIRY_TIME) redis.set(redis_key, token, ex: EXPIRY_TIME)
end
token token
end
def value
Gitlab::Redis.with do |redis|
redis.get(redis_key)
end end
end end
......
...@@ -11,13 +11,6 @@ module Gitlab ...@@ -11,13 +11,6 @@ module Gitlab
DEFAULT_REDIS_URL = 'redis://localhost:6379' DEFAULT_REDIS_URL = 'redis://localhost:6379'
CONFIG_FILE = File.expand_path('../../config/resque.yml', __dir__) CONFIG_FILE = File.expand_path('../../config/resque.yml', __dir__)
# To be thread-safe we must be careful when writing the class instance
# variables @_raw_config and @pool. Because @pool depends on @_raw_config we need two
# mutexes to prevent deadlock.
RAW_CONFIG_MUTEX = Mutex.new
POOL_MUTEX = Mutex.new
private_constant :RAW_CONFIG_MUTEX, :POOL_MUTEX
class << self class << self
# Do NOT cache in an instance variable. Result may be mutated by caller. # Do NOT cache in an instance variable. Result may be mutated by caller.
def params def params
...@@ -31,24 +24,19 @@ module Gitlab ...@@ -31,24 +24,19 @@ module Gitlab
end end
def with def with
if @pool.nil? @pool ||= ConnectionPool.new { ::Redis.new(params) }
POOL_MUTEX.synchronize do
@pool = ConnectionPool.new { ::Redis.new(params) }
end
end
@pool.with { |redis| yield redis } @pool.with { |redis| yield redis }
end end
def _raw_config def _raw_config
return @_raw_config if defined?(@_raw_config) return @_raw_config if defined?(@_raw_config)
RAW_CONFIG_MUTEX.synchronize do begin
begin @_raw_config = File.read(CONFIG_FILE).freeze
@_raw_config = File.read(CONFIG_FILE).freeze rescue Errno::ENOENT
rescue Errno::ENOENT @_raw_config = false
@_raw_config = false
end
end end
@_raw_config @_raw_config
end end
end end
......
desc 'Code complexity analyze via flog'
task :flog do
output = %x(bundle exec flog -m app/ lib/gitlab)
exit_code = 0
minimum_score = 70
output = output.lines
# Skip total complexity score
output.shift
# Skip some trash info
output.shift
output.each do |line|
score, method = line.split(" ")
score = score.to_i
if score > minimum_score
exit_code = 1
puts "High complexity in #{method}. Score: #{score}"
end
end
exit exit_code
end
...@@ -39,7 +39,7 @@ if [ -f /.dockerenv ] || [ -f ./dockerinit ]; then ...@@ -39,7 +39,7 @@ if [ -f /.dockerenv ] || [ -f ./dockerinit ]; then
cp config/resque.yml.example config/resque.yml cp config/resque.yml.example config/resque.yml
sed -i 's/localhost/redis/g' config/resque.yml sed -i 's/localhost/redis/g' config/resque.yml
export FLAGS=(--path vendor --retry 3) export FLAGS=(--path vendor --retry 3 --quiet)
else else
export PATH=$HOME/bin:/usr/local/bin:/usr/bin:/bin export PATH=$HOME/bin:/usr/local/bin:/usr/bin:/bin
cp config/database.yml.mysql config/database.yml cp config/database.yml.mysql config/database.yml
......
...@@ -8,7 +8,7 @@ describe Projects::RepositoriesController do ...@@ -8,7 +8,7 @@ describe Projects::RepositoriesController do
it 'responds with redirect in correct format' do it 'responds with redirect in correct format' do
get :archive, namespace_id: project.namespace.path, project_id: project.path, format: "zip" get :archive, namespace_id: project.namespace.path, project_id: project.path, format: "zip"
expect(response.content_type).to start_with 'text/html' expect(response.header["Content-Type"]).to start_with('text/html')
expect(response).to be_redirect expect(response).to be_redirect
end end
end end
......
...@@ -63,6 +63,28 @@ describe ProjectsController do ...@@ -63,6 +63,28 @@ describe ProjectsController do
end end
end end
context "project with broken repo" do
let(:empty_project) { create(:project_broken_repo, :public) }
before { sign_in(user) }
User.project_views.keys.each do |project_view|
context "with #{project_view} view set" do
before do
user.update_attributes(project_view: project_view)
get :show, namespace_id: empty_project.namespace.path, id: empty_project.path
end
it "renders the empty project view" do
allow(Project).to receive(:repo).and_raise(Gitlab::Git::Repository::NoRepository)
expect(response).to render_template('projects/no_repo')
end
end
end
end
context "rendering default project view" do context "rendering default project view" do
render_views render_views
......
...@@ -27,6 +27,14 @@ FactoryGirl.define do ...@@ -27,6 +27,14 @@ FactoryGirl.define do
end end
end end
trait :broken_repo do
after(:create) do |project|
project.create_repository
FileUtils.rm_r(File.join(project.repository_storage_path, "#{project.path_with_namespace}.git", 'refs'))
end
end
# Nest Project Feature attributes # Nest Project Feature attributes
transient do transient do
wiki_access_level ProjectFeature::ENABLED wiki_access_level ProjectFeature::ENABLED
...@@ -56,6 +64,13 @@ FactoryGirl.define do ...@@ -56,6 +64,13 @@ FactoryGirl.define do
empty_repo empty_repo
end end
# Project with broken repository
#
# Project with an invalid repository state
factory :project_broken_repo, parent: :empty_project do
broken_repo
end
# Project with test repository # Project with test repository
# #
# Test repository source can be found at # Test repository source can be found at
......
...@@ -21,6 +21,7 @@ describe "Dashboard Issues filtering", feature: true, js: true do ...@@ -21,6 +21,7 @@ describe "Dashboard Issues filtering", feature: true, js: true do
click_link 'No Milestone' click_link 'No Milestone'
expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
expect(page).to have_selector('.issue', count: 1) expect(page).to have_selector('.issue', count: 1)
end end
...@@ -29,6 +30,7 @@ describe "Dashboard Issues filtering", feature: true, js: true do ...@@ -29,6 +30,7 @@ describe "Dashboard Issues filtering", feature: true, js: true do
click_link 'Any Milestone' click_link 'Any Milestone'
expect(page).to have_issuable_counts(open: 2, closed: 0, all: 2)
expect(page).to have_selector('.issue', count: 2) expect(page).to have_selector('.issue', count: 2)
end end
...@@ -39,6 +41,7 @@ describe "Dashboard Issues filtering", feature: true, js: true do ...@@ -39,6 +41,7 @@ describe "Dashboard Issues filtering", feature: true, js: true do
click_link milestone.title click_link milestone.title
end end
expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
expect(page).to have_selector('.issue', count: 1) expect(page).to have_selector('.issue', count: 1)
end end
end end
......
require 'rails_helper' require 'rails_helper'
feature 'Issue filtering by Labels', feature: true do feature 'Issue filtering by Labels', feature: true, js: true do
include WaitForAjax include WaitForAjax
let(:project) { create(:project, :public) } let(:project) { create(:project, :public) }
let!(:user) { create(:user)} let!(:user) { create(:user) }
let!(:label) { create(:label, project: project) } let!(:label) { create(:label, project: project) }
before do before do
...@@ -28,156 +28,81 @@ feature 'Issue filtering by Labels', feature: true do ...@@ -28,156 +28,81 @@ feature 'Issue filtering by Labels', feature: true do
visit namespace_project_issues_path(project.namespace, project) visit namespace_project_issues_path(project.namespace, project)
end end
context 'filter by label bug', js: true do context 'filter by label bug' do
before do before do
page.find('.js-label-select').click select_labels('bug')
wait_for_ajax
execute_script("$('.dropdown-menu-labels li:contains(\"bug\") a').click()")
page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click
wait_for_ajax
end end
it 'shows issue "Bugfix1" and "Bugfix2" in issues list' do it 'apply the filter' do
expect(page).to have_content "Bugfix1" expect(page).to have_content "Bugfix1"
expect(page).to have_content "Bugfix2" expect(page).to have_content "Bugfix2"
end
it 'does not show "Feature1" in issues list' do
expect(page).not_to have_content "Feature1" expect(page).not_to have_content "Feature1"
end
it 'shows label "bug" in filtered-labels' do
expect(find('.filtered-labels')).to have_content "bug" expect(find('.filtered-labels')).to have_content "bug"
end
it 'does not show label "feature" and "enhancement" in filtered-labels' do
expect(find('.filtered-labels')).not_to have_content "feature" expect(find('.filtered-labels')).not_to have_content "feature"
expect(find('.filtered-labels')).not_to have_content "enhancement" expect(find('.filtered-labels')).not_to have_content "enhancement"
end
it 'removes label "bug"' do
find('.js-label-filter-remove').click find('.js-label-filter-remove').click
wait_for_ajax wait_for_ajax
expect(find('.filtered-labels', visible: false)).to have_no_content "bug" expect(find('.filtered-labels', visible: false)).to have_no_content "bug"
end end
end end
context 'filter by label feature', js: true do context 'filter by label feature' do
before do before do
page.find('.js-label-select').click select_labels('feature')
wait_for_ajax
execute_script("$('.dropdown-menu-labels li:contains(\"feature\") a').click()")
page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click
wait_for_ajax
end end
it 'shows issue "Feature1" in issues list' do it 'applies the filter' do
expect(page).to have_content "Feature1" expect(page).to have_content "Feature1"
end
it 'does not show "Bugfix1" and "Bugfix2" in issues list' do
expect(page).not_to have_content "Bugfix2" expect(page).not_to have_content "Bugfix2"
expect(page).not_to have_content "Bugfix1" expect(page).not_to have_content "Bugfix1"
end
it 'shows label "feature" in filtered-labels' do
expect(find('.filtered-labels')).to have_content "feature" expect(find('.filtered-labels')).to have_content "feature"
end
it 'does not show label "bug" and "enhancement" in filtered-labels' do
expect(find('.filtered-labels')).not_to have_content "bug" expect(find('.filtered-labels')).not_to have_content "bug"
expect(find('.filtered-labels')).not_to have_content "enhancement" expect(find('.filtered-labels')).not_to have_content "enhancement"
end end
end end
context 'filter by label enhancement', js: true do context 'filter by label enhancement' do
before do before do
page.find('.js-label-select').click select_labels('enhancement')
wait_for_ajax
execute_script("$('.dropdown-menu-labels li:contains(\"enhancement\") a').click()")
page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click
wait_for_ajax
end end
it 'shows issue "Bugfix2" in issues list' do it 'applies the filter' do
expect(page).to have_content "Bugfix2" expect(page).to have_content "Bugfix2"
end
it 'does not show "Feature1" and "Bugfix1" in issues list' do
expect(page).not_to have_content "Feature1" expect(page).not_to have_content "Feature1"
expect(page).not_to have_content "Bugfix1" expect(page).not_to have_content "Bugfix1"
end
it 'shows label "enhancement" in filtered-labels' do
expect(find('.filtered-labels')).to have_content "enhancement" expect(find('.filtered-labels')).to have_content "enhancement"
end
it 'does not show label "feature" and "bug" in filtered-labels' do
expect(find('.filtered-labels')).not_to have_content "bug" expect(find('.filtered-labels')).not_to have_content "bug"
expect(find('.filtered-labels')).not_to have_content "feature" expect(find('.filtered-labels')).not_to have_content "feature"
end end
end end
context 'filter by label enhancement or feature', js: true do context 'filter by label enhancement and bug in issues list' do
before do before do
page.find('.js-label-select').click select_labels('bug', 'enhancement')
wait_for_ajax
execute_script("$('.dropdown-menu-labels li:contains(\"enhancement\") a').click()")
execute_script("$('.dropdown-menu-labels li:contains(\"feature\") a').click()")
page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click
wait_for_ajax
end end
it 'does not show "Bugfix1" or "Feature1" in issues list' do it 'applies the filters' do
expect(page).not_to have_content "Bugfix1" expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
expect(page).to have_content "Bugfix2"
expect(page).not_to have_content "Feature1" expect(page).not_to have_content "Feature1"
end expect(find('.filtered-labels')).to have_content "bug"
it 'shows label "enhancement" and "feature" in filtered-labels' do
expect(find('.filtered-labels')).to have_content "enhancement" expect(find('.filtered-labels')).to have_content "enhancement"
expect(find('.filtered-labels')).to have_content "feature" expect(find('.filtered-labels')).not_to have_content "feature"
end
it 'does not show label "bug" in filtered-labels' do
expect(find('.filtered-labels')).not_to have_content "bug"
end
it 'removes label "enhancement"' do
find('.js-label-filter-remove', match: :first).click find('.js-label-filter-remove', match: :first).click
wait_for_ajax wait_for_ajax
expect(find('.filtered-labels')).to have_no_content "enhancement"
end
end
context 'filter by label enhancement and bug in issues list', js: true do
before do
page.find('.js-label-select').click
wait_for_ajax
execute_script("$('.dropdown-menu-labels li:contains(\"enhancement\") a').click()")
execute_script("$('.dropdown-menu-labels li:contains(\"bug\") a').click()")
page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click
wait_for_ajax
end
it 'shows issue "Bugfix2" in issues list' do
expect(page).to have_content "Bugfix2" expect(page).to have_content "Bugfix2"
end
it 'does not show "Feature1"' do
expect(page).not_to have_content "Feature1" expect(page).not_to have_content "Feature1"
end expect(page).not_to have_content "Bugfix1"
expect(find('.filtered-labels')).not_to have_content "bug"
it 'shows label "bug" and "enhancement" in filtered-labels' do
expect(find('.filtered-labels')).to have_content "bug"
expect(find('.filtered-labels')).to have_content "enhancement" expect(find('.filtered-labels')).to have_content "enhancement"
end
it 'does not show label "feature" in filtered-labels' do
expect(find('.filtered-labels')).not_to have_content "feature" expect(find('.filtered-labels')).not_to have_content "feature"
end end
end end
context 'remove filtered labels', js: true do context 'remove filtered labels' do
before do before do
page.within '.labels-filter' do page.within '.labels-filter' do
click_button 'Label' click_button 'Label'
...@@ -200,7 +125,7 @@ feature 'Issue filtering by Labels', feature: true do ...@@ -200,7 +125,7 @@ feature 'Issue filtering by Labels', feature: true do
end end
end end
context 'dropdown filtering', js: true do context 'dropdown filtering' do
it 'filters by label name' do it 'filters by label name' do
page.within '.labels-filter' do page.within '.labels-filter' do
click_button 'Label' click_button 'Label'
...@@ -214,4 +139,14 @@ feature 'Issue filtering by Labels', feature: true do ...@@ -214,4 +139,14 @@ feature 'Issue filtering by Labels', feature: true do
end end
end end
end end
def select_labels(*labels)
page.find('.js-label-select').click
wait_for_ajax
labels.each do |label|
execute_script("$('.dropdown-menu-labels li:contains(\"#{label}\") a').click()")
end
page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click
wait_for_ajax
end
end end
...@@ -7,15 +7,15 @@ describe 'Filter issues', feature: true do ...@@ -7,15 +7,15 @@ describe 'Filter issues', feature: true do
let!(:user) { create(:user)} let!(:user) { create(:user)}
let!(:milestone) { create(:milestone, project: project) } let!(:milestone) { create(:milestone, project: project) }
let!(:label) { create(:label, project: project) } let!(:label) { create(:label, project: project) }
let!(:issue1) { create(:issue, project: project) }
let!(:wontfix) { create(:label, project: project, title: "Won't fix") } let!(:wontfix) { create(:label, project: project, title: "Won't fix") }
before do before do
project.team << [user, :master] project.team << [user, :master]
login_as(user) login_as(user)
create(:issue, project: project)
end end
describe 'Filter issues for assignee from issues#index' do describe 'for assignee from issues#index' do
before do before do
visit namespace_project_issues_path(project.namespace, project) visit namespace_project_issues_path(project.namespace, project)
...@@ -45,7 +45,7 @@ describe 'Filter issues', feature: true do ...@@ -45,7 +45,7 @@ describe 'Filter issues', feature: true do
end end
end end
describe 'Filter issues for milestone from issues#index' do describe 'for milestone from issues#index' do
before do before do
visit namespace_project_issues_path(project.namespace, project) visit namespace_project_issues_path(project.namespace, project)
...@@ -75,7 +75,7 @@ describe 'Filter issues', feature: true do ...@@ -75,7 +75,7 @@ describe 'Filter issues', feature: true do
end end
end end
describe 'Filter issues for label from issues#index', js: true do describe 'for label from issues#index', js: true do
before do before do
visit namespace_project_issues_path(project.namespace, project) visit namespace_project_issues_path(project.namespace, project)
find('.js-label-select').click find('.js-label-select').click
...@@ -115,6 +115,7 @@ describe 'Filter issues', feature: true do ...@@ -115,6 +115,7 @@ describe 'Filter issues', feature: true do
expect(page).to have_content wontfix.title expect(page).to have_content wontfix.title
click_link wontfix.title click_link wontfix.title
end end
expect(find('.js-label-select .dropdown-toggle-text')).to have_content(wontfix.title) expect(find('.js-label-select .dropdown-toggle-text')).to have_content(wontfix.title)
end end
...@@ -146,7 +147,7 @@ describe 'Filter issues', feature: true do ...@@ -146,7 +147,7 @@ describe 'Filter issues', feature: true do
end end
end end
describe 'Filter issues for assignee and label from issues#index' do describe 'for assignee and label from issues#index' do
before do before do
visit namespace_project_issues_path(project.namespace, project) visit namespace_project_issues_path(project.namespace, project)
...@@ -226,6 +227,7 @@ describe 'Filter issues', feature: true do ...@@ -226,6 +227,7 @@ describe 'Filter issues', feature: true do
it 'filters by text and label' do it 'filters by text and label' do
fill_in 'issuable_search', with: 'Bug' fill_in 'issuable_search', with: 'Bug'
expect(page).to have_issuable_counts(open: 2, closed: 0, all: 2)
page.within '.issues-list' do page.within '.issues-list' do
expect(page).to have_selector('.issue', count: 2) expect(page).to have_selector('.issue', count: 2)
end end
...@@ -236,6 +238,7 @@ describe 'Filter issues', feature: true do ...@@ -236,6 +238,7 @@ describe 'Filter issues', feature: true do
end end
find('.dropdown-menu-close-icon').click find('.dropdown-menu-close-icon').click
expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
page.within '.issues-list' do page.within '.issues-list' do
expect(page).to have_selector('.issue', count: 1) expect(page).to have_selector('.issue', count: 1)
end end
...@@ -244,6 +247,7 @@ describe 'Filter issues', feature: true do ...@@ -244,6 +247,7 @@ describe 'Filter issues', feature: true do
it 'filters by text and milestone' do it 'filters by text and milestone' do
fill_in 'issuable_search', with: 'Bug' fill_in 'issuable_search', with: 'Bug'
expect(page).to have_issuable_counts(open: 2, closed: 0, all: 2)
page.within '.issues-list' do page.within '.issues-list' do
expect(page).to have_selector('.issue', count: 2) expect(page).to have_selector('.issue', count: 2)
end end
...@@ -253,6 +257,7 @@ describe 'Filter issues', feature: true do ...@@ -253,6 +257,7 @@ describe 'Filter issues', feature: true do
click_link '8' click_link '8'
end end
expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
page.within '.issues-list' do page.within '.issues-list' do
expect(page).to have_selector('.issue', count: 1) expect(page).to have_selector('.issue', count: 1)
end end
...@@ -261,6 +266,7 @@ describe 'Filter issues', feature: true do ...@@ -261,6 +266,7 @@ describe 'Filter issues', feature: true do
it 'filters by text and assignee' do it 'filters by text and assignee' do
fill_in 'issuable_search', with: 'Bug' fill_in 'issuable_search', with: 'Bug'
expect(page).to have_issuable_counts(open: 2, closed: 0, all: 2)
page.within '.issues-list' do page.within '.issues-list' do
expect(page).to have_selector('.issue', count: 2) expect(page).to have_selector('.issue', count: 2)
end end
...@@ -270,6 +276,7 @@ describe 'Filter issues', feature: true do ...@@ -270,6 +276,7 @@ describe 'Filter issues', feature: true do
click_link user.name click_link user.name
end end
expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
page.within '.issues-list' do page.within '.issues-list' do
expect(page).to have_selector('.issue', count: 1) expect(page).to have_selector('.issue', count: 1)
end end
...@@ -278,6 +285,7 @@ describe 'Filter issues', feature: true do ...@@ -278,6 +285,7 @@ describe 'Filter issues', feature: true do
it 'filters by text and author' do it 'filters by text and author' do
fill_in 'issuable_search', with: 'Bug' fill_in 'issuable_search', with: 'Bug'
expect(page).to have_issuable_counts(open: 2, closed: 0, all: 2)
page.within '.issues-list' do page.within '.issues-list' do
expect(page).to have_selector('.issue', count: 2) expect(page).to have_selector('.issue', count: 2)
end end
...@@ -287,6 +295,7 @@ describe 'Filter issues', feature: true do ...@@ -287,6 +295,7 @@ describe 'Filter issues', feature: true do
click_link user.name click_link user.name
end end
expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
page.within '.issues-list' do page.within '.issues-list' do
expect(page).to have_selector('.issue', count: 1) expect(page).to have_selector('.issue', count: 1)
end end
...@@ -315,6 +324,7 @@ describe 'Filter issues', feature: true do ...@@ -315,6 +324,7 @@ describe 'Filter issues', feature: true do
find('.dropdown-menu-close-icon').click find('.dropdown-menu-close-icon').click
wait_for_ajax wait_for_ajax
expect(page).to have_issuable_counts(open: 2, closed: 0, all: 2)
page.within '.issues-list' do page.within '.issues-list' do
expect(page).to have_selector('.issue', count: 2) expect(page).to have_selector('.issue', count: 2)
end end
......
...@@ -25,32 +25,78 @@ feature 'Issues > User uses slash commands', feature: true, js: true do ...@@ -25,32 +25,78 @@ feature 'Issues > User uses slash commands', feature: true, js: true do
describe 'adding a due date from note' do describe 'adding a due date from note' do
let(:issue) { create(:issue, project: project) } let(:issue) { create(:issue, project: project) }
it 'does not create a note, and sets the due date accordingly' do context 'when the current user can update the due date' do
write_note("/due 2016-08-28") it 'does not create a note, and sets the due date accordingly' do
write_note("/due 2016-08-28")
expect(page).not_to have_content '/due 2016-08-28' expect(page).not_to have_content '/due 2016-08-28'
expect(page).to have_content 'Your commands have been executed!' expect(page).to have_content 'Your commands have been executed!'
issue.reload issue.reload
expect(issue.due_date).to eq Date.new(2016, 8, 28) expect(issue.due_date).to eq Date.new(2016, 8, 28)
end
end
context 'when the current user cannot update the due date' do
let(:guest) { create(:user) }
before do
project.team << [guest, :guest]
logout
login_with(guest)
visit namespace_project_issue_path(project.namespace, project, issue)
end
it 'does not create a note, and sets the due date accordingly' do
write_note("/due 2016-08-28")
expect(page).to have_content '/due 2016-08-28'
expect(page).not_to have_content 'Your commands have been executed!'
issue.reload
expect(issue.due_date).to be_nil
end
end end
end end
describe 'removing a due date from note' do describe 'removing a due date from note' do
let(:issue) { create(:issue, project: project, due_date: Date.new(2016, 8, 28)) } let(:issue) { create(:issue, project: project, due_date: Date.new(2016, 8, 28)) }
it 'does not create a note, and removes the due date accordingly' do context 'when the current user can update the due date' do
expect(issue.due_date).to eq Date.new(2016, 8, 28) it 'does not create a note, and removes the due date accordingly' do
expect(issue.due_date).to eq Date.new(2016, 8, 28)
write_note("/remove_due_date")
expect(page).not_to have_content '/remove_due_date'
expect(page).to have_content 'Your commands have been executed!'
issue.reload
expect(issue.due_date).to be_nil
end
end
context 'when the current user cannot update the due date' do
let(:guest) { create(:user) }
before do
project.team << [guest, :guest]
logout
login_with(guest)
visit namespace_project_issue_path(project.namespace, project, issue)
end
write_note("/remove_due_date") it 'does not create a note, and sets the due date accordingly' do
write_note("/remove_due_date")
expect(page).not_to have_content '/remove_due_date' expect(page).to have_content '/remove_due_date'
expect(page).to have_content 'Your commands have been executed!' expect(page).not_to have_content 'Your commands have been executed!'
issue.reload issue.reload
expect(issue.due_date).to be_nil expect(issue.due_date).to eq Date.new(2016, 8, 28)
end
end end
end end
end end
......
...@@ -17,6 +17,7 @@ feature 'Merge Request filtering by Milestone', feature: true do ...@@ -17,6 +17,7 @@ feature 'Merge Request filtering by Milestone', feature: true do
visit_merge_requests(project) visit_merge_requests(project)
filter_by_milestone(Milestone::None.title) filter_by_milestone(Milestone::None.title)
expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
expect(page).to have_css('.merge-request', count: 1) expect(page).to have_css('.merge-request', count: 1)
end end
...@@ -39,6 +40,7 @@ feature 'Merge Request filtering by Milestone', feature: true do ...@@ -39,6 +40,7 @@ feature 'Merge Request filtering by Milestone', feature: true do
visit_merge_requests(project) visit_merge_requests(project)
filter_by_milestone(Milestone::Upcoming.title) filter_by_milestone(Milestone::Upcoming.title)
expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
expect(page).to have_css('.merge-request', count: 1) expect(page).to have_css('.merge-request', count: 1)
end end
...@@ -61,6 +63,7 @@ feature 'Merge Request filtering by Milestone', feature: true do ...@@ -61,6 +63,7 @@ feature 'Merge Request filtering by Milestone', feature: true do
visit_merge_requests(project) visit_merge_requests(project)
filter_by_milestone(milestone.title) filter_by_milestone(milestone.title)
expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
expect(page).to have_css('.merge-request', count: 1) expect(page).to have_css('.merge-request', count: 1)
end end
......
...@@ -60,7 +60,7 @@ describe "Runners" do ...@@ -60,7 +60,7 @@ describe "Runners" do
it "removes specific runner for project if this is last project for that runners" do it "removes specific runner for project if this is last project for that runners" do
within ".activated-specific-runners" do within ".activated-specific-runners" do
click_on "Remove runner" click_on "Remove Runner"
end end
expect(Ci::Runner.exists?(id: @specific_runner)).to be_falsey expect(Ci::Runner.exists?(id: @specific_runner)).to be_falsey
...@@ -75,7 +75,7 @@ describe "Runners" do ...@@ -75,7 +75,7 @@ describe "Runners" do
end end
it "enables shared runners" do it "enables shared runners" do
click_on "Enable shared runners" click_on "Enable shared Runners"
expect(@project.reload.shared_runners_enabled).to be_truthy expect(@project.reload.shared_runners_enabled).to be_truthy
end end
end end
......
require 'spec_helper'
describe AccessRequestsFinder, services: true do
let(:user) { create(:user) }
let(:access_requester) { create(:user) }
let(:project) { create(:project) }
let(:group) { create(:group) }
before do
project.request_access(access_requester)
group.request_access(access_requester)
end
shared_examples 'a finder returning access requesters' do |method_name|
it 'returns access requesters' do
access_requesters = described_class.new(source).public_send(method_name, user)
expect(access_requesters.size).to eq(1)
expect(access_requesters.first).to be_a "#{source.class.to_s}Member".constantize
expect(access_requesters.first.user).to eq(access_requester)
end
end
shared_examples 'a finder returning no results' do |method_name|
it 'raises Gitlab::Access::AccessDeniedError' do
expect(described_class.new(source).public_send(method_name, user)).to be_empty
end
end
shared_examples 'a finder raising Gitlab::Access::AccessDeniedError' do |method_name|
it 'raises Gitlab::Access::AccessDeniedError' do
expect { described_class.new(source).public_send(method_name, user) }.to raise_error(Gitlab::Access::AccessDeniedError)
end
end
describe '#execute' do
context 'when current user cannot see project access requests' do
it_behaves_like 'a finder returning no results', :execute do
let(:source) { project }
end
it_behaves_like 'a finder returning no results', :execute do
let(:source) { group }
end
end
context 'when current user can see access requests' do
before do
project.team << [user, :master]
group.add_owner(user)
end
it_behaves_like 'a finder returning access requesters', :execute do
let(:source) { project }
end
it_behaves_like 'a finder returning access requesters', :execute do
let(:source) { group }
end
end
end
describe '#execute!' do
context 'when current user cannot see access requests' do
it_behaves_like 'a finder raising Gitlab::Access::AccessDeniedError', :execute! do
let(:source) { project }
end
it_behaves_like 'a finder raising Gitlab::Access::AccessDeniedError', :execute! do
let(:source) { group }
end
end
context 'when current user can see access requests' do
before do
project.team << [user, :master]
group.add_owner(user)
end
it_behaves_like 'a finder returning access requesters', :execute! do
let(:source) { project }
end
it_behaves_like 'a finder returning access requesters', :execute! do
let(:source) { group }
end
end
end
end
require 'spec_helper' require 'spec_helper'
describe IssuablesHelper do describe IssuablesHelper do
let(:label) { build_stubbed(:label) } let(:label) { build_stubbed(:label) }
let(:label2) { build_stubbed(:label) } let(:label2) { build_stubbed(:label) }
context 'label tooltip' do describe '#issuable_labels_tooltip' do
it 'returns label text' do it 'returns label text' do
expect(issuable_labels_tooltip([label])).to eq(label.title) expect(issuable_labels_tooltip([label])).to eq(label.title)
end end
...@@ -13,4 +13,105 @@ describe IssuablesHelper do ...@@ -13,4 +13,105 @@ describe IssuablesHelper do
expect(issuable_labels_tooltip([label, label2], limit: 1)).to eq("#{label.title}, and 1 more") expect(issuable_labels_tooltip([label, label2], limit: 1)).to eq("#{label.title}, and 1 more")
end end
end end
describe '#issuables_state_counter_text' do
let(:user) { create(:user) }
describe 'state text' do
before do
allow(helper).to receive(:issuables_count_for_state).and_return(42)
end
it 'returns "Open" when state is :opened' do
expect(helper.issuables_state_counter_text(:issues, :opened)).
to eq('<span>Open</span> <span class="badge">42</span>')
end
it 'returns "Closed" when state is :closed' do
expect(helper.issuables_state_counter_text(:issues, :closed)).
to eq('<span>Closed</span> <span class="badge">42</span>')
end
it 'returns "Merged" when state is :merged' do
expect(helper.issuables_state_counter_text(:merge_requests, :merged)).
to eq('<span>Merged</span> <span class="badge">42</span>')
end
it 'returns "All" when state is :all' do
expect(helper.issuables_state_counter_text(:merge_requests, :all)).
to eq('<span>All</span> <span class="badge">42</span>')
end
end
describe 'counter caching based on issuable type and params', :caching do
let(:params) do
{
scope: 'created-by-me',
state: 'opened',
utf8: '✓',
author_id: '11',
assignee_id: '18',
label_name: ['bug', 'discussion', 'documentation'],
milestone_title: 'v4.0',
sort: 'due_date_asc',
namespace_id: 'gitlab-org',
project_id: 'gitlab-ce',
page: 2
}.with_indifferent_access
end
it 'returns the cached value when called for the same issuable type & with the same params' do
expect(helper).to receive(:params).twice.and_return(params)
expect(helper).to receive(:issuables_count_for_state).with(:issues, :opened).and_return(42)
expect(helper.issuables_state_counter_text(:issues, :opened)).
to eq('<span>Open</span> <span class="badge">42</span>')
expect(helper).not_to receive(:issuables_count_for_state)
expect(helper.issuables_state_counter_text(:issues, :opened)).
to eq('<span>Open</span> <span class="badge">42</span>')
end
it 'does not take some keys into account in the cache key' do
expect(helper).to receive(:params).and_return({
author_id: '11',
state: 'foo',
sort: 'foo',
utf8: 'foo',
page: 'foo'
}.with_indifferent_access)
expect(helper).to receive(:issuables_count_for_state).with(:issues, :opened).and_return(42)
expect(helper.issuables_state_counter_text(:issues, :opened)).
to eq('<span>Open</span> <span class="badge">42</span>')
expect(helper).to receive(:params).and_return({
author_id: '11',
state: 'bar',
sort: 'bar',
utf8: 'bar',
page: 'bar'
}.with_indifferent_access)
expect(helper).not_to receive(:issuables_count_for_state)
expect(helper.issuables_state_counter_text(:issues, :opened)).
to eq('<span>Open</span> <span class="badge">42</span>')
end
it 'does not take params order into account in the cache key' do
expect(helper).to receive(:params).and_return('author_id' => '11', 'state' => 'opened')
expect(helper).to receive(:issuables_count_for_state).with(:issues, :opened).and_return(42)
expect(helper.issuables_state_counter_text(:issues, :opened)).
to eq('<span>Open</span> <span class="badge">42</span>')
expect(helper).to receive(:params).and_return('state' => 'opened', 'author_id' => '11')
expect(helper).not_to receive(:issuables_count_for_state)
expect(helper.issuables_state_counter_text(:issues, :opened)).
to eq('<span>Open</span> <span class="badge">42</span>')
end
end
end
end end
...@@ -64,7 +64,7 @@ describe Gitlab::Auth, lib: true do ...@@ -64,7 +64,7 @@ describe Gitlab::Auth, lib: true do
it 'recognizes user lfs tokens' do it 'recognizes user lfs tokens' do
user = create(:user) user = create(:user)
ip = 'ip' ip = 'ip'
token = Gitlab::LfsToken.new(user).generate token = Gitlab::LfsToken.new(user).token
expect(gl_auth).to receive(:rate_limit!).with(ip, success: true, login: user.username) expect(gl_auth).to receive(:rate_limit!).with(ip, success: true, login: user.username)
expect(gl_auth.find_for_git_client(user.username, token, project: nil, ip: ip)).to eq(Gitlab::Auth::Result.new(user, nil, :lfs_token, full_authentication_abilities)) expect(gl_auth.find_for_git_client(user.username, token, project: nil, ip: ip)).to eq(Gitlab::Auth::Result.new(user, nil, :lfs_token, full_authentication_abilities))
...@@ -73,7 +73,7 @@ describe Gitlab::Auth, lib: true do ...@@ -73,7 +73,7 @@ describe Gitlab::Auth, lib: true do
it 'recognizes deploy key lfs tokens' do it 'recognizes deploy key lfs tokens' do
key = create(:deploy_key) key = create(:deploy_key)
ip = 'ip' ip = 'ip'
token = Gitlab::LfsToken.new(key).generate token = Gitlab::LfsToken.new(key).token
expect(gl_auth).to receive(:rate_limit!).with(ip, success: true, login: "lfs+deploy-key-#{key.id}") expect(gl_auth).to receive(:rate_limit!).with(ip, success: true, login: "lfs+deploy-key-#{key.id}")
expect(gl_auth.find_for_git_client("lfs+deploy-key-#{key.id}", token, project: nil, ip: ip)).to eq(Gitlab::Auth::Result.new(key, nil, :lfs_deploy_token, read_authentication_abilities)) expect(gl_auth.find_for_git_client("lfs+deploy-key-#{key.id}", token, project: nil, ip: ip)).to eq(Gitlab::Auth::Result.new(key, nil, :lfs_deploy_token, read_authentication_abilities))
......
require 'spec_helper' require 'spec_helper'
describe Gitlab::LfsToken, lib: true do describe Gitlab::LfsToken, lib: true do
describe '#generate and #value' do describe '#token' do
shared_examples 'an LFS token generator' do shared_examples 'an LFS token generator' do
it 'returns a randomly generated token' do it 'returns a randomly generated token' do
token = handler.generate token = handler.token
expect(token).not_to be_nil expect(token).not_to be_nil
expect(token).to be_a String expect(token).to be_a String
...@@ -12,9 +12,9 @@ describe Gitlab::LfsToken, lib: true do ...@@ -12,9 +12,9 @@ describe Gitlab::LfsToken, lib: true do
end end
it 'returns the correct token based on the key' do it 'returns the correct token based on the key' do
token = handler.generate token = handler.token
expect(handler.value).to eq(token) expect(handler.token).to eq(token)
end end
end end
......
...@@ -20,10 +20,10 @@ describe Milestone, models: true do ...@@ -20,10 +20,10 @@ describe Milestone, models: true do
let(:user) { create(:user) } let(:user) { create(:user) }
describe "#title" do describe "#title" do
let(:milestone) { create(:milestone, title: "<b>test</b>") } let(:milestone) { create(:milestone, title: "<b>foo & bar -> 2.2</b>") }
it "sanitizes title" do it "sanitizes title" do
expect(milestone.title).to eq("test") expect(milestone.title).to eq("foo & bar -> 2.2")
end end
end end
......
...@@ -30,6 +30,15 @@ describe API::API, api: true do ...@@ -30,6 +30,15 @@ describe API::API, api: true do
expect(json_response.first['commit']['id']).to eq project.commit.id expect(json_response.first['commit']['id']).to eq project.commit.id
end end
it 'returns pipeline data' do
json_build = json_response.first
expect(json_build['pipeline']).not_to be_empty
expect(json_build['pipeline']['id']).to eq build.pipeline.id
expect(json_build['pipeline']['ref']).to eq build.pipeline.ref
expect(json_build['pipeline']['sha']).to eq build.pipeline.sha
expect(json_build['pipeline']['status']).to eq build.pipeline.status
end
context 'filter project with one scope element' do context 'filter project with one scope element' do
let(:query) { 'scope=pending' } let(:query) { 'scope=pending' }
...@@ -91,6 +100,15 @@ describe API::API, api: true do ...@@ -91,6 +100,15 @@ describe API::API, api: true do
expect(json_response).to be_an Array expect(json_response).to be_an Array
expect(json_response.size).to eq 2 expect(json_response.size).to eq 2
end end
it 'returns pipeline data' do
json_build = json_response.first
expect(json_build['pipeline']).not_to be_empty
expect(json_build['pipeline']['id']).to eq build.pipeline.id
expect(json_build['pipeline']['ref']).to eq build.pipeline.ref
expect(json_build['pipeline']['sha']).to eq build.pipeline.sha
expect(json_build['pipeline']['status']).to eq build.pipeline.status
end
end end
context 'when pipeline has no builds' do context 'when pipeline has no builds' do
...@@ -133,6 +151,15 @@ describe API::API, api: true do ...@@ -133,6 +151,15 @@ describe API::API, api: true do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(json_response['name']).to eq('test') expect(json_response['name']).to eq('test')
end end
it 'returns pipeline data' do
json_build = json_response
expect(json_build['pipeline']).not_to be_empty
expect(json_build['pipeline']['id']).to eq build.pipeline.id
expect(json_build['pipeline']['ref']).to eq build.pipeline.ref
expect(json_build['pipeline']['sha']).to eq build.pipeline.sha
expect(json_build['pipeline']['status']).to eq build.pipeline.status
end
end end
context 'unauthorized user' do context 'unauthorized user' do
......
...@@ -111,7 +111,7 @@ describe API::API, api: true do ...@@ -111,7 +111,7 @@ describe API::API, api: true do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(json_response['username']).to eq(user.username) expect(json_response['username']).to eq(user.username)
expect(json_response['lfs_token']).to eq(Gitlab::LfsToken.new(key).value) expect(json_response['lfs_token']).to eq(Gitlab::LfsToken.new(key).token)
expect(json_response['repository_http_path']).to eq(project.http_url_to_repo) expect(json_response['repository_http_path']).to eq(project.http_url_to_repo)
end end
...@@ -131,7 +131,7 @@ describe API::API, api: true do ...@@ -131,7 +131,7 @@ describe API::API, api: true do
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(json_response['username']).to eq("lfs+deploy-key-#{key.id}") expect(json_response['username']).to eq("lfs+deploy-key-#{key.id}")
expect(json_response['lfs_token']).to eq(Gitlab::LfsToken.new(key).value) expect(json_response['lfs_token']).to eq(Gitlab::LfsToken.new(key).token)
expect(json_response['repository_http_path']).to eq(project.http_url_to_repo) expect(json_response['repository_http_path']).to eq(project.http_url_to_repo)
end end
end end
......
...@@ -104,6 +104,14 @@ describe API::API, api: true do ...@@ -104,6 +104,14 @@ describe API::API, api: true do
expect(response).to have_http_status(400) expect(response).to have_http_status(400)
end end
it 'creates a new project with reserved html characters' do
post api("/projects/#{project.id}/milestones", user), title: 'foo & bar 1.1 -> 2.2'
expect(response).to have_http_status(201)
expect(json_response['title']).to eq('foo & bar 1.1 -> 2.2')
expect(json_response['description']).to be_nil
end
end end
describe 'PUT /projects/:id/milestones/:milestone_id' do describe 'PUT /projects/:id/milestones/:milestone_id' do
......
...@@ -761,13 +761,16 @@ describe API::API, api: true do ...@@ -761,13 +761,16 @@ describe API::API, api: true do
let(:group) { create(:group) } let(:group) { create(:group) }
it "shares project with group" do it "shares project with group" do
expires_at = 10.days.from_now.to_date
expect do expect do
post api("/projects/#{project.id}/share", user), group_id: group.id, group_access: Gitlab::Access::DEVELOPER post api("/projects/#{project.id}/share", user), group_id: group.id, group_access: Gitlab::Access::DEVELOPER, expires_at: expires_at
end.to change { ProjectGroupLink.count }.by(1) end.to change { ProjectGroupLink.count }.by(1)
expect(response.status).to eq 201 expect(response.status).to eq 201
expect(json_response['group_id']).to eq group.id expect(json_response['group_id']).to eq(group.id)
expect(json_response['group_access']).to eq Gitlab::Access::DEVELOPER expect(json_response['group_access']).to eq(Gitlab::Access::DEVELOPER)
expect(json_response['expires_at']).to eq(expires_at.to_s)
end end
it "returns a 400 error when group id is not given" do it "returns a 400 error when group id is not given" do
......
...@@ -14,22 +14,38 @@ describe API::API, 'Settings', api: true do ...@@ -14,22 +14,38 @@ describe API::API, 'Settings', api: true do
expect(json_response['default_projects_limit']).to eq(42) expect(json_response['default_projects_limit']).to eq(42)
expect(json_response['signin_enabled']).to be_truthy expect(json_response['signin_enabled']).to be_truthy
expect(json_response['repository_storage']).to eq('default') expect(json_response['repository_storage']).to eq('default')
expect(json_response['koding_enabled']).to be_falsey
expect(json_response['koding_url']).to be_nil
end end
end end
describe "PUT /application/settings" do describe "PUT /application/settings" do
before do context "custom repository storage type set in the config" do
storages = { 'custom' => 'tmp/tests/custom_repositories' } before do
allow(Gitlab.config.repositories).to receive(:storages).and_return(storages) storages = { 'custom' => 'tmp/tests/custom_repositories' }
allow(Gitlab.config.repositories).to receive(:storages).and_return(storages)
end
it "updates application settings" do
put api("/application/settings", admin),
default_projects_limit: 3, signin_enabled: false, repository_storage: 'custom', koding_enabled: true, koding_url: 'http://koding.example.com'
expect(response).to have_http_status(200)
expect(json_response['default_projects_limit']).to eq(3)
expect(json_response['signin_enabled']).to be_falsey
expect(json_response['repository_storage']).to eq('custom')
expect(json_response['koding_enabled']).to be_truthy
expect(json_response['koding_url']).to eq('http://koding.example.com')
end
end end
it "updates application settings" do context "missing koding_url value when koding_enabled is true" do
put api("/application/settings", admin), it "returns a blank parameter error message" do
default_projects_limit: 3, signin_enabled: false, repository_storage: 'custom' put api("/application/settings", admin), koding_enabled: true
expect(response).to have_http_status(200)
expect(json_response['default_projects_limit']).to eq(3) expect(response).to have_http_status(400)
expect(json_response['signin_enabled']).to be_falsey expect(json_response['message']).to have_key('koding_url')
expect(json_response['repository_storage']).to eq('custom') expect(json_response['message']['koding_url']).to include "can't be blank"
end
end end
end end
end end
...@@ -257,6 +257,29 @@ describe 'Git LFS API and storage' do ...@@ -257,6 +257,29 @@ describe 'Git LFS API and storage' do
it_behaves_like 'responds with a file' it_behaves_like 'responds with a file'
end end
describe 'when using a user key' do
let(:authorization) { authorize_user_key }
context 'when user allowed' do
let(:update_permissions) do
project.team << [user, :master]
project.lfs_objects << lfs_object
end
it_behaves_like 'responds with a file'
end
context 'when user not allowed' do
let(:update_permissions) do
project.lfs_objects << lfs_object
end
it 'responds with status 404' do
expect(response).to have_http_status(404)
end
end
end
context 'when build is authorized as' do context 'when build is authorized as' do
let(:authorization) { authorize_ci_project } let(:authorization) { authorize_ci_project }
...@@ -1110,7 +1133,11 @@ describe 'Git LFS API and storage' do ...@@ -1110,7 +1133,11 @@ describe 'Git LFS API and storage' do
end end
def authorize_deploy_key def authorize_deploy_key
ActionController::HttpAuthentication::Basic.encode_credentials("lfs+deploy-key-#{key.id}", Gitlab::LfsToken.new(key).generate) ActionController::HttpAuthentication::Basic.encode_credentials("lfs+deploy-key-#{key.id}", Gitlab::LfsToken.new(key).token)
end
def authorize_user_key
ActionController::HttpAuthentication::Basic.encode_credentials(user.username, Gitlab::LfsToken.new(user).token)
end end
def fork_project(project, user, object = nil) def fork_project(project, user, object = nil)
......
...@@ -20,16 +20,38 @@ describe Issues::CreateService, services: true do ...@@ -20,16 +20,38 @@ describe Issues::CreateService, services: true do
let(:opts) do let(:opts) do
{ title: 'Awesome issue', { title: 'Awesome issue',
description: 'please fix', description: 'please fix',
assignee: assignee, assignee_id: assignee.id,
label_ids: labels.map(&:id), label_ids: labels.map(&:id),
milestone_id: milestone.id } milestone_id: milestone.id,
due_date: Date.tomorrow }
end end
it { expect(issue).to be_valid } it 'creates the issue with the given params' do
it { expect(issue.title).to eq('Awesome issue') } expect(issue).to be_persisted
it { expect(issue.assignee).to eq assignee } expect(issue.title).to eq('Awesome issue')
it { expect(issue.labels).to match_array labels } expect(issue.assignee).to eq assignee
it { expect(issue.milestone).to eq milestone } expect(issue.labels).to match_array labels
expect(issue.milestone).to eq milestone
expect(issue.due_date).to eq Date.tomorrow
end
context 'when current user cannot admin issues in the project' do
let(:guest) { create(:user) }
before do
project.team << [guest, :guest]
end
it 'filters out params that cannot be set without the :admin_issue permission' do
issue = described_class.new(project, guest, opts).execute
expect(issue).to be_persisted
expect(issue.title).to eq('Awesome issue')
expect(issue.assignee).to be_nil
expect(issue.labels).to be_empty
expect(issue.milestone).to be_nil
expect(issue.due_date).to be_nil
end
end
it 'creates a pending todo for new assignee' do it 'creates a pending todo for new assignee' do
attributes = { attributes = {
......
...@@ -32,55 +32,84 @@ describe Issues::UpdateService, services: true do ...@@ -32,55 +32,84 @@ describe Issues::UpdateService, services: true do
described_class.new(project, user, opts).execute(issue) described_class.new(project, user, opts).execute(issue)
end end
context "valid params" do context 'valid params' do
before do let(:opts) do
opts = { {
title: 'New title', title: 'New title',
description: 'Also please fix', description: 'Also please fix',
assignee_id: user2.id, assignee_id: user2.id,
state_event: 'close', state_event: 'close',
label_ids: [label.id] label_ids: [label.id],
due_date: Date.tomorrow
} }
perform_enqueued_jobs do
update_issue(opts)
end
end end
it { expect(issue).to be_valid } it 'updates the issue with the given params' do
it { expect(issue.title).to eq('New title') } update_issue(opts)
it { expect(issue.assignee).to eq(user2) }
it { expect(issue).to be_closed } expect(issue).to be_valid
it { expect(issue.labels.count).to eq(1) } expect(issue.title).to eq 'New title'
it { expect(issue.labels.first.title).to eq(label.name) } expect(issue.description).to eq 'Also please fix'
expect(issue.assignee).to eq user2
it 'sends email to user2 about assign of new issue and email to user3 about issue unassignment' do expect(issue).to be_closed
deliveries = ActionMailer::Base.deliveries expect(issue.labels).to match_array [label]
email = deliveries.last expect(issue.due_date).to eq Date.tomorrow
recipients = deliveries.last(2).map(&:to).flatten
expect(recipients).to include(user2.email, user3.email)
expect(email.subject).to include(issue.title)
end end
it 'creates system note about issue reassign' do context 'when current user cannot admin issues in the project' do
note = find_note('Reassigned to') let(:guest) { create(:user) }
before do
project.team << [guest, :guest]
end
expect(note).not_to be_nil it 'filters out params that cannot be set without the :admin_issue permission' do
expect(note.note).to include "Reassigned to \@#{user2.username}" described_class.new(project, guest, opts).execute(issue)
expect(issue).to be_valid
expect(issue.title).to eq 'New title'
expect(issue.description).to eq 'Also please fix'
expect(issue.assignee).to eq user3
expect(issue.labels).to be_empty
expect(issue.milestone).to be_nil
expect(issue.due_date).to be_nil
end
end end
it 'creates system note about issue label edit' do context 'with background jobs processed' do
note = find_note('Added ~') before do
perform_enqueued_jobs do
update_issue(opts)
end
end
it 'sends email to user2 about assign of new issue and email to user3 about issue unassignment' do
deliveries = ActionMailer::Base.deliveries
email = deliveries.last
recipients = deliveries.last(2).map(&:to).flatten
expect(recipients).to include(user2.email, user3.email)
expect(email.subject).to include(issue.title)
end
expect(note).not_to be_nil it 'creates system note about issue reassign' do
expect(note.note).to include "Added ~#{label.id} label" note = find_note('Reassigned to')
end
it 'creates system note about title change' do expect(note).not_to be_nil
note = find_note('Changed title:') expect(note.note).to include "Reassigned to \@#{user2.username}"
end
expect(note).not_to be_nil it 'creates system note about issue label edit' do
expect(note.note).to eq 'Changed title: **{-Old-} title** → **{+New+} title**' note = find_note('Added ~')
expect(note).not_to be_nil
expect(note.note).to include "Added ~#{label.id} label"
end
it 'creates system note about title change' do
note = find_note('Changed title:')
expect(note).not_to be_nil
expect(note.note).to eq 'Changed title: **{-Old-} title** → **{+New+} title**'
end
end end
end end
......
...@@ -38,6 +38,30 @@ describe MergeRequests::MergeService, services: true do ...@@ -38,6 +38,30 @@ describe MergeRequests::MergeService, services: true do
end end
end end
context 'closes related todos' do
let(:merge_request) { create(:merge_request, assignee: user, author: user) }
let(:project) { merge_request.project }
let(:service) { MergeRequests::MergeService.new(project, user, commit_message: 'Awesome message') }
let!(:todo) do
create(:todo, :assigned,
project: project,
author: user,
user: user,
target: merge_request)
end
before do
allow(service).to receive(:execute_hooks)
perform_enqueued_jobs do
service.execute(merge_request)
todo.reload
end
end
it { expect(todo).to be_done }
end
context 'remove source branch by author' do context 'remove source branch by author' do
let(:service) do let(:service) do
merge_request.merge_params['force_remove_source_branch'] = '1' merge_request.merge_params['force_remove_source_branch'] = '1'
......
...@@ -79,8 +79,8 @@ describe MergeRequests::RefreshService, services: true do ...@@ -79,8 +79,8 @@ describe MergeRequests::RefreshService, services: true do
it { expect(@merge_request).to be_merged } it { expect(@merge_request).to be_merged }
it { expect(@fork_merge_request).to be_merged } it { expect(@fork_merge_request).to be_merged }
it { expect(@fork_merge_request.notes.last.note).to include('changed to merged') } it { expect(@fork_merge_request.notes.last.note).to include('changed to merged') }
it { expect(@build_failed_todo).to be_pending } it { expect(@build_failed_todo).to be_done }
it { expect(@fork_build_failed_todo).to be_pending } it { expect(@fork_build_failed_todo).to be_done }
end end
context 'manual merge of source branch' do context 'manual merge of source branch' do
...@@ -99,8 +99,8 @@ describe MergeRequests::RefreshService, services: true do ...@@ -99,8 +99,8 @@ describe MergeRequests::RefreshService, services: true do
it { expect(@merge_request.diffs.size).to be > 0 } it { expect(@merge_request.diffs.size).to be > 0 }
it { expect(@fork_merge_request).to be_merged } it { expect(@fork_merge_request).to be_merged }
it { expect(@fork_merge_request.notes.last.note).to include('changed to merged') } it { expect(@fork_merge_request.notes.last.note).to include('changed to merged') }
it { expect(@build_failed_todo).to be_pending } it { expect(@build_failed_todo).to be_done }
it { expect(@fork_build_failed_todo).to be_pending } it { expect(@fork_build_failed_todo).to be_done }
end end
context 'push to fork repo source branch' do context 'push to fork repo source branch' do
...@@ -149,8 +149,8 @@ describe MergeRequests::RefreshService, services: true do ...@@ -149,8 +149,8 @@ describe MergeRequests::RefreshService, services: true do
it { expect(@merge_request).to be_merged } it { expect(@merge_request).to be_merged }
it { expect(@fork_merge_request).to be_open } it { expect(@fork_merge_request).to be_open }
it { expect(@fork_merge_request.notes).to be_empty } it { expect(@fork_merge_request.notes).to be_empty }
it { expect(@build_failed_todo).to be_pending } it { expect(@build_failed_todo).to be_done }
it { expect(@fork_build_failed_todo).to be_pending } it { expect(@fork_build_failed_todo).to be_done }
end end
context 'push new branch that exists in a merge request' do context 'push new branch that exists in a merge request' do
......
...@@ -108,6 +108,16 @@ describe Projects::ImportService, services: true do ...@@ -108,6 +108,16 @@ describe Projects::ImportService, services: true do
expect(result[:status]).to eq :error expect(result[:status]).to eq :error
expect(result[:message]).to eq 'Github: failed to connect API' expect(result[:message]).to eq 'Github: failed to connect API'
end end
it 'expires existence cache after error' do
allow_any_instance_of(Project).to receive(:repository_exists?).and_return(true)
expect_any_instance_of(Gitlab::Shell).to receive(:import_repository).with(project.repository_storage_path, project.path_with_namespace, project.import_url).and_raise(Gitlab::Shell::Error.new('Failed to import the repository'))
expect_any_instance_of(Repository).to receive(:expire_emptiness_caches).and_call_original
expect_any_instance_of(Repository).to receive(:expire_exists_cache).and_call_original
subject.execute
end
end end
def stub_github_omniauth_provider def stub_github_omniauth_provider
......
require 'spec_helper' require 'spec_helper'
describe SlashCommands::InterpretService, services: true do describe SlashCommands::InterpretService, services: true do
let(:project) { create(:project) } let(:project) { create(:empty_project, :public) }
let(:user) { create(:user) } let(:developer) { create(:user) }
let(:issue) { create(:issue, project: project) } let(:issue) { create(:issue, project: project) }
let(:milestone) { create(:milestone, project: project, title: '9.10') } let(:milestone) { create(:milestone, project: project, title: '9.10') }
let(:inprogress) { create(:label, project: project, title: 'In Progress') } let(:inprogress) { create(:label, project: project, title: 'In Progress') }
let(:bug) { create(:label, project: project, title: 'Bug') } let(:bug) { create(:label, project: project, title: 'Bug') }
before do before do
project.team << [user, :developer] project.team << [developer, :developer]
end end
describe '#execute' do describe '#execute' do
let(:service) { described_class.new(project, user) } let(:service) { described_class.new(project, developer) }
let(:merge_request) { create(:merge_request, source_project: project) } let(:merge_request) { create(:merge_request, source_project: project) }
shared_examples 'reopen command' do shared_examples 'reopen command' do
...@@ -45,13 +45,13 @@ describe SlashCommands::InterpretService, services: true do ...@@ -45,13 +45,13 @@ describe SlashCommands::InterpretService, services: true do
it 'fetches assignee and populates assignee_id if content contains /assign' do it 'fetches assignee and populates assignee_id if content contains /assign' do
_, updates = service.execute(content, issuable) _, updates = service.execute(content, issuable)
expect(updates).to eq(assignee_id: user.id) expect(updates).to eq(assignee_id: developer.id)
end end
end end
shared_examples 'unassign command' do shared_examples 'unassign command' do
it 'populates assignee_id: nil if content contains /unassign' do it 'populates assignee_id: nil if content contains /unassign' do
issuable.update(assignee_id: user.id) issuable.update(assignee_id: developer.id)
_, updates = service.execute(content, issuable) _, updates = service.execute(content, issuable)
expect(updates).to eq(assignee_id: nil) expect(updates).to eq(assignee_id: nil)
...@@ -124,7 +124,7 @@ describe SlashCommands::InterpretService, services: true do ...@@ -124,7 +124,7 @@ describe SlashCommands::InterpretService, services: true do
shared_examples 'done command' do shared_examples 'done command' do
it 'populates todo_event: "done" if content contains /done' do it 'populates todo_event: "done" if content contains /done' do
TodoService.new.mark_todo(issuable, user) TodoService.new.mark_todo(issuable, developer)
_, updates = service.execute(content, issuable) _, updates = service.execute(content, issuable)
expect(updates).to eq(todo_event: 'done') expect(updates).to eq(todo_event: 'done')
...@@ -141,7 +141,7 @@ describe SlashCommands::InterpretService, services: true do ...@@ -141,7 +141,7 @@ describe SlashCommands::InterpretService, services: true do
shared_examples 'unsubscribe command' do shared_examples 'unsubscribe command' do
it 'populates subscription_event: "unsubscribe" if content contains /unsubscribe' do it 'populates subscription_event: "unsubscribe" if content contains /unsubscribe' do
issuable.subscribe(user) issuable.subscribe(developer)
_, updates = service.execute(content, issuable) _, updates = service.execute(content, issuable)
expect(updates).to eq(subscription_event: 'unsubscribe') expect(updates).to eq(subscription_event: 'unsubscribe')
...@@ -209,12 +209,12 @@ describe SlashCommands::InterpretService, services: true do ...@@ -209,12 +209,12 @@ describe SlashCommands::InterpretService, services: true do
end end
it_behaves_like 'assign command' do it_behaves_like 'assign command' do
let(:content) { "/assign @#{user.username}" } let(:content) { "/assign @#{developer.username}" }
let(:issuable) { issue } let(:issuable) { issue }
end end
it_behaves_like 'assign command' do it_behaves_like 'assign command' do
let(:content) { "/assign @#{user.username}" } let(:content) { "/assign @#{developer.username}" }
let(:issuable) { merge_request } let(:issuable) { merge_request }
end end
...@@ -380,5 +380,56 @@ describe SlashCommands::InterpretService, services: true do ...@@ -380,5 +380,56 @@ describe SlashCommands::InterpretService, services: true do
let(:content) { '/remove_due_date' } let(:content) { '/remove_due_date' }
let(:issuable) { merge_request } let(:issuable) { merge_request }
end end
context 'when current_user cannot :admin_issue' do
let(:visitor) { create(:user) }
let(:issue) { create(:issue, project: project, author: visitor) }
let(:service) { described_class.new(project, visitor) }
it_behaves_like 'empty command' do
let(:content) { "/assign @#{developer.username}" }
let(:issuable) { issue }
end
it_behaves_like 'empty command' do
let(:content) { '/unassign' }
let(:issuable) { issue }
end
it_behaves_like 'empty command' do
let(:content) { "/milestone %#{milestone.title}" }
let(:issuable) { issue }
end
it_behaves_like 'empty command' do
let(:content) { '/remove_milestone' }
let(:issuable) { issue }
end
it_behaves_like 'empty command' do
let(:content) { %(/label ~"#{inprogress.title}" ~#{bug.title} ~unknown) }
let(:issuable) { issue }
end
it_behaves_like 'empty command' do
let(:content) { %(/unlabel ~"#{inprogress.title}") }
let(:issuable) { issue }
end
it_behaves_like 'empty command' do
let(:content) { %(/relabel ~"#{inprogress.title}") }
let(:issuable) { issue }
end
it_behaves_like 'empty command' do
let(:content) { '/due tomorrow' }
let(:issuable) { issue }
end
it_behaves_like 'empty command' do
let(:content) { '/remove_due_date' }
let(:issuable) { issue }
end
end
end end
end end
...@@ -26,7 +26,7 @@ RSpec.configure do |config| ...@@ -26,7 +26,7 @@ RSpec.configure do |config|
config.verbose_retry = true config.verbose_retry = true
config.display_try_failure_messages = true config.display_try_failure_messages = true
config.include Devise::TestHelpers, type: :controller config.include Devise::Test::ControllerHelpers, type: :controller
config.include Warden::Test::Helpers, type: :request config.include Warden::Test::Helpers, type: :request
config.include LoginHelpers, type: :feature config.include LoginHelpers, type: :feature
config.include StubConfiguration config.include StubConfiguration
......
RSpec::Matchers.define :have_issuable_counts do |opts|
match do |actual|
expected_counts = opts.map do |state, count|
"#{state.to_s.humanize} #{count}"
end
actual.within '.issues-state-filters' do
expected_counts.each do |expected_count|
expect(actual).to have_content(expected_count)
end
end
end
description do
"displays the following issuable counts: #{expected_counts.inspect}"
end
failure_message do
"expected the following issuable counts: #{expected_counts.inspect} to be displayed"
end
end
require 'spec_helper' require 'spec_helper'
describe 'admin/dashboard/index.html.haml' do describe 'admin/dashboard/index.html.haml' do
include Devise::TestHelpers include Devise::Test::ControllerHelpers
before do before do
assign(:projects, create_list(:empty_project, 1)) assign(:projects, create_list(:empty_project, 1))
......
require 'spec_helper' require 'spec_helper'
describe 'projects/builds/show' do describe 'projects/builds/show' do
include Devise::TestHelpers include Devise::Test::ControllerHelpers
let(:project) { create(:project) } let(:project) { create(:project) }
let(:pipeline) do let(:pipeline) do
......
require 'spec_helper' require 'spec_helper'
describe 'projects/issues/_related_branches' do describe 'projects/issues/_related_branches' do
include Devise::TestHelpers include Devise::Test::ControllerHelpers
let(:project) { create(:project) } let(:project) { create(:project) }
let(:branch) { project.repository.find_branch('feature') } let(:branch) { project.repository.find_branch('feature') }
......
require 'spec_helper' require 'spec_helper'
describe 'projects/merge_requests/widget/_heading' do describe 'projects/merge_requests/widget/_heading' do
include Devise::TestHelpers include Devise::Test::ControllerHelpers
context 'when released to an environment' do context 'when released to an environment' do
let(:project) { merge_request.target_project } let(:project) { merge_request.target_project }
......
require 'spec_helper' require 'spec_helper'
describe 'projects/merge_requests/edit.html.haml' do describe 'projects/merge_requests/edit.html.haml' do
include Devise::TestHelpers include Devise::Test::ControllerHelpers
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { create(:project) } let(:project) { create(:project) }
......
require 'spec_helper' require 'spec_helper'
describe 'projects/merge_requests/show.html.haml' do describe 'projects/merge_requests/show.html.haml' do
include Devise::TestHelpers include Devise::Test::ControllerHelpers
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { create(:project) } let(:project) { create(:project) }
......
require 'spec_helper' require 'spec_helper'
describe 'projects/notes/_form' do describe 'projects/notes/_form' do
include Devise::TestHelpers include Devise::Test::ControllerHelpers
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { create(:empty_project) } let(:project) { create(:empty_project) }
......
require 'spec_helper' require 'spec_helper'
describe 'projects/pipelines/show' do describe 'projects/pipelines/show' do
include Devise::TestHelpers include Devise::Test::ControllerHelpers
let(:project) { create(:project) } let(:project) { create(:project) }
let(:pipeline) { create(:ci_empty_pipeline, project: project, sha: project.commit.id) } let(:pipeline) { create(:ci_empty_pipeline, project: project, sha: project.commit.id) }
......
require 'spec_helper' require 'spec_helper'
describe 'projects/tree/show' do describe 'projects/tree/show' do
include Devise::TestHelpers include Devise::Test::ControllerHelpers
let(:project) { create(:project) } let(:project) { create(:project) }
let(:repository) { project.repository } let(:repository) { project.repository }
......
...@@ -4,7 +4,7 @@ deps ...@@ -4,7 +4,7 @@ deps
*.beam *.beam
*.plt *.plt
erl_crash.dump erl_crash.dump
ebin ebin/*.beam
rel/example_project rel/example_project
.concrete/DEV_MODE .concrete/DEV_MODE
.rebar .rebar
...@@ -8,3 +8,6 @@ ...@@ -8,3 +8,6 @@
# Linux trash folder which might appear on any partition or disk # Linux trash folder which might appear on any partition or disk
.Trash-* .Trash-*
# .nfs files are created when an open file is removed but is still being accessed
.nfs*
...@@ -25,3 +25,6 @@ _testmain.go ...@@ -25,3 +25,6 @@ _testmain.go
# Output of the go coverage tool, specifically when used with LiteIDE # Output of the go coverage tool, specifically when used with LiteIDE
*.out *.out
# external packages folder
vendor/
...@@ -39,3 +39,6 @@ jspm_packages ...@@ -39,3 +39,6 @@ jspm_packages
# Optional REPL history # Optional REPL history
.node_repl_history .node_repl_history
# Output of 'npm pack'
*.tgz
...@@ -192,3 +192,6 @@ TSWLatexianTemp* ...@@ -192,3 +192,6 @@ TSWLatexianTemp*
# KBibTeX # KBibTeX
*~[0-9]* *~[0-9]*
# auto folder when using emacs and auctex
/auto/*
...@@ -110,6 +110,10 @@ _TeamCity* ...@@ -110,6 +110,10 @@ _TeamCity*
# DotCover is a Code Coverage Tool # DotCover is a Code Coverage Tool
*.dotCover *.dotCover
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch # NCrunch
_NCrunch_* _NCrunch_*
.*crunch*.local.xml .*crunch*.local.xml
...@@ -189,6 +193,7 @@ ClientBin/ ...@@ -189,6 +193,7 @@ ClientBin/
*~ *~
*.dbmdl *.dbmdl
*.dbproj.schemaview *.dbproj.schemaview
*.jfm
*.pfx *.pfx
*.publishsettings *.publishsettings
node_modules/ node_modules/
...@@ -258,3 +263,6 @@ paket-files/ ...@@ -258,3 +263,6 @@ paket-files/
# Python Tools for Visual Studio (PTVS) # Python Tools for Visual Studio (PTVS)
__pycache__/ __pycache__/
*.pyc *.pyc
# Cake - Uncomment if you are using it
# tools/
image: ruby:2.3-alpine
test:
script: ruby verify_templates.rb
# This template uses the java:8 docker image because there isn't any
# official Gradle image at this moment
#
# This is the Gradle build system for JVM applications
# https://gradle.org/
# https://github.com/gradle/gradle
image: java:8
# Make the gradle wrapper executable. This essentially downloads a copy of
# Gradle to build the project with.
# https://docs.gradle.org/current/userguide/gradle_wrapper.html
# It is expected that any modern gradle project has a wrapper
before_script:
- chmod +x gradlew
# We redirect the gradle user home using -g so that it caches the
# wrapper and dependencies.
# https://docs.gradle.org/current/userguide/gradle_command_line.html
#
# Unfortunately it also caches the build output so
# cleaning removes reminants of any cached builds.
# The assemble task actually builds the project.
# If it fails here, the tests can't run.
build:
stage: build
script:
- ./gradlew -g /cache/.gradle clean assemble
allow_failure: false
# Use the generated build output to run the tests.
test:
stage: test
script:
- ./gradlew -g /cache./gradle check
# An example .gitlab-ci.yml file to test (and optionally report the coverage
# results of) your [Julia][1] packages. Please refer to the [documentation][2]
# for more information about package development in Julia.
#
# Here, it is assumed that your Julia package is named `MyPackage`. Change it to
# whatever name you have given to your package.
#
# [1]: http://julialang.org/
# [2]: http://julia.readthedocs.org/
# Below is the template to run your tests in Julia
.test_template: &test_definition
# Uncomment below if you would like to run the tests on specific references
# only, such as the branches `master`, `development`, etc.
# only:
# - master
# - development
script:
# Let's run the tests. Substitute `coverage = false` below, if you do not
# want coverage results.
- /opt/julia/bin/julia -e 'Pkg.clone(pwd()); Pkg.test("MyPackage",
coverage = true)'
# Comment out below if you do not want coverage results.
- /opt/julia/bin/julia -e 'Pkg.add("Coverage"); cd(Pkg.dir("MyPackage"));
using Coverage; cl, tl = get_summary(process_folder());
println("(", cl/tl*100, "%) covered")'
# Name a test and select an appropriate image.
test:0.4.6:
image: julialang/julia:v0.4.6
<<: *test_definition
# Maybe you would like to test your package against the development branch:
test:0.5.0-dev:
image: julialang/julia:v0.5.0-dev
# ... allowing for failures, since we are testing against the development
# branch:
allow_failure: true
<<: *test_definition
# REMARK: Do not forget to enable the coverage feature for your project, if you
# are using code coverage reporting above. This can be done by
#
# - Navigating to the `CI/CD Pipelines` settings of your project,
# - Copying and pasting the default `Simplecov` regex example provided, i.e.,
# `\(\d+.\d+\%\) covered` in the `test coverage parsing` textfield.
#
# WARNING: This template is using the `julialang/julia` images from [Docker
# Hub][3]. One can use custom Julia images and/or the official ones found
# in the same place. However, care must be taken to correctly locate the binary
# file (`/opt/julia/bin/julia` above), which is usually given on the image's
# description page.
#
# [3]: http://hub.docker.com/
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment