summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorTomasz Maczukin <tomasz@maczukin.pl>2015-12-21 13:27:34 +0100
committerTomasz Maczukin <tomasz@maczukin.pl>2015-12-21 13:27:34 +0100
commit3cfd892f382d3784f614fea75f929c44fe838559 (patch)
treecb9aa9ab48ee02e8f00e8506fecc182d1e66b9ea /lib
parent85ad95be741848fbf15a01789f065e001326cefa (diff)
parent4b4cbf0ce4925e22a635e4432e7ac8602199fa5b (diff)
downloadgitlab-ce-3cfd892f382d3784f614fea75f929c44fe838559.tar.gz
Merge branch 'master' into fix/visibility-level-setting-in-forked-projects
* master: (723 commits) Bump Rack Attack to v4.3.1 for security fix Remove duplicate entry in the changelog Remove extra spaces after branchname Fix merge-request-reopen button title Add branch and tag operation to tree dropdown Use gitlab-shell 2.6.9 Clarify Windows shell executor artifact upload support Fix feature specs: we always show the build status if ci_commit is present Do not display project group/name when issue and MR are in same project Don't create CI status for refs that doesn't have .gitlab-ci.yml, even if the builds are enabled Use gitlab-workhorse 0.5.1 Fix ci_projects migration by using the value only from latest row [ci skip] Revert sidebar position for issue and merge request Add info on using private Docker registries in CI [ci skip] Upgrade Poltergeist to 1.8.1. #4131 Fix ux issue with "This issue will be closed automatically" message Move MR Builds tab next to Commits Api support for requesting starred projects for user Fix Rubocop complain. Fix merge widget JS for buttons ... Conflicts: app/models/project.rb
Diffstat (limited to 'lib')
-rw-r--r--lib/api/api.rb1
-rw-r--r--lib/api/commit_statuses.rb2
-rw-r--r--lib/api/entities.rb25
-rw-r--r--lib/api/groups.rb12
-rw-r--r--lib/api/helpers.rb7
-rw-r--r--lib/api/merge_requests.rb80
-rw-r--r--lib/api/project_hooks.rb2
-rw-r--r--lib/api/projects.rb25
-rw-r--r--lib/api/tags.rb37
-rw-r--r--lib/api/triggers.rb48
-rw-r--r--lib/award_emoji.rb35
-rw-r--r--lib/backup/lfs.rb13
-rw-r--r--lib/backup/manager.rb2
-rw-r--r--lib/banzai.rb13
-rw-r--r--lib/banzai/cross_project_reference.rb22
-rw-r--r--lib/banzai/filter.rb10
-rw-r--r--lib/banzai/filter/abstract_reference_filter.rb145
-rw-r--r--lib/banzai/filter/autolink_filter.rb (renamed from lib/gitlab/markdown/autolink_filter.rb)6
-rw-r--r--lib/banzai/filter/commit_range_reference_filter.rb58
-rw-r--r--lib/banzai/filter/commit_reference_filter.rb63
-rw-r--r--lib/banzai/filter/emoji_filter.rb (renamed from lib/gitlab/markdown/emoji_filter.rb)6
-rw-r--r--lib/banzai/filter/external_issue_reference_filter.rb (renamed from lib/gitlab/markdown/external_issue_reference_filter.rb)16
-rw-r--r--lib/banzai/filter/external_link_filter.rb (renamed from lib/gitlab/markdown/external_link_filter.rb)10
-rw-r--r--lib/banzai/filter/issue_reference_filter.rb (renamed from lib/gitlab/markdown/issue_reference_filter.rb)6
-rw-r--r--lib/banzai/filter/label_reference_filter.rb (renamed from lib/gitlab/markdown/label_reference_filter.rb)27
-rw-r--r--lib/banzai/filter/markdown_filter.rb42
-rw-r--r--lib/banzai/filter/merge_request_reference_filter.rb (renamed from lib/gitlab/markdown/merge_request_reference_filter.rb)22
-rw-r--r--lib/banzai/filter/redactor_filter.rb (renamed from lib/gitlab/markdown/redactor_filter.rb)13
-rw-r--r--lib/banzai/filter/reference_filter.rb (renamed from lib/gitlab/markdown/reference_filter.rb)103
-rw-r--r--lib/banzai/filter/reference_gatherer_filter.rb (renamed from lib/gitlab/markdown/reference_gatherer_filter.rb)13
-rw-r--r--lib/banzai/filter/relative_link_filter.rb (renamed from lib/gitlab/markdown/relative_link_filter.rb)8
-rw-r--r--lib/banzai/filter/sanitization_filter.rb (renamed from lib/gitlab/markdown/sanitization_filter.rb)12
-rw-r--r--lib/banzai/filter/snippet_reference_filter.rb (renamed from lib/gitlab/markdown/snippet_reference_filter.rb)6
-rw-r--r--lib/banzai/filter/syntax_highlight_filter.rb (renamed from lib/gitlab/markdown/syntax_highlight_filter.rb)6
-rw-r--r--lib/banzai/filter/table_of_contents_filter.rb (renamed from lib/gitlab/markdown/table_of_contents_filter.rb)8
-rw-r--r--lib/banzai/filter/task_list_filter.rb (renamed from lib/gitlab/markdown/task_list_filter.rb)6
-rw-r--r--lib/banzai/filter/upload_link_filter.rb (renamed from lib/gitlab/markdown/upload_link_filter.rb)6
-rw-r--r--lib/banzai/filter/user_reference_filter.rb (renamed from lib/gitlab/markdown/user_reference_filter.rb)34
-rw-r--r--lib/banzai/lazy_reference.rb27
-rw-r--r--lib/banzai/pipeline.rb10
-rw-r--r--lib/banzai/pipeline/asciidoc_pipeline.rb13
-rw-r--r--lib/banzai/pipeline/atom_pipeline.rb14
-rw-r--r--lib/banzai/pipeline/base_pipeline.rb30
-rw-r--r--lib/banzai/pipeline/combined_pipeline.rb27
-rw-r--r--lib/banzai/pipeline/description_pipeline.rb14
-rw-r--r--lib/banzai/pipeline/email_pipeline.rb13
-rw-r--r--lib/banzai/pipeline/full_pipeline.rb9
-rw-r--r--lib/banzai/pipeline/gfm_pipeline.rb41
-rw-r--r--lib/banzai/pipeline/note_pipeline.rb14
-rw-r--r--lib/banzai/pipeline/plain_markdown_pipeline.rb13
-rw-r--r--lib/banzai/pipeline/post_process_pipeline.rb20
-rw-r--r--lib/banzai/pipeline/reference_extraction_pipeline.rb13
-rw-r--r--lib/banzai/pipeline/single_line_pipeline.rb9
-rw-r--r--lib/banzai/reference_extractor.rb55
-rw-r--r--lib/banzai/renderer.rb78
-rw-r--r--lib/ci/api/api.rb2
-rw-r--r--lib/ci/api/builds.rb2
-rw-r--r--lib/ci/api/commits.rb66
-rw-r--r--lib/ci/api/entities.rb9
-rw-r--r--lib/ci/api/helpers.rb10
-rw-r--r--lib/ci/api/projects.rb195
-rw-r--r--lib/ci/api/runners.rb15
-rw-r--r--lib/ci/api/triggers.rb2
-rw-r--r--lib/ci/charts.rb2
-rw-r--r--lib/ci/current_settings.rb22
-rw-r--r--lib/ci/git.rb5
-rw-r--r--lib/ci/gitlab_ci_yaml_processor.rb68
-rw-r--r--lib/ci/scheduler.rb16
-rw-r--r--lib/email_validator.rb21
-rw-r--r--lib/gitlab/asciidoc.rb27
-rw-r--r--lib/gitlab/backend/grack_auth.rb4
-rw-r--r--lib/gitlab/backend/shell.rb2
-rw-r--r--lib/gitlab/bitbucket_import/project_creator.rb3
-rw-r--r--lib/gitlab/blacklist.rb34
-rw-r--r--lib/gitlab/build_data_builder.rb64
-rw-r--r--lib/gitlab/closing_issue_extractor.rb16
-rw-r--r--lib/gitlab/current_settings.rb2
-rw-r--r--lib/gitlab/database.rb18
-rw-r--r--lib/gitlab/diff/file.rb4
-rw-r--r--lib/gitlab/email/message/repository_push.rb137
-rw-r--r--lib/gitlab/fogbugz_import/importer.rb2
-rw-r--r--lib/gitlab/fogbugz_import/project_creator.rb3
-rw-r--r--lib/gitlab/git/hook.rb17
-rw-r--r--lib/gitlab/github_import/client.rb2
-rw-r--r--lib/gitlab/gitlab_import/client.rb2
-rw-r--r--lib/gitlab/gitlab_import/project_creator.rb3
-rw-r--r--lib/gitlab/gitorious_import/project_creator.rb3
-rw-r--r--lib/gitlab/google_code_import/importer.rb6
-rw-r--r--lib/gitlab/google_code_import/project_creator.rb3
-rw-r--r--lib/gitlab/ldap/access.rb4
-rw-r--r--lib/gitlab/lfs/response.rb155
-rw-r--r--lib/gitlab/lfs/router.rb6
-rw-r--r--lib/gitlab/markdown.rb200
-rw-r--r--lib/gitlab/markdown/abstract_reference_filter.rb100
-rw-r--r--lib/gitlab/markdown/commit_range_reference_filter.rb92
-rw-r--r--lib/gitlab/markdown/commit_reference_filter.rb88
-rw-r--r--lib/gitlab/markdown/cross_project_reference.rb24
-rw-r--r--lib/gitlab/markdown/pipeline.rb34
-rw-r--r--lib/gitlab/o_auth/auth_hash.rb2
-rw-r--r--lib/gitlab/push_data_builder.rb36
-rw-r--r--lib/gitlab/reference_extractor.rb68
-rw-r--r--lib/gitlab/seeder.rb2
-rw-r--r--lib/gitlab/sherlock/transaction.rb5
-rw-r--r--lib/omni_auth/request_forgery_protection.rb63
-rw-r--r--lib/rouge/formatters/html_gitlab.rb2
-rwxr-xr-xlib/support/init.d/gitlab5
-rwxr-xr-xlib/support/init.d/gitlab.default.example2
-rw-r--r--lib/support/nginx/gitlab140
-rw-r--r--lib/support/nginx/gitlab-ssl143
-rw-r--r--lib/tasks/ci/schedule_builds.rake6
-rw-r--r--lib/tasks/gitlab/backup.rake21
-rw-r--r--lib/tasks/gitlab/check.rake58
-rw-r--r--lib/tasks/gitlab/git.rake55
-rw-r--r--lib/tasks/gitlab/import.rake2
-rw-r--r--lib/tasks/gitlab/list_repos.rake17
-rw-r--r--lib/tasks/gitlab/task_helpers.rake20
-rw-r--r--lib/tasks/spec.rake29
-rw-r--r--lib/tasks/spinach.rake26
118 files changed, 1897 insertions, 1645 deletions
diff --git a/lib/api/api.rb b/lib/api/api.rb
index fe1bf8a4816..7834262d612 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -53,5 +53,6 @@ module API
mount Settings
mount Keys
mount Tags
+ mount Triggers
end
end
diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb
index 2c0596c9dfb..1162271f5fc 100644
--- a/lib/api/commit_statuses.rb
+++ b/lib/api/commit_statuses.rb
@@ -53,7 +53,7 @@ module API
name = params[:name] || params[:context]
status = GenericCommitStatus.running_or_pending.find_by(commit: ci_commit, name: name, ref: params[:ref])
- status ||= GenericCommitStatus.new(commit: ci_commit, user: current_user)
+ status ||= GenericCommitStatus.new(project: @project, commit: ci_commit, user: current_user)
status.update(attrs)
case params[:state].to_s
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 3da6bc415d6..f8511ac5f5c 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -45,7 +45,8 @@ module API
class ProjectHook < Hook
expose :project_id, :push_events
- expose :issues_events, :merge_requests_events, :tag_push_events, :note_events, :enable_ssl_verification
+ expose :issues_events, :merge_requests_events, :tag_push_events, :note_events, :build_events
+ expose :enable_ssl_verification
end
class ForkedFromProject < Grape::Entity
@@ -63,11 +64,13 @@ module API
expose :name, :name_with_namespace
expose :path, :path_with_namespace
expose :issues_enabled, :merge_requests_enabled, :wiki_enabled, :builds_enabled, :snippets_enabled, :created_at, :last_activity_at
+ expose :shared_runners_enabled
expose :creator_id
expose :namespace
- expose :forked_from_project, using: Entities::ForkedFromProject, if: lambda{ | project, options | project.forked? }
+ expose :forked_from_project, using: Entities::ForkedFromProject, if: lambda{ |project, options| project.forked? }
expose :avatar_url
expose :star_count, :forks_count
+ expose :open_issues_count, if: lambda { |project, options| project.issues_enabled? && project.default_issues_tracker? }
end
class ProjectMember < UserBasic
@@ -163,12 +166,15 @@ module API
class MergeRequest < ProjectEntity
expose :target_branch, :source_branch
+ # deprecated, always returns 0
+ expose :upvotes, :downvotes
expose :author, :assignee, using: Entities::UserBasic
expose :source_project_id, :target_project_id
expose :label_names, as: :labels
expose :description
expose :work_in_progress?, as: :work_in_progress
expose :milestone, using: Entities::Milestone
+ expose :merge_when_build_succeeds
end
class MergeRequestChanges < MergeRequest
@@ -192,6 +198,10 @@ module API
expose :author, using: Entities::UserBasic
expose :created_at
expose :system?, as: :system
+ expose :noteable_id, :noteable_type
+ # upvote? and downvote? are deprecated, always return false
+ expose :upvote?, as: :upvote
+ expose :downvote?, as: :downvote
end
class MRNote < Grape::Entity
@@ -219,6 +229,8 @@ module API
expose :target_id, :target_type, :author_id
expose :data, :target_title
expose :created_at
+ expose :note, using: Entities::Note, if: ->(event, options) { event.note? }
+ expose :author, using: Entities::UserBasic, if: ->(event, options) { event.author }
expose :author_username do |event, options|
if event.author
@@ -243,7 +255,7 @@ module API
class ProjectService < Grape::Entity
expose :id, :title, :created_at, :updated_at, :active
- expose :push_events, :issues_events, :merge_requests_events, :tag_push_events, :note_events
+ expose :push_events, :issues_events, :merge_requests_events, :tag_push_events, :note_events, :build_events
# Expose serialized properties
expose :properties do |service, options|
field_names = service.fields.
@@ -322,7 +334,8 @@ module API
end
class Release < Grape::Entity
- expose :tag, :description
+ expose :tag, as: :tag_name
+ expose :description
end
class RepoTag < Grape::Entity
@@ -349,5 +362,9 @@ module API
end
end
end
+
+ class TriggerRequest < Grape::Entity
+ expose :id, :variables
+ end
end
end
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index 024aeec2e14..1a14d870a4a 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -65,6 +65,18 @@ module API
DestroyGroupService.new(group, current_user).execute
end
+ # Get a list of projects in this group
+ #
+ # Example Request:
+ # GET /groups/:id/projects
+ get ":id/projects" do
+ group = find_group(params[:id])
+ projects = group.projects
+ projects = filter_projects(projects)
+ projects = paginate projects
+ present projects, with: Entities::Project
+ end
+
# Transfer a project to the Group namespace
#
# Parameters:
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 92540ccf2b1..a4df810e755 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -266,12 +266,7 @@ module API
projects = projects.search(params[:search])
end
- if params[:ci_enabled_first].present?
- projects.includes(:gitlab_ci_service).
- reorder("services.active DESC, projects.#{project_order_by} #{project_sort}")
- else
- projects.reorder(project_order_by => project_sort)
- end
+ projects.reorder(project_order_by => project_sort)
end
def project_order_by
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index 6eb84baf9cb..3c1c6bda260 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -76,6 +76,22 @@ module API
present merge_request, with: Entities::MergeRequest
end
+ # Show MR commits
+ #
+ # Parameters:
+ # id (required) - The ID of a project
+ # merge_request_id (required) - The ID of MR
+ #
+ # Example:
+ # GET /projects/:id/merge_request/:merge_request_id/commits
+ #
+ get ':id/merge_request/:merge_request_id/commits' do
+ merge_request = user_project.merge_requests.
+ find(params[:merge_request_id])
+ authorize! :read_merge_request, merge_request
+ present merge_request.commits, with: Entities::RepoCommit
+ end
+
# Show MR changes
#
# Parameters:
@@ -179,46 +195,54 @@ module API
# Merge MR
#
# Parameters:
- # id (required) - The ID of a project
- # merge_request_id (required) - ID of MR
- # merge_commit_message (optional) - Custom merge commit message
+ # id (required) - The ID of a project
+ # merge_request_id (required) - ID of MR
+ # merge_commit_message (optional) - Custom merge commit message
+ # should_remove_source_branch (optional) - When true, the source branch will be deleted if possible
+ # merge_when_build_succeeds (optional) - When true, this MR will be merged when the build succeeds
# Example:
# PUT /projects/:id/merge_request/:merge_request_id/merge
#
put ":id/merge_request/:merge_request_id/merge" do
merge_request = user_project.merge_requests.find(params[:merge_request_id])
- allowed = ::Gitlab::GitAccess.new(current_user, user_project).
- can_push_to_branch?(merge_request.target_branch)
+ # Merge request can not be merged
+ # because user dont have permissions to push into target branch
+ unauthorized! unless merge_request.can_be_merged_by?(current_user)
+ not_allowed! if !merge_request.open? || merge_request.work_in_progress?
- if allowed
- if merge_request.unchecked?
- merge_request.check_if_can_be_merged
- end
+ merge_request.check_if_can_be_merged if merge_request.unchecked?
- if merge_request.open? && !merge_request.work_in_progress?
- if merge_request.can_be_merged?
- commit_message = params[:merge_commit_message] || merge_request.merge_commit_message
-
- ::MergeRequests::MergeService.new(merge_request.target_project, current_user).
- execute(merge_request, commit_message)
-
- present merge_request, with: Entities::MergeRequest
- else
- render_api_error!('Branch cannot be merged', 405)
- end
- else
- # Merge request can not be merged
- # because it is already closed/merged or marked as WIP
- not_allowed!
- end
+ render_api_error!('Branch cannot be merged', 406) unless merge_request.can_be_merged?
+
+ merge_params = {
+ commit_message: params[:merge_commit_message],
+ should_remove_source_branch: params[:should_remove_source_branch]
+ }
+
+ if parse_boolean(params[:merge_when_build_succeeds]) && merge_request.ci_commit && merge_request.ci_commit.active?
+ ::MergeRequests::MergeWhenBuildSucceedsService.new(merge_request.target_project, current_user, merge_params).
+ execute(merge_request)
else
- # Merge request can not be merged
- # because user dont have permissions to push into target branch
- unauthorized!
+ ::MergeRequests::MergeService.new(merge_request.target_project, current_user, merge_params).
+ execute(merge_request)
end
+
+ present merge_request, with: Entities::MergeRequest
end
+ # Cancel Merge if Merge When build succeeds is enabled
+ # Parameters:
+ # id (required) - The ID of a project
+ # merge_request_id (required) - ID of MR
+ #
+ post ":id/merge_request/:merge_request_id/cancel_merge_when_build_succeeds" do
+ merge_request = user_project.merge_requests.find(params[:merge_request_id])
+
+ unauthorized! unless merge_request.can_cancel_merge_when_build_succeeds?(current_user)
+
+ ::MergeRequest::MergeWhenBuildSucceedsService.new(merge_request.target_project, current_user).cancel(merge_request)
+ end
# Get a merge request's comments
#
diff --git a/lib/api/project_hooks.rb b/lib/api/project_hooks.rb
index 882d1a083ad..cf9938d25a7 100644
--- a/lib/api/project_hooks.rb
+++ b/lib/api/project_hooks.rb
@@ -45,6 +45,7 @@ module API
:merge_requests_events,
:tag_push_events,
:note_events,
+ :build_events,
:enable_ssl_verification
]
@hook = user_project.hooks.new(attrs)
@@ -77,6 +78,7 @@ module API
:merge_requests_events,
:tag_push_events,
:note_events,
+ :build_events,
:enable_ssl_verification
]
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 2b4ada6e2eb..5e75cd35c56 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -7,8 +7,12 @@ module API
helpers do
def map_public_to_visibility_level(attrs)
publik = attrs.delete(:public)
- publik = parse_boolean(publik)
- attrs[:visibility_level] = Gitlab::VisibilityLevel::PUBLIC if !attrs[:visibility_level].present? && publik == true
+ if publik.present? && !attrs[:visibility_level].present?
+ publik = parse_boolean(publik)
+ # Since setting the public attribute to private could mean either
+ # private or internal, use the more conservative option, private.
+ attrs[:visibility_level] = (publik == true) ? Gitlab::VisibilityLevel::PUBLIC : Gitlab::VisibilityLevel::PRIVATE
+ end
attrs
end
end
@@ -35,6 +39,17 @@ module API
present @projects, with: Entities::Project
end
+ # Gets starred project for the authenticated user
+ #
+ # Example Request:
+ # GET /projects/starred
+ get '/starred' do
+ @projects = current_user.starred_projects
+ @projects = filter_projects(@projects)
+ @projects = paginate @projects
+ present @projects, with: Entities::Project
+ end
+
# Get all projects for admin user
#
# Example Request:
@@ -78,6 +93,7 @@ module API
# builds_enabled (optional)
# wiki_enabled (optional)
# snippets_enabled (optional)
+ # shared_runners_enabled (optional)
# namespace_id (optional) - defaults to user namespace
# public (optional) - if true same as setting visibility_level = 20
# visibility_level (optional) - 0 by default
@@ -94,6 +110,7 @@ module API
:builds_enabled,
:wiki_enabled,
:snippets_enabled,
+ :shared_runners_enabled,
:namespace_id,
:public,
:visibility_level,
@@ -122,6 +139,7 @@ module API
# builds_enabled (optional)
# wiki_enabled (optional)
# snippets_enabled (optional)
+ # shared_runners_enabled (optional)
# public (optional) - if true same as setting visibility_level = 20
# visibility_level (optional)
# import_url (optional)
@@ -138,6 +156,7 @@ module API
:builds_enabled,
:wiki_enabled,
:snippets_enabled,
+ :shared_runners_enabled,
:public,
:visibility_level,
:import_url]
@@ -179,6 +198,7 @@ module API
# builds_enabled (optional)
# wiki_enabled (optional)
# snippets_enabled (optional)
+ # shared_runners_enabled (optional)
# public (optional) - if true same as setting visibility_level = 20
# visibility_level (optional) - visibility level of a project
# Example Request
@@ -193,6 +213,7 @@ module API
:builds_enabled,
:wiki_enabled,
:snippets_enabled,
+ :shared_runners_enabled,
:public,
:visibility_level]
attrs = map_public_to_visibility_level(attrs)
diff --git a/lib/api/tags.rb b/lib/api/tags.rb
index 673342dd447..47621f443e6 100644
--- a/lib/api/tags.rb
+++ b/lib/api/tags.rb
@@ -44,17 +44,42 @@ module API
#
# Parameters:
# id (required) - The ID of a project
- # tag (required) - The name of the tag
+ # tag_name (required) - The name of the tag
+ # description (required) - Release notes with markdown support
+ # Example Request:
+ # POST /projects/:id/repository/tags/:tag_name/release
+ post ':id/repository/tags/:tag_name/release', requirements: { tag_name: /.*/ } do
+ authorize_push_project
+ required_attributes! [:description]
+ result = CreateReleaseService.new(user_project, current_user).
+ execute(params[:tag_name], params[:description])
+
+ if result[:status] == :success
+ present result[:release], with: Entities::Release
+ else
+ render_api_error!(result[:message], result[:http_status])
+ end
+ end
+
+ # Updates a release notes of a tag
+ #
+ # Parameters:
+ # id (required) - The ID of a project
+ # tag_name (required) - The name of the tag
# description (required) - Release notes with markdown support
# Example Request:
- # PUT /projects/:id/repository/tags
- put ':id/repository/:tag/release', requirements: { tag: /.*/ } do
+ # PUT /projects/:id/repository/tags/:tag_name/release
+ put ':id/repository/tags/:tag_name/release', requirements: { tag_name: /.*/ } do
authorize_push_project
required_attributes! [:description]
- release = user_project.releases.find_or_initialize_by(tag: params[:tag])
- release.update_attributes(description: params[:description])
+ result = UpdateReleaseService.new(user_project, current_user).
+ execute(params[:tag_name], params[:description])
- present release, with: Entities::Release
+ if result[:status] == :success
+ present result[:release], with: Entities::Release
+ else
+ render_api_error!(result[:message], result[:http_status])
+ end
end
end
end
diff --git a/lib/api/triggers.rb b/lib/api/triggers.rb
new file mode 100644
index 00000000000..2781f1cf191
--- /dev/null
+++ b/lib/api/triggers.rb
@@ -0,0 +1,48 @@
+module API
+ # Triggers API
+ class Triggers < Grape::API
+ resource :projects do
+ # Trigger a GitLab project build
+ #
+ # Parameters:
+ # id (required) - The ID of a CI project
+ # ref (required) - The name of project's branch or tag
+ # token (required) - The uniq token of trigger
+ # variables (optional) - The list of variables to be injected into build
+ # Example Request:
+ # POST /projects/:id/trigger/builds
+ post ":id/trigger/builds" do
+ required_attributes! [:ref, :token]
+
+ project = Project.find_with_namespace(params[:id]) || Project.find_by(id: params[:id])
+ trigger = Ci::Trigger.find_by_token(params[:token].to_s)
+ not_found! unless project && trigger
+ unauthorized! unless trigger.project == project
+
+ # validate variables
+ variables = params[:variables]
+ if variables
+ unless variables.is_a?(Hash)
+ render_api_error!('variables needs to be a hash', 400)
+ end
+
+ unless variables.all? { |key, value| key.is_a?(String) && value.is_a?(String) }
+ render_api_error!('variables needs to be a map of key-valued strings', 400)
+ end
+
+ # convert variables from Mash to Hash
+ variables = variables.to_h
+ end
+
+ # create request and trigger builds
+ trigger_request = Ci::CreateTriggerRequestService.new.execute(project, trigger, params[:ref].to_s, variables)
+ if trigger_request
+ present trigger_request, with: Entities::TriggerRequest
+ else
+ errors = 'No builds created'
+ render_api_error!(errors, 400)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/award_emoji.rb b/lib/award_emoji.rb
index d58a196c4ef..4d99164bc33 100644
--- a/lib/award_emoji.rb
+++ b/lib/award_emoji.rb
@@ -6,7 +6,42 @@ class AwardEmoji
"ambulance", "anguished", "two_hearts", "wink"
]
+ ALIASES = {
+ pout: "rage",
+ satisfied: "laughing",
+ hankey: "shit",
+ poop: "shit",
+ collision: "boom",
+ thumbsup: "+1",
+ thumbsdown: "-1",
+ punch: "facepunch",
+ raised_hand: "hand",
+ running: "runner",
+ ng_woman: "no_good",
+ shoe: "mans_shoe",
+ tshirt: "shirt",
+ honeybee: "bee",
+ flipper: "dolphin",
+ paw_prints: "feet",
+ waxing_gibbous_moon: "moon",
+ telephone: "phone",
+ knife: "hocho",
+ envelope: "email",
+ pencil: "memo",
+ open_book: "book",
+ sailboat: "boat",
+ red_car: "car",
+ lantern: "izakaya_lantern",
+ uk: "gb",
+ heavy_exclamation_mark: "exclamation",
+ squirrel: "shipit"
+ }.with_indifferent_access
+
def self.path_to_emoji_image(name)
"emoji/#{Emoji.emoji_filename(name)}.png"
end
+
+ def self.normilize_emoji_name(name)
+ ALIASES[name] || name
+ end
end
diff --git a/lib/backup/lfs.rb b/lib/backup/lfs.rb
new file mode 100644
index 00000000000..4153467fbee
--- /dev/null
+++ b/lib/backup/lfs.rb
@@ -0,0 +1,13 @@
+require 'backup/files'
+
+module Backup
+ class Lfs < Files
+ def initialize
+ super('lfs', Settings.lfs.storage_path)
+ end
+
+ def create_files_dir
+ Dir.mkdir(app_files_dir, 0700)
+ end
+ end
+end
diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb
index e7eda7c6f45..099062eeb8b 100644
--- a/lib/backup/manager.rb
+++ b/lib/backup/manager.rb
@@ -154,7 +154,7 @@ module Backup
end
def archives_to_backup
- %w{uploads builds artifacts}.map{ |name| (name + ".tar.gz") unless skipped?(name) }.compact
+ %w{uploads builds artifacts lfs}.map{ |name| (name + ".tar.gz") unless skipped?(name) }.compact
end
def folders_to_backup
diff --git a/lib/banzai.rb b/lib/banzai.rb
new file mode 100644
index 00000000000..093382261ae
--- /dev/null
+++ b/lib/banzai.rb
@@ -0,0 +1,13 @@
+module Banzai
+ def self.render(text, context = {})
+ Renderer.render(text, context)
+ end
+
+ def self.render_result(text, context = {})
+ Renderer.render_result(text, context)
+ end
+
+ def self.post_process(html, context)
+ Renderer.post_process(html, context)
+ end
+end
diff --git a/lib/banzai/cross_project_reference.rb b/lib/banzai/cross_project_reference.rb
new file mode 100644
index 00000000000..ba2866e1efa
--- /dev/null
+++ b/lib/banzai/cross_project_reference.rb
@@ -0,0 +1,22 @@
+require 'banzai'
+
+module Banzai
+ # Common methods for ReferenceFilters that support an optional cross-project
+ # reference.
+ module CrossProjectReference
+ # Given a cross-project reference string, get the Project record
+ #
+ # Defaults to value of `context[:project]` if:
+ # * No reference is given OR
+ # * Reference given doesn't exist
+ #
+ # ref - String reference.
+ #
+ # Returns a Project, or nil if the reference can't be found
+ def project_from_ref(ref)
+ return context[:project] unless ref
+
+ Project.find_with_namespace(ref)
+ end
+ end
+end
diff --git a/lib/banzai/filter.rb b/lib/banzai/filter.rb
new file mode 100644
index 00000000000..fd4fe024252
--- /dev/null
+++ b/lib/banzai/filter.rb
@@ -0,0 +1,10 @@
+require 'active_support/core_ext/string/output_safety'
+require 'banzai'
+
+module Banzai
+ module Filter
+ def self.[](name)
+ const_get("#{name.to_s.camelize}Filter")
+ end
+ end
+end
diff --git a/lib/banzai/filter/abstract_reference_filter.rb b/lib/banzai/filter/abstract_reference_filter.rb
new file mode 100644
index 00000000000..bdaa4721b4b
--- /dev/null
+++ b/lib/banzai/filter/abstract_reference_filter.rb
@@ -0,0 +1,145 @@
+require 'banzai'
+
+module Banzai
+ module Filter
+ # Issues, Merge Requests, Snippets, Commits and Commit Ranges share
+ # similar functionality in reference filtering.
+ class AbstractReferenceFilter < ReferenceFilter
+ include CrossProjectReference
+
+ def self.object_class
+ # Implement in child class
+ # Example: MergeRequest
+ end
+
+ def self.object_name
+ object_class.name.underscore
+ end
+
+ def self.object_sym
+ object_name.to_sym
+ end
+
+ def self.data_reference
+ "data-#{object_name.dasherize}"
+ end
+
+ # Public: Find references in text (like `!123` for merge requests)
+ #
+ # AnyReferenceFilter.references_in(text) do |match, id, project_ref, matches|
+ # object = find_object(project_ref, id)
+ # "<a href=...>#{object.to_reference}</a>"
+ # end
+ #
+ # text - String text to search.
+ #
+ # Yields the String match, the Integer referenced object ID, an optional String
+ # of the external project reference, and all of the matchdata.
+ #
+ # Returns a String replaced with the return of the block.
+ def self.references_in(text, pattern = object_class.reference_pattern)
+ text.gsub(pattern) do |match|
+ yield match, $~[object_sym].to_i, $~[:project], $~
+ end
+ end
+
+ def self.referenced_by(node)
+ { object_sym => LazyReference.new(object_class, node.attr(data_reference)) }
+ end
+
+ delegate :object_class, :object_sym, :references_in, to: :class
+
+ def find_object(project, id)
+ # Implement in child class
+ # Example: project.merge_requests.find
+ end
+
+ def url_for_object(object, project)
+ # Implement in child class
+ # Example: project_merge_request_url
+ end
+
+ def call
+ # `#123`
+ replace_text_nodes_matching(object_class.reference_pattern) do |content|
+ object_link_filter(content, object_class.reference_pattern)
+ end
+
+ # `[Issue](#123)`, which is turned into
+ # `<a href="#123">Issue</a>`
+ replace_link_nodes_with_href(object_class.reference_pattern) do |link, text|
+ object_link_filter(link, object_class.reference_pattern, link_text: text)
+ end
+
+ # `http://gitlab.example.com/namespace/project/issues/123`, which is turned into
+ # `<a href="http://gitlab.example.com/namespace/project/issues/123">http://gitlab.example.com/namespace/project/issues/123</a>`
+ replace_link_nodes_with_text(object_class.link_reference_pattern) do |text|
+ object_link_filter(text, object_class.link_reference_pattern)
+ end
+
+ # `[Issue](http://gitlab.example.com/namespace/project/issues/123)`, which is turned into
+ # `<a href="http://gitlab.example.com/namespace/project/issues/123">Issue</a>`
+ replace_link_nodes_with_href(object_class.link_reference_pattern) do |link, text|
+ object_link_filter(link, object_class.link_reference_pattern, link_text: text)
+ end
+ end
+
+ # Replace references (like `!123` for merge requests) in text with links
+ # to the referenced object's details page.
+ #
+ # text - String text to replace references in.
+ # pattern - Reference pattern to match against.
+ # link_text - Original content of the link being replaced.
+ #
+ # Returns a String with references replaced with links. All links
+ # have `gfm` and `gfm-OBJECT_NAME` class names attached for styling.
+ def object_link_filter(text, pattern, link_text: nil)
+ references_in(text, pattern) do |match, id, project_ref, matches|
+ project = project_from_ref(project_ref)
+
+ if project && object = find_object(project, id)
+ title = escape_once(object_link_title(object))
+ klass = reference_class(object_sym)
+
+ data = data_attribute(
+ original: link_text || match,
+ project: project.id,
+ object_sym => object.id
+ )
+
+ url = matches[:url] if matches.names.include?("url")
+ url ||= url_for_object(object, project)
+
+ text = link_text
+ unless text
+ text = object.reference_link_text(context[:project])
+
+ extras = object_link_text_extras(object, matches)
+ text += " (#{extras.join(", ")})" if extras.any?
+ end
+
+ %(<a href="#{url}" #{data}
+ title="#{title}"
+ class="#{klass}">#{text}</a>)
+ else
+ match
+ end
+ end
+ end
+
+ def object_link_text_extras(object, matches)
+ extras = []
+
+ if matches.names.include?("anchor") && matches[:anchor] && matches[:anchor] =~ /\A\#note_(\d+)\z/
+ extras << "comment #{$1}"
+ end
+
+ extras
+ end
+
+ def object_link_title(object)
+ "#{object_class.name.titleize}: #{object.title}"
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/markdown/autolink_filter.rb b/lib/banzai/filter/autolink_filter.rb
index c37c3bc55bf..da4ee80c1b5 100644
--- a/lib/gitlab/markdown/autolink_filter.rb
+++ b/lib/banzai/filter/autolink_filter.rb
@@ -1,9 +1,9 @@
-require 'gitlab/markdown'
+require 'banzai'
require 'html/pipeline/filter'
require 'uri'
-module Gitlab
- module Markdown
+module Banzai
+ module Filter
# HTML Filter for auto-linking URLs in HTML.
#
# Based on HTML::Pipeline::AutolinkFilter
diff --git a/lib/banzai/filter/commit_range_reference_filter.rb b/lib/banzai/filter/commit_range_reference_filter.rb
new file mode 100644
index 00000000000..e67cd45ab9b
--- /dev/null
+++ b/lib/banzai/filter/commit_range_reference_filter.rb
@@ -0,0 +1,58 @@
+require 'banzai'
+
+module Banzai
+ module Filter
+ # HTML filter that replaces commit range references with links.
+ #
+ # This filter supports cross-project references.
+ class CommitRangeReferenceFilter < AbstractReferenceFilter
+ def self.object_class
+ CommitRange
+ end
+
+ def self.references_in(text, pattern = CommitRange.reference_pattern)
+ text.gsub(pattern) do |match|
+ yield match, $~[:commit_range], $~[:project], $~
+ end
+ end
+
+ def self.referenced_by(node)
+ project = Project.find(node.attr("data-project")) rescue nil
+ return unless project
+
+ id = node.attr("data-commit-range")
+ range = find_object(project, id)
+
+ return unless range
+
+ { commit_range: range }
+ end
+
+ def initialize(*args)
+ super
+
+ @commit_map = {}
+ end
+
+ def self.find_object(project, id)
+ range = CommitRange.new(id, project)
+
+ range.valid_commits? ? range : nil
+ end
+
+ def find_object(*args)
+ self.class.find_object(*args)
+ end
+
+ def url_for_object(range, project)
+ h = Gitlab::Application.routes.url_helpers
+ h.namespace_project_compare_url(project.namespace, project,
+ range.to_param.merge(only_path: context[:only_path]))
+ end
+
+ def object_link_title(range)
+ range.reference_title
+ end
+ end
+ end
+end
diff --git a/lib/banzai/filter/commit_reference_filter.rb b/lib/banzai/filter/commit_reference_filter.rb
new file mode 100644
index 00000000000..9e57608b483
--- /dev/null
+++ b/lib/banzai/filter/commit_reference_filter.rb
@@ -0,0 +1,63 @@
+require 'banzai'
+
+module Banzai
+ module Filter
+ # HTML filter that replaces commit references with links.
+ #
+ # This filter supports cross-project references.
+ class CommitReferenceFilter < AbstractReferenceFilter
+ def self.object_class
+ Commit
+ end
+
+ def self.references_in(text, pattern = Commit.reference_pattern)
+ text.gsub(pattern) do |match|
+ yield match, $~[:commit], $~[:project], $~
+ end
+ end
+
+ def self.referenced_by(node)
+ project = Project.find(node.attr("data-project")) rescue nil
+ return unless project
+
+ id = node.attr("data-commit")
+ commit = find_object(project, id)
+
+ return unless commit
+
+ { commit: commit }
+ end
+
+ def self.find_object(project, id)
+ if project && project.valid_repo?
+ project.commit(id)
+ end
+ end
+
+ def find_object(*args)
+ self.class.find_object(*args)
+ end
+
+ def url_for_object(commit, project)
+ h = Gitlab::Application.routes.url_helpers
+ h.namespace_project_commit_url(project.namespace, project, commit,
+ only_path: context[:only_path])
+ end
+
+ def object_link_title(commit)
+ commit.link_title
+ end
+
+ def object_link_text_extras(object, matches)
+ extras = super
+
+ path = matches[:path] if matches.names.include?("path")
+ if path == '/builds'
+ extras.unshift "builds"
+ end
+
+ extras
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/markdown/emoji_filter.rb b/lib/banzai/filter/emoji_filter.rb
index da10e4d3760..86838e1483c 100644
--- a/lib/gitlab/markdown/emoji_filter.rb
+++ b/lib/banzai/filter/emoji_filter.rb
@@ -1,10 +1,10 @@
require 'action_controller'
-require 'gitlab/markdown'
+require 'banzai'
require 'gitlab_emoji'
require 'html/pipeline/filter'
-module Gitlab
- module Markdown
+module Banzai
+ module Filter
# HTML filter that replaces :emoji: with images.
#
# Based on HTML::Pipeline::EmojiFilter
diff --git a/lib/gitlab/markdown/external_issue_reference_filter.rb b/lib/banzai/filter/external_issue_reference_filter.rb
index 8f86f13976a..f5737a7ac19 100644
--- a/lib/gitlab/markdown/external_issue_reference_filter.rb
+++ b/lib/banzai/filter/external_issue_reference_filter.rb
@@ -1,7 +1,7 @@
-require 'gitlab/markdown'
+require 'banzai'
-module Gitlab
- module Markdown
+module Banzai
+ module Filter
# HTML filter that replaces external issue tracker references with links.
# References are ignored if the project doesn't use an external issue
# tracker.
@@ -30,6 +30,10 @@ module Gitlab
replace_text_nodes_matching(ExternalIssue.reference_pattern) do |content|
issue_link_filter(content)
end
+
+ replace_link_nodes_with_href(ExternalIssue.reference_pattern) do |link, text|
+ issue_link_filter(link, link_text: text)
+ end
end
# Replace `JIRA-123` issue references in text with links to the referenced
@@ -39,7 +43,7 @@ module Gitlab
#
# Returns a String with `JIRA-123` references replaced with links. All
# links have `gfm` and `gfm-issue` class names attached for styling.
- def issue_link_filter(text)
+ def issue_link_filter(text, link_text: nil)
project = context[:project]
self.class.references_in(text) do |match, issue|
@@ -49,9 +53,11 @@ module Gitlab
klass = reference_class(:issue)
data = data_attribute(project: project.id)
+ text = link_text || match
+
%(<a href="#{url}" #{data}
title="#{title}"
- class="#{klass}">#{match}</a>)
+ class="#{klass}">#{text}</a>)
end
end
diff --git a/lib/gitlab/markdown/external_link_filter.rb b/lib/banzai/filter/external_link_filter.rb
index 29e51b6ade6..ac87b9820af 100644
--- a/lib/gitlab/markdown/external_link_filter.rb
+++ b/lib/banzai/filter/external_link_filter.rb
@@ -1,16 +1,16 @@
-require 'gitlab/markdown'
+require 'banzai'
require 'html/pipeline/filter'
-module Gitlab
- module Markdown
+module Banzai
+ module Filter
# HTML Filter to add a `rel="nofollow"` attribute to external links
#
class ExternalLinkFilter < HTML::Pipeline::Filter
def call
doc.search('a').each do |node|
- next unless node.has_attribute?('href')
+ link = node.attr('href')
- link = node.attribute('href').value
+ next unless link
# Skip non-HTTP(S) links
next unless link.start_with?('http')
diff --git a/lib/gitlab/markdown/issue_reference_filter.rb b/lib/banzai/filter/issue_reference_filter.rb
index 1ed69e2f431..51180cb901a 100644
--- a/lib/gitlab/markdown/issue_reference_filter.rb
+++ b/lib/banzai/filter/issue_reference_filter.rb
@@ -1,7 +1,7 @@
-require 'gitlab/markdown'
+require 'banzai'
-module Gitlab
- module Markdown
+module Banzai
+ module Filter
# HTML filter that replaces issue references with links. References to
# issues that do not exist are ignored.
#
diff --git a/lib/gitlab/markdown/label_reference_filter.rb b/lib/banzai/filter/label_reference_filter.rb
index 618acb7a578..07bac2dd7fd 100644
--- a/lib/gitlab/markdown/label_reference_filter.rb
+++ b/lib/banzai/filter/label_reference_filter.rb
@@ -1,7 +1,7 @@
-require 'gitlab/markdown'
+require 'banzai'
-module Gitlab
- module Markdown
+module Banzai
+ module Filter
# HTML filter that replaces label references with links.
class LabelReferenceFilter < ReferenceFilter
# Public: Find label references in text
@@ -30,6 +30,10 @@ module Gitlab
replace_text_nodes_matching(Label.reference_pattern) do |content|
label_link_filter(content)
end
+
+ replace_link_nodes_with_href(Label.reference_pattern) do |link, text|
+ label_link_filter(link, link_text: text)
+ end
end
# Replace label references in text with links to the label specified.
@@ -38,7 +42,7 @@ module Gitlab
#
# Returns a String with label references replaced with links. All links
# have `gfm` and `gfm-label` class names attached for styling.
- def label_link_filter(text)
+ def label_link_filter(text, link_text: nil)
project = context[:project]
self.class.references_in(text) do |match, id, name|
@@ -47,10 +51,16 @@ module Gitlab
if label = project.labels.find_by(params)
url = url_for_label(project, label)
klass = reference_class(:label)
- data = data_attribute(project: project.id, label: label.id)
+ data = data_attribute(
+ original: link_text || match,
+ project: project.id,
+ label: label.id
+ )
+
+ text = link_text || render_colored_label(label)
%(<a href="#{url}" #{data}
- class="#{klass}">#{render_colored_label(label)}</a>)
+ class="#{klass}">#{text}</a>)
else
match
end
@@ -59,9 +69,8 @@ module Gitlab
def url_for_label(project, label)
h = Gitlab::Application.routes.url_helpers
- h.namespace_project_issues_path(project.namespace, project,
- label_name: label.name,
- only_path: context[:only_path])
+ h.namespace_project_issues_url( project.namespace, project, label_name: label.name,
+ only_path: context[:only_path])
end
def render_colored_label(label)
diff --git a/lib/banzai/filter/markdown_filter.rb b/lib/banzai/filter/markdown_filter.rb
new file mode 100644
index 00000000000..d09cf41df39
--- /dev/null
+++ b/lib/banzai/filter/markdown_filter.rb
@@ -0,0 +1,42 @@
+require 'banzai'
+require 'html/pipeline/filter'
+
+module Banzai
+ module Filter
+ class MarkdownFilter < HTML::Pipeline::TextFilter
+ def initialize(text, context = nil, result = nil)
+ super text, context, result
+ @text = @text.delete "\r"
+ end
+
+ def call
+ html = self.class.renderer.render(@text)
+ html.rstrip!
+ html
+ end
+
+ private
+
+ def self.redcarpet_options
+ # https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use
+ @redcarpet_options ||= {
+ fenced_code_blocks: true,
+ footnotes: true,
+ lax_spacing: true,
+ no_intra_emphasis: true,
+ space_after_headers: true,
+ strikethrough: true,
+ superscript: true,
+ tables: true
+ }.freeze
+ end
+
+ def self.renderer
+ @renderer ||= begin
+ renderer = Redcarpet::Render::HTML.new
+ Redcarpet::Markdown.new(renderer, redcarpet_options)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/markdown/merge_request_reference_filter.rb b/lib/banzai/filter/merge_request_reference_filter.rb
index 1f47f03c94e..755b946a34b 100644
--- a/lib/gitlab/markdown/merge_request_reference_filter.rb
+++ b/lib/banzai/filter/merge_request_reference_filter.rb
@@ -1,7 +1,7 @@
-require 'gitlab/markdown'
+require 'banzai'
-module Gitlab
- module Markdown
+module Banzai
+ module Filter
# HTML filter that replaces merge request references with links. References
# to merge requests that do not exist are ignored.
#
@@ -20,6 +20,22 @@ module Gitlab
h.namespace_project_merge_request_url(project.namespace, project, mr,
only_path: context[:only_path])
end
+
+ def object_link_text_extras(object, matches)
+ extras = super
+
+ path = matches[:path] if matches.names.include?("path")
+ case path
+ when '/diffs'
+ extras.unshift "diffs"
+ when '/commits'
+ extras.unshift "commits"
+ when '/builds'
+ extras.unshift "builds"
+ end
+
+ extras
+ end
end
end
end
diff --git a/lib/gitlab/markdown/redactor_filter.rb b/lib/banzai/filter/redactor_filter.rb
index a1f3a8a8ebf..89e7a79789a 100644
--- a/lib/gitlab/markdown/redactor_filter.rb
+++ b/lib/banzai/filter/redactor_filter.rb
@@ -1,8 +1,8 @@
-require 'gitlab/markdown'
+require 'banzai'
require 'html/pipeline/filter'
-module Gitlab
- module Markdown
+module Banzai
+ module Filter
# HTML filter that removes references to records that the current user does
# not have permission to view.
#
@@ -12,7 +12,10 @@ module Gitlab
def call
doc.css('a.gfm').each do |node|
unless user_can_reference?(node)
- node.replace(node.text)
+ # The reference should be replaced by the original text,
+ # which is not always the same as the rendered text.
+ text = node.attr('data-original') || node.text
+ node.replace(text)
end
end
@@ -24,7 +27,7 @@ module Gitlab
def user_can_reference?(node)
if node.has_attribute?('data-reference-filter')
reference_type = node.attr('data-reference-filter')
- reference_filter = reference_type.constantize
+ reference_filter = Banzai::Filter.const_get(reference_type)
reference_filter.user_can_reference?(current_user, node, context)
else
diff --git a/lib/gitlab/markdown/reference_filter.rb b/lib/banzai/filter/reference_filter.rb
index a4c560f578c..33457a3f361 100644
--- a/lib/gitlab/markdown/reference_filter.rb
+++ b/lib/banzai/filter/reference_filter.rb
@@ -1,9 +1,9 @@
require 'active_support/core_ext/string/output_safety'
-require 'gitlab/markdown'
+require 'banzai'
require 'html/pipeline/filter'
-module Gitlab
- module Markdown
+module Banzai
+ module Filter
# Base class for GitLab Flavored Markdown reference filters.
#
# References within <pre>, <code>, <a>, and <style> elements are ignored.
@@ -12,23 +12,6 @@ module Gitlab
# :project (required) - Current project, ignored if reference is cross-project.
# :only_path - Generate path-only links.
class ReferenceFilter < HTML::Pipeline::Filter
- LazyReference = Struct.new(:klass, :ids) do
- def self.load(refs)
- lazy_references, values = refs.partition { |ref| ref.is_a?(self) }
-
- lazy_values = lazy_references.group_by(&:klass).flat_map do |klass, refs|
- ids = refs.flat_map(&:ids)
- klass.where(id: ids)
- end
-
- values + lazy_values
- end
-
- def load
- self.klass.where(id: self.ids)
- end
- end
-
def self.user_can_reference?(user, node, context)
if node.has_attribute?('data-project')
project_id = node.attr('data-project').to_i
@@ -53,14 +36,14 @@ module Gitlab
# Examples:
#
# data_attribute(project: 1, issue: 2)
- # # => "data-reference-filter=\"Gitlab::Markdown::SomeReferenceFilter\" data-project=\"1\" data-issue=\"2\""
+ # # => "data-reference-filter=\"SomeReferenceFilter\" data-project=\"1\" data-issue=\"2\""
#
# data_attribute(project: 3, merge_request: 4)
- # # => "data-reference-filter=\"Gitlab::Markdown::SomeReferenceFilter\" data-project=\"3\" data-merge-request=\"4\""
+ # # => "data-reference-filter=\"SomeReferenceFilter\" data-project=\"3\" data-merge-request=\"4\""
#
# Returns a String
def data_attribute(attributes = {})
- attributes[:reference_filter] = self.class.name
+ attributes[:reference_filter] = self.class.name.demodulize
attributes.map { |key, value| %Q(data-#{key.to_s.dasherize}="#{value}") }.join(" ")
end
@@ -122,6 +105,80 @@ module Gitlab
doc
end
+ # Iterate through the document's link nodes, yielding the current node's
+ # content if:
+ #
+ # * The `project` context value is present AND
+ # * The node's content matches `pattern`
+ #
+ # pattern - Regex pattern against which to match the node's content
+ #
+ # Yields the current node's String contents. The result of the block will
+ # replace the node and update the current document.
+ #
+ # Returns the updated Nokogiri::HTML::DocumentFragment object.
+ def replace_link_nodes_with_text(pattern)
+ return doc if project.nil?
+
+ doc.search('a').each do |node|
+ klass = node.attr('class')
+ next if klass && klass.include?('gfm')
+
+ link = node.attr('href')
+ text = node.text
+
+ next unless link && text
+
+ link = URI.decode(link)
+ # Ignore ending punctionation like periods or commas
+ next unless link == text && text =~ /\A#{pattern}/
+
+ html = yield text
+
+ next if html == text
+
+ node.replace(html)
+ end
+
+ doc
+ end
+
+ # Iterate through the document's link nodes, yielding the current node's
+ # content if:
+ #
+ # * The `project` context value is present AND
+ # * The node's HREF matches `pattern`
+ #
+ # pattern - Regex pattern against which to match the node's HREF
+ #
+ # Yields the current node's String HREF and String content.
+ # The result of the block will replace the node and update the current document.
+ #
+ # Returns the updated Nokogiri::HTML::DocumentFragment object.
+ def replace_link_nodes_with_href(pattern)
+ return doc if project.nil?
+
+ doc.search('a').each do |node|
+ klass = node.attr('class')
+ next if klass && klass.include?('gfm')
+
+ link = node.attr('href')
+ text = node.text
+
+ next unless link && text
+ link = URI.decode(link)
+ next unless link && link =~ /\A#{pattern}\z/
+
+ html = yield link, text
+
+ next if html == link
+
+ node.replace(html)
+ end
+
+ doc
+ end
+
# Ensure that a :project key exists in context
#
# Note that while the key might exist, its value could be nil!
diff --git a/lib/gitlab/markdown/reference_gatherer_filter.rb b/lib/banzai/filter/reference_gatherer_filter.rb
index 00f983675e6..855f238ac1e 100644
--- a/lib/gitlab/markdown/reference_gatherer_filter.rb
+++ b/lib/banzai/filter/reference_gatherer_filter.rb
@@ -1,8 +1,8 @@
-require 'gitlab/markdown'
+require 'banzai'
require 'html/pipeline/filter'
-module Gitlab
- module Markdown
+module Banzai
+ module Filter
# HTML filter that gathers all referenced records that the current user has
# permission to view.
#
@@ -20,7 +20,7 @@ module Gitlab
gather_references(node)
end
- load_lazy_references unless context[:load_lazy_references] == false
+ load_lazy_references unless ReferenceExtractor.lazy?
doc
end
@@ -31,7 +31,7 @@ module Gitlab
return unless node.has_attribute?('data-reference-filter')
reference_type = node.attr('data-reference-filter')
- reference_filter = reference_type.constantize
+ reference_filter = Banzai::Filter.const_get(reference_type)
return if context[:reference_filter] && reference_filter != context[:reference_filter]
@@ -47,11 +47,10 @@ module Gitlab
end
end
- # Will load all references of one type using one query.
def load_lazy_references
refs = result[:references]
refs.each do |type, values|
- refs[type] = ReferenceFilter::LazyReference.load(values)
+ refs[type] = ReferenceExtractor.lazily(values)
end
end
diff --git a/lib/gitlab/markdown/relative_link_filter.rb b/lib/banzai/filter/relative_link_filter.rb
index 632be4d7542..5a081125f21 100644
--- a/lib/gitlab/markdown/relative_link_filter.rb
+++ b/lib/banzai/filter/relative_link_filter.rb
@@ -1,9 +1,9 @@
-require 'gitlab/markdown'
+require 'banzai'
require 'html/pipeline/filter'
require 'uri'
-module Gitlab
- module Markdown
+module Banzai
+ module Filter
# HTML filter that "fixes" relative links to files in a repository.
#
# Context options:
@@ -16,7 +16,7 @@ module Gitlab
def call
return doc unless linkable_files?
- doc.search('a').each do |el|
+ doc.search('a:not(.gfm)').each do |el|
process_link_attr el.attribute('href')
end
diff --git a/lib/gitlab/markdown/sanitization_filter.rb b/lib/banzai/filter/sanitization_filter.rb
index ffb9dc33b64..d03e3ae4b3c 100644
--- a/lib/gitlab/markdown/sanitization_filter.rb
+++ b/lib/banzai/filter/sanitization_filter.rb
@@ -1,9 +1,9 @@
-require 'gitlab/markdown'
+require 'banzai'
require 'html/pipeline/filter'
require 'html/pipeline/sanitization_filter'
-module Gitlab
- module Markdown
+module Banzai
+ module Filter
# Sanitize HTML
#
# Extends HTML::Pipeline::SanitizationFilter with a custom whitelist.
@@ -11,7 +11,7 @@ module Gitlab
def whitelist
# Descriptions are more heavily sanitized, allowing only a few elements.
# See http://git.io/vkuAN
- if pipeline == :description
+ if context[:inline_sanitization]
whitelist = LIMITED
whitelist[:elements] -= %w(pre code img ol ul li)
else
@@ -25,10 +25,6 @@ module Gitlab
private
- def pipeline
- context[:pipeline] || :default
- end
-
def customized?(transformers)
transformers.last.source_location[0] == __FILE__
end
diff --git a/lib/gitlab/markdown/snippet_reference_filter.rb b/lib/banzai/filter/snippet_reference_filter.rb
index f7bd07c2a34..1ad5df96f85 100644
--- a/lib/gitlab/markdown/snippet_reference_filter.rb
+++ b/lib/banzai/filter/snippet_reference_filter.rb
@@ -1,7 +1,7 @@
-require 'gitlab/markdown'
+require 'banzai'
-module Gitlab
- module Markdown
+module Banzai
+ module Filter
# HTML filter that replaces snippet references with links. References to
# snippets that do not exist are ignored.
#
diff --git a/lib/gitlab/markdown/syntax_highlight_filter.rb b/lib/banzai/filter/syntax_highlight_filter.rb
index 8597e02f0de..c889cc1e97c 100644
--- a/lib/gitlab/markdown/syntax_highlight_filter.rb
+++ b/lib/banzai/filter/syntax_highlight_filter.rb
@@ -1,9 +1,9 @@
-require 'gitlab/markdown'
+require 'banzai'
require 'html/pipeline/filter'
require 'rouge/plugins/redcarpet'
-module Gitlab
- module Markdown
+module Banzai
+ module Filter
# HTML Filter to highlight fenced code blocks
#
class SyntaxHighlightFilter < HTML::Pipeline::Filter
diff --git a/lib/gitlab/markdown/table_of_contents_filter.rb b/lib/banzai/filter/table_of_contents_filter.rb
index bbb3bf7fc8b..9b3e67206d5 100644
--- a/lib/gitlab/markdown/table_of_contents_filter.rb
+++ b/lib/banzai/filter/table_of_contents_filter.rb
@@ -1,8 +1,8 @@
-require 'gitlab/markdown'
+require 'banzai'
require 'html/pipeline/filter'
-module Gitlab
- module Markdown
+module Banzai
+ module Filter
# HTML filter that adds an anchor child element to all Headers in a
# document, so that they can be linked to.
#
@@ -31,7 +31,7 @@ module Gitlab
id = text.downcase
id.gsub!(PUNCTUATION_REGEXP, '') # remove punctuation
- id.gsub!(' ', '-') # replace spaces with dash
+ id.tr!(' ', '-') # replace spaces with dash
id.squeeze!('-') # replace multiple dashes with one
uniq = (headers[id] > 0) ? "-#{headers[id]}" : ''
diff --git a/lib/gitlab/markdown/task_list_filter.rb b/lib/banzai/filter/task_list_filter.rb
index 2f133ae8500..bdf7c2ebdfc 100644
--- a/lib/gitlab/markdown/task_list_filter.rb
+++ b/lib/banzai/filter/task_list_filter.rb
@@ -1,8 +1,8 @@
-require 'gitlab/markdown'
+require 'banzai'
require 'task_list/filter'
-module Gitlab
- module Markdown
+module Banzai
+ module Filter
# Work around a bug in the default TaskList::Filter that adds a `task-list`
# class to every list element, regardless of whether or not it contains a
# task list.
diff --git a/lib/gitlab/markdown/upload_link_filter.rb b/lib/banzai/filter/upload_link_filter.rb
index fbada73ab86..1a1d0aad8ca 100644
--- a/lib/gitlab/markdown/upload_link_filter.rb
+++ b/lib/banzai/filter/upload_link_filter.rb
@@ -1,9 +1,9 @@
-require 'gitlab/markdown'
+require 'banzai'
require 'html/pipeline/filter'
require 'uri'
-module Gitlab
- module Markdown
+module Banzai
+ module Filter
# HTML filter that "fixes" relative upload links to files.
# Context options:
# :project (required) - Current project
diff --git a/lib/gitlab/markdown/user_reference_filter.rb b/lib/banzai/filter/user_reference_filter.rb
index ab5e1f6fe9e..67c24faf991 100644
--- a/lib/gitlab/markdown/user_reference_filter.rb
+++ b/lib/banzai/filter/user_reference_filter.rb
@@ -1,7 +1,7 @@
-require 'gitlab/markdown'
+require 'banzai'
-module Gitlab
- module Markdown
+module Banzai
+ module Filter
# HTML filter that replaces user or group references with links.
#
# A special `@all` reference is also supported.
@@ -52,6 +52,10 @@ module Gitlab
replace_text_nodes_matching(User.reference_pattern) do |content|
user_link_filter(content)
end
+
+ replace_link_nodes_with_href(User.reference_pattern) do |link, text|
+ user_link_filter(link, link_text: text)
+ end
end
# Replace `@user` user references in text with links to the referenced
@@ -61,12 +65,12 @@ module Gitlab
#
# Returns a String with `@user` references replaced with links. All links
# have `gfm` and `gfm-project_member` class names attached for styling.
- def user_link_filter(text)
+ def user_link_filter(text, link_text: nil)
self.class.references_in(text) do |match, username|
if username == 'all'
- link_to_all
+ link_to_all(link_text: link_text)
elsif namespace = Namespace.find_by(path: username)
- link_to_namespace(namespace) || match
+ link_to_namespace(namespace, link_text: link_text) || match
else
match
end
@@ -83,36 +87,36 @@ module Gitlab
reference_class(:project_member)
end
- def link_to_all
+ def link_to_all(link_text: nil)
project = context[:project]
url = urls.namespace_project_url(project.namespace, project,
only_path: context[:only_path])
data = data_attribute(project: project.id)
- text = User.reference_prefix + 'all'
+ text = link_text || User.reference_prefix + 'all'
link_tag(url, data, text)
end
- def link_to_namespace(namespace)
+ def link_to_namespace(namespace, link_text: nil)
if namespace.is_a?(Group)
- link_to_group(namespace.path, namespace)
+ link_to_group(namespace.path, namespace, link_text: link_text)
else
- link_to_user(namespace.path, namespace)
+ link_to_user(namespace.path, namespace, link_text: link_text)
end
end
- def link_to_group(group, namespace)
+ def link_to_group(group, namespace, link_text: nil)
url = urls.group_url(group, only_path: context[:only_path])
data = data_attribute(group: namespace.id)
- text = Group.reference_prefix + group
+ text = link_text || Group.reference_prefix + group
link_tag(url, data, text)
end
- def link_to_user(user, namespace)
+ def link_to_user(user, namespace, link_text: nil)
url = urls.user_url(user, only_path: context[:only_path])
data = data_attribute(user: namespace.owner_id)
- text = User.reference_prefix + user
+ text = link_text || User.reference_prefix + user
link_tag(url, data, text)
end
diff --git a/lib/banzai/lazy_reference.rb b/lib/banzai/lazy_reference.rb
new file mode 100644
index 00000000000..073ec5d9801
--- /dev/null
+++ b/lib/banzai/lazy_reference.rb
@@ -0,0 +1,27 @@
+require 'banzai'
+
+module Banzai
+ class LazyReference
+ def self.load(refs)
+ lazy_references, values = refs.partition { |ref| ref.is_a?(self) }
+
+ lazy_values = lazy_references.group_by(&:klass).flat_map do |klass, refs|
+ ids = refs.flat_map(&:ids)
+ klass.where(id: ids)
+ end
+
+ values + lazy_values
+ end
+
+ attr_reader :klass, :ids
+
+ def initialize(klass, ids)
+ @klass = klass
+ @ids = Array.wrap(ids).map(&:to_i)
+ end
+
+ def load
+ self.klass.where(id: self.ids)
+ end
+ end
+end
diff --git a/lib/banzai/pipeline.rb b/lib/banzai/pipeline.rb
new file mode 100644
index 00000000000..4e017809d9d
--- /dev/null
+++ b/lib/banzai/pipeline.rb
@@ -0,0 +1,10 @@
+require 'banzai'
+
+module Banzai
+ module Pipeline
+ def self.[](name)
+ name ||= :full
+ const_get("#{name.to_s.camelize}Pipeline")
+ end
+ end
+end
diff --git a/lib/banzai/pipeline/asciidoc_pipeline.rb b/lib/banzai/pipeline/asciidoc_pipeline.rb
new file mode 100644
index 00000000000..5e76a817be5
--- /dev/null
+++ b/lib/banzai/pipeline/asciidoc_pipeline.rb
@@ -0,0 +1,13 @@
+require 'banzai'
+
+module Banzai
+ module Pipeline
+ class AsciidocPipeline < BasePipeline
+ def self.filters
+ [
+ Filter::RelativeLinkFilter
+ ]
+ end
+ end
+ end
+end
diff --git a/lib/banzai/pipeline/atom_pipeline.rb b/lib/banzai/pipeline/atom_pipeline.rb
new file mode 100644
index 00000000000..957f352aec5
--- /dev/null
+++ b/lib/banzai/pipeline/atom_pipeline.rb
@@ -0,0 +1,14 @@
+require 'banzai'
+
+module Banzai
+ module Pipeline
+ class AtomPipeline < FullPipeline
+ def self.transform_context(context)
+ super(context).merge(
+ only_path: false,
+ xhtml: true
+ )
+ end
+ end
+ end
+end
diff --git a/lib/banzai/pipeline/base_pipeline.rb b/lib/banzai/pipeline/base_pipeline.rb
new file mode 100644
index 00000000000..cd30009e5c0
--- /dev/null
+++ b/lib/banzai/pipeline/base_pipeline.rb
@@ -0,0 +1,30 @@
+require 'banzai'
+require 'html/pipeline'
+
+module Banzai
+ module Pipeline
+ class BasePipeline
+ def self.filters
+ []
+ end
+
+ def self.transform_context(context)
+ context
+ end
+
+ def self.html_pipeline
+ @html_pipeline ||= HTML::Pipeline.new(filters)
+ end
+
+ class << self
+ %i(call to_document to_html).each do |meth|
+ define_method(meth) do |text, context|
+ context = transform_context(context)
+
+ html_pipeline.send(meth, text, context)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/banzai/pipeline/combined_pipeline.rb b/lib/banzai/pipeline/combined_pipeline.rb
new file mode 100644
index 00000000000..f3bf1809d18
--- /dev/null
+++ b/lib/banzai/pipeline/combined_pipeline.rb
@@ -0,0 +1,27 @@
+require 'banzai'
+
+module Banzai
+ module Pipeline
+ module CombinedPipeline
+ def self.new(*pipelines)
+ Class.new(BasePipeline) do
+ const_set :PIPELINES, pipelines
+
+ def self.pipelines
+ self::PIPELINES
+ end
+
+ def self.filters
+ pipelines.flat_map(&:filters)
+ end
+
+ def self.transform_context(context)
+ pipelines.reduce(context) do |context, pipeline|
+ pipeline.transform_context(context)
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/banzai/pipeline/description_pipeline.rb b/lib/banzai/pipeline/description_pipeline.rb
new file mode 100644
index 00000000000..94c2cb165a5
--- /dev/null
+++ b/lib/banzai/pipeline/description_pipeline.rb
@@ -0,0 +1,14 @@
+require 'banzai'
+
+module Banzai
+ module Pipeline
+ class DescriptionPipeline < FullPipeline
+ def self.transform_context(context)
+ super(context).merge(
+ # SanitizationFilter
+ inline_sanitization: true
+ )
+ end
+ end
+ end
+end
diff --git a/lib/banzai/pipeline/email_pipeline.rb b/lib/banzai/pipeline/email_pipeline.rb
new file mode 100644
index 00000000000..14356145a35
--- /dev/null
+++ b/lib/banzai/pipeline/email_pipeline.rb
@@ -0,0 +1,13 @@
+require 'banzai'
+
+module Banzai
+ module Pipeline
+ class EmailPipeline < FullPipeline
+ def self.transform_context(context)
+ super(context).merge(
+ only_path: false
+ )
+ end
+ end
+ end
+end
diff --git a/lib/banzai/pipeline/full_pipeline.rb b/lib/banzai/pipeline/full_pipeline.rb
new file mode 100644
index 00000000000..72395a5d50e
--- /dev/null
+++ b/lib/banzai/pipeline/full_pipeline.rb
@@ -0,0 +1,9 @@
+require 'banzai'
+
+module Banzai
+ module Pipeline
+ class FullPipeline < CombinedPipeline.new(PlainMarkdownPipeline, GfmPipeline)
+
+ end
+ end
+end
diff --git a/lib/banzai/pipeline/gfm_pipeline.rb b/lib/banzai/pipeline/gfm_pipeline.rb
new file mode 100644
index 00000000000..38750b55ec7
--- /dev/null
+++ b/lib/banzai/pipeline/gfm_pipeline.rb
@@ -0,0 +1,41 @@
+require 'banzai'
+
+module Banzai
+ module Pipeline
+ class GfmPipeline < BasePipeline
+ def self.filters
+ @filters ||= [
+ Filter::SyntaxHighlightFilter,
+ Filter::SanitizationFilter,
+
+ Filter::UploadLinkFilter,
+ Filter::EmojiFilter,
+ Filter::TableOfContentsFilter,
+ Filter::AutolinkFilter,
+ Filter::ExternalLinkFilter,
+
+ Filter::UserReferenceFilter,
+ Filter::IssueReferenceFilter,
+ Filter::ExternalIssueReferenceFilter,
+ Filter::MergeRequestReferenceFilter,
+ Filter::SnippetReferenceFilter,
+ Filter::CommitRangeReferenceFilter,
+ Filter::CommitReferenceFilter,
+ Filter::LabelReferenceFilter,
+
+ Filter::TaskListFilter
+ ]
+ end
+
+ def self.transform_context(context)
+ context.merge(
+ only_path: true,
+
+ # EmojiFilter
+ asset_host: Gitlab::Application.config.asset_host,
+ asset_root: Gitlab.config.gitlab.base_url
+ )
+ end
+ end
+ end
+end
diff --git a/lib/banzai/pipeline/note_pipeline.rb b/lib/banzai/pipeline/note_pipeline.rb
new file mode 100644
index 00000000000..89335143852
--- /dev/null
+++ b/lib/banzai/pipeline/note_pipeline.rb
@@ -0,0 +1,14 @@
+require 'banzai'
+
+module Banzai
+ module Pipeline
+ class NotePipeline < FullPipeline
+ def self.transform_context(context)
+ super(context).merge(
+ # TableOfContentsFilter
+ no_header_anchors: true
+ )
+ end
+ end
+ end
+end
diff --git a/lib/banzai/pipeline/plain_markdown_pipeline.rb b/lib/banzai/pipeline/plain_markdown_pipeline.rb
new file mode 100644
index 00000000000..998fd75daa2
--- /dev/null
+++ b/lib/banzai/pipeline/plain_markdown_pipeline.rb
@@ -0,0 +1,13 @@
+require 'banzai'
+
+module Banzai
+ module Pipeline
+ class PlainMarkdownPipeline < BasePipeline
+ def self.filters
+ [
+ Filter::MarkdownFilter
+ ]
+ end
+ end
+ end
+end
diff --git a/lib/banzai/pipeline/post_process_pipeline.rb b/lib/banzai/pipeline/post_process_pipeline.rb
new file mode 100644
index 00000000000..148f24b6ce1
--- /dev/null
+++ b/lib/banzai/pipeline/post_process_pipeline.rb
@@ -0,0 +1,20 @@
+require 'banzai'
+
+module Banzai
+ module Pipeline
+ class PostProcessPipeline < BasePipeline
+ def self.filters
+ [
+ Filter::RelativeLinkFilter,
+ Filter::RedactorFilter
+ ]
+ end
+
+ def self.transform_context(context)
+ context.merge(
+ post_process: true
+ )
+ end
+ end
+ end
+end
diff --git a/lib/banzai/pipeline/reference_extraction_pipeline.rb b/lib/banzai/pipeline/reference_extraction_pipeline.rb
new file mode 100644
index 00000000000..4f9bc9fcccc
--- /dev/null
+++ b/lib/banzai/pipeline/reference_extraction_pipeline.rb
@@ -0,0 +1,13 @@
+require 'banzai'
+
+module Banzai
+ module Pipeline
+ class ReferenceExtractionPipeline < BasePipeline
+ def self.filters
+ [
+ Filter::ReferenceGathererFilter
+ ]
+ end
+ end
+ end
+end
diff --git a/lib/banzai/pipeline/single_line_pipeline.rb b/lib/banzai/pipeline/single_line_pipeline.rb
new file mode 100644
index 00000000000..6725c9039a9
--- /dev/null
+++ b/lib/banzai/pipeline/single_line_pipeline.rb
@@ -0,0 +1,9 @@
+require 'banzai'
+
+module Banzai
+ module Pipeline
+ class SingleLinePipeline < GfmPipeline
+
+ end
+ end
+end
diff --git a/lib/banzai/reference_extractor.rb b/lib/banzai/reference_extractor.rb
new file mode 100644
index 00000000000..2c197d31898
--- /dev/null
+++ b/lib/banzai/reference_extractor.rb
@@ -0,0 +1,55 @@
+require 'banzai'
+
+module Banzai
+ # Extract possible GFM references from an arbitrary String for further processing.
+ class ReferenceExtractor
+ class << self
+ LAZY_KEY = :banzai_reference_extractor_lazy
+
+ def lazy?
+ Thread.current[LAZY_KEY]
+ end
+
+ def lazily(values = nil, &block)
+ return (values || block.call).uniq if lazy?
+
+ begin
+ Thread.current[LAZY_KEY] = true
+
+ values ||= block.call
+
+ Banzai::LazyReference.load(values.uniq).uniq
+ ensure
+ Thread.current[LAZY_KEY] = false
+ end
+ end
+ end
+
+ def initialize
+ @texts = []
+ end
+
+ def analyze(text, context = {})
+ @texts << Renderer.render(text, context)
+ end
+
+ def references(type, context = {})
+ filter = Banzai::Filter["#{type}_reference"]
+
+ context.merge!(
+ pipeline: :reference_extraction,
+
+ # ReferenceGathererFilter
+ reference_filter: filter
+ )
+
+ self.class.lazily do
+ @texts.flat_map do |html|
+ text_context = context.dup
+ result = Renderer.render_result(html, text_context)
+ result[:references][type]
+ end.uniq
+ end
+ end
+ end
+end
diff --git a/lib/banzai/renderer.rb b/lib/banzai/renderer.rb
new file mode 100644
index 00000000000..115ae914524
--- /dev/null
+++ b/lib/banzai/renderer.rb
@@ -0,0 +1,78 @@
+module Banzai
+ module Renderer
+ CACHE_ENABLED = false
+
+ # Convert a Markdown String into an HTML-safe String of HTML
+ #
+ # Note that while the returned HTML will have been sanitized of dangerous
+ # HTML, it may post a risk of information leakage if it's not also passed
+ # through `post_process`.
+ #
+ # Also note that the returned String is always HTML, not XHTML. Views
+ # requiring XHTML, such as Atom feeds, need to call `post_process` on the
+ # result, providing the appropriate `pipeline` option.
+ #
+ # markdown - Markdown String
+ # context - Hash of context options passed to our HTML Pipeline
+ #
+ # Returns an HTML-safe String
+ def self.render(text, context = {})
+ cache_key = context.delete(:cache_key)
+ cache_key = full_cache_key(cache_key, context[:pipeline])
+
+ if cache_key && CACHE_ENABLED
+ Rails.cache.fetch(cache_key) do
+ cacheless_render(text, context)
+ end
+ else
+ cacheless_render(text, context)
+ end
+ end
+
+ def self.render_result(text, context = {})
+ Pipeline[context[:pipeline]].call(text, context)
+ end
+
+ # Perform post-processing on an HTML String
+ #
+ # This method is used to perform state-dependent changes to a String of
+ # HTML, such as removing references that the current user doesn't have
+ # permission to make (`RedactorFilter`).
+ #
+ # html - String to process
+ # context - Hash of options to customize output
+ # :pipeline - Symbol pipeline type
+ # :project - Project
+ # :user - User object
+ #
+ # Returns an HTML-safe String
+ def self.post_process(html, context)
+ context = Pipeline[context[:pipeline]].transform_context(context)
+
+ pipeline = Pipeline[:post_process]
+ if context[:xhtml]
+ pipeline.to_document(html, context).to_html(save_with: Nokogiri::XML::Node::SaveOptions::AS_XHTML)
+ else
+ pipeline.to_html(html, context)
+ end.html_safe
+ end
+
+ private
+
+ def self.cacheless_render(text, context = {})
+ result = render_result(text, context)
+
+ output = result[:output]
+ if output.respond_to?(:to_html)
+ output.to_html
+ else
+ output.to_s
+ end
+ end
+
+ def self.full_cache_key(cache_key, pipeline_name)
+ return unless cache_key
+ ["banzai", *cache_key, pipeline_name || :full]
+ end
+ end
+end
diff --git a/lib/ci/api/api.rb b/lib/ci/api/api.rb
index 07e68216d7f..5c347e432b4 100644
--- a/lib/ci/api/api.rb
+++ b/lib/ci/api/api.rb
@@ -30,9 +30,7 @@ module Ci
helpers Gitlab::CurrentSettings
mount Builds
- mount Commits
mount Runners
- mount Projects
mount Triggers
end
end
diff --git a/lib/ci/api/builds.rb b/lib/ci/api/builds.rb
index 0a586672807..15faa6edd84 100644
--- a/lib/ci/api/builds.rb
+++ b/lib/ci/api/builds.rb
@@ -58,6 +58,7 @@ module Ci
# POST /builds/:id/artifacts/authorize
post ":id/artifacts/authorize" do
require_gitlab_workhorse!
+ not_allowed! unless Gitlab.config.artifacts.enabled
build = Ci::Build.find_by_id(params[:id])
not_found! unless build
authenticate_build_token!(build)
@@ -91,6 +92,7 @@ module Ci
# POST /builds/:id/artifacts
post ":id/artifacts" do
require_gitlab_workhorse!
+ not_allowed! unless Gitlab.config.artifacts.enabled
build = Ci::Build.find_by_id(params[:id])
not_found! unless build
authenticate_build_token!(build)
diff --git a/lib/ci/api/commits.rb b/lib/ci/api/commits.rb
deleted file mode 100644
index a60769d8305..00000000000
--- a/lib/ci/api/commits.rb
+++ /dev/null
@@ -1,66 +0,0 @@
-module Ci
- module API
- class Commits < Grape::API
- resource :commits do
- # Get list of commits per project
- #
- # Parameters:
- # project_id (required) - The ID of a project
- # project_token (requires) - Project token
- # page (optional)
- # per_page (optional) - items per request (default is 20)
- #
- get do
- required_attributes! [:project_id, :project_token]
- project = Ci::Project.find(params[:project_id])
- authenticate_project_token!(project)
-
- commits = project.commits.page(params[:page]).per(params[:per_page] || 20)
- present commits, with: Entities::CommitWithBuilds
- end
-
- # Create a commit
- #
- # Parameters:
- # project_id (required) - The ID of a project
- # project_token (requires) - Project token
- # data (required) - GitLab push data
- #
- # Sample GitLab push data:
- # {
- # "before": "95790bf891e76fee5e1747ab589903a6a1f80f22",
- # "after": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
- # "ref": "refs/heads/master",
- # "commits": [
- # {
- # "id": "b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327",
- # "message": "Update Catalan translation to e38cb41.",
- # "timestamp": "2011-12-12T14:27:31+02:00",
- # "url": "http://localhost/diaspora/commits/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327",
- # "author": {
- # "name": "Jordi Mallach",
- # "email": "jordi@softcatala.org",
- # }
- # }, .... more commits
- # ]
- # }
- #
- # Example Request:
- # POST /commits
- post do
- required_attributes! [:project_id, :data, :project_token]
- project = Ci::Project.find(params[:project_id])
- authenticate_project_token!(project)
- commit = Ci::CreateCommitService.new.execute(project, current_user, params[:data])
-
- if commit.persisted?
- present commit, with: Entities::CommitWithBuilds
- else
- errors = commit.errors.full_messages.join(", ")
- render_api_error!(errors, 400)
- end
- end
- end
- end
- end
-end
diff --git a/lib/ci/api/entities.rb b/lib/ci/api/entities.rb
index 750f421872d..e4ac0545ea2 100644
--- a/lib/ci/api/entities.rb
+++ b/lib/ci/api/entities.rb
@@ -37,15 +37,6 @@ module Ci
expose :id, :token
end
- class Project < Grape::Entity
- expose :id, :name, :token, :default_ref, :gitlab_url, :path,
- :always_build, :polling_interval, :public, :ssh_url_to_repo, :gitlab_id
-
- expose :timeout do |model|
- model.timeout
- end
- end
-
class RunnerProject < Grape::Entity
expose :id, :project_id, :runner_id
end
diff --git a/lib/ci/api/helpers.rb b/lib/ci/api/helpers.rb
index 02502333756..443563c2e4a 100644
--- a/lib/ci/api/helpers.rb
+++ b/lib/ci/api/helpers.rb
@@ -6,22 +6,22 @@ module Ci
UPDATE_RUNNER_EVERY = 60
def authenticate_runners!
- forbidden! unless params[:token] == GitlabCi::REGISTRATION_TOKEN
+ forbidden! unless runner_registration_token_valid?
end
def authenticate_runner!
forbidden! unless current_runner
end
- def authenticate_project_token!(project)
- forbidden! unless project.valid_token?(params[:project_token])
- end
-
def authenticate_build_token!(build)
token = (params[BUILD_TOKEN_PARAM] || env[BUILD_TOKEN_HEADER]).to_s
forbidden! unless token && build.valid_token?(token)
end
+ def runner_registration_token_valid?
+ params[:token] == current_application_settings.ensure_runners_registration_token
+ end
+
def update_runner_last_contact
# Use a random threshold to prevent beating DB updates
contacted_at_max_age = UPDATE_RUNNER_EVERY + Random.rand(UPDATE_RUNNER_EVERY)
diff --git a/lib/ci/api/projects.rb b/lib/ci/api/projects.rb
deleted file mode 100644
index d719ad9e8d5..00000000000
--- a/lib/ci/api/projects.rb
+++ /dev/null
@@ -1,195 +0,0 @@
-module Ci
- module API
- # Projects API
- class Projects < Grape::API
- before { authenticate! }
-
- resource :projects do
- # Register new webhook for project
- #
- # Parameters
- # project_id (required) - The ID of a project
- # web_hook (required) - WebHook URL
- # Example Request
- # POST /projects/:project_id/webhooks
- post ":project_id/webhooks" do
- required_attributes! [:web_hook]
-
- project = Ci::Project.find(params[:project_id])
-
- unauthorized! unless can?(current_user, :admin_project, project.gl_project)
-
- web_hook = project.web_hooks.new({ url: params[:web_hook] })
-
- if web_hook.save
- present web_hook, with: Entities::WebHook
- else
- errors = web_hook.errors.full_messages.join(", ")
- render_api_error!(errors, 400)
- end
- end
-
- # Retrieve all Gitlab CI projects that the user has access to
- #
- # Example Request:
- # GET /projects
- get do
- gitlab_projects = current_user.authorized_projects
- gitlab_projects = filter_projects(gitlab_projects)
- gitlab_projects = paginate gitlab_projects
-
- ids = gitlab_projects.map { |project| project.id }
-
- projects = Ci::Project.where("gitlab_id IN (?)", ids).load
- present projects, with: Entities::Project
- end
-
- # Retrieve all Gitlab CI projects that the user owns
- #
- # Example Request:
- # GET /projects/owned
- get "owned" do
- gitlab_projects = current_user.owned_projects
- gitlab_projects = filter_projects(gitlab_projects)
- gitlab_projects = paginate gitlab_projects
-
- ids = gitlab_projects.map { |project| project.id }
-
- projects = Ci::Project.where("gitlab_id IN (?)", ids).load
- present projects, with: Entities::Project
- end
-
- # Retrieve info for a Gitlab CI project
- #
- # Parameters:
- # id (required) - The ID of a project
- # Example Request:
- # GET /projects/:id
- get ":id" do
- project = Ci::Project.find(params[:id])
- unauthorized! unless can?(current_user, :read_project, project.gl_project)
-
- present project, with: Entities::Project
- end
-
- # Create Gitlab CI project using Gitlab project info
- #
- # Parameters:
- # gitlab_id (required) - The gitlab id of the project
- # default_ref - The branch to run against (defaults to `master`)
- # Example Request:
- # POST /projects
- post do
- required_attributes! [:gitlab_id]
-
- filtered_params = {
- gitlab_id: params[:gitlab_id],
- # we accept gitlab_url for backward compatibility for a while (added to 7.11)
- default_ref: params[:default_ref] || 'master'
- }
-
- project = Ci::Project.new(filtered_params)
- project.build_missing_services
-
- if project.save
- present project, with: Entities::Project
- else
- errors = project.errors.full_messages.join(", ")
- render_api_error!(errors, 400)
- end
- end
-
- # Update a Gitlab CI project
- #
- # Parameters:
- # id (required) - The ID of a project
- # default_ref - The branch to run against (defaults to `master`)
- # Example Request:
- # PUT /projects/:id
- put ":id" do
- project = Ci::Project.find(params[:id])
-
- unauthorized! unless can?(current_user, :admin_project, project.gl_project)
-
- attrs = attributes_for_keys [:default_ref]
-
- if project.update_attributes(attrs)
- present project, with: Entities::Project
- else
- errors = project.errors.full_messages.join(", ")
- render_api_error!(errors, 400)
- end
- end
-
- # Remove a Gitlab CI project
- #
- # Parameters:
- # id (required) - The ID of a project
- # Example Request:
- # DELETE /projects/:id
- delete ":id" do
- project = Ci::Project.find(params[:id])
-
- unauthorized! unless can?(current_user, :admin_project, project.gl_project)
-
- project.destroy
- end
-
- # Link a Gitlab CI project to a runner
- #
- # Parameters:
- # id (required) - The ID of a CI project
- # runner_id (required) - The ID of a runner
- # Example Request:
- # POST /projects/:id/runners/:runner_id
- post ":id/runners/:runner_id" do
- project = Ci::Project.find(params[:id])
- runner = Ci::Runner.find(params[:runner_id])
-
- unauthorized! unless can?(current_user, :admin_project, project.gl_project)
-
- options = {
- project_id: project.id,
- runner_id: runner.id
- }
-
- runner_project = Ci::RunnerProject.new(options)
-
- if runner_project.save
- present runner_project, with: Entities::RunnerProject
- else
- errors = project.errors.full_messages.join(", ")
- render_api_error!(errors, 400)
- end
- end
-
- # Remove a Gitlab CI project from a runner
- #
- # Parameters:
- # id (required) - The ID of a CI project
- # runner_id (required) - The ID of a runner
- # Example Request:
- # DELETE /projects/:id/runners/:runner_id
- delete ":id/runners/:runner_id" do
- project = Ci::Project.find(params[:id])
- runner = Ci::Runner.find(params[:runner_id])
-
- unauthorized! unless can?(current_user, :admin_project, project.gl_project)
-
- options = {
- project_id: project.id,
- runner_id: runner.id
- }
-
- runner_project = Ci::RunnerProject.find_by(options)
-
- if runner_project.present?
- runner_project.destroy
- else
- not_found!
- end
- end
- end
- end
- end
-end
diff --git a/lib/ci/api/runners.rb b/lib/ci/api/runners.rb
index 1466fe4356e..bfc14fe7a6b 100644
--- a/lib/ci/api/runners.rb
+++ b/lib/ci/api/runners.rb
@@ -3,17 +3,6 @@ module Ci
# Runners API
class Runners < Grape::API
resource :runners do
- # Get list of all available runners
- #
- # Example Request:
- # GET /runners
- get do
- authenticate!
- runners = Ci::Runner.all
-
- present runners, with: Entities::Runner
- end
-
# Delete runner
# Parameters:
# token (required) - The unique token of runner
@@ -40,14 +29,14 @@ module Ci
required_attributes! [:token]
runner =
- if params[:token] == GitlabCi::REGISTRATION_TOKEN
+ if runner_registration_token_valid?
# Create shared runner. Requires admin access
Ci::Runner.create(
description: params[:description],
tag_list: params[:tag_list],
is_shared: true
)
- elsif project = Ci::Project.find_by(token: params[:token])
+ elsif project = Project.find_by(runners_token: params[:token])
# Create a specific runner for project.
project.runners.create(
description: params[:description],
diff --git a/lib/ci/api/triggers.rb b/lib/ci/api/triggers.rb
index 40907d6db54..63b42113513 100644
--- a/lib/ci/api/triggers.rb
+++ b/lib/ci/api/triggers.rb
@@ -14,7 +14,7 @@ module Ci
post ":id/refs/:ref/trigger" do
required_attributes! [:token]
- project = Ci::Project.find(params[:id])
+ project = Project.find_by(ci_id: params[:id].to_i)
trigger = Ci::Trigger.find_by_token(params[:token].to_s)
not_found! unless project && trigger
unauthorized! unless trigger.project == project
diff --git a/lib/ci/charts.rb b/lib/ci/charts.rb
index 5ff7407c6fe..d53bdcbd0f2 100644
--- a/lib/ci/charts.rb
+++ b/lib/ci/charts.rb
@@ -60,7 +60,7 @@ module Ci
class BuildTime < Chart
def collect
- commits = project.commits.last(30)
+ commits = project.ci_commits.last(30)
commits.each do |commit|
@labels << commit.short_sha
diff --git a/lib/ci/current_settings.rb b/lib/ci/current_settings.rb
deleted file mode 100644
index fd78b024970..00000000000
--- a/lib/ci/current_settings.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-module Ci
- module CurrentSettings
- def current_application_settings
- key = :ci_current_application_settings
-
- RequestStore.store[key] ||= begin
- if ActiveRecord::Base.connected? && ActiveRecord::Base.connection.table_exists?('ci_application_settings')
- Ci::ApplicationSetting.current || Ci::ApplicationSetting.create_from_defaults
- else
- fake_application_settings
- end
- end
- end
-
- def fake_application_settings
- OpenStruct.new(
- all_broken_builds: Ci::Settings.gitlab_ci['all_broken_builds'],
- add_pusher: Ci::Settings.gitlab_ci['add_pusher'],
- )
- end
- end
-end
diff --git a/lib/ci/git.rb b/lib/ci/git.rb
deleted file mode 100644
index 7acc3f38edb..00000000000
--- a/lib/ci/git.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-module Ci
- module Git
- BLANK_SHA = '0' * 40
- end
-end
diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb
index 3beafcad117..bcdfd38d292 100644
--- a/lib/ci/gitlab_ci_yaml_processor.rb
+++ b/lib/ci/gitlab_ci_yaml_processor.rb
@@ -10,7 +10,7 @@ module Ci
attr_reader :before_script, :image, :services, :variables, :path, :cache
def initialize(config, path = nil)
- @config = YAML.load(config)
+ @config = YAML.safe_load(config, [Symbol])
@path = path
unless @config.is_a? Hash
@@ -132,26 +132,36 @@ module Ci
end
def validate_job!(name, job)
+ validate_job_name!(name)
+ validate_job_keys!(name, job)
+ validate_job_types!(name, job)
+
+ validate_job_stage!(name, job) if job[:stage]
+ validate_job_cache!(name, job) if job[:cache]
+ validate_job_artifacts!(name, job) if job[:artifacts]
+ end
+
+ private
+
+ def validate_job_name!(name)
if name.blank? || !validate_string(name)
raise ValidationError, "job name should be non-empty string"
end
+ end
+ def validate_job_keys!(name, job)
job.keys.each do |key|
unless ALLOWED_JOB_KEYS.include? key
raise ValidationError, "#{name} job: unknown parameter #{key}"
end
end
+ end
+ def validate_job_types!(name, job)
if !validate_string(job[:script]) && !validate_array_of_strings(job[:script])
raise ValidationError, "#{name} job: script should be a string or an array of a strings"
end
- if job[:stage]
- unless job[:stage].is_a?(String) && job[:stage].in?(stages)
- raise ValidationError, "#{name} job: stage parameter should be #{stages.join(", ")}"
- end
- end
-
if job[:image] && !validate_string(job[:image])
raise ValidationError, "#{name} job: image should be a string"
end
@@ -172,36 +182,40 @@ module Ci
raise ValidationError, "#{name} job: except parameter should be an array of strings"
end
- if job[:cache]
- if job[:cache][:untracked] && !validate_boolean(job[:cache][:untracked])
- raise ValidationError, "#{name} job: cache:untracked parameter should be an boolean"
- end
-
- if job[:cache][:paths] && !validate_array_of_strings(job[:cache][:paths])
- raise ValidationError, "#{name} job: cache:paths parameter should be an array of strings"
- end
+ if job[:allow_failure] && !validate_boolean(job[:allow_failure])
+ raise ValidationError, "#{name} job: allow_failure parameter should be an boolean"
end
- if job[:artifacts]
- if job[:artifacts][:untracked] && !validate_boolean(job[:artifacts][:untracked])
- raise ValidationError, "#{name} job: artifacts:untracked parameter should be an boolean"
- end
+ if job[:when] && !job[:when].in?(%w(on_success on_failure always))
+ raise ValidationError, "#{name} job: when parameter should be on_success, on_failure or always"
+ end
+ end
- if job[:artifacts][:paths] && !validate_array_of_strings(job[:artifacts][:paths])
- raise ValidationError, "#{name} job: artifacts:paths parameter should be an array of strings"
- end
+ def validate_job_stage!(name, job)
+ unless job[:stage].is_a?(String) && job[:stage].in?(stages)
+ raise ValidationError, "#{name} job: stage parameter should be #{stages.join(", ")}"
end
+ end
- if job[:allow_failure] && !validate_boolean(job[:allow_failure])
- raise ValidationError, "#{name} job: allow_failure parameter should be an boolean"
+ def validate_job_cache!(name, job)
+ if job[:cache][:untracked] && !validate_boolean(job[:cache][:untracked])
+ raise ValidationError, "#{name} job: cache:untracked parameter should be an boolean"
end
- if job[:when] && !job[:when].in?(%w(on_success on_failure always))
- raise ValidationError, "#{name} job: when parameter should be on_success, on_failure or always"
+ if job[:cache][:paths] && !validate_array_of_strings(job[:cache][:paths])
+ raise ValidationError, "#{name} job: cache:paths parameter should be an array of strings"
end
end
- private
+ def validate_job_artifacts!(name, job)
+ if job[:artifacts][:untracked] && !validate_boolean(job[:artifacts][:untracked])
+ raise ValidationError, "#{name} job: artifacts:untracked parameter should be an boolean"
+ end
+
+ if job[:artifacts][:paths] && !validate_array_of_strings(job[:artifacts][:paths])
+ raise ValidationError, "#{name} job: artifacts:paths parameter should be an array of strings"
+ end
+ end
def validate_array_of_strings(values)
values.is_a?(Array) && values.all? { |value| validate_string(value) }
diff --git a/lib/ci/scheduler.rb b/lib/ci/scheduler.rb
deleted file mode 100644
index ee0958f4be1..00000000000
--- a/lib/ci/scheduler.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-module Ci
- class Scheduler
- def perform
- projects = Ci::Project.where(always_build: true).all
- projects.each do |project|
- last_commit = project.commits.last
- next unless last_commit && last_commit.last_build
-
- interval = project.polling_interval
- if (last_commit.last_build.created_at + interval.hours) < Time.now
- last_commit.retry
- end
- end
- end
- end
-end
diff --git a/lib/email_validator.rb b/lib/email_validator.rb
deleted file mode 100644
index f509f0a5843..00000000000
--- a/lib/email_validator.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-# Based on https://github.com/balexand/email_validator
-#
-# Extended to use only strict mode with following allowed characters:
-# ' - apostrophe
-#
-# See http://www.remote.org/jochen/mail/info/chars.html
-#
-class EmailValidator < ActiveModel::EachValidator
- @@default_options = {}
-
- def self.default_options
- @@default_options
- end
-
- def validate_each(record, attribute, value)
- options = @@default_options.merge(self.options)
- unless value =~ /\A\s*([-a-z0-9+._']{1,64})@((?:[-a-z0-9]+\.)+[a-z]{2,})\s*\z/i
- record.errors.add(attribute, options[:message] || :invalid)
- end
- end
-end
diff --git a/lib/gitlab/asciidoc.rb b/lib/gitlab/asciidoc.rb
index bf33e5b1b1e..b203b9d70e4 100644
--- a/lib/gitlab/asciidoc.rb
+++ b/lib/gitlab/asciidoc.rb
@@ -1,14 +1,10 @@
require 'asciidoctor'
-require 'html/pipeline'
module Gitlab
# Parser/renderer for the AsciiDoc format that uses Asciidoctor and filters
# the resulting HTML through HTML pipeline filters.
module Asciidoc
- # Provide autoload paths for filters to prevent a circular dependency error
- autoload :RelativeLinkFilter, 'gitlab/markdown/relative_link_filter'
-
DEFAULT_ADOC_ATTRS = [
'showtitle', 'idprefix=user-content-', 'idseparator=-', 'env=gitlab',
'env-gitlab', 'source-highlighter=html-pipeline'
@@ -24,13 +20,11 @@ module Gitlab
# :requested_path
# :ref
# asciidoc_opts - a Hash of options to pass to the Asciidoctor converter
- # html_opts - a Hash of options for HTML output:
- # :xhtml - output XHTML instead of HTML
#
- def self.render(input, context, asciidoc_opts = {}, html_opts = {})
- asciidoc_opts = asciidoc_opts.reverse_merge(
+ def self.render(input, context, asciidoc_opts = {})
+ asciidoc_opts.reverse_merge!(
safe: :secure,
- backend: html_opts[:xhtml] ? :xhtml5 : :html5,
+ backend: :html5,
attributes: []
)
asciidoc_opts[:attributes].unshift(*DEFAULT_ADOC_ATTRS)
@@ -38,23 +32,10 @@ module Gitlab
html = ::Asciidoctor.convert(input, asciidoc_opts)
if context[:project]
- result = HTML::Pipeline.new(filters).call(html, context)
-
- save_opts = html_opts[:xhtml] ?
- Nokogiri::XML::Node::SaveOptions::AS_XHTML : 0
-
- html = result[:output].to_html(save_with: save_opts)
+ html = Banzai.render(html, context.merge(pipeline: :asciidoc))
end
html.html_safe
end
-
- private
-
- def self.filters
- [
- Gitlab::Markdown::RelativeLinkFilter
- ]
- end
end
end
diff --git a/lib/gitlab/backend/grack_auth.rb b/lib/gitlab/backend/grack_auth.rb
index 0d156047ff0..cdcaae8094c 100644
--- a/lib/gitlab/backend/grack_auth.rb
+++ b/lib/gitlab/backend/grack_auth.rb
@@ -77,7 +77,9 @@ module Grack
if project && matched_login.present? && git_cmd == 'git-upload-pack'
underscored_service = matched_login['s'].underscore
- if Service.available_services_names.include?(underscored_service)
+ if underscored_service == 'gitlab_ci'
+ return project && project.valid_build_token?(password)
+ elsif Service.available_services_names.include?(underscored_service)
service_method = "#{underscored_service}_service"
service = project.send(service_method)
diff --git a/lib/gitlab/backend/shell.rb b/lib/gitlab/backend/shell.rb
index 87ac30b5ffe..459e3d6bcdb 100644
--- a/lib/gitlab/backend/shell.rb
+++ b/lib/gitlab/backend/shell.rb
@@ -2,7 +2,7 @@ module Gitlab
class Shell
class Error < StandardError; end
- class KeyAdder < Struct.new(:io)
+ KeyAdder = Struct.new(:io) do
def add_key(id, key)
key.gsub!(/[[:space:]]+/, ' ').strip!
io.puts("#{id}\t#{key}")
diff --git a/lib/gitlab/bitbucket_import/project_creator.rb b/lib/gitlab/bitbucket_import/project_creator.rb
index 35e34d033e0..03aac1a025a 100644
--- a/lib/gitlab/bitbucket_import/project_creator.rb
+++ b/lib/gitlab/bitbucket_import/project_creator.rb
@@ -11,7 +11,8 @@ module Gitlab
end
def execute
- project = ::Projects::CreateService.new(current_user,
+ project = ::Projects::CreateService.new(
+ current_user,
name: repo["name"],
path: repo["slug"],
description: repo["description"],
diff --git a/lib/gitlab/blacklist.rb b/lib/gitlab/blacklist.rb
deleted file mode 100644
index 43145e0ee1b..00000000000
--- a/lib/gitlab/blacklist.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-module Gitlab
- module Blacklist
- extend self
-
- def path
- %w(
- admin
- dashboard
- files
- groups
- help
- profile
- projects
- search
- public
- assets
- u
- s
- teams
- merge_requests
- issues
- users
- snippets
- services
- repository
- hooks
- notes
- unsubscribes
- all
- ci
- )
- end
- end
-end
diff --git a/lib/gitlab/build_data_builder.rb b/lib/gitlab/build_data_builder.rb
new file mode 100644
index 00000000000..86bfa0a4378
--- /dev/null
+++ b/lib/gitlab/build_data_builder.rb
@@ -0,0 +1,64 @@
+module Gitlab
+ class BuildDataBuilder
+ class << self
+ def build(build)
+ project = build.project
+ commit = build.commit
+ user = build.user
+
+ data = {
+ object_kind: 'build',
+
+ ref: build.ref,
+ tag: build.tag,
+ before_sha: build.before_sha,
+ sha: build.sha,
+
+ # TODO: should this be not prefixed with build_?
+ # Leaving this way to have backward compatibility
+ build_id: build.id,
+ build_name: build.name,
+ build_stage: build.stage,
+ build_status: build.status,
+ build_started_at: build.started_at,
+ build_finished_at: build.finished_at,
+ build_duration: build.duration,
+
+ # TODO: do we still need it?
+ project_id: project.id,
+ project_name: project.name_with_namespace,
+
+ user: {
+ id: user.try(:id),
+ name: user.try(:name),
+ email: user.try(:email),
+ },
+
+ commit: {
+ id: commit.id,
+ sha: commit.sha,
+ message: commit.git_commit_message,
+ author_name: commit.git_author_name,
+ author_email: commit.git_author_email,
+ status: commit.status,
+ duration: commit.duration,
+ started_at: commit.started_at,
+ finished_at: commit.finished_at,
+ },
+
+ repository: {
+ name: project.name,
+ url: project.url_to_repo,
+ description: project.description,
+ homepage: project.web_url,
+ git_http_url: project.http_url_to_repo,
+ git_ssh_url: project.ssh_url_to_repo,
+ visibility_level: project.visibility_level
+ },
+ }
+
+ data
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/closing_issue_extractor.rb b/lib/gitlab/closing_issue_extractor.rb
index aeec595782c..9bef9037ad6 100644
--- a/lib/gitlab/closing_issue_extractor.rb
+++ b/lib/gitlab/closing_issue_extractor.rb
@@ -1,6 +1,12 @@
module Gitlab
class ClosingIssueExtractor
- ISSUE_CLOSING_REGEX = Regexp.new(Gitlab.config.gitlab.issue_closing_pattern)
+ ISSUE_CLOSING_REGEX = begin
+ link_pattern = URI.regexp(%w(http https))
+
+ pattern = Gitlab.config.gitlab.issue_closing_pattern
+ pattern = pattern.sub('%{issue_ref}', "(?:(?:#{link_pattern})|(?:#{Issue.reference_pattern}))")
+ Regexp.new(pattern).freeze
+ end
def initialize(project, current_user = nil)
@extractor = Gitlab::ReferenceExtractor.new(project, current_user)
@@ -9,10 +15,12 @@ module Gitlab
def closed_by_message(message)
return [] if message.nil?
- closing_statements = message.scan(ISSUE_CLOSING_REGEX).
- map { |ref| ref[0] }.join(" ")
+ closing_statements = []
+ message.scan(ISSUE_CLOSING_REGEX) do
+ closing_statements << Regexp.last_match[0]
+ end
- @extractor.analyze(closing_statements)
+ @extractor.analyze(closing_statements.join(" "))
@extractor.issues
end
diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb
index 2d3e32d9539..46a4ef0e31f 100644
--- a/lib/gitlab/current_settings.rb
+++ b/lib/gitlab/current_settings.rb
@@ -25,7 +25,7 @@ module Gitlab
session_expire_delay: Settings.gitlab['session_expire_delay'],
import_sources: Settings.gitlab['import_sources'],
shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'],
- max_artifacts_size: Ci::Settings.gitlab_ci['max_artifacts_size'],
+ max_artifacts_size: Settings.artifacts['max_size'],
)
end
diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb
index 71f37f1fef8..de77a6fbff1 100644
--- a/lib/gitlab/database.rb
+++ b/lib/gitlab/database.rb
@@ -7,5 +7,23 @@ module Gitlab
def self.postgresql?
ActiveRecord::Base.connection.adapter_name.downcase == 'postgresql'
end
+
+ def true_value
+ case ActiveRecord::Base.connection.adapter_name.downcase
+ when 'postgresql'
+ "'t'"
+ else
+ 1
+ end
+ end
+
+ def false_value
+ case ActiveRecord::Base.connection.adapter_name.downcase
+ when 'postgresql'
+ "'f'"
+ else
+ 0
+ end
+ end
end
end
diff --git a/lib/gitlab/diff/file.rb b/lib/gitlab/diff/file.rb
index 142058aa69d..79061cd0141 100644
--- a/lib/gitlab/diff/file.rb
+++ b/lib/gitlab/diff/file.rb
@@ -46,11 +46,11 @@ module Gitlab
end
def added_lines
- diff_lines.select(&:added?).size
+ diff_lines.count(&:added?)
end
def removed_lines
- diff_lines.select(&:removed?).size
+ diff_lines.count(&:removed?)
end
end
end
diff --git a/lib/gitlab/email/message/repository_push.rb b/lib/gitlab/email/message/repository_push.rb
new file mode 100644
index 00000000000..a2eb7a70bd2
--- /dev/null
+++ b/lib/gitlab/email/message/repository_push.rb
@@ -0,0 +1,137 @@
+module Gitlab
+ module Email
+ module Message
+ class RepositoryPush
+ attr_accessor :recipient
+ attr_reader :author_id, :ref, :action
+
+ include Gitlab::Application.routes.url_helpers
+
+ delegate :namespace, :name_with_namespace, to: :project, prefix: :project
+ delegate :name, to: :author, prefix: :author
+
+ def initialize(notify, project_id, recipient, opts = {})
+ raise ArgumentError, 'Missing options: author_id, ref, action' unless
+ opts[:author_id] && opts[:ref] && opts[:action]
+
+ @notify = notify
+ @project_id = project_id
+ @recipient = recipient
+ @opts = opts.dup
+
+ @author_id = @opts.delete(:author_id)
+ @ref = @opts.delete(:ref)
+ @action = @opts.delete(:action)
+ end
+
+ def project
+ @project ||= Project.find(@project_id)
+ end
+
+ def author
+ @author ||= User.find(@author_id)
+ end
+
+ def commits
+ @commits ||= (Commit.decorate(compare.commits, project) if compare)
+ end
+
+ def diffs
+ @diffs ||= (compare.diffs if compare)
+ end
+
+ def diffs_count
+ diffs.count if diffs
+ end
+
+ def compare
+ @opts[:compare]
+ end
+
+ def compare_timeout
+ compare.timeout if compare
+ end
+
+ def reverse_compare?
+ @opts[:reverse_compare] || false
+ end
+
+ def disable_diffs?
+ @opts[:disable_diffs] || false
+ end
+
+ def send_from_committer_email?
+ @opts[:send_from_committer_email] || false
+ end
+
+ def action_name
+ @action_name ||=
+ case @action
+ when :create
+ 'pushed new'
+ when :delete
+ 'deleted'
+ else
+ 'pushed to'
+ end
+ end
+
+ def ref_name
+ @ref_name ||= Gitlab::Git.ref_name(@ref)
+ end
+
+ def ref_type
+ @ref_type ||= Gitlab::Git.tag_ref?(@ref) ? 'tag' : 'branch'
+ end
+
+ def target_url
+ if @action == :push && commits
+ if commits.length > 1
+ namespace_project_compare_url(project_namespace,
+ project,
+ from: Commit.new(compare.base, project),
+ to: Commit.new(compare.head, project))
+ else
+ namespace_project_commit_url(project_namespace,
+ project, commits.first)
+ end
+ else
+ unless @action == :delete
+ namespace_project_tree_url(project_namespace,
+ project, ref_name)
+ end
+ end
+ end
+
+ def reply_to
+ if send_from_committer_email? && @notify.can_send_from_user_email?(author)
+ author.email
+ else
+ Gitlab.config.gitlab.email_reply_to
+ end
+ end
+
+ def subject
+ subject_text = '[Git]'
+ subject_text << "[#{project.path_with_namespace}]"
+ subject_text << "[#{ref_name}]" if @action == :push
+ subject_text << ' '
+
+ if @action == :push && commits
+ if commits.length > 1
+ subject_text << "Deleted " if reverse_compare?
+ subject_text << "#{commits.length} commits: #{commits.first.title}"
+ else
+ subject_text << "Deleted 1 commit: " if reverse_compare?
+ subject_text << commits.first.title
+ end
+ else
+ subject_action = action_name.dup
+ subject_action[0] = subject_action[0].capitalize
+ subject_text << "#{subject_action} #{ref_type} #{ref_name}"
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/fogbugz_import/importer.rb b/lib/gitlab/fogbugz_import/importer.rb
index 496256700b8..403ebeec474 100644
--- a/lib/gitlab/fogbugz_import/importer.rb
+++ b/lib/gitlab/fogbugz_import/importer.rb
@@ -199,7 +199,7 @@ module Gitlab
s = s.gsub(/^#/, "\\#")
s = s.gsub(/^-/, "\\-")
s = s.gsub("`", "\\~")
- s = s.gsub("\r", "")
+ s = s.delete("\r")
s = s.gsub("\n", " \n")
s
end
diff --git a/lib/gitlab/fogbugz_import/project_creator.rb b/lib/gitlab/fogbugz_import/project_creator.rb
index 8b1b6f48ed5..e0163499e30 100644
--- a/lib/gitlab/fogbugz_import/project_creator.rb
+++ b/lib/gitlab/fogbugz_import/project_creator.rb
@@ -12,7 +12,8 @@ module Gitlab
end
def execute
- project = ::Projects::CreateService.new(current_user,
+ project = ::Projects::CreateService.new(
+ current_user,
name: repo.safe_name,
path: repo.path,
namespace: namespace,
diff --git a/lib/gitlab/git/hook.rb b/lib/gitlab/git/hook.rb
index dd393fe09d2..07b856ca64c 100644
--- a/lib/gitlab/git/hook.rb
+++ b/lib/gitlab/git/hook.rb
@@ -16,6 +16,17 @@ module Gitlab
def trigger(gl_id, oldrev, newrev, ref)
return true unless exists?
+ case name
+ when "pre-receive", "post-receive"
+ call_receive_hook(gl_id, oldrev, newrev, ref)
+ when "update"
+ call_update_hook(gl_id, oldrev, newrev, ref)
+ end
+ end
+
+ private
+
+ def call_receive_hook(gl_id, oldrev, newrev, ref)
changes = [oldrev, newrev, ref].join(" ")
# function will return true if succesful
@@ -54,6 +65,12 @@ module Gitlab
exit_status
end
+
+ def call_update_hook(gl_id, oldrev, newrev, ref)
+ Dir.chdir(repo_path) do
+ system({ 'GL_ID' => gl_id }, path, ref, oldrev, newrev)
+ end
+ end
end
end
end
diff --git a/lib/gitlab/github_import/client.rb b/lib/gitlab/github_import/client.rb
index 270cbcd9ccd..74d1529e1ff 100644
--- a/lib/gitlab/github_import/client.rb
+++ b/lib/gitlab/github_import/client.rb
@@ -46,7 +46,7 @@ module Gitlab
end
def github_options
- OmniAuth::Strategies::GitHub.default_options[:client_options].symbolize_keys
+ OmniAuth::Strategies::GitHub.default_options[:client_options].to_h.symbolize_keys
end
end
end
diff --git a/lib/gitlab/gitlab_import/client.rb b/lib/gitlab/gitlab_import/client.rb
index 9c00896c913..86fb6c51765 100644
--- a/lib/gitlab/gitlab_import/client.rb
+++ b/lib/gitlab/gitlab_import/client.rb
@@ -75,7 +75,7 @@ module Gitlab
end
def gitlab_options
- OmniAuth::Strategies::GitLab.default_options[:client_options].symbolize_keys
+ OmniAuth::Strategies::GitLab.default_options[:client_options].to_h.symbolize_keys
end
end
end
diff --git a/lib/gitlab/gitlab_import/project_creator.rb b/lib/gitlab/gitlab_import/project_creator.rb
index d9452de6a50..7baaadb813c 100644
--- a/lib/gitlab/gitlab_import/project_creator.rb
+++ b/lib/gitlab/gitlab_import/project_creator.rb
@@ -11,7 +11,8 @@ module Gitlab
end
def execute
- project = ::Projects::CreateService.new(current_user,
+ project = ::Projects::CreateService.new(
+ current_user,
name: repo["name"],
path: repo["path"],
description: repo["description"],
diff --git a/lib/gitlab/gitorious_import/project_creator.rb b/lib/gitlab/gitorious_import/project_creator.rb
index cc9a91c91f4..8e22aa9286d 100644
--- a/lib/gitlab/gitorious_import/project_creator.rb
+++ b/lib/gitlab/gitorious_import/project_creator.rb
@@ -10,7 +10,8 @@ module Gitlab
end
def execute
- ::Projects::CreateService.new(current_user,
+ ::Projects::CreateService.new(
+ current_user,
name: repo.name,
path: repo.path,
description: repo.description,
diff --git a/lib/gitlab/google_code_import/importer.rb b/lib/gitlab/google_code_import/importer.rb
index 87fee28dc01..62da327931f 100644
--- a/lib/gitlab/google_code_import/importer.rb
+++ b/lib/gitlab/google_code_import/importer.rb
@@ -171,8 +171,6 @@ module Gitlab
when /\AMilestone:/
"#fee3ff"
- when *@closed_statuses.map { |s| nice_status_name(s) }
- "#cfcfcf"
when "Status: New"
"#428bca"
when "Status: Accepted"
@@ -199,6 +197,8 @@ module Gitlab
"#8e44ad"
when "Type: Other"
"#7f8c8d"
+ when *@closed_statuses.map { |s| nice_status_name(s) }
+ "#cfcfcf"
else
"#e2e2e2"
end
@@ -227,7 +227,7 @@ module Gitlab
s = s.gsub("`", "\\`")
# Carriage returns make me sad
- s = s.gsub("\r", "")
+ s = s.delete("\r")
# Markdown ignores single newlines, but we need them as <br />.
s = s.gsub("\n", " \n")
diff --git a/lib/gitlab/google_code_import/project_creator.rb b/lib/gitlab/google_code_import/project_creator.rb
index 1cb7d16aeb3..87821c23460 100644
--- a/lib/gitlab/google_code_import/project_creator.rb
+++ b/lib/gitlab/google_code_import/project_creator.rb
@@ -11,7 +11,8 @@ module Gitlab
end
def execute
- project = ::Projects::CreateService.new(current_user,
+ project = ::Projects::CreateService.new(
+ current_user,
name: repo.name,
path: repo.name,
description: repo.summary,
diff --git a/lib/gitlab/ldap/access.rb b/lib/gitlab/ldap/access.rb
index 16ff03c38d4..c438a3d167b 100644
--- a/lib/gitlab/ldap/access.rb
+++ b/lib/gitlab/ldap/access.rb
@@ -37,13 +37,15 @@ module Gitlab
# Block user in GitLab if he/she was blocked in AD
if Gitlab::LDAP::Person.disabled_via_active_directory?(user.ldap_identity.extern_uid, adapter)
- user.block unless user.blocked?
+ user.block
false
else
user.activate if user.blocked? && !ldap_config.block_auto_created_users
true
end
else
+ # Block the user if they no longer exist in LDAP/AD
+ user.block
false
end
rescue
diff --git a/lib/gitlab/lfs/response.rb b/lib/gitlab/lfs/response.rb
index 4202c786466..9d9617761b3 100644
--- a/lib/gitlab/lfs/response.rb
+++ b/lib/gitlab/lfs/response.rb
@@ -10,23 +10,9 @@ module Gitlab
@request = request
end
- # Return a response for a download request
- # Can be a response to:
- # Request from a user to get the file
- # Request from gitlab-workhorse which file to serve to the user
- def render_download_hypermedia_response(oid)
- render_response_to_download do
- if check_download_accept_header?
- render_lfs_download_hypermedia(oid)
- else
- render_not_found
- end
- end
- end
-
def render_download_object_response(oid)
render_response_to_download do
- if check_download_sendfile_header? && check_download_accept_header?
+ if check_download_sendfile_header?
render_lfs_sendfile(oid)
else
render_not_found
@@ -34,20 +20,15 @@ module Gitlab
end
end
- def render_lfs_api_auth
- render_response_to_push do
- request_body = JSON.parse(@request.body.read)
- return render_not_found if request_body.empty? || request_body['objects'].empty?
-
- response = build_response(request_body['objects'])
- [
- 200,
- {
- "Content-Type" => "application/json; charset=utf-8",
- "Cache-Control" => "private",
- },
- [JSON.dump(response)]
- ]
+ def render_batch_operation_response
+ request_body = JSON.parse(@request.body.read)
+ case request_body["operation"]
+ when "download"
+ render_batch_download(request_body)
+ when "upload"
+ render_batch_upload(request_body)
+ else
+ render_not_found
end
end
@@ -71,13 +52,24 @@ module Gitlab
end
end
+ def render_unsupported_deprecated_api
+ [
+ 501,
+ { "Content-Type" => "application/json; charset=utf-8" },
+ [JSON.dump({
+ 'message' => 'Server supports batch API only, please update your Git LFS client to version 1.0.1 and up.',
+ 'documentation_url' => "#{Gitlab.config.gitlab.url}/help",
+ })]
+ ]
+ end
+
private
def render_not_enabled
[
501,
{
- "Content-Type" => "application/vnd.git-lfs+json",
+ "Content-Type" => "application/json; charset=utf-8",
},
[JSON.dump({
'message' => 'Git LFS is not enabled on this GitLab server, contact your admin.',
@@ -142,18 +134,35 @@ module Gitlab
end
end
- def render_lfs_download_hypermedia(oid)
- return render_not_found unless oid.present?
+ def render_batch_upload(body)
+ return render_not_found if body.empty? || body['objects'].nil?
- lfs_object = object_for_download(oid)
- if lfs_object
+ render_response_to_push do
+ response = build_upload_batch_response(body['objects'])
[
200,
- { "Content-Type" => "application/vnd.git-lfs+json" },
- [JSON.dump(download_hypermedia(oid))]
+ {
+ "Content-Type" => "application/json; charset=utf-8",
+ "Cache-Control" => "private",
+ },
+ [JSON.dump(response)]
+ ]
+ end
+ end
+
+ def render_batch_download(body)
+ return render_not_found if body.empty? || body['objects'].nil?
+
+ render_response_to_download do
+ response = build_download_batch_response(body['objects'])
+ [
+ 200,
+ {
+ "Content-Type" => "application/json; charset=utf-8",
+ "Cache-Control" => "private",
+ },
+ [JSON.dump(response)]
]
- else
- render_not_found
end
end
@@ -199,10 +208,6 @@ module Gitlab
@env['HTTP_X_SENDFILE_TYPE'].to_s == "X-Sendfile"
end
- def check_download_accept_header?
- @env['HTTP_ACCEPT'].to_s == "application/vnd.git-lfs+json; charset=utf-8"
- end
-
def user_can_fetch?
# Check user access against the project they used to initiate the pull
@user.can?(:download_code, @origin_project)
@@ -215,7 +220,7 @@ module Gitlab
def storage_project(project)
if project.forked?
- project.forked_from_project
+ storage_project(project.forked_from_project)
else
project
end
@@ -255,7 +260,7 @@ module Gitlab
end
def link_to_project(object)
- if object && !object.projects.exists?(@project)
+ if object && !object.projects.exists?(@project.id)
object.projects << @project
object.save
end
@@ -266,42 +271,56 @@ module Gitlab
@project.lfs_objects.where(oid: objects_oids).pluck(:oid).to_set
end
- def build_response(objects)
+ def build_upload_batch_response(objects)
selected_objects = select_existing_objects(objects)
- upload_hypermedia(objects, selected_objects)
+ upload_hypermedia_links(objects, selected_objects)
end
- def download_hypermedia(oid)
- {
- '_links' => {
- 'download' =>
- {
- 'href' => "#{@origin_project.http_url_to_repo}/gitlab-lfs/objects/#{oid}",
- 'header' => {
- 'Accept' => "application/vnd.git-lfs+json; charset=utf-8",
- 'Authorization' => @env['HTTP_AUTHORIZATION']
- }.compact
- }
- }
- }
+ def build_download_batch_response(objects)
+ selected_objects = select_existing_objects(objects)
+
+ download_hypermedia_links(objects, selected_objects)
end
- def upload_hypermedia(all_objects, existing_objects)
+ def download_hypermedia_links(all_objects, existing_objects)
all_objects.each do |object|
- object['_links'] = hypermedia_links(object) unless existing_objects.include?(object['oid'])
+ if existing_objects.include?(object['oid'])
+ object['actions'] = {
+ 'download' => {
+ 'href' => "#{@origin_project.http_url_to_repo}/gitlab-lfs/objects/#{object['oid']}",
+ 'header' => {
+ 'Authorization' => @env['HTTP_AUTHORIZATION']
+ }.compact
+ }
+ }
+ else
+ object['error'] = {
+ 'code' => 404,
+ 'message' => "Object does not exist on the server or you don't have permissions to access it",
+ }
+ end
end
{ 'objects' => all_objects }
end
- def hypermedia_links(object)
- {
- "upload" => {
- 'href' => "#{@origin_project.http_url_to_repo}/gitlab-lfs/objects/#{object['oid']}/#{object['size']}",
- 'header' => { 'Authorization' => @env['HTTP_AUTHORIZATION'] }
- }.compact
- }
+ def upload_hypermedia_links(all_objects, existing_objects)
+ all_objects.each do |object|
+ # generate actions only for non-existing objects
+ next if existing_objects.include?(object['oid'])
+
+ object['actions'] = {
+ 'upload' => {
+ 'href' => "#{@origin_project.http_url_to_repo}/gitlab-lfs/objects/#{object['oid']}/#{object['size']}",
+ 'header' => {
+ 'Authorization' => @env['HTTP_AUTHORIZATION']
+ }.compact
+ }
+ }
+ end
+
+ { 'objects' => all_objects }
end
end
end
diff --git a/lib/gitlab/lfs/router.rb b/lib/gitlab/lfs/router.rb
index 4809e834984..78d02891102 100644
--- a/lib/gitlab/lfs/router.rb
+++ b/lib/gitlab/lfs/router.rb
@@ -34,7 +34,7 @@ module Gitlab
case path_match[1]
when "info/lfs"
- lfs.render_download_hypermedia_response(oid)
+ lfs.render_unsupported_deprecated_api
when "gitlab-lfs"
lfs.render_download_object_response(oid)
else
@@ -48,7 +48,9 @@ module Gitlab
# Check for Batch API
if post_path[0].ends_with?("/info/lfs/objects/batch")
- lfs.render_lfs_api_auth
+ lfs.render_batch_operation_response
+ elsif post_path[0].ends_with?("/info/lfs/objects")
+ lfs.render_unsupported_deprecated_api
else
nil
end
diff --git a/lib/gitlab/markdown.rb b/lib/gitlab/markdown.rb
deleted file mode 100644
index b082bfc434b..00000000000
--- a/lib/gitlab/markdown.rb
+++ /dev/null
@@ -1,200 +0,0 @@
-require 'html/pipeline'
-
-module Gitlab
- # Custom parser for GitLab-flavored Markdown
- #
- # See the files in `lib/gitlab/markdown/` for specific processing information.
- module Markdown
- # Convert a Markdown String into an HTML-safe String of HTML
- #
- # Note that while the returned HTML will have been sanitized of dangerous
- # HTML, it may post a risk of information leakage if it's not also passed
- # through `post_process`.
- #
- # Also note that the returned String is always HTML, not XHTML. Views
- # requiring XHTML, such as Atom feeds, need to call `post_process` on the
- # result, providing the appropriate `pipeline` option.
- #
- # markdown - Markdown String
- # context - Hash of context options passed to our HTML Pipeline
- #
- # Returns an HTML-safe String
- def self.render(markdown, context = {})
- html = renderer.render(markdown)
- html = gfm(html, context)
-
- html.html_safe
- end
-
- # Convert a Markdown String into HTML without going through the HTML
- # Pipeline.
- #
- # Note that because the pipeline is skipped, SanitizationFilter is as well.
- # Do not output the result of this method to the user.
- #
- # markdown - Markdown String
- #
- # Returns a String
- def self.render_without_gfm(markdown)
- renderer.render(markdown)
- end
-
- # Perform post-processing on an HTML String
- #
- # This method is used to perform state-dependent changes to a String of
- # HTML, such as removing references that the current user doesn't have
- # permission to make (`RedactorFilter`).
- #
- # html - String to process
- # options - Hash of options to customize output
- # :pipeline - Symbol pipeline type
- # :project - Project
- # :user - User object
- #
- # Returns an HTML-safe String
- def self.post_process(html, options)
- context = {
- project: options[:project],
- current_user: options[:user]
- }
- doc = post_processor.to_document(html, context)
-
- if options[:pipeline] == :atom
- doc.to_html(save_with: Nokogiri::XML::Node::SaveOptions::AS_XHTML)
- else
- doc.to_html
- end.html_safe
- end
-
- # Provide autoload paths for filters to prevent a circular dependency error
- autoload :AutolinkFilter, 'gitlab/markdown/autolink_filter'
- autoload :CommitRangeReferenceFilter, 'gitlab/markdown/commit_range_reference_filter'
- autoload :CommitReferenceFilter, 'gitlab/markdown/commit_reference_filter'
- autoload :EmojiFilter, 'gitlab/markdown/emoji_filter'
- autoload :ExternalIssueReferenceFilter, 'gitlab/markdown/external_issue_reference_filter'
- autoload :ExternalLinkFilter, 'gitlab/markdown/external_link_filter'
- autoload :IssueReferenceFilter, 'gitlab/markdown/issue_reference_filter'
- autoload :LabelReferenceFilter, 'gitlab/markdown/label_reference_filter'
- autoload :MergeRequestReferenceFilter, 'gitlab/markdown/merge_request_reference_filter'
- autoload :RedactorFilter, 'gitlab/markdown/redactor_filter'
- autoload :RelativeLinkFilter, 'gitlab/markdown/relative_link_filter'
- autoload :SanitizationFilter, 'gitlab/markdown/sanitization_filter'
- autoload :SnippetReferenceFilter, 'gitlab/markdown/snippet_reference_filter'
- autoload :SyntaxHighlightFilter, 'gitlab/markdown/syntax_highlight_filter'
- autoload :TableOfContentsFilter, 'gitlab/markdown/table_of_contents_filter'
- autoload :TaskListFilter, 'gitlab/markdown/task_list_filter'
- autoload :UserReferenceFilter, 'gitlab/markdown/user_reference_filter'
- autoload :UploadLinkFilter, 'gitlab/markdown/upload_link_filter'
-
- # Public: Parse the provided HTML with GitLab-Flavored Markdown
- #
- # html - HTML String
- # options - A Hash of options used to customize output (default: {})
- # :no_header_anchors - Disable header anchors in TableOfContentsFilter
- # :path - Current path String
- # :pipeline - Symbol pipeline type
- # :project - Current Project object
- # :project_wiki - Current ProjectWiki object
- # :ref - Current ref String
- #
- # Returns an HTML-safe String
- def self.gfm(html, options = {})
- return '' unless html.present?
-
- @pipeline ||= HTML::Pipeline.new(filters)
-
- context = {
- # SanitizationFilter
- pipeline: options[:pipeline],
-
- # EmojiFilter
- asset_host: Gitlab::Application.config.asset_host,
- asset_root: Gitlab.config.gitlab.base_url,
-
- # ReferenceFilter
- only_path: only_path_pipeline?(options[:pipeline]),
- project: options[:project],
-
- # RelativeLinkFilter
- project_wiki: options[:project_wiki],
- ref: options[:ref],
- requested_path: options[:path],
-
- # TableOfContentsFilter
- no_header_anchors: options[:no_header_anchors]
- }
-
- @pipeline.to_html(html, context).html_safe
- end
-
- private
-
- # Check if a pipeline enables the `only_path` context option
- #
- # Returns Boolean
- def self.only_path_pipeline?(pipeline)
- case pipeline
- when :atom, :email
- false
- else
- true
- end
- end
-
- def self.redcarpet_options
- # https://github.com/vmg/redcarpet#and-its-like-really-simple-to-use
- @redcarpet_options ||= {
- fenced_code_blocks: true,
- footnotes: true,
- lax_spacing: true,
- no_intra_emphasis: true,
- space_after_headers: true,
- strikethrough: true,
- superscript: true,
- tables: true
- }.freeze
- end
-
- def self.renderer
- @markdown ||= begin
- renderer = Redcarpet::Render::HTML.new
- Redcarpet::Markdown.new(renderer, redcarpet_options)
- end
- end
-
- def self.post_processor
- @post_processor ||= HTML::Pipeline.new([Gitlab::Markdown::RedactorFilter])
- end
-
- # Filters used in our pipeline
- #
- # SanitizationFilter should come first so that all generated reference HTML
- # goes through untouched.
- #
- # See https://github.com/jch/html-pipeline#filters for more filters.
- def self.filters
- [
- Gitlab::Markdown::SyntaxHighlightFilter,
- Gitlab::Markdown::SanitizationFilter,
-
- Gitlab::Markdown::UploadLinkFilter,
- Gitlab::Markdown::RelativeLinkFilter,
- Gitlab::Markdown::EmojiFilter,
- Gitlab::Markdown::TableOfContentsFilter,
- Gitlab::Markdown::AutolinkFilter,
- Gitlab::Markdown::ExternalLinkFilter,
-
- Gitlab::Markdown::UserReferenceFilter,
- Gitlab::Markdown::IssueReferenceFilter,
- Gitlab::Markdown::ExternalIssueReferenceFilter,
- Gitlab::Markdown::MergeRequestReferenceFilter,
- Gitlab::Markdown::SnippetReferenceFilter,
- Gitlab::Markdown::CommitRangeReferenceFilter,
- Gitlab::Markdown::CommitReferenceFilter,
- Gitlab::Markdown::LabelReferenceFilter,
-
- Gitlab::Markdown::TaskListFilter
- ]
- end
- end
-end
diff --git a/lib/gitlab/markdown/abstract_reference_filter.rb b/lib/gitlab/markdown/abstract_reference_filter.rb
deleted file mode 100644
index fd5b7eb9332..00000000000
--- a/lib/gitlab/markdown/abstract_reference_filter.rb
+++ /dev/null
@@ -1,100 +0,0 @@
-require 'gitlab/markdown'
-
-module Gitlab
- module Markdown
- # Issues, Snippets and Merge Requests shares similar functionality in refernce filtering.
- # All this functionality moved to this class
- class AbstractReferenceFilter < ReferenceFilter
- include CrossProjectReference
-
- def self.object_class
- # Implement in child class
- # Example: MergeRequest
- end
-
- def self.object_name
- object_class.name.underscore
- end
-
- def self.object_sym
- object_name.to_sym
- end
-
- def self.data_reference
- "data-#{object_name.dasherize}"
- end
-
- # Public: Find references in text (like `!123` for merge requests)
- #
- # AnyReferenceFilter.references_in(text) do |match, object|
- # "<a href=...>PREFIX#{object}</a>"
- # end
- #
- # PREFIX - symbol that detects reference (like ! for merge requests)
- # object - reference object (snippet, merget request etc)
- # text - String text to search.
- #
- # Yields the String match, the Integer referenced object ID, and an optional String
- # of the external project reference.
- #
- # Returns a String replaced with the return of the block.
- def self.references_in(text)
- text.gsub(object_class.reference_pattern) do |match|
- yield match, $~[object_sym].to_i, $~[:project]
- end
- end
-
- def self.referenced_by(node)
- { object_sym => LazyReference.new(object_class, node.attr(data_reference)) }
- end
-
- delegate :object_class, :object_sym, :references_in, to: :class
-
- def find_object(project, id)
- # Implement in child class
- # Example: project.merge_requests.find
- end
-
- def url_for_object(object, project)
- # Implement in child class
- # Example: project_merge_request_url
- end
-
- def call
- replace_text_nodes_matching(object_class.reference_pattern) do |content|
- object_link_filter(content)
- end
- end
-
- # Replace references (like `!123` for merge requests) in text with links
- # to the referenced object's details page.
- #
- # text - String text to replace references in.
- #
- # Returns a String with references replaced with links. All links
- # have `gfm` and `gfm-OBJECT_NAME` class names attached for styling.
- def object_link_filter(text)
- references_in(text) do |match, id, project_ref|
- project = project_from_ref(project_ref)
-
- if project && object = find_object(project, id)
- title = escape_once("#{object_title}: #{object.title}")
- klass = reference_class(object_sym)
- data = data_attribute(project: project.id, object_sym => object.id)
- url = url_for_object(object, project)
-
- %(<a href="#{url}" #{data}
- title="#{title}"
- class="#{klass}">#{match}</a>)
- else
- match
- end
- end
- end
-
- def object_title
- object_class.name.titleize
- end
- end
- end
-end
diff --git a/lib/gitlab/markdown/commit_range_reference_filter.rb b/lib/gitlab/markdown/commit_range_reference_filter.rb
deleted file mode 100644
index e070edae0a4..00000000000
--- a/lib/gitlab/markdown/commit_range_reference_filter.rb
+++ /dev/null
@@ -1,92 +0,0 @@
-require 'gitlab/markdown'
-
-module Gitlab
- module Markdown
- # HTML filter that replaces commit range references with links.
- #
- # This filter supports cross-project references.
- class CommitRangeReferenceFilter < ReferenceFilter
- include CrossProjectReference
-
- # Public: Find commit range references in text
- #
- # CommitRangeReferenceFilter.references_in(text) do |match, commit_range, project_ref|
- # "<a href=...>#{commit_range}</a>"
- # end
- #
- # text - String text to search.
- #
- # Yields the String match, the String commit range, and an optional String
- # of the external project reference.
- #
- # Returns a String replaced with the return of the block.
- def self.references_in(text)
- text.gsub(CommitRange.reference_pattern) do |match|
- yield match, $~[:commit_range], $~[:project]
- end
- end
-
- def self.referenced_by(node)
- project = Project.find(node.attr("data-project")) rescue nil
- return unless project
-
- id = node.attr("data-commit-range")
- range = CommitRange.new(id, project)
-
- return unless range.valid_commits?
-
- { commit_range: range }
- end
-
- def initialize(*args)
- super
-
- @commit_map = {}
- end
-
- def call
- replace_text_nodes_matching(CommitRange.reference_pattern) do |content|
- commit_range_link_filter(content)
- end
- end
-
- # Replace commit range references in text with links to compare the commit
- # ranges.
- #
- # text - String text to replace references in.
- #
- # Returns a String with commit range references replaced with links. All
- # links have `gfm` and `gfm-commit_range` class names attached for
- # styling.
- def commit_range_link_filter(text)
- self.class.references_in(text) do |match, id, project_ref|
- project = self.project_from_ref(project_ref)
-
- range = CommitRange.new(id, project)
-
- if range.valid_commits?
- url = url_for_commit_range(project, range)
-
- title = range.reference_title
- klass = reference_class(:commit_range)
- data = data_attribute(project: project.id, commit_range: id)
-
- project_ref += '@' if project_ref
-
- %(<a href="#{url}" #{data}
- title="#{title}"
- class="#{klass}">#{project_ref}#{range}</a>)
- else
- match
- end
- end
- end
-
- def url_for_commit_range(project, range)
- h = Gitlab::Application.routes.url_helpers
- h.namespace_project_compare_url(project.namespace, project,
- range.to_param.merge(only_path: context[:only_path]))
- end
- end
- end
-end
diff --git a/lib/gitlab/markdown/commit_reference_filter.rb b/lib/gitlab/markdown/commit_reference_filter.rb
deleted file mode 100644
index 8cdbeb1f9cf..00000000000
--- a/lib/gitlab/markdown/commit_reference_filter.rb
+++ /dev/null
@@ -1,88 +0,0 @@
-require 'gitlab/markdown'
-
-module Gitlab
- module Markdown
- # HTML filter that replaces commit references with links.
- #
- # This filter supports cross-project references.
- class CommitReferenceFilter < ReferenceFilter
- include CrossProjectReference
-
- # Public: Find commit references in text
- #
- # CommitReferenceFilter.references_in(text) do |match, commit, project_ref|
- # "<a href=...>#{commit}</a>"
- # end
- #
- # text - String text to search.
- #
- # Yields the String match, the String commit identifier, and an optional
- # String of the external project reference.
- #
- # Returns a String replaced with the return of the block.
- def self.references_in(text)
- text.gsub(Commit.reference_pattern) do |match|
- yield match, $~[:commit], $~[:project]
- end
- end
-
- def self.referenced_by(node)
- project = Project.find(node.attr("data-project")) rescue nil
- return unless project
-
- id = node.attr("data-commit")
- commit = commit_from_ref(project, id)
-
- return unless commit
-
- { commit: commit }
- end
-
- def call
- replace_text_nodes_matching(Commit.reference_pattern) do |content|
- commit_link_filter(content)
- end
- end
-
- # Replace commit references in text with links to the commit specified.
- #
- # text - String text to replace references in.
- #
- # Returns a String with commit references replaced with links. All links
- # have `gfm` and `gfm-commit` class names attached for styling.
- def commit_link_filter(text)
- self.class.references_in(text) do |match, id, project_ref|
- project = self.project_from_ref(project_ref)
-
- if commit = self.class.commit_from_ref(project, id)
- url = url_for_commit(project, commit)
-
- title = escape_once(commit.link_title)
- klass = reference_class(:commit)
- data = data_attribute(project: project.id, commit: id)
-
- project_ref += '@' if project_ref
-
- %(<a href="#{url}" #{data}
- title="#{title}"
- class="#{klass}">#{project_ref}#{commit.short_id}</a>)
- else
- match
- end
- end
- end
-
- def self.commit_from_ref(project, id)
- if project && project.valid_repo?
- project.commit(id)
- end
- end
-
- def url_for_commit(project, commit)
- h = Gitlab::Application.routes.url_helpers
- h.namespace_project_commit_url(project.namespace, project, commit,
- only_path: context[:only_path])
- end
- end
- end
-end
diff --git a/lib/gitlab/markdown/cross_project_reference.rb b/lib/gitlab/markdown/cross_project_reference.rb
deleted file mode 100644
index 6ab04a584b0..00000000000
--- a/lib/gitlab/markdown/cross_project_reference.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-require 'gitlab/markdown'
-
-module Gitlab
- module Markdown
- # Common methods for ReferenceFilters that support an optional cross-project
- # reference.
- module CrossProjectReference
- # Given a cross-project reference string, get the Project record
- #
- # Defaults to value of `context[:project]` if:
- # * No reference is given OR
- # * Reference given doesn't exist
- #
- # ref - String reference.
- #
- # Returns a Project, or nil if the reference can't be found
- def project_from_ref(ref)
- return context[:project] unless ref
-
- Project.find_with_namespace(ref)
- end
- end
- end
-end
diff --git a/lib/gitlab/markdown/pipeline.rb b/lib/gitlab/markdown/pipeline.rb
new file mode 100644
index 00000000000..8f3f43c0e91
--- /dev/null
+++ b/lib/gitlab/markdown/pipeline.rb
@@ -0,0 +1,34 @@
+require 'banzai'
+
+module Gitlab
+ module Markdown
+ class Pipeline
+ def self.[](name)
+ name ||= :full
+ const_get("#{name.to_s.camelize}Pipeline")
+ end
+
+ def self.filters
+ []
+ end
+
+ def self.transform_context(context)
+ context
+ end
+
+ def self.html_pipeline
+ @html_pipeline ||= HTML::Pipeline.new(filters)
+ end
+
+ class << self
+ %i(call to_document to_html).each do |meth|
+ define_method(meth) do |text, context|
+ context = transform_context(context)
+
+ html_pipeline.send(meth, text, context)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/o_auth/auth_hash.rb b/lib/gitlab/o_auth/auth_hash.rb
index d94b104bbf8..ba31599432b 100644
--- a/lib/gitlab/o_auth/auth_hash.rb
+++ b/lib/gitlab/o_auth/auth_hash.rb
@@ -62,7 +62,7 @@ module Gitlab
# Get the first part of the email address (before @)
# In addtion in removes illegal characters
def generate_username(email)
- email.match(/^[^@]*/)[0].parameterize
+ email.match(/^[^@]*/)[0].mb_chars.normalize(:kd).gsub(/[^\x00-\x7F]/,'').to_s
end
def generate_temporarily_email(username)
diff --git a/lib/gitlab/push_data_builder.rb b/lib/gitlab/push_data_builder.rb
index fa068d50763..4f9cdef3869 100644
--- a/lib/gitlab/push_data_builder.rb
+++ b/lib/gitlab/push_data_builder.rb
@@ -18,10 +18,7 @@ module Gitlab
# homepage: String,
# },
# commits: Array,
- # total_commits_count: Fixnum,
- # added: ["CHANGELOG"],
- # modified: [],
- # removed: ["tmp/file.txt"]
+ # total_commits_count: Fixnum
# }
#
def build(project, user, oldrev, newrev, ref, commits = [], message = nil)
@@ -33,11 +30,12 @@ module Gitlab
# For performance purposes maximum 20 latest commits
# will be passed as post receive hook data.
- commit_attrs = commits_limited.map(&:hook_attrs)
+ commit_attrs = commits_limited.map do |commit|
+ commit.hook_attrs(with_changed_files: true)
+ end
type = Gitlab::Git.tag_ref?(ref) ? "tag_push" : "push"
- repo_changes = repo_changes(project, newrev, oldrev)
# Hash to be passed as post_receive_data
data = {
object_kind: type,
@@ -60,10 +58,7 @@ module Gitlab
visibility_level: project.visibility_level
},
commits: commit_attrs,
- total_commits_count: commits_count,
- added: repo_changes[:added],
- modified: repo_changes[:modified],
- removed: repo_changes[:removed]
+ total_commits_count: commits_count
}
data
@@ -94,27 +89,6 @@ module Gitlab
newrev
end
end
-
- def repo_changes(project, newrev, oldrev)
- changes = { added: [], modified: [], removed: [] }
- compare_result = CompareService.new.
- execute(project, newrev, project, oldrev)
-
- if compare_result
- compare_result.diffs.each do |diff|
- case true
- when diff.deleted_file
- changes[:removed] << diff.old_path
- when diff.renamed_file, diff.new_file
- changes[:added] << diff.new_path
- else
- changes[:modified] << diff.new_path
- end
- end
- end
-
- changes
- end
end
end
end
diff --git a/lib/gitlab/reference_extractor.rb b/lib/gitlab/reference_extractor.rb
index da8df8a3025..42f7c26f3c4 100644
--- a/lib/gitlab/reference_extractor.rb
+++ b/lib/gitlab/reference_extractor.rb
@@ -1,73 +1,27 @@
-require 'gitlab/markdown'
+require 'banzai'
module Gitlab
# Extract possible GFM references from an arbitrary String for further processing.
- class ReferenceExtractor
- attr_accessor :project, :current_user, :load_lazy_references
+ class ReferenceExtractor < Banzai::ReferenceExtractor
+ attr_accessor :project, :current_user
- def initialize(project, current_user = nil, load_lazy_references: true)
+ def initialize(project, current_user = nil)
@project = project
@current_user = current_user
- @load_lazy_references = load_lazy_references
- end
- def analyze(text)
- references.clear
- @text = Gitlab::Markdown.render_without_gfm(text)
- end
+ @references = {}
- %i(user label issue merge_request snippet commit commit_range).each do |type|
- define_method("#{type}s") do
- references[type]
- end
+ super()
end
- private
-
- def references
- @references ||= Hash.new do |references, type|
- type = type.to_sym
- next references[type] if references.has_key?(type)
-
- references[type] = pipeline_result(type)
- end
+ def analyze(text, context = {})
+ super(text, context.merge(project: project))
end
- # Instantiate and call HTML::Pipeline with a single reference filter type,
- # returning the result
- #
- # filter_type - Symbol reference type (e.g., :commit, :issue, etc.)
- #
- # Returns the results Array for the requested filter type
- def pipeline_result(filter_type)
- return [] if @text.blank?
-
- klass = "#{filter_type.to_s.camelize}ReferenceFilter"
- filter = Gitlab::Markdown.const_get(klass)
-
- context = {
- project: project,
- current_user: current_user,
-
- # We don't actually care about the links generated
- only_path: true,
- ignore_blockquotes: true,
-
- # ReferenceGathererFilter
- load_lazy_references: false,
- reference_filter: filter
- }
-
- pipeline = HTML::Pipeline.new([filter, Gitlab::Markdown::ReferenceGathererFilter], context)
- result = pipeline.call(@text)
-
- values = result[:references][filter_type].uniq
-
- if @load_lazy_references
- values = Gitlab::Markdown::ReferenceFilter::LazyReference.load(values).uniq
+ %i(user label issue merge_request snippet commit commit_range).each do |type|
+ define_method("#{type}s") do
+ @references[type] ||= references(type, project: project, current_user: current_user)
end
-
- values
end
end
end
diff --git a/lib/gitlab/seeder.rb b/lib/gitlab/seeder.rb
index 31aa3528c4c..2ef0e982256 100644
--- a/lib/gitlab/seeder.rb
+++ b/lib/gitlab/seeder.rb
@@ -14,7 +14,7 @@ module Gitlab
def self.mute_mailer
code = <<-eos
-def Notify.delay
+def Notify.deliver_later
self
end
eos
diff --git a/lib/gitlab/sherlock/transaction.rb b/lib/gitlab/sherlock/transaction.rb
index d87a4c9bb4a..3489fb251b6 100644
--- a/lib/gitlab/sherlock/transaction.rb
+++ b/lib/gitlab/sherlock/transaction.rb
@@ -36,6 +36,11 @@ module Gitlab
@duration ||= started_at && finished_at ? finished_at - started_at : 0
end
+ # Returns the total query duration in seconds.
+ def query_duration
+ @query_duration ||= @queries.map { |q| q.duration }.inject(:+) / 1000.0
+ end
+
def to_param
@id
end
diff --git a/lib/omni_auth/request_forgery_protection.rb b/lib/omni_auth/request_forgery_protection.rb
index 3557522d3c9..69155131d8d 100644
--- a/lib/omni_auth/request_forgery_protection.rb
+++ b/lib/omni_auth/request_forgery_protection.rb
@@ -1,66 +1,21 @@
# Protects OmniAuth request phase against CSRF.
module OmniAuth
- # Based on ActionController::RequestForgeryProtection.
- class RequestForgeryProtection
- def initialize(env)
- @env = env
- end
-
- def request
- @request ||= ActionDispatch::Request.new(@env)
- end
-
- def session
- request.session
- end
-
- def reset_session
- request.reset_session
- end
-
- def params
- request.params
- end
-
- def call
- verify_authenticity_token
- end
+ module RequestForgeryProtection
+ class Controller < ActionController::Base
+ protect_from_forgery with: :exception
- def verify_authenticity_token
- if !verified_request?
- Rails.logger.warn "Can't verify CSRF token authenticity" if Rails.logger
- handle_unverified_request
+ def index
+ head :ok
end
end
- private
-
- def protect_against_forgery?
- ApplicationController.allow_forgery_protection
- end
-
- def request_forgery_protection_token
- ApplicationController.request_forgery_protection_token
- end
-
- def forgery_protection_strategy
- ApplicationController.forgery_protection_strategy
- end
-
- def verified_request?
- !protect_against_forgery? || request.get? || request.head? ||
- form_authenticity_token == params[request_forgery_protection_token] ||
- form_authenticity_token == request.headers['X-CSRF-Token']
- end
-
- def handle_unverified_request
- forgery_protection_strategy.new(self).handle_unverified_request
+ def self.app
+ @app ||= Controller.action(:index)
end
- # Sets the token value for the current session.
- def form_authenticity_token
- session[:_csrf_token] ||= SecureRandom.base64(32)
+ def self.call(env)
+ app.call(env)
end
end
end
diff --git a/lib/rouge/formatters/html_gitlab.rb b/lib/rouge/formatters/html_gitlab.rb
index 6762ca47c32..8c309efc7b8 100644
--- a/lib/rouge/formatters/html_gitlab.rb
+++ b/lib/rouge/formatters/html_gitlab.rb
@@ -39,7 +39,7 @@ module Rouge
lineanchorsid: 'L',
anchorlinenos: false,
inline_theme: nil
- )
+ )
@nowrap = nowrap
@cssclass = cssclass
@linenos = linenos
diff --git a/lib/support/init.d/gitlab b/lib/support/init.d/gitlab
index f0a6c2b30e9..aa2da92c138 100755
--- a/lib/support/init.d/gitlab
+++ b/lib/support/init.d/gitlab
@@ -33,12 +33,13 @@ app_user="git"
app_root="/home/$app_user/gitlab"
pid_path="$app_root/tmp/pids"
socket_path="$app_root/tmp/sockets"
+rails_socket="$socket_path/gitlab.socket"
web_server_pid_path="$pid_path/unicorn.pid"
sidekiq_pid_path="$pid_path/sidekiq.pid"
mail_room_enabled=false
mail_room_pid_path="$pid_path/mail_room.pid"
gitlab_workhorse_pid_path="$pid_path/gitlab-workhorse.pid"
-gitlab_workhorse_options="-listenUmask 0 -listenNetwork unix -listenAddr $socket_path/gitlab-workhorse.socket -authBackend http://127.0.0.1:8080"
+gitlab_workhorse_options="-listenUmask 0 -listenNetwork unix -listenAddr $socket_path/gitlab-workhorse.socket -authBackend http://127.0.0.1:8080 -authSocket $rails_socket -documentRoot $app_root/public"
gitlab_workhorse_log="$app_root/log/gitlab-workhorse.log"
shell_path="/bin/bash"
@@ -327,7 +328,7 @@ print_status() {
printf "The GitLab MailRoom email processor is \033[31mnot running\033[0m.\n"
fi
fi
- if [ "$web_status" = "0" ] && [ "$sidekiq_status" = "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" = "0" ]; }; then
+ if [ "$web_status" = "0" ] && [ "$sidekiq_status" = "0" ] && [ "$gitlab_workhorse_status" = "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" = "0" ]; }; then
printf "GitLab and all its components are \033[32mup and running\033[0m.\n"
fi
}
diff --git a/lib/support/init.d/gitlab.default.example b/lib/support/init.d/gitlab.default.example
index 79ae8e0ae55..7fc495db545 100755
--- a/lib/support/init.d/gitlab.default.example
+++ b/lib/support/init.d/gitlab.default.example
@@ -36,7 +36,7 @@ gitlab_workhorse_pid_path="$pid_path/gitlab-workhorse.pid"
# '-listenNetwork tcp -listenAddr localhost:8181'.
# The -authBackend setting tells gitlab-workhorse where it can reach
# Unicorn.
-gitlab_workhorse_options="-listenUmask 0 -listenNetwork unix -listenAddr $socket_path/gitlab-workhorse.socket -authBackend http://127.0.0.1:8080"
+gitlab_workhorse_options="-listenUmask 0 -listenNetwork unix -listenAddr $socket_path/gitlab-workhorse.socket -authBackend http://127.0.0.1:8080 -authSocket $socket_path/gitlab.socket -documentRoot $app_root/public"
gitlab_workhorse_log="$app_root/log/gitlab-workhorse.log"
# mail_room_enabled specifies whether mail_room, which is used to process incoming email, is enabled.
diff --git a/lib/support/nginx/gitlab b/lib/support/nginx/gitlab
index 0cf5292b290..fc5475c4eef 100644
--- a/lib/support/nginx/gitlab
+++ b/lib/support/nginx/gitlab
@@ -10,34 +10,12 @@
## If you change this file in a Merge Request, please also create
## a Merge Request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests
##
-##################################
-## CHUNKED TRANSFER ##
-##################################
-##
-## It is a known issue that Git-over-HTTP requires chunked transfer encoding [0]
-## which is not supported by Nginx < 1.3.9 [1]. As a result, pushing a large object
-## with Git (i.e. a single large file) can lead to a 411 error. In theory you can get
-## around this by tweaking this configuration file and either:
-## - installing an old version of Nginx with the chunkin module [2] compiled in, or
-## - using a newer version of Nginx.
-##
-## At the time of writing we do not know if either of these theoretical solutions works.
-## As a workaround users can use Git over SSH to push large files.
-##
-## [0] https://git.kernel.org/cgit/git/git.git/tree/Documentation/technical/http-protocol.txt#n99
-## [1] https://github.com/agentzh/chunkin-nginx-module#status
-## [2] https://github.com/agentzh/chunkin-nginx-module
-##
###################################
## configuration ##
###################################
##
## See installation.md#using-https for additional HTTPS configuration details.
-upstream gitlab {
- server unix:/home/git/gitlab/tmp/sockets/gitlab.socket fail_timeout=0;
-}
-
upstream gitlab-workhorse {
server unix:/home/git/gitlab/tmp/sockets/gitlab-workhorse.socket fail_timeout=0;
}
@@ -54,10 +32,6 @@ server {
server_tokens off; ## Don't show the nginx version number, a security best practice
root /home/git/gitlab/public;
- ## Increase this if you want to upload large attachments
- ## Or if you want to accept large git objects over http
- client_max_body_size 20m;
-
## See app/controllers/application_controller.rb for headers set
## Individual nginx logs for this GitLab vhost
@@ -65,97 +39,8 @@ server {
error_log /var/log/nginx/gitlab_error.log;
location / {
- ## Serve static files from defined root folder.
- ## @gitlab is a named location for the upstream fallback, see below.
- try_files $uri /index.html $uri.html @gitlab;
- }
-
- ## We route uploads through GitLab to prevent XSS and enforce access control.
- location /uploads/ {
- ## If you use HTTPS make sure you disable gzip compression
- ## to be safe against BREACH attack.
- # gzip off;
-
- ## https://github.com/gitlabhq/gitlabhq/issues/694
- ## Some requests take more than 30 seconds.
- proxy_read_timeout 300;
- proxy_connect_timeout 300;
- proxy_redirect off;
-
- proxy_set_header Host $http_host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
- proxy_set_header X-Frame-Options SAMEORIGIN;
-
- proxy_pass http://gitlab;
- }
-
- ## If a file, which is not found in the root folder is requested,
- ## then the proxy passes the request to the upsteam (gitlab unicorn).
- location @gitlab {
- ## If you use HTTPS make sure you disable gzip compression
- ## to be safe against BREACH attack.
- # gzip off;
-
- ## https://github.com/gitlabhq/gitlabhq/issues/694
- ## Some requests take more than 30 seconds.
- proxy_read_timeout 300;
- proxy_connect_timeout 300;
- proxy_redirect off;
-
- proxy_set_header Host $http_host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
- proxy_set_header X-Frame-Options SAMEORIGIN;
-
- proxy_pass http://gitlab;
- }
-
- location ~ ^/[\w\.-]+/[\w\.-]+/gitlab-lfs/objects {
- # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
- error_page 418 = @gitlab-workhorse;
- return 418;
- }
-
- location ~ ^/[\w\.-]+/[\w\.-]+/(info/refs|git-upload-pack|git-receive-pack)$ {
- # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
- error_page 418 = @gitlab-workhorse;
- return 418;
- }
-
- location ~ ^/[\w\.-]+/[\w\.-]+/repository/archive {
- # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
- error_page 418 = @gitlab-workhorse;
- return 418;
- }
-
- location ~ ^/api/v3/projects/.*/repository/archive {
- # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
- error_page 418 = @gitlab-workhorse;
- return 418;
- }
-
- # Build artifacts should be submitted to this location
- location ~ ^/[\w\.-]+/[\w\.-]+/builds/download {
- # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
- error_page 418 = @gitlab-workhorse;
- return 418;
- }
-
- # Build artifacts should be submitted to this location
- location ~ /ci/api/v1/builds/[0-9]+/artifacts {
- # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
- error_page 418 = @gitlab-workhorse;
- return 418;
- }
-
- location @gitlab-workhorse {
client_max_body_size 0;
- ## If you use HTTPS make sure you disable gzip compression
- ## to be safe against BREACH attack.
- # gzip off;
+ gzip off;
## https://github.com/gitlabhq/gitlabhq/issues/694
## Some requests take more than 30 seconds.
@@ -163,14 +48,7 @@ server {
proxy_connect_timeout 300;
proxy_redirect off;
- # Do not buffer Git HTTP responses
- proxy_buffering off;
-
- # The following settings only work with NGINX 1.7.11 or newer
- #
- # # Pass chunked request bodies to gitlab-workhorse as-is
- # proxy_request_buffering off;
- # proxy_http_version 1.1;
+ proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
@@ -179,18 +57,4 @@ server {
proxy_pass http://gitlab-workhorse;
}
-
- ## Enable gzip compression as per rails guide:
- ## http://guides.rubyonrails.org/asset_pipeline.html#gzip-compression
- ## WARNING: If you are using relative urls remove the block below
- ## See config/application.rb under "Relative url support" for the list of
- ## other files that need to be changed for relative url support
- location ~ ^/(assets)/ {
- root /home/git/gitlab/public;
- gzip_static on; # to serve pre-gzipped version
- expires max;
- add_header Cache-Control public;
- }
-
- error_page 502 /502.html;
}
diff --git a/lib/support/nginx/gitlab-ssl b/lib/support/nginx/gitlab-ssl
index 31a651c87fd..1e5f85413ec 100644
--- a/lib/support/nginx/gitlab-ssl
+++ b/lib/support/nginx/gitlab-ssl
@@ -14,34 +14,12 @@
## If you change this file in a Merge Request, please also create
## a Merge Request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests
##
-##################################
-## CHUNKED TRANSFER ##
-##################################
-##
-## It is a known issue that Git-over-HTTP requires chunked transfer encoding [0]
-## which is not supported by Nginx < 1.3.9 [1]. As a result, pushing a large object
-## with Git (i.e. a single large file) can lead to a 411 error. In theory you can get
-## around this by tweaking this configuration file and either:
-## - installing an old version of Nginx with the chunkin module [2] compiled in, or
-## - using a newer version of Nginx.
-##
-## At the time of writing we do not know if either of these theoretical solutions works.
-## As a workaround users can use Git over SSH to push large files.
-##
-## [0] https://git.kernel.org/cgit/git/git.git/tree/Documentation/technical/http-protocol.txt#n99
-## [1] https://github.com/agentzh/chunkin-nginx-module#status
-## [2] https://github.com/agentzh/chunkin-nginx-module
-##
###################################
## configuration ##
###################################
##
## See installation.md#using-https for additional HTTPS configuration details.
-upstream gitlab {
- server unix:/home/git/gitlab/tmp/sockets/gitlab.socket fail_timeout=0;
-}
-
upstream gitlab-workhorse {
server unix:/home/git/gitlab/tmp/sockets/gitlab-workhorse.socket fail_timeout=0;
}
@@ -56,12 +34,11 @@ server {
listen [::]:80 ipv6only=on default_server;
server_name YOUR_SERVER_FQDN; ## Replace this with something like gitlab.example.com
server_tokens off; ## Don't show the nginx version number, a security best practice
- return 301 https://$server_name$request_uri;
+ return 301 https://$http_host$request_uri;
access_log /var/log/nginx/gitlab_access.log;
error_log /var/log/nginx/gitlab_error.log;
}
-
## HTTPS host
server {
listen 0.0.0.0:443 ssl;
@@ -70,10 +47,6 @@ server {
server_tokens off; ## Don't show the nginx version number, a security best practice
root /home/git/gitlab/public;
- ## Increase this if you want to upload large attachments
- ## Or if you want to accept large git objects over http
- client_max_body_size 20m;
-
## Strong SSL Security
## https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html & https://cipherli.st/
ssl on;
@@ -110,98 +83,7 @@ server {
error_log /var/log/nginx/gitlab_error.log;
location / {
- ## Serve static files from defined root folder.
- ## @gitlab is a named location for the upstream fallback, see below.
- try_files $uri /index.html $uri.html @gitlab;
- }
-
- ## We route uploads through GitLab to prevent XSS and enforce access control.
- location /uploads/ {
- ## If you use HTTPS make sure you disable gzip compression
- ## to be safe against BREACH attack.
- gzip off;
-
- ## https://github.com/gitlabhq/gitlabhq/issues/694
- ## Some requests take more than 30 seconds.
- proxy_read_timeout 300;
- proxy_connect_timeout 300;
- proxy_redirect off;
-
- proxy_set_header Host $http_host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-Ssl on;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
- proxy_set_header X-Frame-Options SAMEORIGIN;
-
- proxy_pass http://gitlab;
- }
-
- ## If a file, which is not found in the root folder is requested,
- ## then the proxy passes the request to the upsteam (gitlab unicorn).
- location @gitlab {
- ## If you use HTTPS make sure you disable gzip compression
- ## to be safe against BREACH attack.
- gzip off;
-
- ## https://github.com/gitlabhq/gitlabhq/issues/694
- ## Some requests take more than 30 seconds.
- proxy_read_timeout 300;
- proxy_connect_timeout 300;
- proxy_redirect off;
-
- proxy_set_header Host $http_host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-Ssl on;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto $scheme;
- proxy_set_header X-Frame-Options SAMEORIGIN;
-
- proxy_pass http://gitlab;
- }
-
- location ~ ^/[\w\.-]+/[\w\.-]+/gitlab-lfs/objects {
- # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
- error_page 418 = @gitlab-workhorse;
- return 418;
- }
-
- location ~ ^/[\w\.-]+/[\w\.-]+/(info/refs|git-upload-pack|git-receive-pack)$ {
- # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
- error_page 418 = @gitlab-workhorse;
- return 418;
- }
-
- location ~ ^/[\w\.-]+/[\w\.-]+/repository/archive {
- # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
- error_page 418 = @gitlab-workhorse;
- return 418;
- }
-
- location ~ ^/api/v3/projects/.*/repository/archive {
- # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
- error_page 418 = @gitlab-workhorse;
- return 418;
- }
-
- # Build artifacts should be submitted to this location
- location ~ ^/[\w\.-]+/[\w\.-]+/builds/download {
- # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
- error_page 418 = @gitlab-workhorse;
- return 418;
- }
-
- # Build artifacts should be submitted to this location
- location ~ /ci/api/v1/builds/[0-9]+/artifacts {
- # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
- error_page 418 = @gitlab-workhorse;
- return 418;
- }
-
- location @gitlab-workhorse {
client_max_body_size 0;
- ## If you use HTTPS make sure you disable gzip compression
- ## to be safe against BREACH attack.
gzip off;
## https://github.com/gitlabhq/gitlabhq/issues/694
@@ -210,14 +92,7 @@ server {
proxy_connect_timeout 300;
proxy_redirect off;
- # Do not buffer Git HTTP responses
- proxy_buffering off;
-
- # The following settings only work with NGINX 1.7.11 or newer
- #
- # # Pass chunked request bodies to gitlab-workhorse as-is
- # proxy_request_buffering off;
- # proxy_http_version 1.1;
+ proxy_http_version 1.1;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
@@ -226,18 +101,4 @@ server {
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://gitlab-workhorse;
}
-
- ## Enable gzip compression as per rails guide:
- ## http://guides.rubyonrails.org/asset_pipeline.html#gzip-compression
- ## WARNING: If you are using relative urls remove the block below
- ## See config/application.rb under "Relative url support" for the list of
- ## other files that need to be changed for relative url support
- location ~ ^/(assets)/ {
- root /home/git/gitlab/public;
- gzip_static on; # to serve pre-gzipped version
- expires max;
- add_header Cache-Control public;
- }
-
- error_page 502 /502.html;
}
diff --git a/lib/tasks/ci/schedule_builds.rake b/lib/tasks/ci/schedule_builds.rake
deleted file mode 100644
index 49435504c67..00000000000
--- a/lib/tasks/ci/schedule_builds.rake
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace :ci do
- desc "GitLab CI | Clean running builds"
- task schedule_builds: :environment do
- Ci::Scheduler.new.perform
- end
-end
diff --git a/lib/tasks/gitlab/backup.rake b/lib/tasks/gitlab/backup.rake
index 3c46bcea40e..cb4abe13799 100644
--- a/lib/tasks/gitlab/backup.rake
+++ b/lib/tasks/gitlab/backup.rake
@@ -13,6 +13,7 @@ namespace :gitlab do
Rake::Task["gitlab:backup:uploads:create"].invoke
Rake::Task["gitlab:backup:builds:create"].invoke
Rake::Task["gitlab:backup:artifacts:create"].invoke
+ Rake::Task["gitlab:backup:lfs:create"].invoke
backup = Backup::Manager.new
backup.pack
@@ -34,6 +35,7 @@ namespace :gitlab do
Rake::Task["gitlab:backup:uploads:restore"].invoke unless backup.skipped?("uploads")
Rake::Task["gitlab:backup:builds:restore"].invoke unless backup.skipped?("builds")
Rake::Task["gitlab:backup:artifacts:restore"].invoke unless backup.skipped?("artifacts")
+ Rake::Task["gitlab:backup:lfs:restore"].invoke unless backup.skipped?("lfs")
Rake::Task["gitlab:shell:setup"].invoke
backup.cleanup
@@ -134,6 +136,25 @@ namespace :gitlab do
end
end
+ namespace :lfs do
+ task create: :environment do
+ $progress.puts "Dumping lfs objects ... ".blue
+
+ if ENV["SKIP"] && ENV["SKIP"].include?("lfs")
+ $progress.puts "[SKIPPED]".cyan
+ else
+ Backup::Lfs.new.dump
+ $progress.puts "done".green
+ end
+ end
+
+ task restore: :environment do
+ $progress.puts "Restoring lfs objects ... ".blue
+ Backup::Lfs.new.restore
+ $progress.puts "done".green
+ end
+ end
+
def configure_cron_mode
if ENV['CRON']
# We need an object we can say 'puts' and 'print' to; let's use a
diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake
index a25fac62cfc..0469c5a61c3 100644
--- a/lib/tasks/gitlab/check.rake
+++ b/lib/tasks/gitlab/check.rake
@@ -331,7 +331,7 @@ namespace :gitlab do
end
def check_redis_version
- min_redis_version = "2.4.0"
+ min_redis_version = "2.8.0"
print "Redis version >= #{min_redis_version}? ... "
redis_version = run(%W(redis-cli --version))
@@ -822,10 +822,27 @@ namespace :gitlab do
namespace_dirs.each do |namespace_dir|
repo_dirs = Dir.glob(File.join(namespace_dir, '*'))
- repo_dirs.each do |dir|
- puts "\nChecking repo at #{dir}"
- system(*%W(#{Gitlab.config.git.bin_path} fsck), chdir: dir)
- end
+ repo_dirs.each { |repo_dir| check_repo_integrity(repo_dir) }
+ end
+ end
+ end
+
+ namespace :user do
+ desc "GitLab | Check the integrity of a specific user's repositories"
+ task :check_repos, [:username] => :environment do |t, args|
+ username = args[:username] || prompt("Check repository integrity for which username? ".blue)
+ user = User.find_by(username: username)
+ if user
+ repo_dirs = user.authorized_projects.map do |p|
+ File.join(
+ Gitlab.config.gitlab_shell.repos_path,
+ "#{p.path_with_namespace}.git"
+ )
+ end
+
+ repo_dirs.each { |repo_dir| check_repo_integrity(repo_dir) }
+ else
+ puts "\nUser '#{username}' not found".red
end
end
end
@@ -952,4 +969,35 @@ namespace :gitlab do
false
end
end
+
+ def check_repo_integrity(repo_dir)
+ puts "\nChecking repo at #{repo_dir.yellow}"
+
+ git_fsck(repo_dir)
+ check_config_lock(repo_dir)
+ check_ref_locks(repo_dir)
+ end
+
+ def git_fsck(repo_dir)
+ puts "Running `git fsck`".yellow
+ system(*%W(#{Gitlab.config.git.bin_path} fsck), chdir: repo_dir)
+ end
+
+ def check_config_lock(repo_dir)
+ config_exists = File.exist?(File.join(repo_dir,'config.lock'))
+ config_output = config_exists ? 'yes'.red : 'no'.green
+ puts "'config.lock' file exists?".yellow + " ... #{config_output}"
+ end
+
+ def check_ref_locks(repo_dir)
+ lock_files = Dir.glob(File.join(repo_dir,'refs/heads/*.lock'))
+ if lock_files.present?
+ puts "Ref lock files exist:".red
+ lock_files.each do |lock_file|
+ puts " #{lock_file}"
+ end
+ else
+ puts "No ref lock files exist".green
+ end
+ end
end
diff --git a/lib/tasks/gitlab/git.rake b/lib/tasks/gitlab/git.rake
new file mode 100644
index 00000000000..65ee430d550
--- /dev/null
+++ b/lib/tasks/gitlab/git.rake
@@ -0,0 +1,55 @@
+namespace :gitlab do
+ namespace :git do
+
+ desc "GitLab | Git | Repack"
+ task repack: :environment do
+ failures = perform_git_cmd(%W(git repack -a --quiet), "Repacking repo")
+ if failures.empty?
+ puts "Done".green
+ else
+ output_failures(failures)
+ end
+ end
+
+ desc "GitLab | Git | Run garbage collection on all repos"
+ task gc: :environment do
+ failures = perform_git_cmd(%W(git gc --auto --quiet), "Garbage Collecting")
+ if failures.empty?
+ puts "Done".green
+ else
+ output_failures(failures)
+ end
+ end
+
+ desc "GitLab | Git | Prune all repos"
+ task prune: :environment do
+ failures = perform_git_cmd(%W(git prune), "Git Prune")
+ if failures.empty?
+ puts "Done".green
+ else
+ output_failures(failures)
+ end
+ end
+
+ def perform_git_cmd(cmd, message)
+ puts "Starting #{message} on all repositories"
+
+ failures = []
+ all_repos do |repo|
+ if system(*cmd, chdir: repo)
+ puts "Performed #{message} at #{repo}"
+ else
+ failures << repo
+ end
+ end
+
+ failures
+ end
+
+ def output_failures(failures)
+ puts "The following repositories reported errors:".red
+ failures.each { |f| puts "- #{f}" }
+ end
+
+ end
+end
diff --git a/lib/tasks/gitlab/import.rake b/lib/tasks/gitlab/import.rake
index c1ee271ae2b..1c04f47f08f 100644
--- a/lib/tasks/gitlab/import.rake
+++ b/lib/tasks/gitlab/import.rake
@@ -64,6 +64,8 @@ namespace :gitlab do
if project.persisted?
puts " * Created #{project.name} (#{repo_path})".green
+ project.update_repository_size
+ project.update_commit_count
else
puts " * Failed trying to create #{project.name} (#{repo_path})".red
puts " Errors: #{project.errors.messages}".red
diff --git a/lib/tasks/gitlab/list_repos.rake b/lib/tasks/gitlab/list_repos.rake
new file mode 100644
index 00000000000..c7596e7abcb
--- /dev/null
+++ b/lib/tasks/gitlab/list_repos.rake
@@ -0,0 +1,17 @@
+namespace :gitlab do
+ task list_repos: :environment do
+ scope = Project
+ if ENV['SINCE']
+ date = Time.parse(ENV['SINCE'])
+ warn "Listing repositories with activity or changes since #{date}"
+ project_ids = Project.where('last_activity_at > ? OR updated_at > ?', date, date).pluck(:id).sort
+ namespace_ids = Namespace.where(['updated_at > ?', date]).pluck(:id).sort
+ scope = scope.where('id IN (?) OR namespace_id in (?)', project_ids, namespace_ids)
+ end
+ scope.find_each do |project|
+ base = File.join(Gitlab.config.gitlab_shell.repos_path, project.path_with_namespace)
+ puts base + '.git'
+ puts base + '.wiki.git'
+ end
+ end
+end
diff --git a/lib/tasks/gitlab/task_helpers.rake b/lib/tasks/gitlab/task_helpers.rake
index c95b6540ebc..ebe516ec879 100644
--- a/lib/tasks/gitlab/task_helpers.rake
+++ b/lib/tasks/gitlab/task_helpers.rake
@@ -2,16 +2,6 @@ module Gitlab
class TaskAbortedByUserError < StandardError; end
end
-unless STDOUT.isatty
- module Colored
- extend self
-
- def colorize(string, options={})
- string
- end
- end
-end
-
namespace :gitlab do
# Ask if the user wants to continue
@@ -103,7 +93,7 @@ namespace :gitlab do
gitlab_user = Gitlab.config.gitlab.user
current_user = run(%W(whoami)).chomp
unless current_user == gitlab_user
- puts "#{Colored.color(:black)+Colored.color(:on_yellow)} Warning #{Colored.extra(:clear)}"
+ puts " Warning ".colorize(:black).on_yellow
puts " You are running as user #{current_user.magenta}, we hope you know what you are doing."
puts " Things may work\/fail for the wrong reasons."
puts " For correct results you should run this as user #{gitlab_user.magenta}."
@@ -128,4 +118,12 @@ namespace :gitlab do
false
end
end
+
+ def all_repos
+ IO.popen(%W(find #{Gitlab.config.gitlab_shell.repos_path} -mindepth 2 -maxdepth 2 -type d -name *.git)) do |find|
+ find.each_line do |path|
+ yield path.chomp
+ end
+ end
+ end
end
diff --git a/lib/tasks/spec.rake b/lib/tasks/spec.rake
index 365ff2defd4..0985ef3a669 100644
--- a/lib/tasks/spec.rake
+++ b/lib/tasks/spec.rake
@@ -19,6 +19,33 @@ namespace :spec do
run_commands(cmds)
end
+ desc 'GitLab | Rspec | Run model specs'
+ task :models do
+ cmds = [
+ %W(rake gitlab:setup),
+ %W(rspec spec --tag @models)
+ ]
+ run_commands(cmds)
+ end
+
+ desc 'GitLab | Rspec | Run service specs'
+ task :services do
+ cmds = [
+ %W(rake gitlab:setup),
+ %W(rspec spec --tag @services)
+ ]
+ run_commands(cmds)
+ end
+
+ desc 'GitLab | Rspec | Run lib specs'
+ task :lib do
+ cmds = [
+ %W(rake gitlab:setup),
+ %W(rspec spec --tag @lib)
+ ]
+ run_commands(cmds)
+ end
+
desc 'GitLab | Rspec | Run benchmark specs'
task :benchmark do
cmds = [
@@ -32,7 +59,7 @@ namespace :spec do
task :other do
cmds = [
%W(rake gitlab:setup),
- %W(rspec spec --tag ~@api --tag ~@feature --tag ~@benchmark)
+ %W(rspec spec --tag ~@api --tag ~@feature --tag ~@models --tag ~@lib --tag ~@services --tag ~@benchmark)
]
run_commands(cmds)
end
diff --git a/lib/tasks/spinach.rake b/lib/tasks/spinach.rake
index d5a96fd38f4..3acfc6e2075 100644
--- a/lib/tasks/spinach.rake
+++ b/lib/tasks/spinach.rake
@@ -1,11 +1,31 @@
Rake::Task["spinach"].clear if Rake::Task.task_defined?('spinach')
namespace :spinach do
+ namespace :project do
+ desc "GitLab | Spinach | Run project commits, issues and merge requests spinach features"
+ task :half do
+ cmds = [
+ %W(rake gitlab:setup),
+ %W(spinach --tags @project_commits,@project_issues,@project_merge_requests),
+ ]
+ run_commands(cmds)
+ end
+
+ desc "GitLab | Spinach | Run remaining project spinach features"
+ task :rest do
+ cmds = [
+ %W(rake gitlab:setup),
+ %W(spinach --tags ~@admin,~@dashboard,~@profile,~@public,~@snippets,~@project_commits,~@project_issues,~@project_merge_requests),
+ ]
+ run_commands(cmds)
+ end
+ end
+
desc "GitLab | Spinach | Run project spinach features"
task :project do
cmds = [
%W(rake gitlab:setup),
- %W(spinach --tags ~@admin,~@dashboard,~@profile,~@public,~@snippets,~@commits),
+ %W(spinach --tags ~@admin,~@dashboard,~@profile,~@public,~@snippets),
]
run_commands(cmds)
end
@@ -14,7 +34,7 @@ namespace :spinach do
task :other do
cmds = [
%W(rake gitlab:setup),
- %W(spinach --tags @admin,@dashboard,@profile,@public,@snippets,@commits),
+ %W(spinach --tags @admin,@dashboard,@profile,@public,@snippets),
]
run_commands(cmds)
end
@@ -33,4 +53,4 @@ def run_commands(cmds)
cmds.each do |cmd|
system({'RAILS_ENV' => 'test', 'force' => 'yes'}, *cmd) or raise("#{cmd} failed!")
end
-end \ No newline at end of file
+end