Commit c3da8849 authored by Sean McGivern's avatar Sean McGivern

Merge branch 'master' into fabsrc/gitlab-ce-2105-add-setting-for-first-day-of-the-week

parents 1a0bab0a c8fe0d6a
1.17.0 1.18.0
\ No newline at end of file
...@@ -422,7 +422,7 @@ group :ed25519 do ...@@ -422,7 +422,7 @@ group :ed25519 do
end end
# Gitaly GRPC client # Gitaly GRPC client
gem 'gitaly-proto', '~> 1.5.0', require: 'gitaly' gem 'gitaly-proto', '~> 1.10.0', require: 'gitaly'
gem 'grpc', '~> 1.15.0' gem 'grpc', '~> 1.15.0'
gem 'google-protobuf', '~> 3.6' gem 'google-protobuf', '~> 3.6'
......
...@@ -278,7 +278,7 @@ GEM ...@@ -278,7 +278,7 @@ GEM
gettext_i18n_rails (>= 0.7.1) gettext_i18n_rails (>= 0.7.1)
po_to_json (>= 1.0.0) po_to_json (>= 1.0.0)
rails (>= 3.2.0) rails (>= 3.2.0)
gitaly-proto (1.5.0) gitaly-proto (1.10.0)
grpc (~> 1.0) grpc (~> 1.0)
github-markup (1.7.0) github-markup (1.7.0)
gitlab-default_value_for (3.1.1) gitlab-default_value_for (3.1.1)
...@@ -1020,7 +1020,7 @@ DEPENDENCIES ...@@ -1020,7 +1020,7 @@ DEPENDENCIES
gettext (~> 3.2.2) gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3) gettext_i18n_rails_js (~> 1.3)
gitaly-proto (~> 1.5.0) gitaly-proto (~> 1.10.0)
github-markup (~> 1.7.0) github-markup (~> 1.7.0)
gitlab-default_value_for (~> 3.1.1) gitlab-default_value_for (~> 3.1.1)
gitlab-markup (~> 1.6.5) gitlab-markup (~> 1.6.5)
......
...@@ -6,6 +6,7 @@ import { polyfillSticky } from '~/lib/utils/sticky'; ...@@ -6,6 +6,7 @@ import { polyfillSticky } from '~/lib/utils/sticky';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import CompareVersionsDropdown from './compare_versions_dropdown.vue'; import CompareVersionsDropdown from './compare_versions_dropdown.vue';
import SettingsDropdown from './settings_dropdown.vue'; import SettingsDropdown from './settings_dropdown.vue';
import DiffStats from './diff_stats.vue';
export default { export default {
components: { components: {
...@@ -14,6 +15,7 @@ export default { ...@@ -14,6 +15,7 @@ export default {
GlLink, GlLink,
GlButton, GlButton,
SettingsDropdown, SettingsDropdown,
DiffStats,
}, },
directives: { directives: {
GlTooltip: GlTooltipDirective, GlTooltip: GlTooltipDirective,
...@@ -35,8 +37,15 @@ export default { ...@@ -35,8 +37,15 @@ export default {
}, },
}, },
computed: { computed: {
...mapState('diffs', ['commit', 'showTreeList', 'startVersion', 'latestVersionPath']), ...mapGetters('diffs', ['hasCollapsedFile', 'diffFilesLength']),
...mapGetters('diffs', ['hasCollapsedFile']), ...mapState('diffs', [
'commit',
'showTreeList',
'startVersion',
'latestVersionPath',
'addedLines',
'removedLines',
]),
comparableDiffs() { comparableDiffs() {
return this.mergeRequestDiffs.slice(1); return this.mergeRequestDiffs.slice(1);
}, },
...@@ -104,6 +113,11 @@ export default { ...@@ -104,6 +113,11 @@ export default {
<gl-link :href="commit.commit_url" class="monospace">{{ commit.short_id }}</gl-link> <gl-link :href="commit.commit_url" class="monospace">{{ commit.short_id }}</gl-link>
</div> </div>
<div class="inline-parallel-buttons d-none d-md-flex ml-auto"> <div class="inline-parallel-buttons d-none d-md-flex ml-auto">
<diff-stats
:diff-files-length="diffFilesLength"
:added-lines="addedLines"
:removed-lines="removedLines"
/>
<gl-button <gl-button
v-if="commit || startVersion" v-if="commit || startVersion"
:href="latestVersionPath" :href="latestVersionPath"
......
...@@ -9,6 +9,7 @@ import { GlTooltipDirective } from '@gitlab/ui'; ...@@ -9,6 +9,7 @@ import { GlTooltipDirective } from '@gitlab/ui';
import { truncateSha } from '~/lib/utils/text_utility'; import { truncateSha } from '~/lib/utils/text_utility';
import { __, s__, sprintf } from '~/locale'; import { __, s__, sprintf } from '~/locale';
import EditButton from './edit_button.vue'; import EditButton from './edit_button.vue';
import DiffStats from './diff_stats.vue';
export default { export default {
components: { components: {
...@@ -16,6 +17,7 @@ export default { ...@@ -16,6 +17,7 @@ export default {
EditButton, EditButton,
Icon, Icon,
FileIcon, FileIcon,
DiffStats,
}, },
directives: { directives: {
GlTooltip: GlTooltipDirective, GlTooltip: GlTooltipDirective,
...@@ -202,6 +204,7 @@ export default { ...@@ -202,6 +204,7 @@ export default {
v-if="!diffFile.submodule && addMergeRequestButtons" v-if="!diffFile.submodule && addMergeRequestButtons"
class="file-actions d-none d-sm-block" class="file-actions d-none d-sm-block"
> >
<diff-stats :added-lines="diffFile.added_lines" :removed-lines="diffFile.removed_lines" />
<template v-if="diffFile.blob && diffFile.blob.readable_text"> <template v-if="diffFile.blob && diffFile.blob.readable_text">
<button <button
:disabled="!diffHasDiscussions(diffFile)" :disabled="!diffHasDiscussions(diffFile)"
......
<script>
import Icon from '~/vue_shared/components/icon.vue';
import { n__ } from '~/locale';
export default {
components: { Icon },
props: {
addedLines: {
type: Number,
required: true,
},
removedLines: {
type: Number,
required: true,
},
diffFilesLength: {
type: Number,
required: false,
default: null,
},
},
computed: {
filesText() {
return n__('File', 'Files', this.diffFilesLength);
},
isCompareVersionsHeader() {
return Boolean(this.diffFilesLength);
},
},
};
</script>
<template>
<div
class="diff-stats"
:class="{
'is-compare-versions-header d-none d-lg-inline-flex': isCompareVersionsHeader,
'd-inline-flex': !isCompareVersionsHeader,
}"
>
<div v-if="diffFilesLength !== null" class="diff-stats-group">
<icon name="doc-code" class="diff-stats-icon text-secondary" />
<strong>{{ diffFilesLength }} {{ filesText }}</strong>
</div>
<div class="diff-stats-group cgreen">
<icon name="file-addition" class="diff-stats-icon" /> <strong>{{ addedLines }}</strong>
</div>
<div class="diff-stats-group cred">
<icon name="file-deletion" class="diff-stats-icon" /> <strong>{{ removedLines }}</strong>
</div>
</div>
</template>
...@@ -14,8 +14,8 @@ export default { ...@@ -14,8 +14,8 @@ export default {
FileRow, FileRow,
}, },
computed: { computed: {
...mapState('diffs', ['tree', 'addedLines', 'removedLines', 'renderTreeList']), ...mapState('diffs', ['tree', 'renderTreeList']),
...mapGetters('diffs', ['allBlobs', 'diffFilesLength']), ...mapGetters('diffs', ['allBlobs']),
filteredTreeList() { filteredTreeList() {
return this.renderTreeList ? this.tree : this.allBlobs; return this.renderTreeList ? this.tree : this.allBlobs;
}, },
...@@ -64,13 +64,6 @@ export default { ...@@ -64,13 +64,6 @@ export default {
{{ s__('MergeRequest|No files found') }} {{ s__('MergeRequest|No files found') }}
</p> </p>
</div> </div>
<div v-once class="pt-3 pb-3 text-center">
{{ n__('%d changed file', '%d changed files', diffFilesLength) }}
<div>
<span class="cgreen"> {{ n__('%d addition', '%d additions', addedLines) }} </span>
<span class="cred"> {{ n__('%d deleted', '%d deletions', removedLines) }} </span>
</div>
</div>
</div> </div>
</template> </template>
......
...@@ -11,6 +11,8 @@ const storedTreeShow = localStorage.getItem(MR_TREE_SHOW_KEY); ...@@ -11,6 +11,8 @@ const storedTreeShow = localStorage.getItem(MR_TREE_SHOW_KEY);
export default () => ({ export default () => ({
isLoading: true, isLoading: true,
addedLines: null,
removedLines: null,
endpoint: '', endpoint: '',
basePath: '', basePath: '',
commit: null, commit: null,
......
export default (buttonSelector, fileSelector) => { export default (buttonSelector, fileSelector) => {
const btn = document.querySelector(buttonSelector); const btn = document.querySelector(buttonSelector);
const fileInput = document.querySelector(fileSelector); const fileInput = document.querySelector(fileSelector);
if (!btn || !fileInput) return;
const form = btn.closest('form'); const form = btn.closest('form');
btn.addEventListener('click', () => { btn.addEventListener('click', () => {
......
...@@ -189,8 +189,8 @@ export default { ...@@ -189,8 +189,8 @@ export default {
<template> <template>
<div class="prometheus-graph col-12 col-lg-6"> <div class="prometheus-graph col-12 col-lg-6">
<div class="prometheus-graph-header"> <div class="prometheus-graph-header">
<h5 class="prometheus-graph-title">{{ graphData.title }}</h5> <h5 ref="graphTitle" class="prometheus-graph-title">{{ graphData.title }}</h5>
<div class="prometheus-graph-widgets"><slot></slot></div> <div ref="graphWidgets" class="prometheus-graph-widgets"><slot></slot></div>
</div> </div>
<gl-area-chart <gl-area-chart
ref="areaChart" ref="areaChart"
......
...@@ -160,7 +160,8 @@ export default { ...@@ -160,7 +160,8 @@ export default {
{{ s__('Metrics|Environment') }} {{ s__('Metrics|Environment') }}
<div class="dropdown prepend-left-10"> <div class="dropdown prepend-left-10">
<button class="dropdown-menu-toggle" data-toggle="dropdown" type="button"> <button class="dropdown-menu-toggle" data-toggle="dropdown" type="button">
<span> {{ currentEnvironmentName }} </span> <icon name="chevron-down" /> <span>{{ currentEnvironmentName }}</span>
<icon name="chevron-down" />
</button> </button>
<div <div
v-if="store.environmentsData.length > 0" v-if="store.environmentsData.length > 0"
...@@ -172,9 +173,8 @@ export default { ...@@ -172,9 +173,8 @@ export default {
:href="environment.metrics_path" :href="environment.metrics_path"
:class="{ 'is-active': environment.name == currentEnvironmentName }" :class="{ 'is-active': environment.name == currentEnvironmentName }"
class="dropdown-item" class="dropdown-item"
>{{ environment.name }}</a
> >
{{ environment.name }}
</a>
</li> </li>
</ul> </ul>
</div> </div>
......
import ProjectsList from '~/projects_list'; import ProjectsList from '~/projects_list';
import Star from '../../../star';
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
new ProjectsList(); // eslint-disable-line no-new new ProjectsList(); // eslint-disable-line no-new
new Star('.project-row'); // eslint-disable-line no-new
}); });
import ProjectsList from '~/projects_list'; import ProjectsList from '~/projects_list';
import Star from '../../../star';
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
new ProjectsList(); // eslint-disable-line no-new new ProjectsList(); // eslint-disable-line no-new
new Star('.project-row'); // eslint-disable-line no-new
}); });
import monitoringBundle from '~/monitoring/monitoring_bundle'; import monitoringBundle from 'ee_else_ce/monitoring/monitoring_bundle';
document.addEventListener('DOMContentLoaded', monitoringBundle); document.addEventListener('DOMContentLoaded', monitoringBundle);
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
*/ */
.file-holder { .file-holder {
border: 1px solid $border-color; border: 1px solid $border-color;
border-top: 0;
border-radius: $border-radius-default; border-radius: $border-radius-default;
&.file-holder-no-border { &.file-holder-no-border {
...@@ -51,6 +52,7 @@ ...@@ -51,6 +52,7 @@
position: absolute; position: absolute;
top: 5px; top: 5px;
right: 15px; right: 15px;
margin-left: auto;
.btn { .btn {
padding: 0 10px; padding: 0 10px;
...@@ -324,10 +326,12 @@ span.idiff { ...@@ -324,10 +326,12 @@ span.idiff {
&, &,
.file-holder & { .file-holder & {
display: flex; display: flex;
flex-wrap: wrap;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
background-color: $gray-light; background-color: $gray-light;
border-bottom: 1px solid $border-color; border-bottom: 1px solid $border-color;
border-top: 1px solid $border-color;
padding: 5px $gl-padding; padding: 5px $gl-padding;
margin: 0; margin: 0;
border-radius: $border-radius-default $border-radius-default 0 0; border-radius: $border-radius-default $border-radius-default 0 0;
...@@ -365,16 +369,12 @@ span.idiff { ...@@ -365,16 +369,12 @@ span.idiff {
margin: 0 10px 0 0; margin: 0 10px 0 0;
} }
.file-actions { .file-actions .btn {
white-space: nowrap; padding: 0 10px;
font-size: 13px;
.btn { line-height: 28px;
padding: 0 10px; display: inline-block;
font-size: 13px; float: none;
line-height: 28px;
display: inline-block;
float: none;
}
} }
@include media-breakpoint-down(xs) { @include media-breakpoint-down(xs) {
......
...@@ -490,6 +490,7 @@ $builds-trace-bg: #111; ...@@ -490,6 +490,7 @@ $builds-trace-bg: #111;
*/ */
$commit-max-width-marker-color: rgba(0, 0, 0, 0); $commit-max-width-marker-color: rgba(0, 0, 0, 0);
$commit-message-text-area-bg: rgba(0, 0, 0, 0); $commit-message-text-area-bg: rgba(0, 0, 0, 0);
$commit-stat-summary-height: 36px;
/* /*
* Common * Common
...@@ -664,8 +665,14 @@ $priority-label-empty-state-width: 114px; ...@@ -664,8 +665,14 @@ $priority-label-empty-state-width: 114px;
Issues Analytics Issues Analytics
*/ */
$issues-analytics-popover-boarder-color: rgba(0, 0, 0, 0.15); $issues-analytics-popover-boarder-color: rgba(0, 0, 0, 0.15);
/* /*
Merge Requests Merge Requests
*/ */
$mr-tabs-height: 51px; $mr-tabs-height: 51px;
$mr-version-controls-height: 56px; $mr-version-controls-height: 56px;
/*
Compare Branches
*/
$compare-branches-sticky-header-height: 68px;
...@@ -7,22 +7,13 @@ ...@@ -7,22 +7,13 @@
cursor: pointer; cursor: pointer;
@media (min-width: map-get($grid-breakpoints, md)) { @media (min-width: map-get($grid-breakpoints, md)) {
$mr-file-header-top: $mr-version-controls-height + $header-height + $mr-tabs-height;
position: -webkit-sticky; position: -webkit-sticky;
position: sticky; position: sticky;
top: $mr-version-controls-height + $header-height + $mr-tabs-height; top: $mr-file-header-top;
margin-left: -1px;
border-left: 1px solid $border-color;
z-index: 102; z-index: 102;
&.is-commit {
top: $header-height + 36px;
.with-performance-bar & {
top: $header-height + 36px + $performance-bar-height;
}
}
&::before { &::before {
content: ''; content: '';
position: absolute; position: absolute;
...@@ -35,7 +26,23 @@ ...@@ -35,7 +26,23 @@
} }
.with-performance-bar & { .with-performance-bar & {
top: $header-height + $performance-bar-height + $mr-version-controls-height + $mr-tabs-height; top: $mr-file-header-top + $performance-bar-height;
}
&.is-commit {
top: $header-height + $commit-stat-summary-height;
.with-performance-bar & {
top: $header-height + $commit-stat-summary-height + $performance-bar-height;
}
}
&.is-compare {
top: $header-height + $compare-branches-sticky-header-height;
.with-performance-bar & {
top: $performance-bar-height + $header-height + $compare-branches-sticky-header-height;
}
} }
} }
...@@ -501,6 +508,25 @@ ...@@ -501,6 +508,25 @@
} }
} }
.diff-stats {
align-items: center;
padding: 0 .25rem;
.diff-stats-group {
padding: 0 .25rem;
}
svg.diff-stats-icon {
vertical-align: text-bottom;
}
&.is-compare-versions-header {
.diff-stats-group {
padding: 0 .5rem;
}
}
}
.file-content .diff-file { .file-content .diff-file {
margin: 0; margin: 0;
border: 0; border: 0;
......
...@@ -946,6 +946,11 @@ pre.light-well { ...@@ -946,6 +946,11 @@ pre.light-well {
.flex-wrapper { .flex-wrapper {
min-width: 0; min-width: 0;
margin-top: -$gl-padding-8; // negative margin required for flex-wrap margin-top: -$gl-padding-8; // negative margin required for flex-wrap
flex: 1 1 100%;
.project-title {
line-height: 20px;
}
} }
p, p,
...@@ -984,14 +989,16 @@ pre.light-well { ...@@ -984,14 +989,16 @@ pre.light-well {
} }
.controls { .controls {
margin-top: $gl-padding-8; @include media-breakpoint-down(xs) {
margin-top: $gl-padding-8;
}
@include media-breakpoint-down(md) { @include media-breakpoint-up(sm) {
margin-top: 0; margin-top: 0;
} }
@include media-breakpoint-down(xs) { @include media-breakpoint-up(lg) {
margin-top: $gl-padding-8; flex: 1 1 40%;
} }
.icon-wrapper { .icon-wrapper {
...@@ -1041,7 +1048,7 @@ pre.light-well { ...@@ -1041,7 +1048,7 @@ pre.light-well {
min-height: 40px; min-height: 40px;
min-width: 40px; min-width: 40px;
.identicon.s64 { .identicon.s48 {
font-size: 16px; font-size: 16px;
} }
} }
......
...@@ -127,6 +127,7 @@ class Clusters::ClustersController < Clusters::BaseController ...@@ -127,6 +127,7 @@ class Clusters::ClustersController < Clusters::BaseController
params.require(:cluster).permit( params.require(:cluster).permit(
:enabled, :enabled,
:environment_scope, :environment_scope,
:base_domain,
platform_kubernetes_attributes: [ platform_kubernetes_attributes: [
:namespace :namespace
] ]
...@@ -136,6 +137,7 @@ class Clusters::ClustersController < Clusters::BaseController ...@@ -136,6 +137,7 @@ class Clusters::ClustersController < Clusters::BaseController
:enabled, :enabled,
:name, :name,
:environment_scope, :environment_scope,
:base_domain,
platform_kubernetes_attributes: [ platform_kubernetes_attributes: [
:api_url, :api_url,
:token, :token,
......
...@@ -116,8 +116,12 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController ...@@ -116,8 +116,12 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
session[:service_tickets][provider] = ticket session[:service_tickets][provider] = ticket
end end
def build_auth_user(auth_user_class)
auth_user_class.new(oauth)
end
def sign_in_user_flow(auth_user_class) def sign_in_user_flow(auth_user_class)
auth_user = auth_user_class.new(oauth) auth_user = build_auth_user(auth_user_class)
user = auth_user.find_and_update! user = auth_user.find_and_update!
if auth_user.valid_sign_in? if auth_user.valid_sign_in?
......
...@@ -11,11 +11,6 @@ class Projects::EnvironmentsController < Projects::ApplicationController ...@@ -11,11 +11,6 @@ class Projects::EnvironmentsController < Projects::ApplicationController
before_action :verify_api_request!, only: :terminal_websocket_authorize before_action :verify_api_request!, only: :terminal_websocket_authorize
before_action :expire_etag_cache, only: [:index] before_action :expire_etag_cache, only: [:index]
before_action do
push_frontend_feature_flag(:area_chart, project)
end
# Returns all environments or all folders based on the :nested param
def index def index
@environments = project.environments @environments = project.environments
.with_state(params[:scope] || :available) .with_state(params[:scope] || :available)
......
...@@ -305,7 +305,7 @@ class IssuableFinder ...@@ -305,7 +305,7 @@ class IssuableFinder
def use_subquery_for_search? def use_subquery_for_search?
strong_memoize(:use_subquery_for_search) do strong_memoize(:use_subquery_for_search) do
attempt_group_search_optimizations? && attempt_group_search_optimizations? &&
Feature.enabled?(:use_subquery_for_group_issues_search, default_enabled: false) Feature.enabled?(:use_subquery_for_group_issues_search, default_enabled: true)
end end
end end
......
...@@ -232,7 +232,8 @@ module ApplicationSettingsHelper ...@@ -232,7 +232,8 @@ module ApplicationSettingsHelper
:web_ide_clientside_preview_enabled, :web_ide_clientside_preview_enabled,
:diff_max_patch_bytes, :diff_max_patch_bytes,
:commit_email_hostname, :commit_email_hostname,
:protected_ci_variables :protected_ci_variables,
:local_markdown_version
] ]
end end
......
...@@ -9,41 +9,4 @@ module AutoDevopsHelper ...@@ -9,41 +9,4 @@ module AutoDevopsHelper
!project.repository.gitlab_ci_yml && !project.repository.gitlab_ci_yml &&
!project.ci_service !project.ci_service
end end
def auto_devops_warning_message(project)
if missing_auto_devops_service?(project)
params = {
kubernetes: link_to('Kubernetes cluster', project_clusters_path(project))
}
if missing_auto_devops_domain?(project)
_('Auto Review Apps and Auto Deploy need a domain name and a %{kubernetes} to work correctly.') % params
else
_('Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly.') % params
end
elsif missing_auto_devops_domain?(project)
_('Auto Review Apps and Auto Deploy need a domain name to work correctly.')
end
end
# rubocop: disable CodeReuse/ActiveRecord
def cluster_ingress_ip(project)
project
.cluster_ingresses
.where("external_ip is not null")
.limit(1)
.pluck(:external_ip)
.first
end
# rubocop: enable CodeReuse/ActiveRecord
private
def missing_auto_devops_domain?(project)
!(project.auto_devops || project.build_auto_devops)&.has_domain?
end
def missing_auto_devops_service?(project)
!project.deployment_platform&.active?
end
end end
...@@ -193,6 +193,10 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -193,6 +193,10 @@ class ApplicationSetting < ActiveRecord::Base
allow_nil: true, allow_nil: true,
numericality: { only_integer: true, greater_than_or_equal_to: 1.day.seconds } numericality: { only_integer: true, greater_than_or_equal_to: 1.day.seconds }
validates :local_markdown_version,
allow_nil: true,
numericality: { only_integer: true, greater_than_or_equal_to: 0, less_than: 65536 }
SUPPORTED_KEY_TYPES.each do |type| SUPPORTED_KEY_TYPES.each do |type|
validates :"#{type}_key_restriction", presence: true, key_restriction: { type: type } validates :"#{type}_key_restriction", presence: true, key_restriction: { type: type }
end end
...@@ -304,7 +308,8 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -304,7 +308,8 @@ class ApplicationSetting < ActiveRecord::Base
usage_stats_set_by_user_id: nil, usage_stats_set_by_user_id: nil,
diff_max_patch_bytes: Gitlab::Git::Diff::DEFAULT_MAX_PATCH_BYTES, diff_max_patch_bytes: Gitlab::Git::Diff::DEFAULT_MAX_PATCH_BYTES,
commit_email_hostname: default_commit_email_hostname, commit_email_hostname: default_commit_email_hostname,
protected_ci_variables: false protected_ci_variables: false,
local_markdown_version: 0
} }
end end
......
...@@ -18,6 +18,7 @@ module Clusters ...@@ -18,6 +18,7 @@ module Clusters
Applications::Knative.application_name => Applications::Knative Applications::Knative.application_name => Applications::Knative
}.freeze }.freeze
DEFAULT_ENVIRONMENT = '*'.freeze DEFAULT_ENVIRONMENT = '*'.freeze
KUBE_INGRESS_BASE_DOMAIN = 'KUBE_INGRESS_BASE_DOMAIN'.freeze
belongs_to :user belongs_to :user
...@@ -49,7 +50,7 @@ module Clusters ...@@ -49,7 +50,7 @@ module Clusters
validates :name, cluster_name: true validates :name, cluster_name: true
validates :cluster_type, presence: true validates :cluster_type, presence: true
validates :domain, allow_nil: true, hostname: { allow_numeric_hostname: true, require_valid_tld: true } validates :domain, allow_blank: true, hostname: { allow_numeric_hostname: true, require_valid_tld: true }
validate :restrict_modification, on: :update validate :restrict_modification, on: :update
validate :no_groups, unless: :group_type? validate :no_groups, unless: :group_type?
...@@ -65,6 +66,9 @@ module Clusters ...@@ -65,6 +66,9 @@ module Clusters
delegate :available?, to: :application_ingress, prefix: true, allow_nil: true delegate :available?, to: :application_ingress, prefix: true, allow_nil: true
delegate :available?, to: :application_prometheus, prefix: true, allow_nil: true delegate :available?, to: :application_prometheus, prefix: true, allow_nil: true
delegate :available?, to: :application_knative, prefix: true, allow_nil: true delegate :available?, to: :application_knative, prefix: true, allow_nil: true
delegate :external_ip, to: :application_ingress, prefix: true, allow_nil: true
alias_attribute :base_domain, :domain
enum cluster_type: { enum cluster_type: {
instance_type: 1, instance_type: 1,
...@@ -193,8 +197,41 @@ module Clusters ...@@ -193,8 +197,41 @@ module Clusters
project_type? project_type?
end end
def kube_ingress_domain
@kube_ingress_domain ||= domain.presence || instance_domain || legacy_auto_devops_domain
end
def predefined_variables
Gitlab::Ci::Variables::Collection.new.tap do |variables|
break variables unless kube_ingress_domain
variables.append(key: KUBE_INGRESS_BASE_DOMAIN, value: kube_ingress_domain)
end
end
private private
def instance_domain
@instance_domain ||= Gitlab::CurrentSettings.auto_devops_domain
end
# To keep backward compatibility with AUTO_DEVOPS_DOMAIN
# environment variable, we need to ensure KUBE_INGRESS_BASE_DOMAIN
# is set if AUTO_DEVOPS_DOMAIN is set on any of the following options:
# ProjectAutoDevops#Domain, project variables or group variables,
# as the AUTO_DEVOPS_DOMAIN is needed for CI_ENVIRONMENT_URL
#
# This method should be removed on 12.0
def legacy_auto_devops_domain
if project_type?
project&.auto_devops&.domain.presence ||
project.variables.find_by(key: 'AUTO_DEVOPS_DOMAIN')&.value.presence ||
project.group&.variables&.find_by(key: 'AUTO_DEVOPS_DOMAIN')&.value.presence
elsif group_type?
group.variables.find_by(key: 'AUTO_DEVOPS_DOMAIN')&.value.presence
end
end
def restrict_modification def restrict_modification
if provider&.on_creation? if provider&.on_creation?
errors.add(:base, "cannot modify during creation") errors.add(:base, "cannot modify during creation")
......
...@@ -98,6 +98,8 @@ module Clusters ...@@ -98,6 +98,8 @@ module Clusters
.append(key: 'KUBE_NAMESPACE', value: actual_namespace) .append(key: 'KUBE_NAMESPACE', value: actual_namespace)
.append(key: 'KUBECONFIG', value: kubeconfig, public: false, file: true) .append(key: 'KUBECONFIG', value: kubeconfig, public: false, file: true)
end end
variables.concat(cluster.predefined_variables)
end end
end end
......
...@@ -115,7 +115,28 @@ module CacheMarkdownField ...@@ -115,7 +115,28 @@ module CacheMarkdownField
end end
def latest_cached_markdown_version def latest_cached_markdown_version
CacheMarkdownField::CACHE_COMMONMARK_VERSION @latest_cached_markdown_version ||= (CacheMarkdownField::CACHE_COMMONMARK_VERSION << 16) | local_version
end
def local_version
# because local_markdown_version is stored in application_settings which
# uses cached_markdown_version too, we check explicitly to avoid
# endless loop
return local_markdown_version if has_attribute?(:local_markdown_version)
settings = Gitlab::CurrentSettings.current_application_settings
# Following migrations are not properly isolated and
# use real models (by calling .ghost method), in these migrations
# local_markdown_version attribute doesn't exist yet, so we
# use a default value:
# db/migrate/20170825104051_migrate_issues_to_ghost_user.rb
# db/migrate/20171114150259_merge_requests_author_id_foreign_key.rb
if settings.respond_to?(:local_markdown_version)
settings.local_markdown_version
else
0
end
end end
included do included do
......
...@@ -96,7 +96,9 @@ class PoolRepository < ActiveRecord::Base ...@@ -96,7 +96,9 @@ class PoolRepository < ActiveRecord::Base
@object_pool ||= Gitlab::Git::ObjectPool.new( @object_pool ||= Gitlab::Git::ObjectPool.new(
shard.name, shard.name,
disk_path + '.git', disk_path + '.git',
source_project.repository.raw) source_project.repository.raw,
source_project.full_path
)
end end
def inspect def inspect
......
...@@ -1288,7 +1288,7 @@ class Project < ActiveRecord::Base ...@@ -1288,7 +1288,7 @@ class Project < ActiveRecord::Base
# Forked import is handled asynchronously # Forked import is handled asynchronously
return if forked? && !force return if forked? && !force
if gitlab_shell.create_repository(repository_storage, disk_path) if gitlab_shell.create_project_repository(self)
repository.after_create repository.after_create
true true
else else
......
...@@ -24,6 +24,11 @@ class ProjectAutoDevops < ActiveRecord::Base ...@@ -24,6 +24,11 @@ class ProjectAutoDevops < ActiveRecord::Base
domain.present? || instance_domain.present? domain.present? || instance_domain.present?
end end
# From 11.8, AUTO_DEVOPS_DOMAIN has been replaced by KUBE_INGRESS_BASE_DOMAIN.
# See Clusters::Cluster#predefined_variables and https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/24580
# for more info.
# Support for AUTO_DEVOPS_DOMAIN support will be dropped on 12.0 on
# https://gitlab.com/gitlab-org/gitlab-ce/issues/52363
def predefined_variables def predefined_variables
Gitlab::Ci::Variables::Collection.new.tap do |variables| Gitlab::Ci::Variables::Collection.new.tap do |variables|
if has_domain? if has_domain?
......
...@@ -60,7 +60,7 @@ class ProjectWiki ...@@ -60,7 +60,7 @@ class ProjectWiki
def wiki def wiki
@wiki ||= begin @wiki ||= begin
gl_repository = Gitlab::GlRepository.gl_repository(project, true) gl_repository = Gitlab::GlRepository.gl_repository(project, true)
raw_repository = Gitlab::Git::Repository.new(project.repository_storage, disk_path + '.git', gl_repository) raw_repository = Gitlab::Git::Repository.new(project.repository_storage, disk_path + '.git', gl_repository, full_path)
create_repo!(raw_repository) unless raw_repository.exists? create_repo!(raw_repository) unless raw_repository.exists?
...@@ -175,7 +175,7 @@ class ProjectWiki ...@@ -175,7 +175,7 @@ class ProjectWiki
private private
def create_repo!(raw_repository) def create_repo!(raw_repository)
gitlab_shell.create_repository(project.repository_storage, disk_path) gitlab_shell.create_wiki_repository(project)
raise CouldNotCreateWikiError unless raw_repository.exists? raise CouldNotCreateWikiError unless raw_repository.exists?
......
...@@ -1104,6 +1104,9 @@ class Repository ...@@ -1104,6 +1104,9 @@ class Repository
end end
def initialize_raw_repository def initialize_raw_repository
Gitlab::Git::Repository.new(project.repository_storage, disk_path + '.git', Gitlab::GlRepository.gl_repository(project, is_wiki)) Gitlab::Git::Repository.new(project.repository_storage,
disk_path + '.git',
Gitlab::GlRepository.gl_repository(project, is_wiki),
project.full_path)
end end
end end
...@@ -8,6 +8,7 @@ module Labels ...@@ -8,6 +8,7 @@ module Labels
# returns the updated label # returns the updated label
def execute(label) def execute(label)
params[:name] = params.delete(:new_name) if params.key?(:new_name)
params[:color] = convert_color_name_to_hex if params[:color].present? params[:color] = convert_color_name_to_hex if params[:color].present?
label.update(params) label.update(params)
......
...@@ -73,7 +73,7 @@ module Projects ...@@ -73,7 +73,7 @@ module Projects
project.ensure_repository project.ensure_repository
project.repository.fetch_as_mirror(project.import_url, refmap: refmap) project.repository.fetch_as_mirror(project.import_url, refmap: refmap)
else else
gitlab_shell.import_repository(project.repository_storage, project.disk_path, project.import_url) gitlab_shell.import_project_repository(project)
end end
rescue Gitlab::Shell::Error => e rescue Gitlab::Shell::Error => e
# Expire cache to prevent scenarios such as: # Expire cache to prevent scenarios such as:
......
...@@ -20,12 +20,27 @@ ...@@ -20,12 +20,27 @@
.form-text.text-muted= s_("ClusterIntegration|Choose which of your environments will use this cluster.") .form-text.text-muted= s_("ClusterIntegration|Choose which of your environments will use this cluster.")
- else - else
= text_field_tag :environment_scope, '*', class: 'col-md-6 form-control disabled', placeholder: s_('ClusterIntegration|Environment scope'), disabled: true = text_field_tag :environment_scope, '*', class: 'col-md-6 form-control disabled', placeholder: s_('ClusterIntegration|Environment scope'), disabled: true
- environment_scope_url = 'https://docs.gitlab.com/ee/user/project/clusters/#setting-the-environment-scope-premium' - environment_scope_url = help_page_path('user/project/clusters/index', anchor: 'base-domain')
- environment_scope_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: environment_scope_url } - environment_scope_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: environment_scope_url }
.form-text.text-muted .form-text.text-muted
%code * %code *
= s_("ClusterIntegration| is the default environment scope for this cluster. This means that all jobs, regardless of their environment, will use this cluster. %{environment_scope_start}More information%{environment_scope_end}").html_safe % { environment_scope_start: environment_scope_start, environment_scope_end: '</a>'.html_safe } = s_("ClusterIntegration| is the default environment scope for this cluster. This means that all jobs, regardless of their environment, will use this cluster. %{environment_scope_start}More information%{environment_scope_end}").html_safe % { environment_scope_start: environment_scope_start, environment_scope_end: '</a>'.html_safe }
.form-group
%h5= s_('ClusterIntegration|Base domain')
= field.text_field :base_domain, class: 'col-md-6 form-control js-select-on-focus'
.form-text.text-muted
- auto_devops_url = help_page_path('topics/autodevops/index')
- auto_devops_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: auto_devops_url }
= s_('ClusterIntegration|Specifying a domain will allow you to use Auto Review Apps and Auto Deploy stages for %{auto_devops_start}Auto DevOps%{auto_devops_end}. The domain should have a wildcard DNS configured matching the domain.').html_safe % { auto_devops_start: auto_devops_start, auto_devops_end: '</a>'.html_safe }
- if @cluster.application_ingress_external_ip.present?
= s_('ClusterIntegration|Alternatively')
%code #{@cluster.application_ingress_external_ip}.nip.io
= s_('ClusterIntegration| can be used instead of a custom domain.')
- custom_domain_url = help_page_path('user/project/clusters/index', anchor: 'pointing-your-dns-at-the-cluster-ip')
- custom_domain_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: custom_domain_url }
= s_('ClusterIntegration| %{custom_domain_start}More information%{custom_domain_end}.').html_safe % { custom_domain_start: custom_domain_start, custom_domain_end: '</a>'.html_safe }
- if can?(current_user, :update_cluster, @cluster) - if can?(current_user, :update_cluster, @cluster)
.form-group .form-group
= field.submit _('Save changes'), class: 'btn btn-success' = field.submit _('Save changes'), class: 'btn btn-success'
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
.container-fluid{ class: [limited_container_width, container_class] } .container-fluid{ class: [limited_container_width, container_class] }
= render "commit_box" = render "commit_box"
= render "ci_menu" = render "ci_menu"
= render "projects/diffs/diffs", diffs: @diffs, environment: @environment, is_commit: true = render "projects/diffs/diffs", diffs: @diffs, environment: @environment, diff_page_context: "is-commit"
.limited-width-notes .limited-width-notes
= render "shared/notes/notes_with_form", :autocomplete => true = render "shared/notes/notes_with_form", :autocomplete => true
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
- if @commits.present? - if @commits.present?
= render "projects/commits/commit_list" = render "projects/commits/commit_list"
= render "projects/diffs/diffs", diffs: @diffs, environment: @environment = render "projects/diffs/diffs", diffs: @diffs, environment: @environment, diff_page_context: "is-compare"
- else - else
.card.bg-light .card.bg-light
.center .center
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
- show_whitespace_toggle = local_assigns.fetch(:show_whitespace_toggle, true) - show_whitespace_toggle = local_assigns.fetch(:show_whitespace_toggle, true)
- can_create_note = !@diff_notes_disabled && can?(current_user, :create_note, diffs.project) - can_create_note = !@diff_notes_disabled && can?(current_user, :create_note, diffs.project)
- diff_files = diffs.diff_files - diff_files = diffs.diff_files
- is_commit = local_assigns.fetch(:is_commit, false) - diff_page_context = local_assigns.fetch(:diff_page_context, nil)
.content-block.oneline-block.files-changed.diff-files-changed.js-diff-files-changed .content-block.oneline-block.files-changed.diff-files-changed.js-diff-files-changed
.files-changed-inner .files-changed-inner
...@@ -25,4 +25,4 @@ ...@@ -25,4 +25,4 @@
= render 'projects/diffs/warning', diff_files: diffs = render 'projects/diffs/warning', diff_files: diffs
.files{ data: { can_create_note: can_create_note } } .files{ data: { can_create_note: can_create_note } }
= render partial: 'projects/diffs/file', collection: diff_files, as: :diff_file, locals: { project: diffs.project, environment: environment, is_commit: is_commit } = render partial: 'projects/diffs/file', collection: diff_files, as: :diff_file, locals: { project: diffs.project, environment: environment, diff_page_context: diff_page_context }
- environment = local_assigns.fetch(:environment, nil) - environment = local_assigns.fetch(:environment, nil)
- is_commit = local_assigns.fetch(:is_commit, false) - diff_page_context = local_assigns.fetch(:diff_page_context, nil)
- file_hash = hexdigest(diff_file.file_path) - file_hash = hexdigest(diff_file.file_path)
- image_diff = diff_file.rich_viewer && diff_file.rich_viewer.partial_name == 'image' - image_diff = diff_file.rich_viewer && diff_file.rich_viewer.partial_name == 'image'
- image_replaced = diff_file.old_content_sha && diff_file.old_content_sha != diff_file.content_sha - image_replaced = diff_file.old_content_sha && diff_file.old_content_sha != diff_file.content_sha
.diff-file.file-holder{ id: file_hash, data: diff_file_html_data(project, diff_file.file_path, diff_file.content_sha) } .diff-file.file-holder{ id: file_hash, data: diff_file_html_data(project, diff_file.file_path, diff_file.content_sha) }
.js-file-title.file-title-flex-parent{ class: is_commit ? "is-commit" : "" } .js-file-title.file-title-flex-parent{ class: diff_page_context }
.file-header-content .file-header-content
= render "projects/diffs/file_header", diff_file: diff_file, url: "##{file_hash}" = render "projects/diffs/file_header", diff_file: diff_file, url: "##{file_hash}"
......
...@@ -4,10 +4,6 @@ ...@@ -4,10 +4,6 @@
= form_errors(@project) = form_errors(@project)
%fieldset.builds-feature.js-auto-devops-settings %fieldset.builds-feature.js-auto-devops-settings
.form-group .form-group
- message = auto_devops_warning_message(@project)
- if message
%p.auto-devops-warning-message.settings-message.text-center
= message.html_safe
= f.fields_for :auto_devops_attributes, @auto_devops do |form| = f.fields_for :auto_devops_attributes, @auto_devops do |form|
.card.auto-devops-card .card.auto-devops-card
.card-body .card-body
...@@ -21,19 +17,12 @@ ...@@ -21,19 +17,12 @@
= s_('CICD|The Auto DevOps pipeline will run if no alternative CI configuration file is found.') = s_('CICD|The Auto DevOps pipeline will run if no alternative CI configuration file is found.')
= link_to _('More information'), help_page_path('topics/autodevops/index.md'), target: '_blank' = link_to _('More information'), help_page_path('topics/autodevops/index.md'), target: '_blank'
.card-footer.js-extra-settings{ class: @project.auto_devops_enabled? || 'hidden' } .card-footer.js-extra-settings{ class: @project.auto_devops_enabled? || 'hidden' }
= form.label :domain do %p.settings-message.text-center
%strong= _('Domain') - kubernetes_cluster_link = help_page_path('user/project/clusters/index')
= form.text_field :domain, class: 'form-control', placeholder: 'domain.com' - kubernetes_cluster_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: kubernetes_cluster_link }
.form-text.text-muted = s_('CICD|You must add a %{kubernetes_cluster_start}Kubernetes cluster integration%{kubernetes_cluster_end} to this project with a domain in order for your deployment strategy to work correctly.').html_safe % { kubernetes_cluster_start: kubernetes_cluster_start, kubernetes_cluster_end: '</a>'.html_safe }
= s_('CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages.')
- if cluster_ingress_ip = cluster_ingress_ip(@project)
= s_('%{nip_domain} can be used as an alternative to a custom domain.').html_safe % { nip_domain: "<code>#{cluster_ingress_ip}.nip.io</code>".html_safe }
= link_to icon('question-circle'), help_page_path('topics/autodevops/index.md', anchor: 'auto-devops-base-domain'), target: '_blank'
%label.prepend-top-10 %label.prepend-top-10
%strong= s_('CICD|Deployment strategy') %strong= s_('CICD|Deployment strategy')
%p.settings-message.text-center
= s_('CICD|Deployment strategy needs a domain name to work correctly.')
.form-check .form-check
= form.radio_button :deploy_strategy, 'continuous', class: 'form-check-input' = form.radio_button :deploy_strategy, 'continuous', class: 'form-check-input'
= form.label :deploy_strategy_continuous, class: 'form-check-label' do = form.label :deploy_strategy_continuous, class: 'form-check-label' do
......
...@@ -12,21 +12,20 @@ ...@@ -12,21 +12,20 @@
- css_class += " no-description" if project.description.blank? && !show_last_commit_as_description - css_class += " no-description" if project.description.blank? && !show_last_commit_as_description
- cache_key = project_list_cache_key(project) - cache_key = project_list_cache_key(project)
- updated_tooltip = time_ago_with_tooltip(project.last_activity_date) - updated_tooltip = time_ago_with_tooltip(project.last_activity_date)
- css_details_class = compact_mode ? "d-flex flex-column flex-sm-row flex-md-row align-items-sm-center" : "align-items-center flex-md-fill flex-lg-column d-sm-flex d-lg-block" - css_controls_class = compact_mode ? "" : "flex-lg-row justify-content-lg-between"
- css_controls_class = compact_mode ? "" : "align-items-md-end align-items-lg-center flex-lg-row"
%li.project-row.d-flex{ class: css_class } %li.project-row.d-flex{ class: css_class }
= cache(cache_key) do = cache(cache_key) do
- if avatar - if avatar
.avatar-container.s64.flex-grow-0.flex-shrink-0 .avatar-container.s48.flex-grow-0.flex-shrink-0
= link_to project_path(project), class: dom_class(project) do = link_to project_path(project), class: dom_class(project) do
- if project.creator && use_creator_avatar - if project.creator && use_creator_avatar
= image_tag avatar_icon_for_user(project.creator, 64), class: "avatar s65", alt:'' = image_tag avatar_icon_for_user(project.creator, 48), class: "avatar s65", alt:''
- else - else
= project_icon(project, alt: '', class: 'avatar project-avatar s64', width: 64, height: 64) = project_icon(project, alt: '', class: 'avatar project-avatar s48', width: 48, height: 48)
.project-details.flex-sm-fill{ class: css_details_class } .project-details.d-sm-flex.flex-sm-fill.align-items-center
.flex-wrapper.flex-fill .flex-wrapper
.d-flex.align-items-center.flex-wrap .d-flex.align-items-center.flex-wrap.project-title
%h2.d-flex.prepend-top-8 %h2.d-flex.prepend-top-8
= link_to project_path(project), class: 'text-plain' do = link_to project_path(project), class: 'text-plain' do
%span.project-full-name.append-right-8>< %span.project-full-name.append-right-8><
...@@ -52,13 +51,13 @@ ...@@ -52,13 +51,13 @@
%span.user-access-role.d-block= Gitlab::Access.human_access(access) %span.user-access-role.d-block= Gitlab::Access.human_access(access)
- if show_last_commit_as_description - if show_last_commit_as_description
.description.d-none.d-sm-block.prepend-top-8.append-right-default .description.d-none.d-sm-block.append-right-default
= link_to_markdown(project.commit.title, project_commit_path(project, project.commit), class: "commit-row-message") = link_to_markdown(project.commit.title, project_commit_path(project, project.commit), class: "commit-row-message")
- elsif project.description.present? - elsif project.description.present?
.description.d-none.d-sm-block.prepend-top-8.append-right-default .description.d-none.d-sm-block.append-right-default
= markdown_field(project, :description) = markdown_field(project, :description)
.controls.d-flex.flex-row.flex-sm-column.flex-md-column.align-items-center.align-items-sm-end.flex-wrap.flex-shrink-0{ class: css_controls_class } .controls.d-flex.flex-sm-column.align-items-center.align-items-sm-end.flex-wrap.flex-shrink-0{ class: css_controls_class }
.icon-container.d-flex.align-items-center .icon-container.d-flex.align-items-center
- if project.archived - if project.archived
%span.d-flex.icon-wrapper.badge.badge-warning archived %span.d-flex.icon-wrapper.badge.badge-warning archived
...@@ -74,13 +73,13 @@ ...@@ -74,13 +73,13 @@
= number_with_delimiter(project.forks_count) = number_with_delimiter(project.forks_count)
- if show_merge_request_count?(disabled: !merge_requests, compact_mode: compact_mode) - if show_merge_request_count?(disabled: !merge_requests, compact_mode: compact_mode)
= link_to project_merge_requests_path(project), = link_to project_merge_requests_path(project),
class: "d-none d-lg-flex align-items-center icon-wrapper merge-requests has-tooltip", class: "d-none d-xl-flex align-items-center icon-wrapper merge-requests has-tooltip",
title: _('Merge Requests'), data: { container: 'body', placement: 'top' } do title: _('Merge Requests'), data: { container: 'body', placement: 'top' } do
= sprite_icon('git-merge', size: 14, css_class: 'append-right-4') = sprite_icon('git-merge', size: 14, css_class: 'append-right-4')
= number_with_delimiter(project.open_merge_requests_count) = number_with_delimiter(project.open_merge_requests_count)
- if show_issue_count?(disabled: !issues, compact_mode: compact_mode) - if show_issue_count?(disabled: !issues, compact_mode: compact_mode)
= link_to project_issues_path(project), = link_to project_issues_path(project),
class: "d-none d-lg-flex align-items-center icon-wrapper issues has-tooltip", class: "d-none d-xl-flex align-items-center icon-wrapper issues has-tooltip",
title: _('Issues'), data: { container: 'body', placement: 'top' } do title: _('Issues'), data: { container: 'body', placement: 'top' } do
= sprite_icon('issues', size: 14, css_class: 'append-right-4') = sprite_icon('issues', size: 14, css_class: 'append-right-4')
= number_with_delimiter(project.open_issues_count) = number_with_delimiter(project.open_issues_count)
...@@ -89,19 +88,3 @@ ...@@ -89,19 +88,3 @@
= render_project_pipeline_status(project.pipeline_status, tooltip_placement: 'top') = render_project_pipeline_status(project.pipeline_status, tooltip_placement: 'top')
.updated-note .updated-note
%span Updated #{updated_tooltip} %span Updated #{updated_tooltip}
.d-none.d-lg-flex.align-item-stretch
- unless compact_mode
- if current_user
%button.star-button.btn.btn-default.d-flex.align-items-center.star-btn.toggle-star{ type: "button", data: { endpoint: toggle_star_project_path(project, :json) } }
- if current_user.starred?(project)
= sprite_icon('star', { css_class: 'icon' })
%span.starred= s_('ProjectOverview|Unstar')
- else
= sprite_icon('star-o', { css_class: 'icon' })
%span= s_('ProjectOverview|Star')
- else
= link_to new_user_session_path, class: 'btn btn-default has-tooltip count-badge-button d-flex align-items-center star-btn', title: s_('ProjectOverview|You must sign in to star a project') do
= sprite_icon('star-o', { css_class: 'icon' })
%span= s_('ProjectOverview|Star')
...@@ -15,19 +15,19 @@ class RepositoryForkWorker ...@@ -15,19 +15,19 @@ class RepositoryForkWorker
return target_project.import_state.mark_as_failed(_('Source project cannot be found.')) return target_project.import_state.mark_as_failed(_('Source project cannot be found.'))
end end
fork_repository(target_project, source_project.repository_storage, source_project.disk_path) fork_repository(target_project, source_project)
end end
private private
def fork_repository(target_project, source_repository_storage_name, source_disk_path) def fork_repository(target_project, source_project)
return unless start_fork(target_project) return unless start_fork(target_project)
Gitlab::Metrics.add_event(:fork_repository) Gitlab::Metrics.add_event(:fork_repository)
result = gitlab_shell.fork_repository(source_repository_storage_name, source_disk_path, result = gitlab_shell.fork_repository(source_project, target_project)
target_project.repository_storage, target_project.disk_path)
raise "Unable to fork project #{target_project.id} for repository #{source_disk_path} -> #{target_project.disk_path}" unless result raise "Unable to fork project #{target_project.id} for repository #{source_project.disk_path} -> #{target_project.disk_path}" unless result
target_project.after_import target_project.after_import
end end
......
---
title: GitLab now supports the profile and email scopes from OpenID Connect
merge_request: 24335
author: Goten Xiao
type: added
---
title: Show MR statistics in diff comparisons
merge_request: !24569
author:
type: changed
---
title: Moves domain setting from Auto DevOps to Cluster's page
merge_request: 24580
author:
type: added
---
title: API allows setting the squash commit message when squashing a merge request
merge_request: 24784
author:
type: added
---
title: Project list UI improvements
merge_request: 24855
author:
type: other
---
title: Clean up unicorn sampler metric labels
merge_request: 24626
author: bjk-gitlab
type: fixed
---
title: Correct spacing for comparison page
merge_request: !24783
author:
type: fixed
---
title: Update metrics dashboard graph design
merge_request: 24653
author:
type: changed
---
title: 'API: Add support for group labels'
merge_request: 21368
author: Robert Schilling
type: added
---
title: Fix bug causing repository mirror settings UI to break
merge_request: 23712
author:
type: fixed
---
title: Upgrade gitaly to 1.18.0
merge_request: 24981
author:
type: other
---
title: Add template for Android with Fastlane
merge_request: 24722
author:
type: changed
---
title: Allow admins to invalidate markdown texts by setting local markdown version.
merge_request:
author:
type: added
---
title: Fix code search when text is larger than max gRPC message size
merge_request: 24111
author:
type: changed
...@@ -31,8 +31,27 @@ Doorkeeper::OpenidConnect.configure do ...@@ -31,8 +31,27 @@ Doorkeeper::OpenidConnect.configure do
o.claim(:name) { |user| user.name } o.claim(:name) { |user| user.name }
o.claim(:nickname) { |user| user.username } o.claim(:nickname) { |user| user.username }
o.claim(:email) { |user| user.public_email }
o.claim(:email_verified) { |user| true if user.public_email? } # Check whether the application has access to the email scope, and grant
# access to the user's primary email address if so, otherwise their
# public email address (if present)
# This allows existing solutions built for GitLab's old behavior to keep
# working without modification.
o.claim(:email) do |user, scopes|
scopes.exists?(:email) ? user.email : user.public_email
end
o.claim(:email_verified) do |user, scopes|
if scopes.exists?(:email)
user.primary_email_verified?
elsif user.public_email?
user.verified_email?(user.public_email)
else
# If there is no public email set, tell doorkicker-openid-connect to
# exclude the email_verified claim by returning nil.
nil
end
end
o.claim(:website) { |user| user.full_website_url if user.website_url? } o.claim(:website) { |user| user.full_website_url if user.website_url? }
o.claim(:profile) { |user| Gitlab::Routing.url_helpers.user_url user } o.claim(:profile) { |user| Gitlab::Routing.url_helpers.user_url user }
o.claim(:picture) { |user| user.avatar_url(only_path: false) } o.claim(:picture) { |user| user.avatar_url(only_path: false) }
......
...@@ -64,6 +64,8 @@ en: ...@@ -64,6 +64,8 @@ en:
read_registry: Grants permission to read container registry images read_registry: Grants permission to read container registry images
openid: Authenticate using OpenID Connect openid: Authenticate using OpenID Connect
sudo: Perform API actions as any user in the system sudo: Perform API actions as any user in the system
profile: Allows read-only access to the user's personal information using OpenID Connect
email: Allows read-only access to the user's primary email address using OpenID Connect
scope_desc: scope_desc:
api: api:
Grants complete read/write access to the API, including all groups and projects. Grants complete read/write access to the API, including all groups and projects.
...@@ -77,6 +79,10 @@ en: ...@@ -77,6 +79,10 @@ en:
Grants permission to authenticate with GitLab using OpenID Connect. Also gives read-only access to the user's profile and group memberships. Grants permission to authenticate with GitLab using OpenID Connect. Also gives read-only access to the user's profile and group memberships.
sudo: sudo:
Grants permission to perform API actions as any user in the system, when authenticated as an admin user. Grants permission to perform API actions as any user in the system, when authenticated as an admin user.
profile:
Grants read-only access to the user's profile data using OpenID Connect.
email:
Grants read-only access to the user's primary email address using OpenID Connect.
flash: flash:
applications: applications:
create: create:
......
...@@ -11,7 +11,7 @@ class MigrateRepoSize < ActiveRecord::Migration[4.2] ...@@ -11,7 +11,7 @@ class MigrateRepoSize < ActiveRecord::Migration[4.2]
path = File.join(namespace_path, project['project_path'] + '.git') path = File.join(namespace_path, project['project_path'] + '.git')
begin begin
repo = Gitlab::Git::Repository.new('default', path, '') repo = Gitlab::Git::Repository.new('default', path, '', '')
if repo.empty? if repo.empty?
print '-' print '-'
else else
......
# frozen_string_literal: true
class AddLocalCachedMarkdownVersion < ActiveRecord::Migration[5.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
add_column :application_settings, :local_markdown_version, :integer, default: 0, null: false
end
end
# frozen_string_literal: true
class MigrateAutoDevOpsDomainToClusterDomain < ActiveRecord::Migration[5.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def up
execute(update_clusters_domain_query)
end
def down
# no-op
end
private
def update_clusters_domain_query
if Gitlab::Database.mysql?
mysql_query
else
postgresql_query
end
end
def mysql_query
<<~HEREDOC
UPDATE clusters, project_auto_devops, cluster_projects
SET
clusters.domain = project_auto_devops.domain
WHERE
cluster_projects.cluster_id = clusters.id
AND project_auto_devops.project_id = cluster_projects.project_id
AND project_auto_devops.domain != ''
HEREDOC
end
def postgresql_query
<<~HEREDOC
UPDATE clusters
SET domain = project_auto_devops.domain
FROM cluster_projects, project_auto_devops
WHERE
cluster_projects.cluster_id = clusters.id
AND project_auto_devops.project_id = cluster_projects.project_id
AND project_auto_devops.domain != ''
HEREDOC
end
end
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20190131122559) do ActiveRecord::Schema.define(version: 20190204115450) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -168,6 +168,7 @@ ActiveRecord::Schema.define(version: 20190131122559) do ...@@ -168,6 +168,7 @@ ActiveRecord::Schema.define(version: 20190131122559) do
t.string "commit_email_hostname" t.string "commit_email_hostname"
t.boolean "protected_ci_variables", default: false, null: false t.boolean "protected_ci_variables", default: false, null: false
t.string "runners_registration_token_encrypted" t.string "runners_registration_token_encrypted"
t.integer "local_markdown_version", default: 0, null: false
t.integer "first_day_of_week", default: 0, null: false t.integer "first_day_of_week", default: 0, null: false
t.index ["usage_stats_set_by_user_id"], name: "index_application_settings_on_usage_stats_set_by_user_id", using: :btree t.index ["usage_stats_set_by_user_id"], name: "index_application_settings_on_usage_stats_set_by_user_id", using: :btree
end end
......
...@@ -65,6 +65,7 @@ Learn how to install, configure, update, and maintain your GitLab instance. ...@@ -65,6 +65,7 @@ Learn how to install, configure, update, and maintain your GitLab instance.
- [Backup and restore](../raketasks/backup_restore.md): Backup and restore your GitLab instance. - [Backup and restore](../raketasks/backup_restore.md): Backup and restore your GitLab instance.
- [Operations](operations/index.md): Keeping GitLab up and running (clean up Redis sessions, moving repositories, Sidekiq MemoryKiller, Unicorn). - [Operations](operations/index.md): Keeping GitLab up and running (clean up Redis sessions, moving repositories, Sidekiq MemoryKiller, Unicorn).
- [Restart GitLab](restart_gitlab.md): Learn how to restart GitLab and its components. - [Restart GitLab](restart_gitlab.md): Learn how to restart GitLab and its components.
- [Invalidate markdown cache](invalidate_markdown_cache.md): Invalidate any cached markdown.
#### Updating GitLab #### Updating GitLab
......
# Invalidate Markdown Cache
For performance reasons, GitLab caches the HTML version of markdown text
(e.g. issue and merge request descriptions, comments). It's possible
that these cached versions become outdated, for example
when the `external_url` configuration option is changed - causing links
in the cached text to refer to the old URL.
To avoid this problem, the administrator can invalidate the existing cache by
increasing the `local_markdown_version` setting in application settings. This can
be done by [changing the application settings through
the API](../api/settings.md#change-application-settings):
```bash
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/application/settings?local_markdown_version=<increased_number>
```
...@@ -48,6 +48,8 @@ The following metrics are available: ...@@ -48,6 +48,8 @@ The following metrics are available:
| upload_file_does_not_exist | Counter | 10.7 in EE, 11.5 in CE | Number of times an upload record could not find its file | | upload_file_does_not_exist | Counter | 10.7 in EE, 11.5 in CE | Number of times an upload record could not find its file |
| failed_login_captcha_total | Gauge | 11.0 | Counter of failed CAPTCHA attempts during login | | failed_login_captcha_total | Gauge | 11.0 | Counter of failed CAPTCHA attempts during login |
| successful_login_captcha_total | Gauge | 11.0 | Counter of successful CAPTCHA attempts during login | | successful_login_captcha_total | Gauge | 11.0 | Counter of successful CAPTCHA attempts during login |
| unicorn_active_connections | Gauge | 11.0 | The number of active Unicorn connections (workers) |
| unicorn_queued_connections | Gauge | 11.0 | The number of queued Unicorn connections |
### Ruby metrics ### Ruby metrics
......
...@@ -29,6 +29,7 @@ The following API resources are available: ...@@ -29,6 +29,7 @@ The following API resources are available:
- [Group access requests](access_requests.md) - [Group access requests](access_requests.md)
- [Group badges](group_badges.md) - [Group badges](group_badges.md)
- [Group issue boards](group_boards.md) - [Group issue boards](group_boards.md)
- [Group labels](group_labels.md)
- [Group-level variables](group_level_variables.md) - [Group-level variables](group_level_variables.md)
- [Group members](members.md) - [Group members](members.md)
- [Group milestones](group_milestones.md) - [Group milestones](group_milestones.md)
......
# Group Label API
>**Note:** This feature was [introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/21368) in GitLab 11.8.
This API supports managing of [group labels](../user/project/labels.md#project-labels-and-group-labels). It allows to list, create, update, and delete group labels. Furthermore, users can subscribe and unsubscribe to and from group labels.
## List group labels
Get all labels for a given group.
```
GET /groups/:id/labels
```
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
```bash
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/5/labels
```
Example response:
```json
[
{
"id": 7,
"name": "bug",
"color": "#FF0000",
"description": null,
"open_issues_count": 0,
"closed_issues_count": 0,
"open_merge_requests_count": 0,
"subscribed": false
},
{
"id": 4,
"name": "feature",
"color": "#228B22",
"description": null,
"open_issues_count": 0,
"closed_issues_count": 0,
"open_merge_requests_count": 0,
"subscribed": false
}
]
```
## Create a new group label
Create a new group label for a given group.
```
POST /groups/:id/labels
```
| Attribute | Type | Required | Description |
| ------------- | ------- | -------- | ---------------------------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
| `name` | string | yes | The name of the label |
| `color` | string | yes | The color of the label given in 6-digit hex notation with leading '#' sign (e.g. #FFAABB) or one of the [CSS color names](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#Color_keywords) |
| `description` | string | no | The description of the label |
```bash
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" --header "Content-Type: application/json" --data '{"name": "Feature Proposal", "color": "#FFA500", "description": "Describes new ideas" }' https://gitlab.example.com/api/v4/groups/5/labels
```
Example response:
```json
{
"id": 9,
"name": "Feature Proposal",
"color": "#FFA500",
"description": "Describes new ideas",
"open_issues_count": 0,
"closed_issues_count": 0,
"open_merge_requests_count": 0,
"subscribed": false
}
```
## Update a group label
Updates an existing group label. At least one parameter is required, to update the group label.
```
PUT /groups/:id/labels
```
| Attribute | Type | Required | Description |
| ------------- | ------- | -------- | ---------------------------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
| `name` | string | yes | The name of the label |
| `new_name` | string | no | The new name of the label |
| `color` | string | no | The color of the label given in 6-digit hex notation with leading '#' sign (e.g. #FFAABB) or one of the [CSS color names](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#Color_keywords) |
| `description` | string | no | The description of the label |
```bash
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" --header "Content-Type: application/json" --data '{"name": "Feature Proposal", "new_name": "Feature Idea" }' https://gitlab.example.com/api/v4/groups/5/labels
```
Example response:
```json
{
"id": 9,
"name": "Feature Idea",
"color": "#FFA500",
"description": "Describes new ideas",
"open_issues_count": 0,
"closed_issues_count": 0,
"open_merge_requests_count": 0,
"subscribed": false
}
```
## Delete a group label
Deletes a group label with a given name.
```
DELETE /groups/:id/labels
```
| Attribute | Type | Required | Description |
| --------- | ------- | -------- | --------------------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
| `name` | string | yes | The name of the label |
```bash
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/5/labels?name=bug
```
## Subscribe to a group label
Subscribes the authenticated user to a group label to receive notifications. If
the user is already subscribed to the label, the status code `304` is returned.
```
POST /groups/:id/labels/:label_id/subscribe
```
| Attribute | Type | Required | Description |
| ---------- | ----------------- | -------- | ------------------------------------ |
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
| `label_id` | integer or string | yes | The ID or title of a group's label |
```bash
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/5/labels/9/subscribe
```
Example response:
```json
{
"id": 9,
"name": "Feature Idea",
"color": "#FFA500",
"description": "Describes new ideas",
"open_issues_count": 0,
"closed_issues_count": 0,
"open_merge_requests_count": 0,
"subscribed": true
}
```
## Unsubscribe from a group label
Unsubscribes the authenticated user from a group label to not receive
notifications from it. If the user is not subscribed to the label, the status
code `304` is returned.
```
POST /groups/:id/labels/:label_id/unsubscribe
```
| Attribute | Type | Required | Description |
| ---------- | ----------------- | -------- | ------------------------------------ |
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
| `label_id` | integer or string | yes | The ID or title of a group's label |
```bash
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/5/labels/9/unsubscribe
```
Example response:
```json
{
"id": 9,
"name": "Feature Idea",
"color": "#FFA500",
"description": "Describes new ideas",
"open_issues_count": 0,
"closed_issues_count": 0,
"open_merge_requests_count": 0,
"subscribed": false
}
```
...@@ -994,6 +994,8 @@ Parameters: ...@@ -994,6 +994,8 @@ Parameters:
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user - `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `merge_request_iid` (required) - Internal ID of MR - `merge_request_iid` (required) - Internal ID of MR
- `merge_commit_message` (optional) - Custom merge commit message - `merge_commit_message` (optional) - Custom merge commit message
- `squash_commit_message` (optional) - Custom squash commit message
- `squash` (optional) - if `true` the commits will be squashed into a single commit on merge
- `should_remove_source_branch` (optional) - if `true` removes the source branch - `should_remove_source_branch` (optional) - if `true` removes the source branch
- `merge_when_pipeline_succeeds` (optional) - if `true` the MR is merged when the pipeline succeeds - `merge_when_pipeline_succeeds` (optional) - if `true` the MR is merged when the pipeline succeeds
- `sha` (optional) - if present, then this SHA must match the HEAD of the source branch, otherwise the merge will fail - `sha` (optional) - if present, then this SHA must match the HEAD of the source branch, otherwise the merge will fail
......
...@@ -62,7 +62,8 @@ Example response: ...@@ -62,7 +62,8 @@ Example response:
"terms": "Hello world!", "terms": "Hello world!",
"performance_bar_allowed_group_id": 42, "performance_bar_allowed_group_id": 42,
"instance_statistics_visibility_private": false, "instance_statistics_visibility_private": false,
"user_show_add_ssh_key_message": true "user_show_add_ssh_key_message": true,
"local_markdown_version": 0
} }
``` ```
...@@ -119,7 +120,8 @@ Example response: ...@@ -119,7 +120,8 @@ Example response:
"terms": "Hello world!", "terms": "Hello world!",
"performance_bar_allowed_group_id": 42, "performance_bar_allowed_group_id": 42,
"instance_statistics_visibility_private": false, "instance_statistics_visibility_private": false,
"user_show_add_ssh_key_message": true "user_show_add_ssh_key_message": true,
"local_markdown_version": 0
} }
``` ```
...@@ -238,3 +240,4 @@ are listed in the descriptions of the relevant settings. ...@@ -238,3 +240,4 @@ are listed in the descriptions of the relevant settings.
| `user_oauth_applications` | boolean | no | Allow users to register any application to use GitLab as an OAuth provider. | | `user_oauth_applications` | boolean | no | Allow users to register any application to use GitLab as an OAuth provider. |
| `user_show_add_ssh_key_message` | boolean | no | When set to `false` disable the "You won't be able to pull or push project code via SSH" warning shown to users with no uploaded SSH key. | | `user_show_add_ssh_key_message` | boolean | no | When set to `false` disable the "You won't be able to pull or push project code via SSH" warning shown to users with no uploaded SSH key. |
| `version_check_enabled` | boolean | no | Let GitLab inform you when an update is available. | | `version_check_enabled` | boolean | no | Let GitLab inform you when an update is available. |
| `local_markdown_version` | integer | no | Increase this value when any cached markdown should be invalidated. |
...@@ -98,7 +98,7 @@ future GitLab releases.** ...@@ -98,7 +98,7 @@ future GitLab releases.**
| **CI_PIPELINE_SOURCE** | 10.0 | all | Indicates how the pipeline was triggered. Possible options are: `push`, `web`, `trigger`, `schedule`, `api`, and `pipeline`. For pipelines created before GitLab 9.5, this will show as `unknown` | | **CI_PIPELINE_SOURCE** | 10.0 | all | Indicates how the pipeline was triggered. Possible options are: `push`, `web`, `trigger`, `schedule`, `api`, and `pipeline`. For pipelines created before GitLab 9.5, this will show as `unknown` |
| **CI_PIPELINE_TRIGGERED** | all | all | The flag to indicate that job was [triggered] | | **CI_PIPELINE_TRIGGERED** | all | all | The flag to indicate that job was [triggered] |
| **CI_PIPELINE_URL** | 11.1 | 0.5 | Pipeline details URL | | **CI_PIPELINE_URL** | 11.1 | 0.5 | Pipeline details URL |
| **CI_PROJECT_DIR** | all | all | The full path where the repository is cloned and where the job is run | | **CI_PROJECT_DIR** | all | all | The full path where the repository is cloned and where the job is run. If the GitLab Runner `builds_dir` parameter is set, this variable is set relative to the value of `builds_dir`. For more information, see [Advanced configuration](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runners-section) for GitLab Runner. |
| **CI_PROJECT_ID** | all | all | The unique id of the current project that GitLab CI uses internally | | **CI_PROJECT_ID** | all | all | The unique id of the current project that GitLab CI uses internally |
| **CI_PROJECT_NAME** | 8.10 | 0.5 | The project name that is currently being built (actually it is project folder name) | | **CI_PROJECT_NAME** | 8.10 | 0.5 | The project name that is currently being built (actually it is project folder name) |
| **CI_PROJECT_NAMESPACE** | 8.10 | 0.5 | The project namespace (username or groupname) that is currently being built | | **CI_PROJECT_NAMESPACE** | 8.10 | 0.5 | The project namespace (username or groupname) that is currently being built |
......
...@@ -86,6 +86,9 @@ request is as follows: ...@@ -86,6 +86,9 @@ request is as follows:
guidelines](../merge_request_performance_guidelines.md). guidelines](../merge_request_performance_guidelines.md).
1. For tests that use Capybara or PhantomJS, see this [article on how 1. For tests that use Capybara or PhantomJS, see this [article on how
to write reliable asynchronous tests](https://robots.thoughtbot.com/write-reliable-asynchronous-integration-tests-with-capybara). to write reliable asynchronous tests](https://robots.thoughtbot.com/write-reliable-asynchronous-integration-tests-with-capybara).
1. If your merge request introduces changes that require additional steps when
installing GitLab from source, add them to `doc/install/installation.md` in
the same merge request.
Please keep the change in a single MR **as small as possible**. If you want to Please keep the change in a single MR **as small as possible**. If you want to
contribute a large feature think very hard what the minimum viable change is. contribute a large feature think very hard what the minimum viable change is.
......
...@@ -345,11 +345,15 @@ cd /home/git ...@@ -345,11 +345,15 @@ cd /home/git
```sh ```sh
# Clone GitLab repository # Clone GitLab repository
sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 11-7-stable gitlab sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b X-Y-stable gitlab
``` ```
Make sure to replace `X-Y-stable` with the stable branch that matches the
version you want to install. For example, if you want to install 11.8 you would
use the branch name `11-8-stable`.
CAUTION: **Caution:** CAUTION: **Caution:**
You can change `11-7-stable` to `master` if you want the *bleeding edge* version, but never install `master` on a production server! You can change `X-Y-stable` to `master` if you want the *bleeding edge* version, but never install `master` on a production server!
### Configure It ### Configure It
...@@ -691,6 +695,11 @@ sudo nginx -t ...@@ -691,6 +695,11 @@ sudo nginx -t
You should receive `syntax is okay` and `test is successful` messages. If you receive errors check your `gitlab` or `gitlab-ssl` Nginx config file for typos, etc. as indicated in the error message given. You should receive `syntax is okay` and `test is successful` messages. If you receive errors check your `gitlab` or `gitlab-ssl` Nginx config file for typos, etc. as indicated in the error message given.
NOTE: **Note:**
Verify that the installed version is greater than 1.12.1 by running `nginx -v`. If it's lower, you may receive the error below:
`nginx: [emerg] unknown "start$temp=[filtered]$rest" variable
nginx: configuration file /etc/nginx/nginx.conf test failed`
### Restart ### Restart
```sh ```sh
......
...@@ -126,14 +126,22 @@ Auto Deploy, and Auto Monitoring will be silently skipped. ...@@ -126,14 +126,22 @@ Auto Deploy, and Auto Monitoring will be silently skipped.
## Auto DevOps base domain ## Auto DevOps base domain
NOTE: **Note**
`AUTO_DEVOPS_DOMAIN` environment variable is deprecated and
[is scheduled to be removed](https://gitlab.com/gitlab-org/gitlab-ce/issues/56959) in GitLab 12.0.
The Auto DevOps base domain is required if you want to make use of [Auto The Auto DevOps base domain is required if you want to make use of [Auto
Review Apps](#auto-review-apps) and [Auto Deploy](#auto-deploy). It can be defined Review Apps](#auto-review-apps) and [Auto Deploy](#auto-deploy). It can be defined
in three places: in any of the following places:
- either under the project's CI/CD settings while [enabling Auto DevOps](#enabling-auto-devops) - either under the cluster's settings, whether for [projects](../../user/project/clusters/index.md#base-domain) or [groups](../../user/group/clusters/index.md#base-domain)
- or in instance-wide settings in the **admin area > Settings** under the "Continuous Integration and Delivery" section - or in instance-wide settings in the **admin area > Settings** under the "Continuous Integration and Delivery" section
- or at the project as a variable: `AUTO_DEVOPS_DOMAIN` (required if you want to use [multiple clusters](#using-multiple-kubernetes-clusters)) - or at the project level as a variable: `KUBE_INGRESS_BASE_DOMAIN`
- or at the group level as a variable: `AUTO_DEVOPS_DOMAIN` - or at the group level as a variable: `KUBE_INGRESS_BASE_DOMAIN`.
NOTE: **Note**
Auto DevOps base domain variable (`KUBE_INGRESS_BASE_DOMAIN`) follows the same order of precedence
as other environment [varibles](../../ci/variables/README.md#priority-of-variables).
A wildcard DNS A record matching the base domain(s) is required, for example, A wildcard DNS A record matching the base domain(s) is required, for example,
given a base domain of `example.com`, you'd need a DNS entry like: given a base domain of `example.com`, you'd need a DNS entry like:
...@@ -170,13 +178,13 @@ In the [Auto DevOps template](https://gitlab.com/gitlab-org/gitlab-ce/blob/maste ...@@ -170,13 +178,13 @@ In the [Auto DevOps template](https://gitlab.com/gitlab-org/gitlab-ce/blob/maste
Those environments are tied to jobs that use [Auto Deploy](#auto-deploy), so Those environments are tied to jobs that use [Auto Deploy](#auto-deploy), so
except for the environment scope, they would also need to have a different except for the environment scope, they would also need to have a different
domain they would be deployed to. This is why you need to define a separate domain they would be deployed to. This is why you need to define a separate
`AUTO_DEVOPS_DOMAIN` variable for all the above `KUBE_INGRESS_BASE_DOMAIN` variable for all the above
[based on the environment](../../ci/variables/README.md#limiting-environment-scopes-of-variables). [based on the environment](../../ci/variables/README.md#limiting-environment-scopes-of-variables).
The following table is an example of how the three different clusters would The following table is an example of how the three different clusters would
be configured. be configured.
| Cluster name | Cluster environment scope | `AUTO_DEVOPS_DOMAIN` variable value | Variable environment scope | Notes | | Cluster name | Cluster environment scope | `KUBE_INGRESS_BASE_DOMAIN` variable value | Variable environment scope | Notes |
| ------------ | -------------- | ----------------------------- | ------------- | ------ | | ------------ | -------------- | ----------------------------- | ------------- | ------ |
| review | `review/*` | `review.example.com` | `review/*` | The review cluster which will run all [Review Apps](../../ci/review_apps/index.md). `*` is a wildcard, which means it will be used by every environment name starting with `review/`. | | review | `review/*` | `review.example.com` | `review/*` | The review cluster which will run all [Review Apps](../../ci/review_apps/index.md). `*` is a wildcard, which means it will be used by every environment name starting with `review/`. |
| staging | `staging` | `staging.example.com` | `staging` | (Optional) The staging cluster which will run the deployments of the staging environments. You need to [enable it first](#deploy-policy-for-staging-and-production-environments). | | staging | `staging` | `staging.example.com` | `staging` | (Optional) The staging cluster which will run the deployments of the staging environments. You need to [enable it first](#deploy-policy-for-staging-and-production-environments). |
...@@ -190,14 +198,11 @@ To add a different cluster for each environment: ...@@ -190,14 +198,11 @@ To add a different cluster for each environment:
![Auto DevOps multiple clusters](img/autodevops_multiple_clusters.png) ![Auto DevOps multiple clusters](img/autodevops_multiple_clusters.png)
1. After the clusters are created, navigate to each one and install Helm Tiller 1. After the clusters are created, navigate to each one and install Helm Tiller
and Ingress. and Ingress. Wait for the Ingress IP address to be assigned.
1. Make sure you have [configured your DNS](#auto-devops-base-domain) with the 1. Make sure you have [configured your DNS](#auto-devops-base-domain) with the
specified Auto DevOps domains. specified Auto DevOps domains.
1. Navigate to your project's **Settings > CI/CD > Environment variables** and add 1. Navigate to each cluster's page, through **Operations > Kubernetes**,
the `AUTO_DEVOPS_DOMAIN` variables with their respective environment and add the domain based on its Ingress IP address.
scope.
![Auto DevOps domain variables](img/autodevops_domain_variables.png)
Now that all is configured, you can test your setup by creating a merge request Now that all is configured, you can test your setup by creating a merge request
and verifying that your app is deployed as a review app in the Kubernetes and verifying that your app is deployed as a review app in the Kubernetes
...@@ -205,10 +210,9 @@ cluster with the `review/*` environment scope. Similarly, you can check the ...@@ -205,10 +210,9 @@ cluster with the `review/*` environment scope. Similarly, you can check the
other environments. other environments.
NOTE: **Note:** NOTE: **Note:**
Auto DevOps is not supported for a group with multiple clusters, as it From GitLab 11.8, `KUBE_INGRESS_BASE_DOMAIN` replaces `AUTO_DEVOPS_DOMAIN`.
is not possible to set `AUTO_DEVOPS_DOMAIN` per environment on the group `AUTO_DEVOPS_DOMAIN` [is scheduled to be removed](https://gitlab.com/gitlab-org/gitlab-ce/issues/56959)
level. This will be resolved in the future with the [following issue]( in GitLab 12.0.
https://gitlab.com/gitlab-org/gitlab-ce/issues/52363).
## Enabling/Disabling Auto DevOps ## Enabling/Disabling Auto DevOps
...@@ -681,7 +685,7 @@ also be customized, and you can easily use a [custom buildpack](#custom-buildpac ...@@ -681,7 +685,7 @@ also be customized, and you can easily use a [custom buildpack](#custom-buildpac
| **Variable** | **Description** | | **Variable** | **Description** |
| ------------ | --------------- | | ------------ | --------------- |
| `AUTO_DEVOPS_DOMAIN` | The [Auto DevOps domain](#auto-devops-domain); by default set automatically by the [Auto DevOps setting](#enabling-auto-devops). | | `AUTO_DEVOPS_DOMAIN` | The [Auto DevOps domain](#auto-devops-domain). By default, set automatically by the [Auto DevOps setting](#enabling-auto-devops). This variable is deprecated and [is scheduled to be removed](https://gitlab.com/gitlab-org/gitlab-ce/issues/56959) in GitLab 12.0. |
| `AUTO_DEVOPS_CHART` | The Helm Chart used to deploy your apps; defaults to the one [provided by GitLab](https://gitlab.com/charts/auto-deploy-app). | | `AUTO_DEVOPS_CHART` | The Helm Chart used to deploy your apps; defaults to the one [provided by GitLab](https://gitlab.com/charts/auto-deploy-app). |
| `AUTO_DEVOPS_CHART_REPOSITORY` | The Helm Chart repository used to search for charts; defaults to `https://charts.gitlab.io`. | | `AUTO_DEVOPS_CHART_REPOSITORY` | The Helm Chart repository used to search for charts; defaults to `https://charts.gitlab.io`. |
| `REPLICAS` | The number of replicas to deploy; defaults to 1. | | `REPLICAS` | The number of replicas to deploy; defaults to 1. |
...@@ -711,6 +715,7 @@ also be customized, and you can easily use a [custom buildpack](#custom-buildpac ...@@ -711,6 +715,7 @@ also be customized, and you can easily use a [custom buildpack](#custom-buildpac
| `DAST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `dast` job. If the variable is present, the job will not be created. | | `DAST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `dast` job. If the variable is present, the job will not be created. |
| `PERFORMANCE_DISABLED` | From GitLab 11.0, this variable can be used to disable the `performance` job. If the variable is present, the job will not be created. | | `PERFORMANCE_DISABLED` | From GitLab 11.0, this variable can be used to disable the `performance` job. If the variable is present, the job will not be created. |
| `K8S_SECRET_*` | From GitLab 11.7, any variable prefixed with [`K8S_SECRET_`](#application-secret-variables) will be made available by Auto DevOps as environment variables to the deployed application. | | `K8S_SECRET_*` | From GitLab 11.7, any variable prefixed with [`K8S_SECRET_`](#application-secret-variables) will be made available by Auto DevOps as environment variables to the deployed application. |
| `KUBE_INGRESS_BASE_DOMAIN` | From GitLab 11.8, this variable can be used to set a domain per cluster. See [cluster domains](../../user/project/clusters/index.md#base-domain) for more information. |
TIP: **Tip:** TIP: **Tip:**
Set up the replica variables using a Set up the replica variables using a
......
...@@ -59,11 +59,17 @@ Add another cluster similar to the first one and make sure to ...@@ -59,11 +59,17 @@ Add another cluster similar to the first one and make sure to
[set an environment scope](#environment-scopes) that will [set an environment scope](#environment-scopes) that will
differentiate the new cluster from the rest. differentiate the new cluster from the rest.
## Base domain
NOTE: **Note:** NOTE: **Note:**
Auto DevOps is not supported for a group with multiple clusters, as it [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/24580) in GitLab 11.8.
is not possible to set `AUTO_DEVOPS_DOMAIN` per environment on the group
level. This will be resolved in the future with the [following issue]( Domains at the cluster level permit support for multiple domains
https://gitlab.com/gitlab-org/gitlab-ce/issues/52363). per [multiple Kubernetes clusters](#multiple-kubernetes-clusters-premium). When specifying a domain,
this will be automatically set as an environment variable (`KUBE_INGRESS_BASE_DOMAIN`) during
the [Auto DevOps](../../../topics/autodevops/index.md) stages.
The domain should have a wildcard DNS configured to the Ingress IP address.
## Environment scopes **[PREMIUM]** ## Environment scopes **[PREMIUM]**
......
...@@ -172,6 +172,18 @@ functionalities needed to successfully build and deploy a containerized ...@@ -172,6 +172,18 @@ functionalities needed to successfully build and deploy a containerized
application. Bear in mind that the same credentials are used for all the application. Bear in mind that the same credentials are used for all the
applications running on the cluster. applications running on the cluster.
## Base domain
NOTE: **Note:**
[Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/24580) in GitLab 11.8.
Domains at the cluster level permit support for multiple domains
per [multiple Kubernetes clusters](#multiple-kubernetes-clusters-premium). When specifying a domain,
this will be automatically set as an environment variable (`KUBE_INGRESS_BASE_DOMAIN`) during
the [Auto DevOps](../../../topics/autodevops/index.md) stages.
The domain should have a wildcard DNS configured to the Ingress IP address.
## Access controls ## Access controls
When creating a cluster in GitLab, you will be asked if you would like to create an When creating a cluster in GitLab, you will be asked if you would like to create an
...@@ -254,6 +266,12 @@ install it manually. ...@@ -254,6 +266,12 @@ install it manually.
## Installing applications ## Installing applications
NOTE: **Note:**
Before starting the installation of applications, make sure that time is synchronized
between your GitLab server and your Kubernetes cluster. Otherwise, installation could fail
and you may get errors like `Error: remote error: tls: bad certificate`
in the `stdout` of pods created by GitLab in your Kubernetes cluster.
GitLab provides a one-click install for various applications which can GitLab provides a one-click install for various applications which can
be added directly to your configured cluster. Those applications are be added directly to your configured cluster. Those applications are
needed for [Review Apps](../../../ci/review_apps/index.md) and needed for [Review Apps](../../../ci/review_apps/index.md) and
...@@ -449,6 +467,7 @@ GitLab CI/CD build environment. ...@@ -449,6 +467,7 @@ GitLab CI/CD build environment.
| `KUBE_CA_PEM_FILE` | Path to a file containing PEM data. Only present if a custom CA bundle was specified. | | `KUBE_CA_PEM_FILE` | Path to a file containing PEM data. Only present if a custom CA bundle was specified. |
| `KUBE_CA_PEM` | (**deprecated**) Raw PEM data. Only if a custom CA bundle was specified. | | `KUBE_CA_PEM` | (**deprecated**) Raw PEM data. Only if a custom CA bundle was specified. |
| `KUBECONFIG` | Path to a file containing `kubeconfig` for this deployment. CA bundle would be embedded if specified. This config also embeds the same token defined in `KUBE_TOKEN` so you likely will only need this variable. This variable name is also automatically picked up by `kubectl` so you won't actually need to reference it explicitly if using `kubectl`. | | `KUBECONFIG` | Path to a file containing `kubeconfig` for this deployment. CA bundle would be embedded if specified. This config also embeds the same token defined in `KUBE_TOKEN` so you likely will only need this variable. This variable name is also automatically picked up by `kubectl` so you won't actually need to reference it explicitly if using `kubectl`. |
| `KUBE_INGRESS_BASE_DOMAIN` | From GitLab 11.8, this variable can be used to set a domain per cluster. See [cluster domains](#base-domain) for more information. | 
NOTE: **NOTE:** NOTE: **NOTE:**
Prior to GitLab 11.5, `KUBE_TOKEN` was the Kubernetes token of the main Prior to GitLab 11.5, `KUBE_TOKEN` was the Kubernetes token of the main
......
...@@ -79,11 +79,14 @@ running on your instance). ...@@ -79,11 +79,14 @@ running on your instance).
![DNS A record pointing to GitLab.com Pages server](img/dns_add_new_a_record_example_updated_2018.png) ![DNS A record pointing to GitLab.com Pages server](img/dns_add_new_a_record_example_updated_2018.png)
NOTE: **Note:** CAUTION: **Caution:**
Note that if you use your root domain for your GitLab Pages website **only**, and if Note that if you use your root domain for your GitLab Pages website
your domain registrar supports this feature, you can add a DNS apex `CNAME` **only**, and if your domain registrar supports this feature, you can
record instead of an `A` record. The main advantage of doing so is that when GitLab Pages add a DNS apex `CNAME` record instead of an `A` record. The main
IP on GitLab.com changes for whatever reason, you don't need to update your `A` record. advantage of doing so is that when GitLab Pages IP on GitLab.com
changes for whatever reason, you don't need to update your `A` record.
There may be a few exceptions, but **this method is not recommended**
as it most likely won't work if you set an `MX` record for your root domain.
#### DNS CNAME record #### DNS CNAME record
...@@ -114,14 +117,16 @@ co-exist, so you need to place the TXT record in a special subdomain of its own. ...@@ -114,14 +117,16 @@ co-exist, so you need to place the TXT record in a special subdomain of its own.
#### TL;DR #### TL;DR
If the domain has multiple uses (e.g., you host email on it as well): For root domains (`domain.com`), set a DNS `A` record and verify your
domain's ownership with a TXT record:
| From | DNS Record | To | | From | DNS Record | To |
| ---- | ---------- | -- | | ---- | ---------- | -- |
| domain.com | A | 35.185.44.232 | | domain.com | A | 35.185.44.232 |
| domain.com | TXT | gitlab-pages-verification-code=00112233445566778899aabbccddeeff | | domain.com | TXT | gitlab-pages-verification-code=00112233445566778899aabbccddeeff |
If the domain is dedicated to GitLab Pages use and no other services run on it: For subdomains (`subdomain.domain.com`), set a DNS `CNAME` record and
verify your domain's ownership with a TXT record:
| From | DNS Record | To | | From | DNS Record | To |
| ---- | ---------- | -- | | ---- | ---------- | -- |
......
...@@ -88,6 +88,14 @@ The mirrored repository will be listed. For example, `https://*****:*****@github ...@@ -88,6 +88,14 @@ The mirrored repository will be listed. For example, `https://*****:*****@github
The repository will push soon. To force a push, click the appropriate button. The repository will push soon. To force a push, click the appropriate button.
## Setting up a push mirror to another GitLab instance with 2FA activated
1. On the destination GitLab instance, create a [personal access token](../user/profile/personal_access_tokens.md) with `API` scope.
1. On the source GitLab instance:
1. Fill in the **Git repository URL** field using this format: `https://oauth2@<destination host>/<your_gitlab_group_or_name>/<your_gitlab_project>.git`.
1. Fill in **Password** field with the GitLab personal access token created on the destination GitLab instance.
1. Click the **Mirror repository** button.
## Pulling from a remote repository **[STARTER]** ## Pulling from a remote repository **[STARTER]**
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/51) in GitLab Enterprise Edition 8.2. > [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/51) in GitLab Enterprise Edition 8.2.
......
...@@ -109,6 +109,7 @@ module API ...@@ -109,6 +109,7 @@ module API
mount ::API::Features mount ::API::Features
mount ::API::Files mount ::API::Files
mount ::API::GroupBoards mount ::API::GroupBoards
mount ::API::GroupLabels
mount ::API::GroupMilestones mount ::API::GroupMilestones
mount ::API::Groups mount ::API::Groups
mount ::API::GroupVariables mount ::API::GroupVariables
......
...@@ -1019,12 +1019,17 @@ module API ...@@ -1019,12 +1019,17 @@ module API
label.open_merge_requests_count(options[:current_user]) label.open_merge_requests_count(options[:current_user])
end end
expose :priority do |label, options| expose :subscribed do |label, options|
label.priority(options[:project]) label.subscribed?(options[:current_user], options[:parent])
end end
end
expose :subscribed do |label, options| class GroupLabel < Label
label.subscribed?(options[:current_user], options[:project]) end
class ProjectLabel < Label
expose :priority do |label, options|
label.priority(options[:parent])
end end
end end
......
# frozen_string_literal: true
module API
class GroupLabels < Grape::API
include PaginationParams
helpers ::API::Helpers::LabelHelpers
before { authenticate! }
params do
requires :id, type: String, desc: 'The ID of a group'
end
resource :groups, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
desc 'Get all labels of the group' do
detail 'This feature was added in GitLab 11.8'
success Entities::GroupLabel
end
params do
use :pagination
end
get ':id/labels' do
get_labels(user_group, Entities::GroupLabel)
end
desc 'Create a new label' do
detail 'This feature was added in GitLab 11.8'
success Entities::GroupLabel
end
params do
use :label_create_params
end
post ':id/labels' do
create_label(user_group, Entities::GroupLabel)
end
desc 'Update an existing label. At least one optional parameter is required.' do
detail 'This feature was added in GitLab 11.8'
success Entities::GroupLabel
end
params do
requires :name, type: String, desc: 'The name of the label to be updated'
optional :new_name, type: String, desc: 'The new name of the label'
optional :color, type: String, desc: "The new color of the label given in 6-digit hex notation with leading '#' sign (e.g. #FFAABB) or one of the allowed CSS color names"
optional :description, type: String, desc: 'The new description of label'
at_least_one_of :new_name, :color, :description
end
put ':id/labels' do
update_label(user_group, Entities::GroupLabel)
end
desc 'Delete an existing label' do
detail 'This feature was added in GitLab 11.8'
success Entities::GroupLabel
end
params do
requires :name, type: String, desc: 'The name of the label to be deleted'
end
delete ':id/labels' do
delete_label(user_group)
end
end
end
end
...@@ -84,8 +84,8 @@ module API ...@@ -84,8 +84,8 @@ module API
page || not_found!('Wiki Page') page || not_found!('Wiki Page')
end end
def available_labels_for(label_parent) def available_labels_for(label_parent, include_ancestor_groups: true)
search_params = { include_ancestor_groups: true } search_params = { include_ancestor_groups: include_ancestor_groups }
if label_parent.is_a?(Project) if label_parent.is_a?(Project)
search_params[:project_id] = label_parent.id search_params[:project_id] = label_parent.id
...@@ -170,13 +170,6 @@ module API ...@@ -170,13 +170,6 @@ module API
end end
end end
def find_project_label(id)
labels = available_labels_for(user_project)
label = labels.find_by_id(id) || labels.find_by_title(id)
label || not_found!('Label')
end
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def find_project_issue(iid) def find_project_issue(iid)
IssuesFinder.new(current_user, project_id: user_project.id).find_by!(iid: iid) IssuesFinder.new(current_user, project_id: user_project.id).find_by!(iid: iid)
......
# frozen_string_literal: true
module API
module Helpers
module LabelHelpers
extend Grape::API::Helpers
params :label_create_params do
requires :name, type: String, desc: 'The name of the label to be created'
requires :color, type: String, desc: "The color of the label given in 6-digit hex notation with leading '#' sign (e.g. #FFAABB) or one of the allowed CSS color names"
optional :description, type: String, desc: 'The description of label to be created'
end
def find_label(parent, id, include_ancestor_groups: true)
labels = available_labels_for(parent, include_ancestor_groups: include_ancestor_groups)
label = labels.find_by_id(id) || labels.find_by_title(id)
label || not_found!('Label')
end
def get_labels(parent, entity)
present paginate(available_labels_for(parent)), with: entity, current_user: current_user, parent: parent
end
def create_label(parent, entity)
authorize! :admin_label, parent
label = available_labels_for(parent).find_by_title(params[:name])
conflict!('Label already exists') if label
priority = params.delete(:priority)
label_params = declared_params(include_missing: false)
label =
if parent.is_a?(Project)
::Labels::CreateService.new(label_params).execute(project: parent)
else
::Labels::CreateService.new(label_params).execute(group: parent)
end
if label.persisted?
if parent.is_a?(Project)
label.prioritize!(parent, priority) if priority
end
present label, with: entity, current_user: current_user, parent: parent
else
render_validation_error!(label)
end
end
def update_label(parent, entity)
authorize! :admin_label, parent
label = find_label(parent, params[:name], include_ancestor_groups: false)
update_priority = params.key?(:priority)
priority = params.delete(:priority)
label = ::Labels::UpdateService.new(declared_params(include_missing: false)).execute(label)
render_validation_error!(label) unless label.valid?
if parent.is_a?(Project) && update_priority
if priority.nil?
label.unprioritize!(parent)
else
label.prioritize!(parent, priority)
end
end
present label, with: entity, current_user: current_user, parent: parent
end
def delete_label(parent)
authorize! :admin_label, parent
label = find_label(parent, params[:name], include_ancestor_groups: false)
destroy_conditionally!(label)
end
end
end
end
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
module API module API
class Labels < Grape::API class Labels < Grape::API
include PaginationParams include PaginationParams
helpers ::API::Helpers::LabelHelpers
before { authenticate! } before { authenticate! }
...@@ -11,62 +12,28 @@ module API ...@@ -11,62 +12,28 @@ module API
end end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
desc 'Get all labels of the project' do desc 'Get all labels of the project' do
success Entities::Label success Entities::ProjectLabel
end end
params do params do
use :pagination use :pagination
end end
get ':id/labels' do get ':id/labels' do
present paginate(available_labels_for(user_project)), with: Entities::Label, current_user: current_user, project: user_project get_labels(user_project, Entities::ProjectLabel)
end end
desc 'Create a new label' do desc 'Create a new label' do
success Entities::Label success Entities::ProjectLabel
end end
params do params do
requires :name, type: String, desc: 'The name of the label to be created' use :label_create_params
requires :color, type: String, desc: "The color of the label given in 6-digit hex notation with leading '#' sign (e.g. #FFAABB) or one of the allowed CSS color names"
optional :description, type: String, desc: 'The description of label to be created'
optional :priority, type: Integer, desc: 'The priority of the label', allow_blank: true optional :priority, type: Integer, desc: 'The priority of the label', allow_blank: true
end end
# rubocop: disable CodeReuse/ActiveRecord
post ':id/labels' do post ':id/labels' do
authorize! :admin_label, user_project create_label(user_project, Entities::ProjectLabel)
label = available_labels_for(user_project).find_by(title: params[:name])
conflict!('Label already exists') if label
priority = params.delete(:priority)
label = ::Labels::CreateService.new(declared_params(include_missing: false)).execute(project: user_project)
if label.valid?
label.prioritize!(user_project, priority) if priority
present label, with: Entities::Label, current_user: current_user, project: user_project
else
render_validation_error!(label)
end
end
# rubocop: enable CodeReuse/ActiveRecord
desc 'Delete an existing label' do
success Entities::Label
end
params do
requires :name, type: String, desc: 'The name of the label to be deleted'
end
# rubocop: disable CodeReuse/ActiveRecord
delete ':id/labels' do
authorize! :admin_label, user_project
label = user_project.labels.find_by(title: params[:name])
not_found!('Label') unless label
destroy_conditionally!(label)
end end
# rubocop: enable CodeReuse/ActiveRecord
desc 'Update an existing label. At least one optional parameter is required.' do desc 'Update an existing label. At least one optional parameter is required.' do
success Entities::Label success Entities::ProjectLabel
end end
params do params do
requires :name, type: String, desc: 'The name of the label to be updated' requires :name, type: String, desc: 'The name of the label to be updated'
...@@ -76,33 +43,19 @@ module API ...@@ -76,33 +43,19 @@ module API
optional :priority, type: Integer, desc: 'The priority of the label', allow_blank: true optional :priority, type: Integer, desc: 'The priority of the label', allow_blank: true
at_least_one_of :new_name, :color, :description, :priority at_least_one_of :new_name, :color, :description, :priority
end end
# rubocop: disable CodeReuse/ActiveRecord
put ':id/labels' do put ':id/labels' do
authorize! :admin_label, user_project update_label(user_project, Entities::ProjectLabel)
end
label = user_project.labels.find_by(title: params[:name])
not_found!('Label not found') unless label
update_priority = params.key?(:priority)
priority = params.delete(:priority)
label_params = declared_params(include_missing: false)
# Rename new name to the actual label attribute name
label_params[:name] = label_params.delete(:new_name) if label_params.key?(:new_name)
label = ::Labels::UpdateService.new(label_params).execute(label)
render_validation_error!(label) unless label.valid?
if update_priority
if priority.nil?
label.unprioritize!(user_project)
else
label.prioritize!(user_project, priority)
end
end
present label, with: Entities::Label, current_user: current_user, project: user_project desc 'Delete an existing label' do
success Entities::ProjectLabel
end
params do
requires :name, type: String, desc: 'The name of the label to be deleted'
end
delete ':id/labels' do
delete_label(user_project)
end end
# rubocop: enable CodeReuse/ActiveRecord
end end
end end
end end
...@@ -343,6 +343,7 @@ module API ...@@ -343,6 +343,7 @@ module API
end end
params do params do
optional :merge_commit_message, type: String, desc: 'Custom merge commit message' optional :merge_commit_message, type: String, desc: 'Custom merge commit message'
optional :squash_commit_message, type: String, desc: 'Custom squash commit message'
optional :should_remove_source_branch, type: Boolean, optional :should_remove_source_branch, type: Boolean,
desc: 'When true, the source branch will be deleted if possible' desc: 'When true, the source branch will be deleted if possible'
optional :merge_when_pipeline_succeeds, type: Boolean, optional :merge_when_pipeline_succeeds, type: Boolean,
...@@ -370,6 +371,7 @@ module API ...@@ -370,6 +371,7 @@ module API
merge_params = { merge_params = {
commit_message: params[:merge_commit_message], commit_message: params[:merge_commit_message],
squash_commit_message: params[:squash_commit_message],
should_remove_source_branch: params[:should_remove_source_branch] should_remove_source_branch: params[:should_remove_source_branch]
} }
......
...@@ -121,6 +121,7 @@ module API ...@@ -121,6 +121,7 @@ module API
optional :terminal_max_session_time, type: Integer, desc: 'Maximum time for web terminal websocket connection (in seconds). Set to 0 for unlimited time.' optional :terminal_max_session_time, type: Integer, desc: 'Maximum time for web terminal websocket connection (in seconds). Set to 0 for unlimited time.'
optional :usage_ping_enabled, type: Boolean, desc: 'Every week GitLab will report license usage back to GitLab, Inc.' optional :usage_ping_enabled, type: Boolean, desc: 'Every week GitLab will report license usage back to GitLab, Inc.'
optional :instance_statistics_visibility_private, type: Boolean, desc: 'When set to `true` Instance statistics will only be available to admins' optional :instance_statistics_visibility_private, type: Boolean, desc: 'When set to `true` Instance statistics will only be available to admins'
optional :local_markdown_version, type: Integer, desc: "Local markdown version, increase this value when any cached markdown should be invalidated"
ApplicationSetting::SUPPORTED_KEY_TYPES.each do |type| ApplicationSetting::SUPPORTED_KEY_TYPES.each do |type|
optional :"#{type}_key_restriction", optional :"#{type}_key_restriction",
......
...@@ -2,51 +2,88 @@ ...@@ -2,51 +2,88 @@
module API module API
class Subscriptions < Grape::API class Subscriptions < Grape::API
helpers ::API::Helpers::LabelHelpers
before { authenticate! } before { authenticate! }
subscribable_types = { subscribables = [
'merge_requests' => proc { |id| find_merge_request_with_access(id, :update_merge_request) }, {
'issues' => proc { |id| find_project_issue(id) }, type: 'merge_requests',
'labels' => proc { |id| find_project_label(id) } entity: Entities::MergeRequest,
} source: Project,
finder: ->(id) { find_merge_request_with_access(id, :update_merge_request) }
},
{
type: 'issues',
entity: Entities::Issue,
source: Project,
finder: ->(id) { find_project_issue(id) }
},
{
type: 'labels',
entity: Entities::ProjectLabel,
source: Project,
finder: ->(id) { find_label(user_project, id) }
},
{
type: 'labels',
entity: Entities::GroupLabel,
source: Group,
finder: ->(id) { find_label(user_group, id) }
}
]
params do subscribables.each do |subscribable|
requires :id, type: String, desc: 'The ID of a project' source_type = subscribable[:source].name.underscore
requires :subscribable_id, type: String, desc: 'The ID of a resource'
end
resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
subscribable_types.each do |type, finder|
type_singularized = type.singularize
entity_class = Entities.const_get(type_singularized.camelcase)
params do
requires :id, type: String, desc: "The #{source_type} ID"
requires :subscribable_id, type: String, desc: 'The ID of a resource'
end
resource source_type.pluralize, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
desc 'Subscribe to a resource' do desc 'Subscribe to a resource' do
success entity_class success subscribable[:entity]
end end
post ":id/#{type}/:subscribable_id/subscribe" do post ":id/#{subscribable[:type]}/:subscribable_id/subscribe" do
resource = instance_exec(params[:subscribable_id], &finder) parent = parent_resource(source_type)
resource = instance_exec(params[:subscribable_id], &subscribable[:finder])
if resource.subscribed?(current_user, user_project) if resource.subscribed?(current_user, parent)
not_modified! not_modified!
else else
resource.subscribe(current_user, user_project) resource.subscribe(current_user, parent)
present resource, with: entity_class, current_user: current_user, project: user_project present resource, with: subscribable[:entity], current_user: current_user, project: parent, parent: parent
end end
end end
desc 'Unsubscribe from a resource' do desc 'Unsubscribe from a resource' do
success entity_class success subscribable[:entity]
end end
post ":id/#{type}/:subscribable_id/unsubscribe" do post ":id/#{subscribable[:type]}/:subscribable_id/unsubscribe" do
resource = instance_exec(params[:subscribable_id], &finder) parent = parent_resource(source_type)
resource = instance_exec(params[:subscribable_id], &subscribable[:finder])
if !resource.subscribed?(current_user, user_project) if !resource.subscribed?(current_user, parent)
not_modified! not_modified!
else else
resource.unsubscribe(current_user, user_project) resource.unsubscribe(current_user, parent)
present resource, with: entity_class, current_user: current_user, project: user_project present resource, with: subscribable[:entity], current_user: current_user, project: parent, parent: parent
end end
end end
end end
end end
private
helpers do
def parent_resource(source_type)
case source_type
when 'project'
user_project
else
nil
end
end
end
end end
end end
...@@ -93,7 +93,7 @@ module Backup ...@@ -93,7 +93,7 @@ module Backup
progress.puts "Error: #{e}".color(:red) progress.puts "Error: #{e}".color(:red)
end end
else else
restore_repo_success = gitlab_shell.create_repository(project.repository_storage, project.disk_path) restore_repo_success = gitlab_shell.create_project_repository(project)
end end
if restore_repo_success if restore_repo_success
......
...@@ -12,6 +12,9 @@ module Gitlab ...@@ -12,6 +12,9 @@ module Gitlab
# Scopes used for OpenID Connect # Scopes used for OpenID Connect
OPENID_SCOPES = [:openid].freeze OPENID_SCOPES = [:openid].freeze
# OpenID Connect profile scopes
PROFILE_SCOPES = [:profile, :email].freeze
# Default scopes for OAuth applications that don't define their own # Default scopes for OAuth applications that don't define their own
DEFAULT_SCOPES = [:api].freeze DEFAULT_SCOPES = [:api].freeze
...@@ -284,7 +287,7 @@ module Gitlab ...@@ -284,7 +287,7 @@ module Gitlab
# Other available scopes # Other available scopes
def optional_scopes def optional_scopes
available_scopes + OPENID_SCOPES - DEFAULT_SCOPES available_scopes + OPENID_SCOPES + PROFILE_SCOPES - DEFAULT_SCOPES
end end
def registry_scopes def registry_scopes
......
...@@ -65,9 +65,9 @@ module Gitlab ...@@ -65,9 +65,9 @@ module Gitlab
def import_wiki def import_wiki
return if project.wiki.repository_exists? return if project.wiki.repository_exists?
disk_path = project.wiki.disk_path wiki = WikiFormatter.new(project)
import_url = project.import_url.sub(/\.git\z/, ".git/wiki")
gitlab_shell.import_repository(project.repository_storage, disk_path, import_url) gitlab_shell.import_wiki_repository(project, wiki)
rescue StandardError => e rescue StandardError => e
errors << { type: :wiki, errors: e.message } errors << { type: :wiki, errors: e.message }
end end
......
# frozen_string_literal: true
module Gitlab
module BitbucketImport
class WikiFormatter
attr_reader :project
def initialize(project)
@project = project
end
def disk_path
project.wiki.disk_path
end
def full_path
project.wiki.full_path
end
def import_url
project.import_url.sub(/\.git\z/, ".git/wiki")
end
end
end
end
# Read more about how to use this script on this blog post https://about.gitlab.com/2019/01/28/android-publishing-with-gitlab-and-fastlane/
# You will also need to configure your build.gradle, Dockerfile, and fastlane configuration to make this work.
# If you are looking for a simpler template that does not publish, see the Android template.
stages:
- environment
- build
- test
- internal
- alpha
- beta
- production
.updateContainerJob:
image: docker:stable
stage: environment
services:
- docker:dind
script:
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
- docker pull $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG || true
- docker build --cache-from $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG -t $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
updateContainer:
extends: .updateContainerJob
only:
changes:
- Dockerfile
ensureContainer:
extends: .updateContainerJob
allow_failure: true
before_script:
- "mkdir -p ~/.docker && echo '{\"experimental\": \"enabled\"}' > ~/.docker/config.json"
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
# Skip update container `script` if the container already exists
# via https://gitlab.com/gitlab-org/gitlab-ce/issues/26866#note_97609397 -> https://stackoverflow.com/a/52077071/796832
- docker manifest inspect $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG > /dev/null && exit || true
.build_job:
image: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
stage: build
before_script:
# We store this binary file in a variable as hex with this command: `xxd -p android-app.jks`
# Then we convert the hex back to a binary file
- echo "$signing_jks_file_hex" | xxd -r -p - > android-signing-keystore.jks
- "export VERSION_CODE=$CI_PIPELINE_IID && echo $VERSION_CODE"
- "export VERSION_SHA=`echo ${CI_COMMIT_SHA:0:8}` && echo $VERSION_SHA"
after_script:
- rm -f android-signing-keystore.jks || true
artifacts:
paths:
- app/build/outputs
buildDebug:
extends: .build_job
script:
- bundle exec fastlane buildDebug
buildRelease:
extends: .build_job
script:
- bundle exec fastlane buildRelease
environment:
name: production
testDebug:
image: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
stage: test
dependencies:
- buildDebug
script:
- bundle exec fastlane test
publishInternal:
image: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
stage: internal
dependencies:
- buildRelease
when: manual
before_script:
- echo $google_play_service_account_api_key_json > ~/google_play_api_key.json
after_script:
- rm ~/google_play_api_key.json
script:
- bundle exec fastlane internal
.promote_job:
image: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
when: manual
dependencies: []
before_script:
- echo $google_play_service_account_api_key_json > ~/google_play_api_key.json
after_script:
- rm ~/google_play_api_key.json
promoteAlpha:
extends: .promote_job
stage: alpha
script:
- bundle exec fastlane promote_internal_to_alpha
promoteBeta:
extends: .promote_job
stage: beta
script:
- bundle exec fastlane promote_alpha_to_beta
promoteProduction:
extends: .promote_job
stage: production
# We only allow production promotion on `master` because
# it has its own production scoped secret variables
only:
- master
script:
- bundle exec fastlane promote_beta_to_production
\ No newline at end of file
# Read more about this script on this blog post https://about.gitlab.com/2018/10/24/setting-up-gitlab-ci-for-android-projects/, by Jason Lenny # Read more about this script on this blog post https://about.gitlab.com/2018/10/24/setting-up-gitlab-ci-for-android-projects/, by Jason Lenny
# If you are interested in using Android with FastLane for publishing take a look at the Android-Fastlane template.
image: openjdk:8-jdk image: openjdk:8-jdk
variables: variables:
......
...@@ -21,8 +21,8 @@ ...@@ -21,8 +21,8 @@
# #
# In order to deploy, you must have a Kubernetes cluster configured either # In order to deploy, you must have a Kubernetes cluster configured either
# via a project integration, or via group/project variables. # via a project integration, or via group/project variables.
# AUTO_DEVOPS_DOMAIN must also be set as a variable at the group or project # KUBE_INGRESS_BASE_DOMAIN must also be set on the cluster settings,
# level, or manually added below. # as a variable at the group or project level, or manually added below.
# #
# Continuous deployment to production is enabled by default. # Continuous deployment to production is enabled by default.
# If you want to deploy to staging first, set STAGING_ENABLED environment variable. # If you want to deploy to staging first, set STAGING_ENABLED environment variable.
...@@ -41,8 +41,8 @@ ...@@ -41,8 +41,8 @@
image: alpine:latest image: alpine:latest
variables: variables:
# AUTO_DEVOPS_DOMAIN is the application deployment domain and should be set as a variable at the group or project level. # KUBE_INGRESS_BASE_DOMAIN is the application deployment domain and should be set as a variable at the group or project level.
# AUTO_DEVOPS_DOMAIN: domain.example.com # KUBE_INGRESS_BASE_DOMAIN: domain.example.com
POSTGRES_USER: user POSTGRES_USER: user
POSTGRES_PASSWORD: testing-password POSTGRES_PASSWORD: testing-password
...@@ -251,7 +251,7 @@ review: ...@@ -251,7 +251,7 @@ review:
- persist_environment_url - persist_environment_url
environment: environment:
name: review/$CI_COMMIT_REF_NAME name: review/$CI_COMMIT_REF_NAME
url: http://$CI_PROJECT_PATH_SLUG-$CI_ENVIRONMENT_SLUG.$AUTO_DEVOPS_DOMAIN url: http://$CI_PROJECT_PATH_SLUG-$CI_ENVIRONMENT_SLUG.$KUBE_INGRESS_BASE_DOMAIN
on_stop: stop_review on_stop: stop_review
artifacts: artifacts:
paths: [environment_url.txt] paths: [environment_url.txt]
...@@ -306,7 +306,7 @@ staging: ...@@ -306,7 +306,7 @@ staging:
- deploy - deploy
environment: environment:
name: staging name: staging
url: http://$CI_PROJECT_PATH_SLUG-staging.$AUTO_DEVOPS_DOMAIN url: http://$CI_PROJECT_PATH_SLUG-staging.$KUBE_INGRESS_BASE_DOMAIN
only: only:
refs: refs:
- master - master
...@@ -330,7 +330,7 @@ canary: ...@@ -330,7 +330,7 @@ canary:
- deploy canary - deploy canary
environment: environment:
name: production name: production
url: http://$CI_PROJECT_PATH_SLUG.$AUTO_DEVOPS_DOMAIN url: http://$CI_PROJECT_PATH_SLUG.$KUBE_INGRESS_BASE_DOMAIN
when: manual when: manual
only: only:
refs: refs:
...@@ -354,7 +354,7 @@ canary: ...@@ -354,7 +354,7 @@ canary:
- persist_environment_url - persist_environment_url
environment: environment:
name: production name: production
url: http://$CI_PROJECT_PATH_SLUG.$AUTO_DEVOPS_DOMAIN url: http://$CI_PROJECT_PATH_SLUG.$KUBE_INGRESS_BASE_DOMAIN
artifacts: artifacts:
paths: [environment_url.txt] paths: [environment_url.txt]
...@@ -403,7 +403,7 @@ production_manual: ...@@ -403,7 +403,7 @@ production_manual:
- persist_environment_url - persist_environment_url
environment: environment:
name: production name: production
url: http://$CI_PROJECT_PATH_SLUG.$AUTO_DEVOPS_DOMAIN url: http://$CI_PROJECT_PATH_SLUG.$KUBE_INGRESS_BASE_DOMAIN
artifacts: artifacts:
paths: [environment_url.txt] paths: [environment_url.txt]
...@@ -689,7 +689,7 @@ rollout 100%: ...@@ -689,7 +689,7 @@ rollout 100%:
--set application.database_url="$DATABASE_URL" \ --set application.database_url="$DATABASE_URL" \
--set application.secretName="$APPLICATION_SECRET_NAME" \ --set application.secretName="$APPLICATION_SECRET_NAME" \
--set application.secretChecksum="$APPLICATION_SECRET_CHECKSUM" \ --set application.secretChecksum="$APPLICATION_SECRET_CHECKSUM" \
--set service.commonName="le.$AUTO_DEVOPS_DOMAIN" \ --set service.commonName="le.$KUBE_INGRESS_BASE_DOMAIN" \
--set service.url="$CI_ENVIRONMENT_URL" \ --set service.url="$CI_ENVIRONMENT_URL" \
--set service.additionalHosts="$additional_hosts" \ --set service.additionalHosts="$additional_hosts" \
--set replicaCount="$replicas" \ --set replicaCount="$replicas" \
...@@ -725,7 +725,7 @@ rollout 100%: ...@@ -725,7 +725,7 @@ rollout 100%:
--set application.database_url="$DATABASE_URL" \ --set application.database_url="$DATABASE_URL" \
--set application.secretName="$APPLICATION_SECRET_NAME" \ --set application.secretName="$APPLICATION_SECRET_NAME" \
--set application.secretChecksum="$APPLICATION_SECRET_CHECKSUM" \ --set application.secretChecksum="$APPLICATION_SECRET_CHECKSUM" \
--set service.commonName="le.$AUTO_DEVOPS_DOMAIN" \ --set service.commonName="le.$KUBE_INGRESS_BASE_DOMAIN" \
--set service.url="$CI_ENVIRONMENT_URL" \ --set service.url="$CI_ENVIRONMENT_URL" \
--set service.additionalHosts="$additional_hosts" \ --set service.additionalHosts="$additional_hosts" \
--set replicaCount="$replicas" \ --set replicaCount="$replicas" \
...@@ -823,11 +823,24 @@ rollout 100%: ...@@ -823,11 +823,24 @@ rollout 100%:
kubectl describe namespace "$KUBE_NAMESPACE" || kubectl create namespace "$KUBE_NAMESPACE" kubectl describe namespace "$KUBE_NAMESPACE" || kubectl create namespace "$KUBE_NAMESPACE"
} }
# Function to ensure backwards compatibility with AUTO_DEVOPS_DOMAIN
function ensure_kube_ingress_base_domain() {
if [ -z ${KUBE_INGRESS_BASE_DOMAIN+x} ]; then
export KUBE_INGRESS_BASE_DOMAIN=$AUTO_DEVOPS_DOMAIN
fi
}
function check_kube_domain() { function check_kube_domain() {
if [ -z ${AUTO_DEVOPS_DOMAIN+x} ]; then ensure_kube_ingress_base_domain
echo "In order to deploy or use Review Apps, AUTO_DEVOPS_DOMAIN variable must be set"
echo "You can do it in Auto DevOps project settings or defining a variable at group or project level" if [ -z ${KUBE_INGRESS_BASE_DOMAIN+x} ]; then
echo "In order to deploy or use Review Apps,"
echo "AUTO_DEVOPS_DOMAIN or KUBE_INGRESS_BASE_DOMAIN variables must be set"
echo "From 11.8, you can set KUBE_INGRESS_BASE_DOMAIN in cluster settings"
echo "or by defining a variable at group or project level."
echo "You can also manually add it in .gitlab-ci.yml" echo "You can also manually add it in .gitlab-ci.yml"
echo "AUTO_DEVOPS_DOMAIN support will be dropped on 12.0"
false false
else else
true true
......
...@@ -10,12 +10,13 @@ module Gitlab ...@@ -10,12 +10,13 @@ module Gitlab
delegate :exists?, :size, to: :repository delegate :exists?, :size, to: :repository
delegate :unlink_repository, :delete, to: :object_pool_service delegate :unlink_repository, :delete, to: :object_pool_service
attr_reader :storage, :relative_path, :source_repository attr_reader :storage, :relative_path, :source_repository, :gl_project_path
def initialize(storage, relative_path, source_repository) def initialize(storage, relative_path, source_repository, gl_project_path)
@storage = storage @storage = storage
@relative_path = relative_path @relative_path = relative_path
@source_repository = source_repository @source_repository = source_repository
@gl_project_path = gl_project_path
end end
def create def create
...@@ -31,12 +32,12 @@ module Gitlab ...@@ -31,12 +32,12 @@ module Gitlab
end end
def to_gitaly_repository def to_gitaly_repository
Gitlab::GitalyClient::Util.repository(storage, relative_path, GL_REPOSITORY) Gitlab::GitalyClient::Util.repository(storage, relative_path, GL_REPOSITORY, gl_project_path)
end end
# Allows for reusing other RPCs by 'tricking' Gitaly to think its a repository # Allows for reusing other RPCs by 'tricking' Gitaly to think its a repository
def repository def repository
@repository ||= Gitlab::Git::Repository.new(storage, relative_path, GL_REPOSITORY) @repository ||= Gitlab::Git::Repository.new(storage, relative_path, GL_REPOSITORY, gl_project_path)
end end
private private
......
...@@ -67,7 +67,7 @@ module Gitlab ...@@ -67,7 +67,7 @@ module Gitlab
# Relative path of repo # Relative path of repo
attr_reader :relative_path attr_reader :relative_path
attr_reader :storage, :gl_repository, :relative_path attr_reader :storage, :gl_repository, :relative_path, :gl_project_path
# This remote name has to be stable for all types of repositories that # This remote name has to be stable for all types of repositories that
# can join an object pool. If it's structure ever changes, a migration # can join an object pool. If it's structure ever changes, a migration
...@@ -78,10 +78,11 @@ module Gitlab ...@@ -78,10 +78,11 @@ module Gitlab
# This initializer method is only used on the client side (gitlab-ce). # This initializer method is only used on the client side (gitlab-ce).
# Gitaly-ruby uses a different initializer. # Gitaly-ruby uses a different initializer.
def initialize(storage, relative_path, gl_repository) def initialize(storage, relative_path, gl_repository, gl_project_path)
@storage = storage @storage = storage
@relative_path = relative_path @relative_path = relative_path
@gl_repository = gl_repository @gl_repository = gl_repository
@gl_project_path = gl_project_path
@name = @relative_path.split("/").last @name = @relative_path.split("/").last
end end
...@@ -872,7 +873,7 @@ module Gitlab ...@@ -872,7 +873,7 @@ module Gitlab
end end
def gitaly_repository def gitaly_repository
Gitlab::GitalyClient::Util.repository(@storage, @relative_path, @gl_repository) Gitlab::GitalyClient::Util.repository(@storage, @relative_path, @gl_repository, @gl_project_path)
end end
def gitaly_ref_client def gitaly_ref_client
......
...@@ -324,13 +324,40 @@ module Gitlab ...@@ -324,13 +324,40 @@ module Gitlab
GitalyClient.call(@storage, :repository_service, :search_files_by_name, request, timeout: GitalyClient.fast_timeout).flat_map(&:files) GitalyClient.call(@storage, :repository_service, :search_files_by_name, request, timeout: GitalyClient.fast_timeout).flat_map(&:files)
end end
def search_files_by_content(ref, query) def search_files_by_content(ref, query, chunked_response: true)
request = Gitaly::SearchFilesByContentRequest.new(repository: @gitaly_repo, ref: ref, query: query) request = Gitaly::SearchFilesByContentRequest.new(repository: @gitaly_repo, ref: ref, query: query, chunked_response: chunked_response)
GitalyClient.call(@storage, :repository_service, :search_files_by_content, request).flat_map(&:matches) response = GitalyClient.call(@storage, :repository_service, :search_files_by_content, request)
search_results_from_response(response)
end end
private private
def search_results_from_response(gitaly_response)
matches = []
current_match = +""
gitaly_response.each do |message|
next if message.nil?
# Old client will ignore :chunked_response flag
# and return messages with `matches` key.
# This code path will be removed post 12.0 release
if message.matches.any?
matches += message.matches
else
current_match << message.match_data
if message.end_of_match
matches << current_match
current_match = +""
end
end
end
matches
end
def gitaly_fetch_stream_to_file(save_path, rpc_name, request_class, timeout) def gitaly_fetch_stream_to_file(save_path, rpc_name, request_class, timeout)
request = request_class.new(repository: @gitaly_repo) request = request_class.new(repository: @gitaly_repo)
response = GitalyClient.call( response = GitalyClient.call(
......
...@@ -4,7 +4,7 @@ module Gitlab ...@@ -4,7 +4,7 @@ module Gitlab
module GitalyClient module GitalyClient
module Util module Util
class << self class << self
def repository(repository_storage, relative_path, gl_repository) def repository(repository_storage, relative_path, gl_repository, gl_project_path)
git_env = Gitlab::Git::HookEnv.all(gl_repository) git_env = Gitlab::Git::HookEnv.all(gl_repository)
git_object_directory = git_env['GIT_OBJECT_DIRECTORY_RELATIVE'].presence git_object_directory = git_env['GIT_OBJECT_DIRECTORY_RELATIVE'].presence
git_alternate_object_directories = Array.wrap(git_env['GIT_ALTERNATE_OBJECT_DIRECTORIES_RELATIVE']) git_alternate_object_directories = Array.wrap(git_env['GIT_ALTERNATE_OBJECT_DIRECTORIES_RELATIVE'])
...@@ -14,14 +14,16 @@ module Gitlab ...@@ -14,14 +14,16 @@ module Gitlab
relative_path: relative_path, relative_path: relative_path,
gl_repository: gl_repository.to_s, gl_repository: gl_repository.to_s,
git_object_directory: git_object_directory.to_s, git_object_directory: git_object_directory.to_s,
git_alternate_object_directories: git_alternate_object_directories git_alternate_object_directories: git_alternate_object_directories,
gl_project_path: gl_project_path
) )
end end
def git_repository(gitaly_repository) def git_repository(gitaly_repository)
Gitlab::Git::Repository.new(gitaly_repository.storage_name, Gitlab::Git::Repository.new(gitaly_repository.storage_name,
gitaly_repository.relative_path, gitaly_repository.relative_path,
gitaly_repository.gl_repository) gitaly_repository.gl_repository,
gitaly_repository.gl_project_path)
end end
end end
end end
......
...@@ -6,11 +6,12 @@ module Gitlab ...@@ -6,11 +6,12 @@ module Gitlab
class RepositoryImporter class RepositoryImporter
include Gitlab::ShellAdapter include Gitlab::ShellAdapter
attr_reader :project, :client attr_reader :project, :client, :wiki_formatter
def initialize(project, client) def initialize(project, client)
@project = project @project = project
@client = client @client = client
@wiki_formatter = ::Gitlab::LegacyGithubImport::WikiFormatter.new(project)
end end
# Returns true if we should import the wiki for the project. # Returns true if we should import the wiki for the project.
...@@ -57,9 +58,7 @@ module Gitlab ...@@ -57,9 +58,7 @@ module Gitlab
end end
def import_wiki_repository def import_wiki_repository
wiki_path = "#{project.disk_path}.wiki" gitlab_shell.import_wiki_repository(project, wiki_formatter)
gitlab_shell.import_repository(project.repository_storage, wiki_path, wiki_url)
true true
rescue Gitlab::Shell::Error => e rescue Gitlab::Shell::Error => e
...@@ -72,7 +71,7 @@ module Gitlab ...@@ -72,7 +71,7 @@ module Gitlab
end end
def wiki_url def wiki_url
project.import_url.sub(/\.git\z/, '.wiki.git') wiki_formatter.import_url
end end
def update_clone_time def update_clone_time
......
...@@ -267,7 +267,7 @@ module Gitlab ...@@ -267,7 +267,7 @@ module Gitlab
def import_wiki def import_wiki
unless project.wiki.repository_exists? unless project.wiki.repository_exists?
wiki = WikiFormatter.new(project) wiki = WikiFormatter.new(project)
gitlab_shell.import_repository(project.repository_storage, wiki.disk_path, wiki.import_url) gitlab_shell.import_wiki_repository(project, wiki)
end end
rescue Gitlab::Shell::Error => e rescue Gitlab::Shell::Error => e
# GitHub error message when the wiki repo has not been created, # GitHub error message when the wiki repo has not been created,
......
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.
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.
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.
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.
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.
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