Commit 2b198ee8 authored by Regis's avatar Regis

Merge branch 'avatar-vue' into auto-pipelines-vue

parents c07180f3 2b9a9e37
......@@ -61,9 +61,10 @@ update-knapsack:
- scripts/merge-reports knapsack/spinach_report.json knapsack/spinach_node_*.json
- rm -f knapsack/*_node_*.json
only:
- master
# Execute all testing suites
- master@gitlab-org/gitlab-ce
- master@gitlab-org/gitlab-ee
- master@gitlab/gitlabhq
- master@gitlab/gitlab-ee
.use-db: &use-db
services:
......@@ -143,7 +144,10 @@ spinach 9 10: *spinach-knapsack
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.1-git-2.7-phantomjs-2.1"
<<: *use-db
only:
- master
- master@gitlab-org/gitlab-ce
- master@gitlab-org/gitlab-ee
- master@gitlab/gitlabhq
- master@gitlab/gitlab-ee
cache:
key: "ruby21"
paths:
......@@ -286,7 +290,10 @@ bundler:audit:
stage: test
<<: *ruby-static-analysis
only:
- master
- master@gitlab-org/gitlab-ce
- master@gitlab-org/gitlab-ee
- master@gitlab/gitlabhq
- master@gitlab/gitlab-ee
script:
- "bundle exec bundle-audit check --update --ignore OSVDB-115941"
......@@ -297,6 +304,9 @@ migration paths:
SETUP_DB: "false"
only:
- master@gitlab-org/gitlab-ce
- master@gitlab-org/gitlab-ee
- master@gitlab/gitlabhq
- master@gitlab/gitlab-ee
script:
- git checkout HEAD .
- git fetch --tags
......
......@@ -55,4 +55,36 @@
</svg>
`,
});
gl.VueCanceledIcon = Vue.extend({
template: `
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" class="ci-status-icon-canceled" viewBox="0 0 14 14">
<g fill="#5C5C5C" fill-rule="evenodd">
<path d="M12.5,7 C12.5,3.96243388 10.0375661,1.5 7,1.5 C3.96243388,1.5 1.5,3.96243388 1.5,7 C1.5,10.0375661 3.96243388,12.5 7,12.5 C10.0375661,12.5 12.5,10.0375661 12.5,7 Z M0,7 C0,3.13400675 3.13400675,0 7,0 C10.8659932,0 14,3.13400675 14,7 C14,10.8659932 10.8659932,14 7,14 C3.13400675,14 0,10.8659932 0,7 Z"></path>
<rect width="8" height="2" x="3" y="6" transform="rotate(45 7 7)" rx=".5"></rect>
</g>
</svg>
`,
});
gl.VueSkippedIcon = Vue.extend({
template: `
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40">
<g fill="#8F8F8F" fill-rule="evenodd">
<path d="M29.513 10.134A15.922 15.922 0 0 0 23 7.28V6h2.993C26.55 6 27 5.552 27 5V2a1 1 0 0 0-1.007-1H14.007C13.45 1 13 1.448 13 2v3a1 1 0 0 0 1.007 1H17v1.28C9.597 8.686 4 15.19 4 23c0 8.837 7.163 16 16 16s16-7.163 16-16c0-3.461-1.099-6.665-2.967-9.283l1.327-1.58a2.498 2.498 0 0 0-.303-3.53 2.499 2.499 0 0 0-3.528.315l-1.016 1.212zM20 34c6.075 0 11-4.925 11-11s-4.925-11-11-11S9 16.925 9 23s4.925 11 11 11z"/><path d="M19 21h-4.002c-.552 0-.998.452-.998 1.01v1.98c0 .567.447 1.01.998 1.01h7.004c.274 0 .521-.111.701-.291a.979.979 0 0 0 .297-.704v-8.01c0-.54-.452-.995-1.01-.995h-1.98a.997.997 0 0 0-1.01.995V21z"/>
</g>
</svg>
`,
});
gl.VueUnstableIcon = Vue.extend({
template: `
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14">
<g fill="#FF8A24" fill-rule="evenodd">
<path d="M12.5,7 C12.5,3.96243388 10.0375661,1.5 7,1.5 C3.96243388,1.5 1.5,3.96243388 1.5,7 C1.5,10.0375661 3.96243388,12.5 7,12.5 C10.0375661,12.5 12.5,10.0375661 12.5,7 Z M0,7 C0,3.13400675 3.13400675,0 7,0 C10.8659932,0 14,3.13400675 14,7 C14,10.8659932 10.8659932,14 7,14 C3.13400675,14 0,10.8659932 0,7 Z"/>
<path d="M6,3.49769878 C6,3.22282734 6.21403503,3 6.50468445,3 L7.49531555,3 C7.77404508,3 8,3.21484375 8,3.49769878 L8,7.50230122 C8,7.77717266 7.78596497,8 7.49531555,8 L6.50468445,8 C6.22595492,8 6,7.78515625 6,7.50230122 L6,3.49769878 Z M6,9.50468445 C6,9.22595492 6.21403503,9 6.50468445,9 L7.49531555,9 C7.77404508,9 8,9.21403503 8,9.50468445 L8,10.4953156 C8,10.7740451 7.78596497,11 7.49531555,11 L6.50468445,11 C6.22595492,11 6,10.785965 6,10.4953156 L6,9.50468445 Z"/>
</g>
</svg
`,
});
})(window.gl || (window.gl = {}));
......@@ -11,13 +11,17 @@
alt() {
return `${this.pipeline.commit.author_name}'s avatar`;
},
avatarUrl() {
const author = this.pipeline.commit.author;
if (author) return author.avatar_url;
return this.pipeline.commit.author_gravatar_url;
},
},
template: `
<td class="branch-commit">
<div class="icon-container">
<i class="fa fa-code-fork"></i>
</div>
<!-- ** will need branch_url for this branch ** -->
<a
class="monospace branch-name"
:href='pipeline.ref.url'
......@@ -45,7 +49,7 @@
:alt='alt'
:title='pipeline.commit.author_name'
data-container="body"
src="http://www.gravatar.com/avatar/80d3b651b4be1f1db39435c2d11f1f23?s=40&amp;d=identicon"
:src='avatarUrl'
>
</a>
<a
......
/* global Vue, VueResource, gl */
/* eslint-disable no-bitwise*/
/* eslint-disable no-bitwise, no-plusplus*/
//= require vue-resource
......
......@@ -10,14 +10,6 @@
download(name) {
return `Download ${name} artifacts`;
},
// retry(e) {
// e.preventDefault();
// this.$http.post(this.pipeline.retry_url, {
// pipeline: { id: this.pipeline.id },
// })
// .then(() => {})
// .catch(() => new Flash('Something went wrong on our end.'));
// },
},
template: `
<td class="pipeline-actions hidden-xs">
......@@ -41,7 +33,7 @@
</a>
<ul class="dropdown-menu dropdown-menu-align-right">
<li v-for='action in pipeline.details.manual_actions'>
<a rel="nofollow" data-method="post" :href='action.url'>
<a rel="nofollow" data-method="post" :href='action.url' title="Manual build">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 11" class="icon-play">
<path
fill-rule="evenodd"
......@@ -78,9 +70,8 @@
</div>
</div>
<div class="cancel-retry-btns inline">
<!-- @click='retry($event)' -->
<a
v-if='!pipeline.cancel_url || pipeline.details.status !== "cancelled"'
v-if='pipeline.retry_url'
class="btn has-tooltip"
title="Retry"
rel="nofollow"
......@@ -92,7 +83,7 @@
<a
v-if='pipeline.cancel_url'
class="btn btn-remove has-tooltip"
title=""
title="Cancel"
rel="nofollow"
data-method="post"
:href='pipeline.cancel_url'
......
......@@ -9,6 +9,7 @@
'failed-icon': gl.VueFailedIcon,
'success-icon': gl.VueSuccessIcon,
'created-icon': gl.VueCreatedIcon,
'canceled-icon': gl.VueCanceledIcon,
},
props: ['stage'],
computed: {
......@@ -30,6 +31,7 @@
<failed-icon v-if='stage.status === "failed"'></failed-icon>
<pending-icon v-if='stage.status === "pending"'></pending-icon>
<created-icon v-if='stage.status === "created"'></created-icon>
<canceled-icon v-if='stage.status === "canceled"'></canceled-icon>
</a>
`,
});
......
......@@ -28,6 +28,15 @@
gl.PipelineStore = class {
fetchDataLoop(Vue, pageNum, url) {
Vue.activeResources = 0;
const resourceChecker = () => {
if (Vue.activeResources === 0) {
Vue.activeResources = 1;
} else {
Vue.activeResources += 1;
}
};
const goFetch = () =>
this.$http.get(`${url}?page=${pageNum}`)
.then((response) => {
......@@ -36,6 +45,7 @@
Vue.set(this, 'pipelines', res.pipelines);
Vue.set(this, 'count', res.count);
this.pageRequest = false;
Vue.activeResources -= 1;
}, () => new Flash(
'Something went wrong on our end.'
));
......@@ -45,15 +55,22 @@
.then((response) => {
const res = JSON.parse(response.body);
const p = new PipelineUpdater(this.pipelines);
Vue.set(this, 'updatedAt', res.updated_at);
Vue.set(this, 'pipelines', p.updatePipelines(res));
Vue.set(this, 'count', res.count);
Vue.activeResources -= 1;
}, () => new Flash(
'Something went wrong on our end.'
));
resourceChecker();
goFetch();
this.intervalId = setInterval(() => {
if (this.updatedAt) goUpdate();
if (this.updatedAt) {
resourceChecker();
goUpdate();
}
}, 3000);
window.onbeforeunload = function removePipelineInterval() {
......
......@@ -19,15 +19,12 @@
seconds(date) {
return this.formatSection(date.getSeconds());
},
},
computed: {
// need started_at or created_at for finish and running
finishdate() {
const date = new Date(
new Date(
this.pipeline.details.finished_at
).getTime() - new Date(
this.pipeline.started_at
this.pipeline.created_at
).getTime()
);
return (
......@@ -36,7 +33,7 @@
},
runningdate() {
const date = new Date(
new Date().getTime() - new Date(this.pipeline.started_at).getTime()
new Date().getTime() - new Date(this.pipeline.created_at).getTime()
);
return (
`${this.hours(date)}:${this.minutes(date)}:${this.seconds(date)}`
......@@ -61,8 +58,8 @@
};
},
duration() {
if (this.timeStopped) return this.finishdate;
return this.runningdate;
if (this.timeStopped()) return this.finishdate();
return this.runningdate();
},
},
template: `
......@@ -78,9 +75,9 @@
<path d="M29.513 10.134A15.922 15.922 0 0 0 23 7.28V6h2.993C26.55 6 27 5.552 27 5V2a1 1 0 0 0-1.007-1H14.007C13.45 1 13 1.448 13 2v3a1 1 0 0 0 1.007 1H17v1.28C9.597 8.686 4 15.19 4 23c0 8.837 7.163 16 16 16s16-7.163 16-16c0-3.461-1.099-6.665-2.967-9.283l1.327-1.58a2.498 2.498 0 0 0-.303-3.53 2.499 2.499 0 0 0-3.528.315l-1.016 1.212zM20 34c6.075 0 11-4.925 11-11s-4.925-11-11-11S9 16.925 9 23s4.925 11 11 11z"></path><path d="M19 21h-4.002c-.552 0-.998.452-.998 1.01v1.98c0 .567.447 1.01.998 1.01h7.004c.274 0 .521-.111.701-.291a.979.979 0 0 0 .297-.704v-8.01c0-.54-.452-.995-1.01-.995h-1.98a.997.997 0 0 0-1.01.995V21z"></path>
</g>
</svg>
{{duration}}
{{duration()}}
</p>
<p class="finished-at" v-if='timeStopped'>
<p class="finished-at" v-if='timeStopped()'>
<i class="fa fa-calendar"></i>
<time
data-toggle="tooltip"
......@@ -88,7 +85,7 @@
data-container="body"
:data-original-title='9 + 9'
>
{{timeStopped.words}}
{{timeStopped().words}}
</time>
</p>
</td>
......
/* global Vue, gl */
/* eslint-disable no-param-reassign */
((gl) => {
gl.VueCanceledScope = Vue.extend({
components: {
'vue-canceled-icon': gl.VueCanceledIcon,
},
props: [
'pipeline',
],
template: `
<td class="commit-link">
<a :href='pipeline.url'>
<span class="ci-status ci-canceled">
<vue-canceled-icon></vue-canceled-icon>
&nbsp;canceled
</span>
</a>
</td>
`,
});
})(window.gl || (window.gl = {}));
//= require ./pending.js.es6
//= require ./failed.js.es6
//= require ./running.js.es6
//= require ./canceled.js.es6
//= require ./status.js.es6
//= require ./unstable.js.es6
//= require ./skipped.js.es6
/* global Vue, gl */
/* eslint-disable no-param-reassign */
((gl) => {
gl.VueSkippedScope = Vue.extend({
components: {
'vue-skipped-icon': gl.VueSkippedIcon,
},
props: [
'pipeline',
],
template: `
<td class="commit-link">
<a :href='pipeline.url'>
<span class="ci-status ci-skipped">
<vue-skipped-icon></vue-skipped-icon>
&nbsp;skipped
</span>
</a>
</td>
`,
});
})(window.gl || (window.gl = {}));
\ No newline at end of file
......@@ -8,6 +8,8 @@
'vue-pending-scope': gl.VuePendingScope,
'vue-failed-scope': gl.VueFailedScope,
'vue-created-scope': gl.VueCreatedScope,
'vue-canceled-scope': gl.VueCanceledScope,
'vue-unstable-scope': gl.VueUnstableScope,
},
props: [
'pipeline',
......@@ -34,6 +36,16 @@
:pipeline='pipeline'
>
</vue-created-scope>
<vue-canceled-scope
v-if="pipeline.details.status === 'canceled'"
:pipeline='pipeline'
>
</vue-canceled-scope>
<vue-unstable-scope
v-if="pipeline.details.status === 'unstable'"
:pipeline='pipeline'
>
</vue-unstable-scope>
</td>
`,
});
......
/* global Vue, gl */
/* eslint-disable no-param-reassign */
((gl) => {
gl.VueUnstableScope = Vue.extend({
components: {
'vue-unstable-icon': gl.VueUnstableIcon,
},
props: [
'pipeline',
],
template: `
<td class="commit-link">
<a :href='pipeline.url'>
<span class="ci-status ci-unstable">
<vue-unstable-icon></vue-unstable-icon>
&nbsp;unstable
</span>
</a>
</td>
`,
});
})(window.gl || (window.gl = {}));
......@@ -63,6 +63,7 @@ header {
&:focus,
&:active {
background-color: $background-color;
color: darken($gl-icon-color, 50%);
}
.fa-caret-down {
......@@ -191,6 +192,10 @@ header {
font-size: 10px;
text-align: center;
cursor: pointer;
&:hover {
color: darken($color: $gl-text-color, $amount: 50%);
}
}
.project-item-select {
......
......@@ -84,7 +84,8 @@
font-weight: 600;
}
.commit {
.commit,
.generic_commit_status {
padding: 10px 0;
position: relative;
......@@ -102,7 +103,6 @@
vertical-align: baseline;
}
.avatar {
margin-left: -46px;
}
......
......@@ -109,10 +109,6 @@
float: none;
}
.api {
color: $code-color;
}
.branch-commit {
.branch-name {
......
......@@ -21,6 +21,11 @@
padding: 4px;
width: $search-input-width;
line-height: 24px;
&:hover {
border-color: lighten($dropdown-input-focus-border, 20%);
box-shadow: 0 0 4px lighten($search-input-focus-shadow-color, 20%);
}
}
.location-text {
......@@ -153,6 +158,7 @@
width: 68%;
}
}
}
.search-holder {
......@@ -229,4 +235,5 @@
&:focus {
color: $gl-link-color;
}
}
......@@ -7,8 +7,8 @@ class HelpController < ApplicationController
@help_index = File.read(Rails.root.join('doc', 'README.md'))
# Prefix Markdown links with `help/` unless they already have been
# See http://rubular.com/r/nwwhzH6Z8X
@help_index.gsub!(/(\]\()(?!help\/)([^\)\(]+)(\))/, '\1help/\2\3')
# See http://rubular.com/r/ie2MlpdUMq
@help_index.gsub!(/(\]\()(\/?help\/)?([^\)\(]+\))/, '\1/help/\3')
end
def show
......
......@@ -507,6 +507,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request.close
end
labels
define_pipelines_vars
end
......
......@@ -3,7 +3,7 @@ module AuthHelper
FORM_BASED_PROVIDERS = [/\Aldap/, 'crowd'].freeze
def ldap_enabled?
Gitlab.config.ldap.enabled
Gitlab::LDAP::Config.enabled?
end
def omniauth_enabled?
......
......@@ -82,6 +82,10 @@ module GitlabRoutingHelper
namespace_project_merge_request_path(entity.project.namespace, entity.project, entity, *args)
end
def pipeline_path(pipeline, *args)
namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id, *args)
end
def milestone_path(entity, *args)
namespace_project_milestone_path(entity.project.namespace, entity.project, entity, *args)
end
......
......@@ -203,6 +203,10 @@ class ApplicationSetting < ActiveRecord::Base
ActiveRecord::Base.connection.column_exists?(:application_settings, :home_page_url)
end
def sidekiq_throttling_column_exists?
ActiveRecord::Base.connection.column_exists?(:application_settings, :sidekiq_throttling_enabled)
end
def domain_whitelist_raw
self.domain_whitelist.join("\n") unless self.domain_whitelist.nil?
end
......@@ -256,6 +260,12 @@ class ApplicationSetting < ActiveRecord::Base
ensure_health_check_access_token!
end
def sidekiq_throttling_enabled?
return false unless sidekiq_throttling_column_exists?
sidekiq_throttling_enabled
end
private
def check_repository_storages
......
......@@ -266,7 +266,7 @@ class Issue < ActiveRecord::Base
def as_json(options = {})
super(options).tap do |json|
json[:subscribed] = subscribed?(options[:user]) if options.has_key?(:user)
json[:subscribed] = subscribed?(options[:user]) if options.has_key?(:user) && options[:user]
if options.has_key?(:labels)
json[:labels] = labels.as_json(
......
......@@ -7,6 +7,7 @@ class Note < ActiveRecord::Base
include Importable
include FasterCacheKeys
include CacheMarkdownField
include AfterCommitQueue
cache_markdown_field :note, pipeline: :note
......
......@@ -1334,6 +1334,10 @@ class Project < ActiveRecord::Base
end
end
def only_allow_merge_if_all_discussions_are_resolved
super || false
end
private
def pushes_since_gc_redis_key
......
......@@ -49,20 +49,14 @@ class ProjectFeature < ActiveRecord::Base
end
def builds_enabled?
return true unless builds_access_level
builds_access_level > DISABLED
end
def wiki_enabled?
return true unless wiki_access_level
wiki_access_level > DISABLED
end
def merge_requests_enabled?
return true unless merge_requests_access_level
merge_requests_access_level > DISABLED
end
......
......@@ -26,9 +26,12 @@ module Notes
note.note = content
end
if !only_commands && note.save
note.run_after_commit do
# Finish the harder work in the background
NewNoteWorker.perform_in(2.seconds, note.id, params)
NewNoteWorker.perform_async(note.id)
end
if !only_commands && note.save
todo_service.new_note(note, current_user)
end
......
%board-sidebar{ "inline-template" => true,
":current-user" => "#{current_user.to_json(only: [:username, :id, :name], methods: [:avatar_url]) if current_user}" }
":current-user" => "#{current_user ? current_user.to_json(only: [:username, :id, :name], methods: [:avatar_url]) : {}}" }
%aside.right-sidebar.right-sidebar-expanded.issue-boards-sidebar{ "v-show" => "showSidebar" }
.issuable-sidebar
.block.issuable-sidebar-header
......
.block.assignee
.title.hide-collapsed
Assignee
= icon("spinner spin", class: "block-loading")
- if can?(current_user, :admin_issue, @project)
= icon("spinner spin", class: "block-loading")
= link_to "Edit", "#", class: "edit-link pull-right"
.value.hide-collapsed
%span.assign-yourself.no-value{ "v-if" => "!issue.assignee" }
......
.block.due_date
.title
Due date
= icon("spinner spin", class: "block-loading")
- if can?(current_user, :admin_issue, @project)
= icon("spinner spin", class: "block-loading")
= link_to "Edit", "#", class: "edit-link pull-right"
.value
.value-content
......
.block.labels
.title
Labels
= icon("spinner spin", class: "block-loading")
- if can?(current_user, :admin_issue, @project)
= icon("spinner spin", class: "block-loading")
= link_to "Edit", "#", class: "edit-link pull-right"
.value.issuable-show-labels
%span.no-value{ "v-if" => "issue.labels && issue.labels.length === 0" }
......
.block.milestone
.title
Milestone
= icon("spinner spin", class: "block-loading")
- if can?(current_user, :admin_issue, @project)
= icon("spinner spin", class: "block-loading")
= link_to "Edit", "#", class: "edit-link pull-right"
.value
%span.no-value{ "v-if" => "!issue.milestone" }
......
......@@ -3,6 +3,9 @@
= ci_status_with_icon(@build.status)
Build
%strong ##{@build.id}
in pipeline
= link_to pipeline_path(@build.pipeline) do
%strong ##{@build.pipeline.id}
for commit
= link_to ci_status_path(@build.pipeline) do
%strong= @build.pipeline.short_sha
......
......@@ -10,6 +10,7 @@
%tr
%th Status
%th Build
%th Pipeline
- if admin
%th Project
%th Runner
......@@ -19,6 +20,6 @@
%th Coverage
%th
= render partial: "projects/ci/builds/build", collection: builds, as: :build, locals: { commit_sha: true, ref: true, stage: true, allow_retry: true, coverage: admin || project.build_coverage_enabled?, admin: admin }
= render partial: "projects/ci/builds/build", collection: builds, as: :build, locals: { commit_sha: true, ref: true, pipeline_link: true, stage: true, allow_retry: true, coverage: admin || project.build_coverage_enabled?, admin: admin }
= paginate builds, theme: 'gitlab'
- if !project.empty_repo? && can?(current_user, :download_code, project)
%span{class: 'hidden-xs hidden-sm download-button'}
%span{class: 'download-button'}
.dropdown.inline
%button.btn{ 'data-toggle' => 'dropdown' }
= icon('download')
......
......@@ -2,6 +2,7 @@
- ref = local_assigns.fetch(:ref, nil)
- commit_sha = local_assigns.fetch(:commit_sha, nil)
- retried = local_assigns.fetch(:retried, false)
- pipeline_link = local_assigns.fetch(:pipeline_link, false)
- stage = local_assigns.fetch(:stage, false)
- coverage = local_assigns.fetch(:coverage, false)
- allow_retry = local_assigns.fetch(:allow_retry, false)
......@@ -51,6 +52,16 @@
- if build.manual?
%span.label.label-info manual
- if pipeline_link
%td
= link_to pipeline_path(build.pipeline) do
%span.pipeline-id ##{build.pipeline.id}
%span by
- if build.pipeline.user
= user_avatar(user: build.pipeline.user, size: 20)
- else
%span.monospace API
- if admin
%td
- if build.project
......
......@@ -8,8 +8,8 @@
- if stage
&nbsp;
= stage.titleize
= render statuses.latest_ci_stages, coverage: @project.build_coverage_enabled?, stage: false, ref: false, allow_retry: true
= render statuses.retried_ci_stages, coverage: @project.build_coverage_enabled?, stage: false, ref: false, retried: true
%tr
%td{colspan: 10}
&nbsp;
= render statuses.latest_ci_stages, coverage: @project.build_coverage_enabled?, stage: false, ref: false, pipeline_link: false, allow_retry: true
= render statuses.retried_ci_stages, coverage: @project.build_coverage_enabled?, stage: false, ref: false, pipeline_link: false, retried: true
%tr
%td{colspan: 10}
&nbsp;
......@@ -15,6 +15,16 @@
- if defined?(retried) && retried
= icon('warning', class: 'text-warning has-tooltip', title: 'Status was retried.')
- if defined?(pipeline_link) && pipeline_link
%td
= link_to pipeline_path(generic_commit_status.pipeline) do
%span.pipeline-id ##{generic_commit_status.pipeline.id}
%span by
- if generic_commit_status.pipeline.user
= user_avatar(user: generic_commit_status.pipeline.user, size: 20)
- else
%span.monospace API
- if defined?(commit_sha) && commit_sha
%td
= link_to generic_commit_status.short_sha, namespace_project_commit_path(generic_commit_status.project.namespace, generic_commit_status.project, generic_commit_status.sha), class: "monospace"
......
......@@ -90,7 +90,8 @@
= f.label :visibility_level, class: 'label-light' do
Visibility Level
= link_to "(?)", help_page_path("public_access/public_access")
= render('shared/visibility_radios', model_method: :visibility_level, form: f, selected_level: @project.visibility_level, form_model: @project)
= render 'shared/visibility_level', f: f, visibility_level: default_project_visibility, can_change_visibility_level: true, form_model: @project
= f.submit 'Create project', class: "btn btn-create project-submit", tabindex: 4
= link_to 'Cancel', dashboard_projects_path, class: 'btn btn-cancel'
......
......@@ -2,10 +2,12 @@ class NewNoteWorker
include Sidekiq::Worker
include DedicatedSidekiqQueue
def perform(note_id, note_params)
note = Note.find(note_id)
NotificationService.new.new_note(note)
Notes::PostProcessService.new(note).execute
def perform(note_id)
if note = Note.find_by(id: note_id)
NotificationService.new.new_note(note)
Notes::PostProcessService.new(note).execute
else
Rails.logger.error("NewNoteWorker: couldn't find note with ID=#{note_id}, skipping job")
end
end
end
---
title: Add link to build pipeline within individual build pages
merge_request: 7082
author:
---
title: Fix issue causing Labels not to appear in sidebar on MR page
merge_request: 7416
author: Alex Sanford
---
title: Project download buttons always show
merge_request: 7405
author: Philip Karpiak
---
title: Fix error links in help index page
merge_request: 7396
author: Fu Xu
---
title: Fix project Visibility Level selector not using default values
merge_request:
author:
---
title: Fix record not found error on NewNoteWorker processing
merge_request: 6863
author: Oswaldo Ferreira
---
title: Added ability to put emojis into repository name
merge_request: 7420
author: Vincent Composieux
---
title: Centralize LDAP config/filter logic
merge_request: 6606
author:
......@@ -3,8 +3,8 @@
#
production:
adapter: mysql2
encoding: utf8
collation: utf8_general_ci
encoding: utf8mb4
collation: utf8mb4_general_ci
reconnect: false
database: gitlabhq_production
pool: 10
......@@ -18,8 +18,8 @@ production:
#
development:
adapter: mysql2
encoding: utf8
collation: utf8_general_ci
encoding: utf8mb4
collation: utf8mb4_general_ci
reconnect: false
database: gitlabhq_development
pool: 5
......@@ -32,8 +32,8 @@ development:
# Do not set this db to the same as development or production.
test: &test
adapter: mysql2
encoding: utf8
collation: utf8_general_ci
encoding: utf8mb4
collation: utf8mb4_general_ci
reconnect: false
database: gitlabhq_test
pool: 5
......
......@@ -213,22 +213,9 @@ Devise.setup do |config|
end
if Gitlab::LDAP::Config.enabled?
Gitlab.config.ldap.servers.values.each do |server|
if server['allow_username_or_email_login']
email_stripping_proc = ->(name) {name.gsub(/@.*\z/, '')}
else
email_stripping_proc = ->(name) {name}
end
config.omniauth server['provider_name'],
host: server['host'],
base: server['base'],
uid: server['uid'],
port: server['port'],
method: server['method'],
bind_dn: server['bind_dn'],
password: server['password'],
name_proc: email_stripping_proc
Gitlab::LDAP::Config.providers.each do |provider|
ldap_config = Gitlab::LDAP::Config.new(provider)
config.omniauth(provider, ldap_config.omniauth_options)
end
end
......
......@@ -44,7 +44,7 @@ listen "127.0.0.1:8080", :tcp_nopush => true
# nuke workers after 30 seconds instead of 60 seconds (the default)
#
# NOTICE: git push over http depends on this value.
# If you want be able to push huge amount of data to git repository over http
# If you want to be able to push huge amount of data to git repository over http
# you will have to increase this value too.
#
# Example of output if you try to push 1GB repo to GitLab over http.
......@@ -82,7 +82,7 @@ GC.respond_to?(:copy_on_write_friendly=) and
check_client_connection false
before_fork do |server, worker|
# the following is highly recomended for Rails + "preload_app true"
# the following is highly recommended for Rails + "preload_app true"
# as there's no need for the master process to hold a connection
defined?(ActiveRecord::Base) and
ActiveRecord::Base.connection.disconnect!
......
......@@ -5,10 +5,7 @@ class OnlyAllowMergeIfAllDiscussionsAreResolved < ActiveRecord::Migration
disable_ddl_transaction!
def up
add_column_with_default(:projects,
:only_allow_merge_if_all_discussions_are_resolved,
:boolean,
default: false)
add_column :projects, :only_allow_merge_if_all_discussions_are_resolved, :boolean
end
def down
......
......@@ -915,7 +915,7 @@ ActiveRecord::Schema.define(version: 20161109150329) do
t.boolean "has_external_wiki"
t.boolean "lfs_enabled"
t.text "description_html"
t.boolean "only_allow_merge_if_all_discussions_are_resolved", default: false, null: false
t.boolean "only_allow_merge_if_all_discussions_are_resolved"
end
add_index "projects", ["ci_id"], name: "index_projects_on_ci_id", using: :btree
......
......@@ -76,7 +76,7 @@ configuration to move each data location to a subdirectory:
user['home'] = '/gitlab-data/home'
git_data_dir '/gitlab-data/git-data'
gitlab_rails['shared_path'] = '/gitlab-data/shared'
gitlab_rails['uploads_directory'] = "/gitlab-data/uploads"
gitlab_rails['uploads_directory'] = '/gitlab-data/uploads'
gitlab_ci['builds_directory'] = '/gitlab-data/builds'
```
......
......@@ -48,15 +48,15 @@ Redis.
redis['password'] = 'Redis Password'
```
1. Run `sudo gitlab-ctl reconfigure` to install and configure PostgreSQL.
1. Run `sudo touch /etc/gitlab/skip-auto-migrations` to prevent database migrations
from running on upgrade. Only the primary GitLab application server should
handle migrations.
1. Run `sudo gitlab-ctl reconfigure` to install and configure Redis.
> **Note**: This `reconfigure` step will result in some errors.
That's OK - don't be alarmed.
1. Run `touch /etc/gitlab/skip-auto-migrations` to prevent database migrations
from running on upgrade. Only the primary GitLab application server should
handle migrations.
## Experimental Redis Sentinel support
> [Introduced][ce-1877] in GitLab 8.11.
......
......@@ -14,7 +14,7 @@
contributing to documentation.
- [SQL Migration Style Guide](migration_style_guide.md) for creating safe SQL migrations
- [Testing standards and style guidelines](testing.md)
- [UX guide](ux_guide/README.md) for building GitLab with existing CSS styles and elements
- [UX guide](ux_guide/index.md) for building GitLab with existing CSS styles and elements
- [Frontend guidelines](frontend.md)
- [SQL guidelines](sql.md) for working with SQL queries
- [Sidekiq guidelines](sidekiq_style_guide.md) for working with Sidekiq workers
......
......@@ -14,8 +14,8 @@ There are two ways to create a new project in GitLab.
1. Fill out the information:
1. "Project name" is the name of your project (you can't use spaces, but you
can use hyphens or underscores).
1. "Project name" is the name of your project (you can't use special characters,
but you can use spaces, hyphens, underscores or even emojis).
1. The "Project description" is optional and will be shown in your project's
dashboard so others can briefly understand what your project is about.
1. Select a [visibility level](../public_access/public_access.md).
......
......@@ -44,7 +44,7 @@ This [resource](http://kb.kerio.com/product/kerio-connect/server-configuration/s
has all the information you need to add a certificate to the main trusted chain.
This [answer](http://superuser.com/questions/437330/how-do-you-add-a-certificate-authority-ca-to-ubuntu)
at SuperUser also has relevant information.
at Super User also has relevant information.
**Omnibus Trusted Chain**
......
......@@ -590,7 +590,7 @@ Quote break.
You can also use raw HTML in your Markdown, and it'll mostly work pretty well.
See the documentation for HTML::Pipeline's [SanitizationFilter](http://www.rubydoc.info/gems/html-pipeline/HTML/Pipeline/SanitizationFilter#WHITELIST-constant) class for the list of allowed HTML tags and attributes. In addition to the default `SanitizationFilter` whitelist, GitLab allows `span` elements.
See the documentation for HTML::Pipeline's [SanitizationFilter](http://www.rubydoc.info/gems/html-pipeline/1.11.0/HTML/Pipeline/SanitizationFilter#WHITELIST-constant) class for the list of allowed HTML tags and attributes. In addition to the default `SanitizationFilter` whitelist, GitLab allows `span` elements.
```no-highlight
<dl>
......
......@@ -23,6 +23,11 @@ module API
warden.try(:authenticate) if %w[GET HEAD].include?(env['REQUEST_METHOD'])
end
def declared_params(options = {})
options = { include_parent_namespaces: false }.merge(options)
declared(params, options).to_h.symbolize_keys
end
def find_user_by_private_token
token = private_token
return nil unless token.present?
......
......@@ -62,9 +62,8 @@ module API
end
post ":id/milestones" do
authorize! :admin_milestone, user_project
milestone_params = declared(params, include_parent_namespaces: false)
milestone = ::Milestones::CreateService.new(user_project, current_user, milestone_params).execute
milestone = ::Milestones::CreateService.new(user_project, current_user, declared_params).execute
if milestone.valid?
present milestone, with: Entities::Milestone
......@@ -86,9 +85,9 @@ module API
end
put ":id/milestones/:milestone_id" do
authorize! :admin_milestone, user_project
milestone_params = declared(params, include_parent_namespaces: false, include_missing: false)
milestone = user_project.milestones.find(params.delete(:milestone_id))
milestone = user_project.milestones.find(milestone_params.delete(:milestone_id))
milestone_params = declared_params(include_missing: false)
milestone = ::Milestones::UpdateService.new(user_project, current_user, milestone_params).execute(milestone)
if milestone.valid?
......
......@@ -57,9 +57,7 @@ module API
runner = get_runner(params.delete(:id))
authenticate_update_runner!(runner)
runner_params = declared(params, include_missing: false)
if runner.update(runner_params)
if runner.update(declared_params(include_missing: false))
present runner, with: Entities::RunnerDetails, current_user: current_user
else
render_validation_error!(runner)
......
......@@ -9,23 +9,20 @@ module API
'labels' => proc { |id| find_project_label(id) },
}
params do
requires :id, type: String, desc: 'The ID of a project'
requires :subscribable_id, type: String, desc: 'The ID of a resource'
end
resource :projects do
subscribable_types.each do |type, finder|
type_singularized = type.singularize
type_id_str = :"#{type_singularized}_id"
entity_class = Entities.const_get(type_singularized.camelcase)
# Subscribe to a resource
#
# Parameters:
# id (required) - The ID of a project
# subscribable_id (required) - The ID of a resource
# Example Request:
# POST /projects/:id/labels/:subscribable_id/subscription
# POST /projects/:id/issues/:subscribable_id/subscription
# POST /projects/:id/merge_requests/:subscribable_id/subscription
post ":id/#{type}/:#{type_id_str}/subscription" do
resource = instance_exec(params[type_id_str], &finder)
desc 'Subscribe to a resource' do
success entity_class
end
post ":id/#{type}/:subscribable_id/subscription" do
resource = instance_exec(params[:subscribable_id], &finder)
if resource.subscribed?(current_user)
not_modified!
......@@ -35,17 +32,11 @@ module API
end
end
# Unsubscribe from a resource
#
# Parameters:
# id (required) - The ID of a project
# subscribable_id (required) - The ID of a resource
# Example Request:
# DELETE /projects/:id/labels/:subscribable_id/subscription
# DELETE /projects/:id/issues/:subscribable_id/subscription
# DELETE /projects/:id/merge_requests/:subscribable_id/subscription
delete ":id/#{type}/:#{type_id_str}/subscription" do
resource = instance_exec(params[type_id_str], &finder)
desc 'Unsubscribe from a resource' do
success entity_class
end
delete ":id/#{type}/:subscribable_id/subscription" do
resource = instance_exec(params[:subscribable_id], &finder)
if !resource.subscribed?(current_user)
not_modified!
......
......@@ -2,7 +2,7 @@ module Ci
class GitlabCiYamlProcessor
class ValidationError < StandardError; end
include Gitlab::Ci::Config::Node::LegacyValidationHelpers
include Gitlab::Ci::Config::Entry::LegacyValidationHelpers
attr_reader :path, :cache, :stages, :jobs
......
......@@ -13,7 +13,7 @@ module Gitlab
def initialize(config)
@config = Loader.new(config).load!
@global = Node::Global.new(@config)
@global = Entry::Global.new(@config)
@global.compose!
end
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# Entry that represents a configuration of job artifacts.
#
class Artifacts < Entry
class Artifacts < Node
include Validatable
include Attributable
......
module Gitlab
module Ci
class Config
module Node
module Entry
module Attributable
extend ActiveSupport::Concern
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# Entry that represents a boolean value.
#
class Boolean < Entry
class Boolean < Node
include Validatable
validations do
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# Entry that represents a cache configuration
#
class Cache < Entry
class Cache < Node
include Configurable
ALLOWED_KEYS = %i[key untracked paths]
......@@ -14,13 +14,13 @@ module Gitlab
validates :config, allowed_keys: ALLOWED_KEYS
end
node :key, Node::Key,
entry :key, Entry::Key,
description: 'Cache key used to define a cache affinity.'
node :untracked, Node::Boolean,
entry :untracked, Entry::Boolean,
description: 'Cache all untracked files.'
node :paths, Node::Paths,
entry :paths, Entry::Paths,
description: 'Specify which paths should be cached across builds.'
end
end
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# Entry that represents a job script.
#
class Commands < Entry
class Commands < Node
include Validatable
validations do
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# This mixin is responsible for adding DSL, which purpose is to
# simplifly process of adding child nodes.
......@@ -48,8 +48,8 @@ module Gitlab
private # rubocop:disable Lint/UselessAccessModifier
def node(key, node, metadata)
factory = Node::Factory.new(node)
def entry(key, entry, metadata)
factory = Entry::Factory.new(entry)
.with(description: metadata[:description])
(@nodes ||= {}).merge!(key.to_sym => factory)
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# Entry that represents an environment.
#
class Environment < Entry
class Environment < Node
include Validatable
ALLOWED_KEYS = %i[name url action on_stop]
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# Factory class responsible for fabricating node entry objects.
# Factory class responsible for fabricating entry objects.
#
class Factory
class InvalidFactory < StandardError; end
def initialize(node)
@node = node
def initialize(entry)
@entry = entry
@metadata = {}
@attributes = {}
end
......@@ -37,11 +37,11 @@ module Gitlab
# See issue #18775.
#
if @value.nil?
Node::Unspecified.new(
Entry::Unspecified.new(
fabricate_unspecified
)
else
fabricate(@node, @value)
fabricate(@entry, @value)
end
end
......@@ -49,21 +49,21 @@ module Gitlab
def fabricate_unspecified
##
# If node has a default value we fabricate concrete node
# If entry has a default value we fabricate concrete node
# with default value.
#
if @node.default.nil?
fabricate(Node::Undefined)
if @entry.default.nil?
fabricate(Entry::Undefined)
else
fabricate(@node, @node.default)
fabricate(@entry, @entry.default)
end
end
def fabricate(node, value = nil)
node.new(value, @metadata).tap do |entry|
entry.key = @attributes[:key]
entry.parent = @attributes[:parent]
entry.description = @attributes[:description]
def fabricate(entry, value = nil)
entry.new(value, @metadata).tap do |node|
node.key = @attributes[:key]
node.parent = @attributes[:parent]
node.description = @attributes[:description]
end
end
end
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# This class represents a global entry - root node for entire
# This class represents a global entry - root Entry for entire
# GitLab CI Configuration file.
#
class Global < Entry
class Global < Node
include Configurable
node :before_script, Node::Script,
entry :before_script, Entry::Script,
description: 'Script that will be executed before each job.'
node :image, Node::Image,
entry :image, Entry::Image,
description: 'Docker image that will be used to execute jobs.'
node :services, Node::Services,
entry :services, Entry::Services,
description: 'Docker images that will be linked to the container.'
node :after_script, Node::Script,
entry :after_script, Entry::Script,
description: 'Script that will be executed after each job.'
node :variables, Node::Variables,
entry :variables, Entry::Variables,
description: 'Environment variables that will be used.'
node :stages, Node::Stages,
entry :stages, Entry::Stages,
description: 'Configuration of stages for this pipeline.'
node :types, Node::Stages,
entry :types, Entry::Stages,
description: 'Deprecated: stages for this pipeline.'
node :cache, Node::Cache,
entry :cache, Entry::Cache,
description: 'Configure caching between build jobs.'
helpers :before_script, :image, :services, :after_script,
......@@ -46,7 +46,7 @@ module Gitlab
private
def compose_jobs!
factory = Node::Factory.new(Node::Jobs)
factory = Entry::Factory.new(Entry::Jobs)
.value(@config.except(*self.class.nodes.keys))
.with(key: :jobs, parent: self,
description: 'Jobs definition for this pipeline')
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# Entry that represents a hidden CI/CD job.
# Entry that represents a hidden CI/CD key.
#
class Hidden < Entry
class Hidden < Node
include Validatable
validations do
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# Entry that represents a Docker image.
#
class Image < Entry
class Image < Node
include Validatable
validations do
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# Entry that represents a concrete CI/CD job.
#
class Job < Entry
class Job < Node
include Configurable
include Attributable
......@@ -34,43 +34,43 @@ module Gitlab
end
end
node :before_script, Node::Script,
entry :before_script, Entry::Script,
description: 'Global before script overridden in this job.'
node :script, Node::Commands,
entry :script, Entry::Commands,
description: 'Commands that will be executed in this job.'
node :stage, Node::Stage,
entry :stage, Entry::Stage,
description: 'Pipeline stage this job will be executed into.'
node :type, Node::Stage,
entry :type, Entry::Stage,
description: 'Deprecated: stage this job will be executed into.'
node :after_script, Node::Script,
entry :after_script, Entry::Script,
description: 'Commands that will be executed when finishing job.'
node :cache, Node::Cache,
entry :cache, Entry::Cache,
description: 'Cache definition for this job.'
node :image, Node::Image,
entry :image, Entry::Image,
description: 'Image that will be used to execute this job.'
node :services, Node::Services,
entry :services, Entry::Services,
description: 'Services that will be used to execute this job.'
node :only, Node::Trigger,
entry :only, Entry::Trigger,
description: 'Refs policy this job will be executed for.'
node :except, Node::Trigger,
entry :except, Entry::Trigger,
description: 'Refs policy this job will be executed for.'
node :variables, Node::Variables,
entry :variables, Entry::Variables,
description: 'Environment variables available for this job.'
node :artifacts, Node::Artifacts,
entry :artifacts, Entry::Artifacts,
description: 'Artifacts configuration for this job.'
node :environment, Node::Environment,
entry :environment, Entry::Environment,
description: 'Environment configuration for this job.'
helpers :before_script, :script, :stage, :type, :after_script,
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# Entry that represents a set of jobs.
#
class Jobs < Entry
class Jobs < Node
include Validatable
validations do
......@@ -29,9 +29,9 @@ module Gitlab
def compose!(deps = nil)
super do
@config.each do |name, config|
node = hidden?(name) ? Node::Hidden : Node::Job
node = hidden?(name) ? Entry::Hidden : Entry::Job
factory = Node::Factory.new(node)
factory = Entry::Factory.new(node)
.value(config || {})
.metadata(name: name)
.with(key: name, parent: self,
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# Entry that represents a key.
#
class Key < Entry
class Key < Node
include Validatable
validations do
......
module Gitlab
module Ci
class Config
module Node
module Entry
module LegacyValidationHelpers
private
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# Base abstract class for each configuration entry node.
#
class Entry
class Node
class InvalidError < StandardError; end
attr_reader :config, :metadata
......@@ -21,7 +21,7 @@ module Gitlab
end
def [](key)
@entries[key] || Node::Undefined.new
@entries[key] || Entry::Undefined.new
end
def compose!(deps = nil)
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# Entry that represents an array of paths.
#
class Paths < Entry
class Paths < Node
include Validatable
validations do
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# Entry that represents a script.
#
class Script < Entry
class Script < Node
include Validatable
validations do
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# Entry that represents a configuration of Docker services.
#
class Services < Entry
class Services < Node
include Validatable
validations do
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# Entry that represents a stage for a job.
#
class Stage < Entry
class Stage < Node
include Validatable
validations do
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# Entry that represents a configuration for pipeline stages.
#
class Stages < Entry
class Stages < Node
include Validatable
validations do
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# Entry that represents a trigger policy for the job.
#
class Trigger < Entry
class Trigger < Node
include Validatable
validations do
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# This class represents an undefined node.
# This class represents an undefined entry.
#
# Implements the Null Object pattern.
#
class Undefined < Entry
class Undefined < Node
def initialize(*)
super(nil)
end
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# This class represents an unspecified entry node.
# This class represents an unspecified entry.
#
# It decorates original entry adding method that indicates it is
# unspecified.
......
module Gitlab
module Ci
class Config
module Node
module Entry
module Validatable
extend ActiveSupport::Concern
class_methods do
def validator
@validator ||= Class.new(Node::Validator).tap do |validator|
@validator ||= Class.new(Entry::Validator).tap do |validator|
if defined?(@validations)
@validations.each { |rules| validator.class_eval(&rules) }
end
......
module Gitlab
module Ci
class Config
module Node
module Entry
class Validator < SimpleDelegator
include ActiveModel::Validations
include Node::Validators
include Entry::Validators
def initialize(node)
super(node)
@node = node
def initialize(entry)
super(entry)
@entry = entry
end
def messages
......@@ -30,7 +30,7 @@ module Gitlab
def key_name
if key.blank?
@node.class.name.demodulize.underscore.humanize
@entry.class.name.demodulize.underscore.humanize
else
key
end
......
module Gitlab
module Ci
class Config
module Node
module Entry
module Validators
class AllowedKeysValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
......
module Gitlab
module Ci
class Config
module Node
module Entry
##
# Entry that represents environment variables.
#
class Variables < Entry
class Variables < Node
include Validatable
validations do
......
......@@ -24,7 +24,7 @@ module Gitlab
end
def sidekiq_throttling_enabled?
current_application_settings.sidekiq_throttling_enabled
current_application_settings.sidekiq_throttling_enabled?
end
def fake_application_settings
......
......@@ -89,9 +89,7 @@ module Gitlab
end
def user_filter(filter = nil)
if config.user_filter.present?
user_filter = Net::LDAP::Filter.construct(config.user_filter)
end
user_filter = config.constructed_user_filter if config.user_filter.present?
if user_filter && filter
Net::LDAP::Filter.join(filter, user_filter)
......
......@@ -54,11 +54,9 @@ module Gitlab
# Apply LDAP user filter if present
if config.user_filter.present?
filter = Net::LDAP::Filter.join(
filter,
Net::LDAP::Filter.construct(config.user_filter)
)
filter = Net::LDAP::Filter.join(filter, config.constructed_user_filter)
end
filter
end
......
......@@ -13,7 +13,7 @@ module Gitlab
end
def self.providers
servers.map {|server| server['provider_name'] }
servers.map { |server| server['provider_name'] }
end
def self.valid_provider?(provider)
......@@ -38,13 +38,31 @@ module Gitlab
end
def adapter_options
{
host: options['host'],
port: options['port'],
encryption: encryption
}.tap do |options|
options.merge!(auth_options) if has_auth?
opts = base_options.merge(
encryption: encryption,
)
opts.merge!(auth_options) if has_auth?
opts
end
def omniauth_options
opts = base_options.merge(
base: base,
method: options['method'],
filter: omniauth_user_filter,
name_proc: name_proc
)
if has_auth?
opts.merge!(
bind_dn: options['bind_dn'],
password: options['password']
)
end
opts
end
def base
......@@ -68,6 +86,10 @@ module Gitlab
options['user_filter']
end
def constructed_user_filter
@constructed_user_filter ||= Net::LDAP::Filter.construct(user_filter)
end
def group_base
options['group_base']
end
......@@ -96,8 +118,27 @@ module Gitlab
options['password'] || options['bind_dn']
end
def allow_username_or_email_login
options['allow_username_or_email_login']
end
def name_proc
if allow_username_or_email_login
Proc.new { |name| name.gsub(/@.*\z/, '') }
else
Proc.new { |name| name }
end
end
protected
def base_options
{
host: options['host'],
port: options['port']
}
end
def base_config
Gitlab.config.ldap
end
......@@ -126,6 +167,16 @@ module Gitlab
}
}
end
def omniauth_user_filter
uid_filter = Net::LDAP::Filter.eq(uid, '%{username}')
if user_filter.present?
Net::LDAP::Filter.join(uid_filter, constructed_user_filter).to_s
else
uid_filter.to_s
end
end
end
end
end
......@@ -26,12 +26,12 @@ module Gitlab
end
def project_name_regex
@project_name_regex ||= /\A[\p{Alnum}_][\p{Alnum}\p{Pd}_\. ]*\z/.freeze
@project_name_regex ||= /\A[\p{Alnum}\u{00A9}-\u{1f9c0}_][\p{Alnum}\p{Pd}\u{00A9}-\u{1f9c0}_\. ]*\z/.freeze
end
def project_name_regex_message
"can contain only letters, digits, '_', '.', dash and space. " \
"It must start with letter, digit or '_'."
"can contain only letters, digits, emojis, '_', '.', dash, space. " \
"It must start with letter, digit, emoji or '_'."
end
def project_path_regex
......
......@@ -7,6 +7,40 @@ describe HelpController do
sign_in(user)
end
describe 'GET #index' do
context 'when url prefixed without /help/' do
it 'has correct url prefix' do
stub_readme("[API](api/README.md)")
get :index
expect(assigns[:help_index]).to eq '[API](/help/api/README.md)'
end
end
context 'when url prefixed with help/' do
it 'will be an absolute path' do
stub_readme("[API](help/api/README.md)")
get :index
expect(assigns[:help_index]).to eq '[API](/help/api/README.md)'
end
end
context 'when url prefixed with help' do
it 'will be an absolute path' do
stub_readme("[API](helpful_hints/README.md)")
get :index
expect(assigns[:help_index]).to eq '[API](/help/helpful_hints/README.md)'
end
end
context 'when url prefixed with /help/' do
it 'will not be changed' do
stub_readme("[API](/help/api/README.md)")
get :index
expect(assigns[:help_index]).to eq '[API](/help/api/README.md)'
end
end
end
describe 'GET #show' do
context 'for Markdown formats' do
context 'when requested file exists' do
......@@ -72,4 +106,8 @@ describe HelpController do
end
end
end
def stub_readme(content)
allow(File).to receive(:read).and_return(content)
end
end
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment