Commit 6ae6fea5 authored by Filipa Lacerda's avatar Filipa Lacerda

[ci skip] Merge branch 'master' into 6169-add-security-reports-pipeline-view

* master: (248 commits)
  SCLAU is a term specific to GitLab the company, not the product.
  Softened language a bit more, even
  Trying it with no features turned on for Monaco
  Disabling source maps for Production to resolve memory leak
  Resolve conflicts in locale/gitlab.pot
  Resolve conflicts in app/views/devise/shared/_signup_box.html.haml
  Resolve conflicts in locale/gitlab.pot
  Upgrade gitlab-markup to fix render issue with reStructuredText
  Resolve schema.rb conflict
  Bump grape-path-helpers to 1.0.5
  Fix conflicts in Gemfile.rails5.lock
  Add more logging before a project is destroyed
  Bump Google Chrome to V67
  Users can accept terms during registration
  Users can accept terms during registration
  teach `render_ce` to also search for full views
  Copyedit Auto DevOps deployment strategy docs
  Add missing QA class to EE-only view
  CE Port of Add QA test for authorized push to protected branch
  Fix artifact breadcrumb
  ...
parents ae5ebd60 11d47876
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.4.4-golang-1.9-git-2.17-chrome-65.0-node-8.x-yarn-1.2-postgresql-9.6-graphicsmagick-1.3.29"
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.4.4-golang-1.9-git-2.17-chrome-67.0-node-8.x-yarn-1.2-postgresql-9.6-graphicsmagick-1.3.29"
.dedicated-runner: &dedicated-runner
retry: 1
......@@ -715,6 +715,8 @@ docs lint:
<<: *except-qa
image: "registry.gitlab.com/gitlab-org/gitlab-build-images:nanoc-bootstrap-ruby-2.4-alpine"
stage: test
variables:
SETUP_DB: "false"
cache: {}
dependencies: []
before_script: []
......
......@@ -144,7 +144,7 @@ gem 'faraday_middleware-aws-signers-v4'
# Markdown and HTML processing
gem 'html-pipeline', '~> 2.7.1'
gem 'deckar01-task_list', '2.0.0'
gem 'gitlab-markup', '~> 1.6.2'
gem 'gitlab-markup', '~> 1.6.4'
gem 'redcarpet', '~> 3.4'
gem 'commonmarker', '~> 0.17'
gem 'RedCloth', '~> 4.3.2'
......@@ -434,7 +434,7 @@ group :ed25519 do
end
# Gitaly GRPC client
gem 'gitaly-proto', '~> 0.100.0', require: 'gitaly'
gem 'gitaly-proto', '~> 0.101.0', require: 'gitaly'
gem 'grpc', '~> 1.11.0'
# Locked until https://github.com/google/protobuf/issues/4210 is closed
......
......@@ -307,7 +307,7 @@ GEM
gettext_i18n_rails (>= 0.7.1)
po_to_json (>= 1.0.0)
rails (>= 3.2.0)
gitaly-proto (0.100.0)
gitaly-proto (0.101.0)
google-protobuf (~> 3.1)
grpc (~> 1.10)
github-linguist (5.3.3)
......@@ -337,7 +337,7 @@ GEM
mime-types (>= 1.16)
posix-spawn (~> 0.3)
gitlab-license (1.0.0)
gitlab-markup (1.6.3)
gitlab-markup (1.6.4)
gitlab-styles (2.3.2)
rubocop (~> 0.51)
rubocop-gitlab-security (~> 0.1.0)
......@@ -385,8 +385,8 @@ GEM
grape-entity (0.7.1)
activesupport (>= 4.0)
multi_json (>= 1.3.2)
grape-path-helpers (1.0.4)
activesupport (~> 4)
grape-path-helpers (1.0.5)
activesupport (>= 4, < 5.1)
grape (~> 1.0)
rake (~> 12)
grape_logging (1.7.0)
......@@ -480,7 +480,6 @@ GEM
kgio (2.10.0)
knapsack (1.16.0)
rake
timecop (>= 0.1.0)
kubeclient (3.1.0)
http (~> 2.2.2)
recursive-open-struct (~> 1.0, >= 1.0.4)
......@@ -1074,13 +1073,13 @@ DEPENDENCIES
gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3)
gitaly-proto (~> 0.100.0)
gitaly-proto (~> 0.101.0)
github-linguist (~> 5.3.3)
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-gollum-lib (~> 4.2)
gitlab-gollum-rugged_adapter (~> 0.4.4)
gitlab-license (~> 1.0)
gitlab-markup (~> 1.6.2)
gitlab-markup (~> 1.6.4)
gitlab-styles (~> 2.3)
gitlab_omniauth-ldap (~> 2.0.4)
gon (~> 6.2)
......
This diff is collapsed.
......@@ -142,4 +142,3 @@ export default {
</div>
</div>
</template>
......@@ -69,9 +69,10 @@ export default () => {
gl.diffNotesCompileComponents();
if (!hasVueMRDiscussionsCookie()) {
const resolveCountAppEl = document.querySelector('#resolve-count-app');
if (!hasVueMRDiscussionsCookie() && resolveCountAppEl) {
new Vue({
el: '#resolve-count-app',
el: resolveCountAppEl,
components: {
'resolve-count': ResolveCount
},
......
......@@ -5,6 +5,7 @@ import Icon from '../../../vue_shared/components/icon.vue';
import { rightSidebarViews } from '../../constants';
import PipelinesList from '../pipelines/list.vue';
import JobsDetail from '../jobs/detail.vue';
import ResizablePanel from '../resizable_panel.vue';
export default {
directives: {
......@@ -14,6 +15,7 @@ export default {
Icon,
PipelinesList,
JobsDetail,
ResizablePanel,
},
computed: {
...mapState(['rightPane']),
......@@ -40,12 +42,16 @@ export default {
<div
class="multi-file-commit-panel ide-right-sidebar"
>
<div
class="multi-file-commit-panel-inner"
<resizable-panel
v-if="rightPane"
class="multi-file-commit-panel-inner"
:collapsible="false"
:initial-width="350"
:min-size="350"
side="right"
>
<component :is="rightPane" />
</div>
</resizable-panel>
<nav class="ide-activity-bar">
<ul class="list-unstyled">
<li>
......
<script>
/* global monaco */
import { mapState, mapGetters, mapActions } from 'vuex';
import flash from '~/flash';
import ContentViewer from '~/vue_shared/components/content_viewer/content_viewer.vue';
import { activityBarViews, viewerTypes } from '../constants';
import monacoLoader from '../monaco_loader';
import Editor from '../lib/editor';
import ExternalLink from './external_link.vue';
......@@ -50,7 +48,7 @@ export default {
// Compare key to allow for files opened in review mode to be cached differently
if (oldVal.key !== this.file.key) {
this.initMonaco();
this.initEditor();
if (this.currentActivityView !== activityBarViews.edit) {
this.setFileViewMode({
......@@ -84,15 +82,10 @@ export default {
this.editor.dispose();
},
mounted() {
if (this.editor && monaco) {
this.initMonaco();
} else {
monacoLoader(['vs/editor/editor.main'], () => {
this.editor = Editor.create(monaco);
this.initMonaco();
});
if (!this.editor) {
this.editor = Editor.create();
}
this.initEditor();
},
methods: {
...mapActions([
......@@ -105,7 +98,7 @@ export default {
'updateViewer',
'removePendingTab',
]),
initMonaco() {
initEditor() {
if (this.shouldHideEditor) return;
this.editor.clearEditor();
......@@ -118,7 +111,7 @@ export default {
this.createEditorInstance();
})
.catch(err => {
flash('Error setting up monaco. Please try again.', 'alert', document, null, false, true);
flash('Error setting up editor. Please try again.', 'alert', document, null, false, true);
throw err;
});
},
......
import { editor as monacoEditor, Uri } from 'monaco-editor';
import Disposable from './disposable';
import eventHub from '../../eventhub';
export default class Model {
constructor(monaco, file, head = null) {
this.monaco = monaco;
constructor(file, head = null) {
this.disposable = new Disposable();
this.file = file;
this.head = head;
this.content = file.content !== '' ? file.content : file.raw;
this.disposable.add(
(this.originalModel = this.monaco.editor.createModel(
(this.originalModel = monacoEditor.createModel(
head ? head.content : this.file.raw,
undefined,
new this.monaco.Uri(null, null, `original/${this.path}`),
new Uri(false, false, `original/${this.path}`),
)),
(this.model = this.monaco.editor.createModel(
(this.model = monacoEditor.createModel(
this.content,
undefined,
new this.monaco.Uri(null, null, this.path),
new Uri(false, false, this.path),
)),
);
if (this.file.mrChange) {
this.disposable.add(
(this.baseModel = this.monaco.editor.createModel(
(this.baseModel = monacoEditor.createModel(
this.file.baseRaw,
undefined,
new this.monaco.Uri(null, null, `target/${this.path}`),
new Uri(false, false, `target/${this.path}`),
)),
);
}
......
......@@ -3,8 +3,7 @@ import Disposable from './disposable';
import Model from './model';
export default class ModelManager {
constructor(monaco) {
this.monaco = monaco;
constructor() {
this.disposable = new Disposable();
this.models = new Map();
}
......@@ -22,7 +21,7 @@ export default class ModelManager {
return this.getModel(file.key);
}
const model = new Model(this.monaco, file, head);
const model = new Model(file, head);
this.models.set(model.path, model);
this.disposable.add(model);
......
/* global monaco */
import { Range } from 'monaco-editor';
import { throttle } from 'underscore';
import DirtyDiffWorker from './diff_worker';
import Disposable from '../common/disposable';
......@@ -16,7 +16,7 @@ export const getDiffChangeType = change => {
};
export const getDecorator = change => ({
range: new monaco.Range(change.lineNumber, 1, change.endLineNumber, 1),
range: new Range(change.lineNumber, 1, change.endLineNumber, 1),
options: {
isWholeLine: true,
linesDecorationsClassName: `dirty-diff dirty-diff-${getDiffChangeType(change)}`,
......
import _ from 'underscore';
import { editor as monacoEditor, KeyCode, KeyMod } from 'monaco-editor';
import store from '../stores';
import DecorationsController from './decorations/controller';
import DirtyDiffController from './diff/controller';
......@@ -8,6 +9,11 @@ import editorOptions, { defaultEditorOptions } from './editor_options';
import gitlabTheme from './themes/gl_theme';
import keymap from './keymap.json';
function setupMonacoTheme() {
monacoEditor.defineTheme(gitlabTheme.themeName, gitlabTheme.monacoTheme);
monacoEditor.setTheme('gitlab');
}
export const clearDomElement = el => {
if (!el || !el.firstChild) return;
......@@ -17,24 +23,22 @@ export const clearDomElement = el => {
};
export default class Editor {
static create(monaco) {
if (this.editorInstance) return this.editorInstance;
this.editorInstance = new Editor(monaco);
static create() {
if (!this.editorInstance) {
this.editorInstance = new Editor();
}
return this.editorInstance;
}
constructor(monaco) {
this.monaco = monaco;
constructor() {
this.currentModel = null;
this.instance = null;
this.dirtyDiffController = null;
this.disposable = new Disposable();
this.modelManager = new ModelManager(this.monaco);
this.modelManager = new ModelManager();
this.decorationsController = new DecorationsController(this);
this.setupMonacoTheme();
setupMonacoTheme();
this.debouncedUpdate = _.debounce(() => {
this.updateDimensions();
......@@ -46,7 +50,7 @@ export default class Editor {
clearDomElement(domElement);
this.disposable.add(
(this.instance = this.monaco.editor.create(domElement, {
(this.instance = monacoEditor.create(domElement, {
...defaultEditorOptions,
})),
(this.dirtyDiffController = new DirtyDiffController(
......@@ -66,7 +70,7 @@ export default class Editor {
clearDomElement(domElement);
this.disposable.add(
(this.instance = this.monaco.editor.createDiffEditor(domElement, {
(this.instance = monacoEditor.createDiffEditor(domElement, {
...defaultEditorOptions,
quickSuggestions: false,
occurrencesHighlight: false,
......@@ -122,17 +126,11 @@ export default class Editor {
modified: model.getModel(),
});
this.monaco.editor.createDiffNavigator(this.instance, {
monacoEditor.createDiffNavigator(this.instance, {
alwaysRevealFirst: true,
});
}
setupMonacoTheme() {
this.monaco.editor.defineTheme(gitlabTheme.themeName, gitlabTheme.monacoTheme);
this.monaco.editor.setTheme('gitlab');
}
clearEditor() {
if (this.instance) {
this.instance.setModel(null);
......@@ -200,7 +198,7 @@ export default class Editor {
const getKeyCode = key => {
const monacoKeyMod = key.indexOf('KEY_') === 0;
return monacoKeyMod ? this.monaco.KeyCode[key] : this.monaco.KeyMod[key];
return monacoKeyMod ? KeyCode[key] : KeyMod[key];
};
keymap.forEach(command => {
......
import monacoContext from 'monaco-editor/dev/vs/loader';
monacoContext.require.config({
paths: {
vs: `${__webpack_public_path__}monaco-editor/vs`, // eslint-disable-line camelcase
},
});
// ignore CDN config and use local assets path for service worker which cannot be cross-domain
const relativeRootPath = (gon && gon.relative_url_root) || '';
const monacoPath = `${relativeRootPath}/assets/webpack/monaco-editor/vs`;
window.MonacoEnvironment = { getWorkerUrl: () => `${monacoPath}/base/worker/workerMain.js` };
// eslint-disable-next-line no-underscore-dangle
window.__monaco_context__ = monacoContext;
export default monacoContext.require;
......@@ -79,37 +79,37 @@ export function getTimeago() {
if (!timeagoInstance) {
const localeRemaining = function getLocaleRemaining(number, index) {
return [
[s__('Timeago|less than a minute ago'), s__('Timeago|right now')],
[s__('Timeago|less than a minute ago'), s__('Timeago|%s seconds remaining')],
[s__('Timeago|about a minute ago'), s__('Timeago|1 minute remaining')],
[s__('Timeago|just now'), s__('Timeago|right now')],
[s__('Timeago|%s seconds ago'), s__('Timeago|%s seconds remaining')],
[s__('Timeago|1 minute ago'), s__('Timeago|1 minute remaining')],
[s__('Timeago|%s minutes ago'), s__('Timeago|%s minutes remaining')],
[s__('Timeago|about an hour ago'), s__('Timeago|1 hour remaining')],
[s__('Timeago|about %s hours ago'), s__('Timeago|%s hours remaining')],
[s__('Timeago|a day ago'), s__('Timeago|1 day remaining')],
[s__('Timeago|1 hour ago'), s__('Timeago|1 hour remaining')],
[s__('Timeago|%s hours ago'), s__('Timeago|%s hours remaining')],
[s__('Timeago|1 day ago'), s__('Timeago|1 day remaining')],
[s__('Timeago|%s days ago'), s__('Timeago|%s days remaining')],
[s__('Timeago|a week ago'), s__('Timeago|1 week remaining')],
[s__('Timeago|1 week ago'), s__('Timeago|1 week remaining')],
[s__('Timeago|%s weeks ago'), s__('Timeago|%s weeks remaining')],
[s__('Timeago|a month ago'), s__('Timeago|1 month remaining')],
[s__('Timeago|1 month ago'), s__('Timeago|1 month remaining')],
[s__('Timeago|%s months ago'), s__('Timeago|%s months remaining')],
[s__('Timeago|a year ago'), s__('Timeago|1 year remaining')],
[s__('Timeago|1 year ago'), s__('Timeago|1 year remaining')],
[s__('Timeago|%s years ago'), s__('Timeago|%s years remaining')],
][index];
};
const locale = function getLocale(number, index) {
return [
[s__('Timeago|less than a minute ago'), s__('Timeago|right now')],
[s__('Timeago|less than a minute ago'), s__('Timeago|in %s seconds')],
[s__('Timeago|about a minute ago'), s__('Timeago|in 1 minute')],
[s__('Timeago|just now'), s__('Timeago|right now')],
[s__('Timeago|%s seconds ago'), s__('Timeago|in %s seconds')],
[s__('Timeago|1 minute ago'), s__('Timeago|in 1 minute')],
[s__('Timeago|%s minutes ago'), s__('Timeago|in %s minutes')],
[s__('Timeago|about an hour ago'), s__('Timeago|in 1 hour')],
[s__('Timeago|about %s hours ago'), s__('Timeago|in %s hours')],
[s__('Timeago|a day ago'), s__('Timeago|in 1 day')],
[s__('Timeago|1 hour ago'), s__('Timeago|in 1 hour')],
[s__('Timeago|%s hours ago'), s__('Timeago|in %s hours')],
[s__('Timeago|1 day ago'), s__('Timeago|in 1 day')],
[s__('Timeago|%s days ago'), s__('Timeago|in %s days')],
[s__('Timeago|a week ago'), s__('Timeago|in 1 week')],
[s__('Timeago|1 week ago'), s__('Timeago|in 1 week')],
[s__('Timeago|%s weeks ago'), s__('Timeago|in %s weeks')],
[s__('Timeago|a month ago'), s__('Timeago|in 1 month')],
[s__('Timeago|1 month ago'), s__('Timeago|in 1 month')],
[s__('Timeago|%s months ago'), s__('Timeago|in %s months')],
[s__('Timeago|a year ago'), s__('Timeago|in 1 year')],
[s__('Timeago|1 year ago'), s__('Timeago|in 1 year')],
[s__('Timeago|%s years ago'), s__('Timeago|in %s years')],
][index];
};
......
......@@ -85,9 +85,9 @@ export function redirectTo(url) {
}
export function webIDEUrl(route = undefined) {
let returnUrl = `${gon.relative_url_root}/-/ide/`;
let returnUrl = `${gon.relative_url_root || ''}/-/ide/`;
if (route) {
returnUrl += `project${route}`;
returnUrl += `project${route.replace(new RegExp(`^${gon.relative_url_root || ''}`), '')}`;
}
return returnUrl;
}
......@@ -147,6 +147,7 @@ document.addEventListener('DOMContentLoaded', () => {
$body.tooltip({
selector: '.has-tooltip, [data-toggle="tooltip"]',
trigger: 'hover',
boundary: 'viewport',
placement(tip, el) {
return $(el).data('placement') || 'bottom';
},
......
......@@ -107,7 +107,7 @@ code {
background-color: $red-100;
border-radius: 3px;
.code & {
.code > & {
background-color: inherit;
padding: unset;
}
......@@ -118,10 +118,6 @@ code {
}
}
.code {
padding: 9.5px;
}
table {
// Remove any table border lines
border-spacing: 0;
......@@ -233,6 +229,13 @@ table {
}
}
.card-header {
h3.card-title,
h4.card-title {
margin-top: 0;
}
}
.nav-tabs {
// Override bootstrap's default border
border-bottom: 0;
......
......@@ -497,6 +497,10 @@ fieldset[disabled] .btn,
}
}
[readonly] {
cursor: default;
}
.btn-no-padding {
padding: 0;
}
......@@ -139,6 +139,8 @@
}
.nav {
flex-wrap: nowrap;
> li:not(.d-none) a {
@include media-breakpoint-down(xs) {
margin-left: 0;
......@@ -158,11 +160,12 @@
}
.navbar-toggler {
position: relative;
right: -10px;
border-radius: 0;
min-width: 45px;
padding: 0;
margin-right: -7px;
margin: $gl-padding-8 -7px $gl-padding-8 0;
font-size: 14px;
text-align: center;
color: currentColor;
......@@ -186,6 +189,7 @@
display: -webkit-flex;
display: flex;
padding-right: 10px;
flex-direction: row;
}
li {
......@@ -290,6 +294,10 @@
margin: 8px;
}
}
.dropdown-menu {
position: absolute;
}
}
.navbar-sub-nav {
......
......@@ -174,11 +174,6 @@ $border-gray-normal: darken($gray-normal, $darken-border-factor);
$border-gray-normal-dashed: darken($gray-normal, $darken-border-dashed-factor);
$border-gray-dark: darken($white-normal, $darken-border-factor);
/*
* Override Bootstrap 4 variables
*/
$secondary: $gray-light;
/*
* UI elements
*/
......@@ -846,3 +841,14 @@ Prometheus
$prometheus-table-row-highlight-color: $theme-gray-100;
$priority-label-empty-state-width: 114px;
/*
* Override Bootstrap 4 variables
*/
$secondary: $gray-light;
$input-disabled-bg: $gray-light;
$input-border-color: $theme-gray-200;
$input-color: $gl-text-color;
$font-family-sans-serif: $regular_font;
$font-family-monospace: $monospace_font;
......@@ -18,7 +18,8 @@
.file-finder-input:hover,
.issuable-search-form:hover,
.search-text-input:hover,
.form-control:hover {
.form-control:hover,
:not[readonly] {
border-color: lighten($dropdown-input-focus-border, 20%);
box-shadow: 0 0 4px lighten($search-input-focus-shadow-color, 20%);
}
......
......@@ -15,6 +15,7 @@
color: $perf-bar-text;
select {
color: $perf-bar-text;
width: 200px;
}
......
......@@ -42,6 +42,6 @@ class Projects::Clusters::ApplicationsController < Projects::ApplicationControll
owner: current_user
}
Applications::CreateService.new(current_user, oauth_application_params).execute
Applications::CreateService.new(current_user, oauth_application_params).execute(request)
end
end
......@@ -5,6 +5,9 @@ class RegistrationsController < Devise::RegistrationsController
prepend EE::RegistrationsController
before_action :whitelist_query_limiting, only: [:destroy]
before_action :ensure_terms_accepted,
if: -> { Gitlab::CurrentSettings.current_application_settings.enforce_terms? },
only: [:create]
def new
redirect_to(new_user_session_path)
......@@ -20,7 +23,9 @@ class RegistrationsController < Devise::RegistrationsController
if !Gitlab::Recaptcha.load_configurations! || verify_recaptcha
accept_pending_invitations
super
super do |new_user|
persist_accepted_terms_if_required(new_user)
end
else
flash[:alert] = 'There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.'
flash.delete :recaptcha_error
......@@ -42,6 +47,16 @@ class RegistrationsController < Devise::RegistrationsController
protected
def persist_accepted_terms_if_required(new_user)
return unless new_user.persisted?
return unless Gitlab::CurrentSettings.current_application_settings.enforce_terms?
if terms_accepted?
terms = ApplicationSetting::Term.latest
Users::RespondToTermsService.new(new_user, terms).execute(accepted: true)
end
end
def destroy_confirmation_valid?
if current_user.confirm_deletion_with_password?
current_user.valid_password?(params[:password])
......@@ -93,4 +108,14 @@ class RegistrationsController < Devise::RegistrationsController
def whitelist_query_limiting
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42380')
end
def ensure_terms_accepted
return if terms_accepted?
redirect_to new_user_session_path, alert: _('You must accept our Terms of Service and privacy policy in order to register an account')
end
def terms_accepted?
Gitlab::Utils.to_boolean(params[:terms_opt_in])
end
end
......@@ -2,6 +2,7 @@ module Users
class TermsController < ApplicationController
include InternalRedirect
skip_before_action :authenticate_user!
skip_before_action :enforce_terms!
skip_before_action :check_password_expiration
skip_before_action :check_two_factor_requirement
......@@ -14,7 +15,7 @@ module Users
def index
@redirect = redirect_path
if @term.accepted_by_user?(current_user)
if current_user && @term.accepted_by_user?(current_user)
flash.now[:notice] = "You have already accepted the Terms of Service as #{current_user.to_reference}"
end
end
......
......@@ -60,7 +60,7 @@ module IconsHelper
def spinner(text = nil, visible = false)
css_class = 'loading'
css_class << ' hidden' unless visible
css_class << ' hide' unless visible
content_tag :div, class: css_class do
icon('spinner spin') + text
......
......@@ -167,7 +167,7 @@ module IssuablesHelper
output << content_tag(:span, (issuable_first_contribution_icon if issuable.first_contribution?), class: 'has-tooltip', title: _('1st contribution!'))
output << content_tag(:span, (issuable.task_status if issuable.tasks?), id: "task_status", class: "d-none d-sm-none d-md-inline-block")
output << content_tag(:span, (issuable.task_status_short if issuable.tasks?), id: "task_status_short", class: "d-md-none d-lg-none d-xl-inline-block")
output << content_tag(:span, (issuable.task_status_short if issuable.tasks?), id: "task_status_short", class: "d-md-none")
output.html_safe
end
......
......@@ -13,6 +13,7 @@ class Group < Namespace
include GroupDescendant
include TokenAuthenticatable
include WithUploads
include Gitlab::Utils::StrongMemoize
has_many :group_members, -> { where(requested_at: nil) }, dependent: :destroy, as: :source # rubocop:disable Cop/ActiveRecordDependent
alias_method :members, :group_members
......@@ -28,7 +29,11 @@ class Group < Namespace
has_many :milestones
has_many :project_group_links, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :shared_projects, through: :project_group_links, source: :project
# Overridden on another method
# Left here just to be dependent: :destroy
has_many :notification_settings, dependent: :destroy, as: :source # rubocop:disable Cop/ActiveRecordDependent
has_many :labels, class_name: 'GroupLabel'
has_many :variables, class_name: 'Ci::GroupVariable'
has_many :custom_attributes, class_name: 'GroupCustomAttribute'
......@@ -90,6 +95,15 @@ class Group < Namespace
end
end
# Overrides notification_settings has_many association
# This allows to apply notification settings from parent groups
# to child groups and projects.
def notification_settings
source_type = self.class.base_class.name
NotificationSetting.where(source_type: source_type, source_id: self_and_ancestors_ids)
end
def to_reference(_from = nil, full: nil)
"#{self.class.reference_prefix}#{full_path}"
end
......@@ -227,6 +241,12 @@ class Group < Namespace
members_with_parents.pluck(:user_id)
end
def self_and_ancestors_ids
strong_memoize(:self_and_ancestors_ids) do
self_and_ancestors.pluck(:id)
end
end
def members_with_parents
# Avoids an unnecessary SELECT when the group has no parents
source_ids =
......
......@@ -1137,8 +1137,11 @@ class MergeRequest < ActiveRecord::Base
project.merge_requests.merged.where(author_id: author_id).empty?
end
# TODO: remove once production database rename completes
alias_attribute :allow_collaboration, :allow_maintainer_to_push
def allow_collaboration
collaborative_push_possible? && super
collaborative_push_possible? && allow_maintainer_to_push
end
alias_method :allow_collaboration?, :allow_collaboration
......
class NotificationRecipient
include Gitlab::Utils::StrongMemoize
attr_reader :user, :type, :reason
def initialize(user, type, **opts)
unless NotificationSetting.levels.key?(type) || type == :subscription
......@@ -64,7 +66,7 @@ class NotificationRecipient
return false unless @target
return false unless @target.respond_to?(:subscriptions)
subscription = @target.subscriptions.find_by_user_id(@user.id)
subscription = @target.subscriptions.find { |subscription| subscription.user_id == @user.id }
subscription && !subscription.subscribed
end
......@@ -142,10 +144,33 @@ class NotificationRecipient
return project_setting unless project_setting.nil? || project_setting.global?
group_setting = @group && user.notification_settings_for(@group)
group_setting = closest_non_global_group_notification_settting
return group_setting unless group_setting.nil? || group_setting.global?
return group_setting unless group_setting.nil?
user.global_notification_setting
end
# Returns the notificaton_setting of the lowest group in hierarchy with non global level
def closest_non_global_group_notification_settting
return unless @group
return if indexed_group_notification_settings.empty?
notification_setting = nil
@group.self_and_ancestors_ids.each do |id|
notification_setting = indexed_group_notification_settings[id]
break if notification_setting
end
notification_setting
end
def indexed_group_notification_settings
strong_memoize(:indexed_group_notification_settings) do
@group.notification_settings.where(user_id: user.id)
.where.not(level: NotificationSetting.levels[:global])
.index_by(&:source_id)
end
end
end
......@@ -11,6 +11,8 @@ class ProjectAutoDevops < ActiveRecord::Base
validates :domain, allow_blank: true, hostname: { allow_numeric_hostname: true }
after_save :create_gitlab_deploy_token, if: :needs_to_create_deploy_token?
def instance_domain
Gitlab::CurrentSettings.auto_devops_domain
end
......@@ -32,4 +34,23 @@ class ProjectAutoDevops < ActiveRecord::Base
end
end
end
private
def create_gitlab_deploy_token
project.deploy_tokens.create!(
name: DeployToken::GITLAB_DEPLOY_TOKEN_NAME,
read_registry: true
)
end
def needs_to_create_deploy_token?
auto_devops_enabled? &&
!project.public? &&
!project.deploy_tokens.find_by(name: DeployToken::GITLAB_DEPLOY_TOKEN_NAME).present?
end
def auto_devops_enabled?
Gitlab::CurrentSettings.auto_devops_enabled? || enabled?
end
end
......@@ -1060,7 +1060,10 @@ class User < ActiveRecord::Base
def notification_settings_for(source)
if notification_settings.loaded?
notification_settings.find { |notification| notification.source == source }
notification_settings.find do |notification|
notification.source_type == source.class.base_class.name &&
notification.source_id == source.id
end
else
notification_settings.find_or_initialize_by(source: source)
end
......
......@@ -7,7 +7,7 @@ module Applications
@params = params.except(:ip_address)
end
def execute(request = nil)
def execute(request)
Doorkeeper::Application.create(@params)
end
end
......
......@@ -41,7 +41,9 @@ module MergeRequests
end
def ref
@ref || project.default_branch || 'master'
return @ref if project.repository.branch_exists?(@ref)
project.default_branch || 'master'
end
def merge_request
......
......@@ -136,6 +136,7 @@ module Projects
raise_error('Failed to remove some tags in project container registry. Please try again or contact administrator.')
end
log_destroy_event
trash_repositories!
# Rails attempts to load all related records into memory before
......@@ -149,6 +150,10 @@ module Projects
end
end
def log_destroy_event
log_info("Attempting to destroy #{project.full_path} (#{project.id})")
end
##
# This method makes sure that we correctly remove registry tags
# for legacy image repository (when repository path equals project path).
......
= form_for @application_setting, url: admin_application_settings_path do |f|
= form_for @application_setting, url: admin_application_settings_path, html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
......@@ -7,13 +7,13 @@
.form-check
= f.check_box :enforce_terms, class: 'form-check-input'
= f.label :enforce_terms, class: 'form-check-label' do
= _("Require all users to accept Terms of Service when they access GitLab.")
= _("Require all users to accept Terms of Service and Privacy Policy when they access GitLab.")
.form-text.text-muted
= _("When enabled, users cannot use GitLab until the terms have been accepted.")
.form-group.row
.col-sm-12
= f.label :terms do
= _("Terms of Service Agreement")
= _("Terms of Service Agreement and Privacy Policy")
.col-sm-12
= f.text_area :terms, class: 'form-control', rows: 8
.form-text.text-muted
......
......@@ -17,7 +17,7 @@
%section.settings.as-account-limit.no-animate#js-account-settings{ class: ('expanded' if expanded) }
.settings-header
%h4
= _('Account and limit settings')
= _('Account and limit')
%button.btn.js-settings-toggle{ type: 'button' }
= expanded ? _('Collapse') : _('Expand')
%p
......@@ -50,11 +50,11 @@
%section.settings.as-terms.no-animate#js-terms-settings{ class: ('expanded' if expanded) }
.settings-header
%h4
= _('Terms of Service')
= _('Terms of Service and Privacy Policy')
%button.btn.btn-default.js-settings-toggle{ type: 'button' }
= expanded ? _('Collapse') : _('Expand')
%p
= _('Include a Terms of Service agreement that all users must accept.')
= _('Include a Terms of Service agreement and Privacy Policy that all users must accept.')
.settings-content
= render 'terms'
......@@ -317,7 +317,7 @@
%section.settings.as-mirror.no-animate#js-mirror-settings{ class: ('expanded' if expanded) }
.settings-header
%h4
= _('Repository mirror settings')
= _('Repository mirror')
%button.btn.js-settings-toggle{ type: 'button' }
= expanded ? 'Collapse' : 'Expand'
%p
......
......@@ -7,5 +7,4 @@
= render 'shared/service_settings', form: form, subject: @service
.footer-block.row-content-block
.form-actions
= form.submit 'Save', class: 'btn btn-save'
= form.submit 'Save', class: 'btn btn-save'
......@@ -22,6 +22,13 @@
= f.label :password
= f.password_field :password, class: "form-control bottom", required: true, pattern: ".{#{@minimum_password_length},}", title: "Minimum length is #{@minimum_password_length} characters."
%p.gl-field-hint Minimum length is #{@minimum_password_length} characters
- if Gitlab::CurrentSettings.current_application_settings.enforce_terms?
.form-group
= check_box_tag :terms_opt_in, '1', false, required: true
= label_tag :terms_opt_in do
- terms_link = link_to s_("I accept the|Terms of Service and Privacy Policy"), terms_path, target: "_blank"
- accept_terms_label = _("I accept the %{terms_link}") % { terms_link: terms_link }
= accept_terms_label.html_safe
= render 'devise/shared/ee/email_opted_in', f: f
%div
- if Gitlab::Recaptcha.enabled?
......
......@@ -18,7 +18,7 @@
%section.settings#runners-settings.no-animate{ class: ('expanded' if expanded) }
.settings-header
%h4
= _('Runners settings')
= _('Runners')
%button.btn.btn-default.js-settings-toggle{ type: "button" }
= expanded ? _('Collapse') : _('Expand')
%p
......
......@@ -9,10 +9,10 @@
.tree-holder
.nav-block
%ul.breadcrumb.repo-breadcrumb
%li
%li.breadcrumb-item
= link_to 'Artifacts', browse_project_job_artifacts_path(@project, @build)
- path_breadcrumbs do |title, path|
%li
%li.breadcrumb-item
= link_to truncate(title, length: 40), browse_project_job_artifacts_path(@project, @build, path)
.tree-controls
......
......@@ -15,7 +15,7 @@
= field.fields_for :provider_gcp, @cluster.provider_gcp do |provider_gcp_field|
.form-group
= provider_gcp_field.label :gcp_project_id, s_('ClusterIntegration|Google Cloud Platform project ID')
= provider_gcp_field.label :gcp_project_id, s_('ClusterIntegration|Google Cloud Platform project')
.js-gcp-project-id-dropdown-entry-point{ data: { docsUrl: 'https://console.cloud.google.com/home/dashboard' } }
= provider_gcp_field.hidden_field :gcp_project_id
.dropdown
......
= form_for @cluster, url: user_namespace_project_clusters_path(@project.namespace, @project), as: :cluster do |field|
= form_errors(@cluster)
.form-group
= field.label :name, s_('ClusterIntegration|Kubernetes cluster name')
= field.label :name, s_('ClusterIntegration|Kubernetes cluster name'), class: 'label-light'
= field.text_field :name, class: 'form-control', placeholder: s_('ClusterIntegration|Kubernetes cluster name')
.form-group
= field.label :environment_scope, s_('ClusterIntegration|Environment scope')
= field.label :environment_scope, s_('ClusterIntegration|Environment scope'), class: 'label-light'
= field.text_field :environment_scope, class: 'form-control', readonly: !has_multiple_clusters?(@project), placeholder: s_('ClusterIntegration|Environment scope')
= field.fields_for :platform_kubernetes, @cluster.platform_kubernetes do |platform_kubernetes_field|
.form-group
= platform_kubernetes_field.label :api_url, s_('ClusterIntegration|API URL')
= platform_kubernetes_field.label :api_url, s_('ClusterIntegration|API URL'), class: 'label-light'
= platform_kubernetes_field.text_field :api_url, class: 'form-control', placeholder: s_('ClusterIntegration|API URL')
.form-group
= platform_kubernetes_field.label :ca_cert, s_('ClusterIntegration|CA Certificate')
= platform_kubernetes_field.label :ca_cert, s_('ClusterIntegration|CA Certificate'), class: 'label-light'
= platform_kubernetes_field.text_area :ca_cert, class: 'form-control', placeholder: s_('ClusterIntegration|Certificate Authority bundle (PEM format)')
.form-group
= platform_kubernetes_field.label :token, s_('ClusterIntegration|Token')
= platform_kubernetes_field.label :token, s_('ClusterIntegration|Token'), class: 'label-light'
= platform_kubernetes_field.text_field :token, class: 'form-control', placeholder: s_('ClusterIntegration|Service token'), autocomplete: 'off'
.form-group
= platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)')
= platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)'), class: 'label-light'
= platform_kubernetes_field.text_field :namespace, class: 'form-control', placeholder: s_('ClusterIntegration|Project namespace')
.form-group
......
= form_for @cluster, url: namespace_project_cluster_path(@project.namespace, @project, @cluster), as: :cluster do |field|
= form_errors(@cluster)
.form-group
= field.label :name, s_('ClusterIntegration|Kubernetes cluster name')
= field.label :name, s_('ClusterIntegration|Kubernetes cluster name'), class: 'label-light'
= field.text_field :name, class: 'form-control', placeholder: s_('ClusterIntegration|Kubernetes cluster name')
= field.fields_for :platform_kubernetes, @cluster.platform_kubernetes do |platform_kubernetes_field|
.form-group
= platform_kubernetes_field.label :api_url, s_('ClusterIntegration|API URL')
= platform_kubernetes_field.label :api_url, s_('ClusterIntegration|API URL'), class: 'label-light'
= platform_kubernetes_field.text_field :api_url, class: 'form-control', placeholder: s_('ClusterIntegration|API URL')
.form-group
= platform_kubernetes_field.label :ca_cert, s_('ClusterIntegration|CA Certificate')
= platform_kubernetes_field.label :ca_cert, s_('ClusterIntegration|CA Certificate'), class: 'label-light'
= platform_kubernetes_field.text_area :ca_cert, class: 'form-control', placeholder: s_('ClusterIntegration|Certificate Authority bundle (PEM format)')
.form-group
= platform_kubernetes_field.label :token, s_('ClusterIntegration|Token')
= platform_kubernetes_field.label :token, s_('ClusterIntegration|Token'), class: 'label-light'
.input-group
= platform_kubernetes_field.text_field :token, class: 'form-control js-cluster-token', type: 'password', placeholder: s_('ClusterIntegration|Token'), autocomplete: 'off'
%span.input-group-append.clipboard-addon
......@@ -23,7 +23,7 @@
= s_('ClusterIntegration|Show')
.form-group
= platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)')
= platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)'), class: 'label-light'
= platform_kubernetes_field.text_field :namespace, class: 'form-control', placeholder: s_('ClusterIntegration|Project namespace')
.form-group
......
......@@ -7,7 +7,7 @@
%section.settings.general-settings.no-animate{ class: ('expanded' if expanded) }
.settings-header
%h4
General project settings
General project
%button.btn.js-settings-toggle{ type: 'button' }
= expanded ? 'Collapse' : 'Expand'
%p
......@@ -85,7 +85,7 @@
%section.settings.merge-requests-feature.no-animate{ class: [('expanded' if expanded), ('hidden' if @project.project_feature.send(:merge_requests_access_level) == 0)] }
.settings-header
%h4
Merge request settings
Merge request
%button.btn.js-settings-toggle{ type: 'button' }
= expanded ? 'Collapse' : 'Expand'
%p
......@@ -104,7 +104,7 @@
%section.settings.advanced-settings.no-animate{ class: ('expanded' if expanded) }
.settings-header
%h4
Advanced settings
Advanced
%button.btn.js-settings-toggle{ type: 'button' }
= expanded ? 'Collapse' : 'Expand'
%p
......
......@@ -3,5 +3,5 @@
Groups with access to
%strong= @project.name
%span.badge.badge-pill= group_links.size
%ul.content-list
%ul.content-list.members-list
= render partial: 'shared/members/group', collection: group_links, as: :group_link
.protected-branches-list.js-protected-branches-list.qa-protected-branches-list
- if @protected_branches.empty?
.card-header
%h3.card-title
.card-header.bg-white
%h3.card-title.mb-0
Protected branch (#{@protected_branches_count})
%p.settings-message.text-center
There are currently no protected branches, protect a branch with the form above.
......
- enabled = Gitlab.config.mattermost.enabled
.card
%p
This service allows users to perform common operations on this
project by entering slash commands in Mattermost.
= link_to help_page_path('user/project/integrations/mattermost_slash_commands.md'), target: '_blank' do
View documentation
= icon('external-link')
%p.inline
See list of available commands in Mattermost after setting up this service,
by entering
%kbd.inline /&lt;trigger&gt; help
- unless enabled || @service.template?
= render 'projects/services/mattermost_slash_commands/detailed_help', subject: @service
.info-well
.well-segment
%p
This service allows users to perform common operations on this
project by entering slash commands in Mattermost.
= link_to help_page_path('user/project/integrations/mattermost_slash_commands.md'), target: '_blank' do
View documentation
= icon('external-link')
%p.inline
See list of available commands in Mattermost after setting up this service,
by entering
%kbd.inline /&lt;trigger&gt; help
- unless enabled || @service.template?
= render 'projects/services/mattermost_slash_commands/detailed_help', subject: @service
- if enabled && !@service.template?
= render 'projects/services/mattermost_slash_commands/installation_info', subject: @service
- pretty_name = defined?(@project) ? @project.full_name : 'namespace / path'
- run_actions_text = "Perform common operations on GitLab project: #{pretty_name}"
.card
%p
This service allows users to perform common operations on this
project by entering slash commands in Slack.
= link_to help_page_path('user/project/integrations/slack_slash_commands.md'), target: '_blank' do
View documentation
= icon('external-link')
%p.inline
See list of available commands in Slack after setting up this service,
by entering
%kbd.inline /&lt;command&gt; help
- unless @service.template?
%p To setup this service:
%ul.list-unstyled.indent-list
%li
1.
= link_to 'https://my.slack.com/services/new/slash-commands', target: '_blank', rel: 'noreferrer noopener nofollow' do
Add a slash command
= icon('external-link')
in your Slack team with these options:
.info-well
.well-segment
%p
This service allows users to perform common operations on this
project by entering slash commands in Slack.
= link_to help_page_path('user/project/integrations/slack_slash_commands.md'), target: '_blank' do
View documentation
= icon('external-link')
%p.inline
See list of available commands in Slack after setting up this service,
by entering
%kbd.inline /&lt;command&gt; help
- unless @service.template?
%p To setup this service:
%ul.list-unstyled.indent-list
%li
1.
= link_to 'https://my.slack.com/services/new/slash-commands', target: '_blank', rel: 'noreferrer noopener nofollow' do
Add a slash command
= icon('external-link')
in your Slack team with these options:
%hr
%hr
.help-form
.form-group
= label_tag nil, 'Command', class: 'col-sm-2 col-12 col-form-label'
.col-sm-10.col-12.text-block
%p Fill in the word that works best for your team.
%p
Suggestions:
%code= 'gitlab'
%code= @project.path # Path contains no spaces, but dashes
%code= @project.full_path
.help-form
.form-group
= label_tag nil, 'Command', class: 'col-sm-2 col-12 col-form-label'
.col-sm-10.col-12.text-block
%p Fill in the word that works best for your team.
%p
Suggestions:
%code= 'gitlab'
%code= @project.path # Path contains no spaces, but dashes
%code= @project.full_path
.form-group
= label_tag :url, 'URL', class: 'col-sm-2 col-12 col-form-label'
.col-sm-10.col-12.input-group
= text_field_tag :url, service_trigger_url(subject), class: 'form-control form-control-sm', readonly: 'readonly'
.input-group-append
= clipboard_button(target: '#url', class: 'input-group-text')
.form-group
= label_tag :url, 'URL', class: 'col-sm-2 col-12 col-form-label'
.col-sm-10.col-12.input-group
= text_field_tag :url, service_trigger_url(subject), class: 'form-control form-control-sm', readonly: 'readonly'
.input-group-append
= clipboard_button(target: '#url', class: 'input-group-text')
.form-group
= label_tag nil, 'Method', class: 'col-sm-2 col-12 col-form-label'
.col-sm-10.col-12.text-block POST
.form-group
= label_tag nil, 'Method', class: 'col-sm-2 col-12 col-form-label'
.col-sm-10.col-12.text-block POST
.form-group
= label_tag :customize_name, 'Customize name', class: 'col-sm-2 col-12 col-form-label'
.col-sm-10.col-12.input-group
= text_field_tag :customize_name, 'GitLab', class: 'form-control form-control-sm', readonly: 'readonly'
.input-group-append
= clipboard_button(target: '#customize_name', class: 'input-group-text')
.form-group
= label_tag :customize_name, 'Customize name', class: 'col-sm-2 col-12 col-form-label'
.col-sm-10.col-12.input-group
= text_field_tag :customize_name, 'GitLab', class: 'form-control form-control-sm', readonly: 'readonly'
.input-group-append
= clipboard_button(target: '#customize_name', class: 'input-group-text')
.form-group
= label_tag nil, 'Customize icon', class: 'col-sm-2 col-12 col-form-label'
.col-sm-10.col-12.text-block
= image_tag(asset_url('slash-command-logo.png'), width: 36, height: 36)
= link_to('Download image', asset_url('gitlab_logo.png'), class: 'btn btn-sm', target: '_blank', rel: 'noopener noreferrer')
.form-group
= label_tag nil, 'Customize icon', class: 'col-sm-2 col-12 col-form-label'
.col-sm-10.col-12.text-block
= image_tag(asset_url('slash-command-logo.png'), width: 36, height: 36)
= link_to('Download image', asset_url('gitlab_logo.png'), class: 'btn btn-sm', target: '_blank', rel: 'noopener noreferrer')
.form-group
= label_tag nil, 'Autocomplete', class: 'col-sm-2 col-12 col-form-label'
.col-sm-10.col-12.text-block Show this command in the autocomplete list
.form-group
= label_tag nil, 'Autocomplete', class: 'col-sm-2 col-12 col-form-label'
.col-sm-10.col-12.text-block Show this command in the autocomplete list
.form-group
= label_tag :autocomplete_description, 'Autocomplete description', class: 'col-sm-2 col-12 col-form-label'
.col-sm-10.col-12.input-group
= text_field_tag :autocomplete_description, run_actions_text, class: 'form-control form-control-sm', readonly: 'readonly'
.input-group-append
= clipboard_button(target: '#autocomplete_description', class: 'input-group-text')
.form-group
= label_tag :autocomplete_description, 'Autocomplete description', class: 'col-sm-2 col-12 col-form-label'
.col-sm-10.col-12.input-group
= text_field_tag :autocomplete_description, run_actions_text, class: 'form-control form-control-sm', readonly: 'readonly'
.input-group-append
= clipboard_button(target: '#autocomplete_description', class: 'input-group-text')
.form-group
= label_tag :autocomplete_usage_hint, 'Autocomplete usage hint', class: 'col-sm-2 col-12 col-form-label'
.col-sm-10.col-12.input-group
= text_field_tag :autocomplete_usage_hint, '[help]', class: 'form-control form-control-sm', readonly: 'readonly'
.input-group-append
= clipboard_button(target: '#autocomplete_usage_hint', class: 'input-group-text')
.form-group
= label_tag :autocomplete_usage_hint, 'Autocomplete usage hint', class: 'col-sm-2 col-12 col-form-label'
.col-sm-10.col-12.input-group
= text_field_tag :autocomplete_usage_hint, '[help]', class: 'form-control form-control-sm', readonly: 'readonly'
.input-group-append
= clipboard_button(target: '#autocomplete_usage_hint', class: 'input-group-text')
.form-group
= label_tag :descriptive_label, 'Descriptive label', class: 'col-sm-2 col-12 col-form-label'
.col-sm-10.col-12.input-group
= text_field_tag :descriptive_label, 'Perform common operations on GitLab project', class: 'form-control form-control-sm', readonly: 'readonly'
.input-group-append
= clipboard_button(target: '#descriptive_label', class: 'input-group-text')
.form-group
= label_tag :descriptive_label, 'Descriptive label', class: 'col-sm-2 col-12 col-form-label'
.col-sm-10.col-12.input-group
= text_field_tag :descriptive_label, 'Perform common operations on GitLab project', class: 'form-control form-control-sm', readonly: 'readonly'
.input-group-append
= clipboard_button(target: '#descriptive_label', class: 'input-group-text')
%hr
%hr
%ul.list-unstyled.indent-list
%li
2. Paste the
%strong Token
into the field below
%li
3. Select the
%strong Active
checkbox, press
%strong Save changes
and start using GitLab inside Slack!
%ul.list-unstyled.indent-list
%li
2. Paste the
%strong Token
into the field below
%li
3. Select the
%strong Active
checkbox, press
%strong Save changes
and start using GitLab inside Slack!
......@@ -7,7 +7,7 @@
= f.label :runners_token, "Runner token", class: 'label-light'
.form-control.js-secret-value-placeholder
= '*' * 20
= f.text_field :runners_token, class: "form-control hidden js-secret-value", placeholder: 'xEeFCaDAB89'
= f.text_field :runners_token, class: "form-control hide js-secret-value", placeholder: 'xEeFCaDAB89'
%p.form-text.text-muted The secure token used by the Runner to checkout the project
%button.btn.btn-info.prepend-top-10.js-secret-value-reveal-button{ type: 'button', data: { secret_reveal_status: 'false' } }
= _('Reveal value')
......
......@@ -8,7 +8,7 @@
%section.settings#js-general-pipeline-settings.no-animate{ class: ('expanded' if general_expanded) }
.settings-header
%h4
General pipelines settings
General pipelines
%button.btn.js-settings-toggle{ type: 'button' }
= expanded ? 'Collapse' : 'Expand'
%p
......@@ -31,7 +31,7 @@
%section.settings.no-animate{ class: ('expanded' if expanded) }
.settings-header
%h4
Runners settings
Runners
%button.btn.js-settings-toggle{ type: 'button' }
= expanded ? 'Collapse' : 'Expand'
%p
......
......@@ -3,8 +3,9 @@
- if lookup_context.template_exists?('help', "projects/services/#{@service.to_param}", true)
= render "projects/services/#{@service.to_param}/help", subject: subject
- elsif @service.help.present?
.card
= markdown @service.help
.info-well
.well-segment
= markdown @service.help
.service-settings
- if @service.show_active_box?
......@@ -15,25 +16,24 @@
- if @service.configurable_events.present?
.form-group.row
= form.label :url, "Trigger", class: 'col-form-label col-sm-2'
.col-sm-2.text-right Trigger
.col-sm-10
- @service.configurable_events.each do |event|
%div
= form.check_box service_event_field_name(event), class: 'float-left'
.prepend-left-20
= form.label service_event_field_name(event), class: 'list-label' do
.form-group
.form-check
= form.check_box service_event_field_name(event), class: 'form-check-input'
= form.label service_event_field_name(event), class: 'form-check-label' do
%strong
= event.humanize
- field = @service.event_field(event)
- field = @service.event_field(event)
- if field
%p
- if field
= form.text_field field[:name], class: "form-control", placeholder: field[:placeholder]
%p.light
= @service.class.event_description(event)
%p.text-muted
= @service.class.event_description(event)
- @service.global_fields.each do |field|
- type = field[:type]
......
.dropdown.prepend-left-10#js-add-list
%button.btn.btn-create.btn-inverted.js-new-board-list{ type: "button", data: board_list_data }
Add list
.dropdown-menu.dropdown-menu-paging.dropdown-menu-right.dropdown-menu-issues-board-new.dropdown-menu-selectable.js-tab-container-labels
= render partial: "shared/issuable/label_page_default", locals: { show_footer: true, show_create: true, show_boards_content: true, title: "Add list" }
- if can?(current_user, :admin_label, board.parent)
= render partial: "shared/issuable/label_page_create"
= dropdown_loading
......@@ -5,16 +5,16 @@
%li.member.group_member{ id: dom_id }
%span.list-item-name
= group_icon(group, class: "avatar s40", alt: '')
%strong
= link_to group.full_name, group_path(group)
.cgray
Given access #{time_ago_with_tooltip(group_link.created_at)}
- if group_link.expires?
·
%span{ class: ('text-warning' if group_link.expires_soon?) }
Expires in #{distance_of_time_in_words_to_now(group_link.expires_at)}
.user-info
= link_to group.full_name, group_path(group), class: 'member'
.cgray
Given access #{time_ago_with_tooltip(group_link.created_at)}
- if group_link.expires?
·
%span{ class: ('text-warning' if group_link.expires_soon?) }
Expires in #{distance_of_time_in_words_to_now(group_link.expires_at)}
.controls.member-controls
= form_tag project_group_link_path(@project, group_link), method: :put, remote: true, class: 'js-edit-member-form' do
= form_tag project_group_link_path(@project, group_link), method: :put, remote: true, class: 'js-edit-member-form form-group row append-right-5' do
= hidden_field_tag "group_link[group_access]", group_link.group_access
.member-form-control.dropdown.append-right-5
%button.dropdown-menu-toggle.js-member-permissions-dropdown{ type: "button",
......
......@@ -5,6 +5,6 @@
- scopes.each do |scope|
%fieldset
= check_box_tag "#{prefix}[scopes][]", scope, token.scopes.include?(scope), id: "#{prefix}_scopes_#{scope}"
= label_tag ("#{prefix}_scopes_#{scope}"), scope
= label_tag ("#{prefix}_scopes_#{scope}"), scope, class: "label-light"
%span= t(scope, scope: [:doorkeeper, :scopes])
.scope-description= t scope, scope: [:doorkeeper, :scope_desc]
......@@ -2,16 +2,17 @@
.card-body.rendered-terms
= markdown_field(@term, :terms)
.card-footer.footer-block.clearfix
- if can?(current_user, :accept_terms, @term)
.float-right
= button_to accept_term_path(@term, redirect_params), class: 'btn btn-success prepend-left-8' do
= _('Accept terms')
- else
.pull-right
= link_to root_path, class: 'btn btn-success prepend-left-8' do
= _('Continue')
- if can?(current_user, :decline_terms, @term)
.float-right
= button_to decline_term_path(@term, redirect_params), class: 'btn btn-default prepend-left-8' do
= _('Decline and sign out')
- if current_user
.card-footer.footer-block.clearfix
- if can?(current_user, :accept_terms, @term)
.float-right
= button_to accept_term_path(@term, redirect_params), class: 'btn btn-success prepend-left-8' do
= _('Accept terms')
- else
.pull-right
= link_to root_path, class: 'btn btn-success prepend-left-8' do
= _('Continue')
- if can?(current_user, :decline_terms, @term)
.float-right
= button_to decline_term_path(@term, redirect_params), class: 'btn btn-default prepend-left-8' do
= _('Decline and sign out')
class StorageMigratorWorker
include ApplicationWorker
BATCH_SIZE = 100
def perform(start, finish)
projects = build_relation(start, finish)
projects.with_route.find_each(batch_size: BATCH_SIZE) do |project|
Rails.logger.info "Starting storage migration of #{project.full_path} (ID=#{project.id})..."
begin
project.migrate_to_hashed_storage!
rescue => err
Rails.logger.error("#{err.message} migrating storage of #{project.full_path} (ID=#{project.id}), trace - #{err.backtrace}")
end
end
end
def build_relation(start, finish)
relation = Project
table = Project.arel_table
relation = relation.where(table[:id].gteq(start)) if start
relation = relation.where(table[:id].lteq(finish)) if finish
relation
migrator = Gitlab::HashedStorage::Migrator.new
migrator.bulk_migrate(start, finish)
end
end
---
title: Improve Failed Jobs tab in the Pipeline detail page
merge_request:
author:
type: changed
---
title: Stop logging email information when emails are disabled
merge_request: 18521
author: Marc Shaw
type: fixed
---
title: Set MR target branch to default branch if target branch is not valid
merge_request: 19067
author:
type: fixed
---
title: Automatize Deploy Token creation for Auto Devops
merge_request: 19507
author:
type: added
---
title: 'Hashed Storage: migration rake task now can be executed to specific project'
merge_request: 19268
author:
type: changed
---
title: Use the default strings of timeago.js for timeago
merge_request: 19350
author: Takuya Noguchi
type: other
---
title: Improve performance of LFS integrity check
merge_request: 19494
author:
type: performance
---
title: Bump grape-path-helpers to 1.0.5
merge_request: 19604
author: "@blackst0ne"
type: other
---
title: Users can accept terms during registration
merge_request: 19583
author:
type: other
---
title: Fixes Web IDE button on merge requests when GitLab is installed with relative
URL
merge_request:
author:
type: fixed
---
title: Make CI job update entrypoint to work as keep-alive endpoint
merge_request: 19543
author:
type: changed
---
title: Apply notification settings level of groups to all child objects
merge_request:
author:
type: changed
---
title: Fix some sources of excessive query counts when calculating notification recipients
merge_request:
author:
type: performance
---
title: Use same gem versions for rails5 as for rails4 where possible
merge_request: 19498
author: Jasper Maes
type: fixed
---
title: Fix extra blank line at start of rendered reStructuredText code block
merge_request: 19596
author:
type: fixed
......@@ -534,3 +534,9 @@
:why: https://github.com/squaremo/bitsyntax-js/blob/master/LICENSE-MIT
:versions: []
:when: 2018-02-20 22:20:25.958123000 Z
- - :approve
- "@webassemblyjs/ieee754"
- :who: Mike Greiling
:why: https://github.com/xtuc/webassemblyjs/blob/master/LICENSE
:versions: []
:when: 2018-06-08 05:30:56.764116000 Z
# Interceptor in lib/disable_email_interceptor.rb
ActionMailer::Base.register_interceptor(DisableEmailInterceptor) unless Gitlab.config.gitlab.email_enabled
unless Gitlab.config.gitlab.email_enabled
ActionMailer::Base.register_interceptor(DisableEmailInterceptor)
ActionMailer::Base.logger = nil
end
......@@ -29,14 +29,14 @@
label: Pod average
unit: ms
- title: "HTTP Error Rate"
y_label: "HTTP 500 Errors / Sec"
y_label: "HTTP Errors"
required_metrics:
- nginx_upstream_responses_total
weight: 1
queries:
- query_range: 'sum(rate(nginx_upstream_responses_total{status_code="5xx", upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[2m]))'
label: HTTP Errors
unit: "errors / sec"
- query_range: 'sum(rate(nginx_upstream_responses_total{status_code="5xx", upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[2m])) / sum(rate(nginx_upstream_responses_total{upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[2m])) * 100'
label: 5xx Errors
unit: "%"
- group: Response metrics (HA Proxy)
priority: 10
metrics:
......
......@@ -4,8 +4,8 @@ const glob = require('glob');
const webpack = require('webpack');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const StatsWriterPlugin = require('webpack-stats-plugin').StatsWriterPlugin;
const CopyWebpackPlugin = require('copy-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const ROOT_PATH = path.resolve(__dirname, '..');
......@@ -181,15 +181,7 @@ module.exports = {
name: '[name].[hash:8].[ext]',
},
},
{
test: /monaco-editor\/\w+\/vs\/loader\.js$/,
use: [
{ loader: 'exports-loader', options: 'l.global' },
{ loader: 'imports-loader', options: 'l=>{},this=>l,AMDLoader=>this,module=>undefined' },
],
},
],
noParse: [/monaco-editor\/\w+\/vs\//],
},
optimization: {
......@@ -239,6 +231,11 @@ module.exports = {
// enable vue-loader to use existing loader rules for other module types
new VueLoaderPlugin(),
// automatically configure monaco editor web workers
new MonacoWebpackPlugin({
features: [],
}),
// prevent pikaday from including moment.js
new webpack.IgnorePlugin(/moment/, /pikaday/),
......@@ -248,29 +245,6 @@ module.exports = {
jQuery: 'jquery',
}),
// copy pre-compiled vendor libraries verbatim
new CopyWebpackPlugin([
{
from: path.join(
ROOT_PATH,
`node_modules/monaco-editor/${IS_PRODUCTION ? 'min' : 'dev'}/vs`
),
to: 'monaco-editor/vs',
transform: function(content, path) {
if (/\.js$/.test(path) && !/worker/i.test(path) && !/typescript/i.test(path)) {
return (
'(function(){\n' +
'var define = this.define, require = this.require;\n' +
'window.define = define; window.require = require;\n' +
content +
'\n}.call(window.__monaco_context__ || (window.__monaco_context__ = {})));'
);
}
return content;
},
},
]),
// compression can require a lot of compute time and is disabled in CI
IS_PRODUCTION && !NO_COMPRESSION && new CompressionPlugin(),
......@@ -328,7 +302,7 @@ module.exports = {
inline: DEV_SERVER_LIVERELOAD,
},
devtool: IS_PRODUCTION ? 'source-map' : 'cheap-module-eval-source-map',
devtool: IS_PRODUCTION ? 'nosources-source-map' : 'cheap-module-eval-source-map',
// sqljs requires fs
node: { fs: 'empty' },
......
......@@ -67,6 +67,10 @@ Sidekiq::Testing.inline! do
skip_disk_validation: true
}
if i % 2 == 0
params[:storage_version] = Project::LATEST_STORAGE_VERSION
end
project = Projects::CreateService.new(User.first, params).execute
# Seed-Fu runs this entire fixture in a transaction, so the `after_commit`
# hook won't run until after the fixture is loaded. That is too late
......
# We want to enable hashed storage for every new project in development
# Details https://gitlab.com/gitlab-org/gitlab-ce/issues/46241
Gitlab::Seeder.quiet do
ApplicationSetting.create_from_defaults unless ApplicationSetting.current_without_cache
ApplicationSetting.current_without_cache.update!(hashed_storage_enabled: true)
print '.'
end
......@@ -6,10 +6,12 @@ class RenameMergeRequestsAllowMaintainerToPush < ActiveRecord::Migration
disable_ddl_transaction!
def up
rename_column_concurrently :merge_requests, :allow_maintainer_to_push, :allow_collaboration
# NOOP
end
def down
cleanup_concurrent_column_rename :merge_requests, :allow_collaboration, :allow_maintainer_to_push
if column_exists?(:merge_requests, :allow_collaboration)
cleanup_concurrent_column_rename :merge_requests, :allow_collaboration, :allow_maintainer_to_push
end
end
end
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class RenameMergeRequestsAllowCollaboration < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
disable_ddl_transaction!
def up
if column_exists?(:merge_requests, :allow_collaboration)
rename_column_concurrently :merge_requests, :allow_collaboration, :allow_maintainer_to_push
end
end
def down
# NOOP
end
end
......@@ -6,10 +6,12 @@ class CleanupMergeRequestsAllowMaintainerToPushRename < ActiveRecord::Migration
disable_ddl_transaction!
def up
cleanup_concurrent_column_rename :merge_requests, :allow_maintainer_to_push, :allow_collaboration
# NOOP
end
def down
rename_column_concurrently :merge_requests, :allow_collaboration, :allow_maintainer_to_push
if column_exists?(:merge_requests, :allow_collaboration)
rename_column_concurrently :merge_requests, :allow_collaboration, :allow_maintainer_to_push
end
end
end
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class CleanupMergeRequestsAllowCollaborationRename < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
if column_exists?(:merge_requests, :allow_collaboration)
cleanup_concurrent_column_rename :merge_requests, :allow_collaboration, :allow_maintainer_to_push
end
end
def down
# NOOP
end
end
......@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20180607154645) do
ActiveRecord::Schema.define(version: 20180608201435) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
......@@ -1641,8 +1641,8 @@ ActiveRecord::Schema.define(version: 20180607154645) do
t.string "merge_jid"
t.boolean "discussion_locked"
t.integer "latest_merge_request_diff_id"
t.boolean "allow_collaboration"
t.boolean "squash", default: false, null: false
t.boolean "allow_maintainer_to_push"
end
add_index "merge_requests", ["assignee_id"], name: "index_merge_requests_on_assignee_id", using: :btree
......
......@@ -20,7 +20,7 @@ Before 10.8, broadcast messages would not propagate without flushing the cache o
This has been fixed in 10.8, but requires one last cache flush on each secondary node:
```bash
gitlab-rake cache:clear
sudo gitlab-rake cache:clear
```
## Upgrading to GitLab 10.6
......@@ -59,8 +59,8 @@ authentication method.
1. **[primary]** Reconfigure and restart
```bash
gitlab-ctl reconfigure
gitlab-ctl restart
sudo gitlab-ctl reconfigure
sudo gitlab-ctl restart
```
1. **[secondary]** Login to all secondary nodes and edit `/etc/gitlab/gitlab.rb`:
......@@ -89,8 +89,8 @@ authentication method.
1. **[secondary]** Reconfigure and restart
```bash
gitlab-ctl reconfigure
gitlab-ctl restart
sudo gitlab-ctl reconfigure
sudo gitlab-ctl restart
```
## Upgrading to GitLab 10.5
......@@ -206,7 +206,7 @@ following to clean this up.
On the SECONDARY Geo nodes, run as root:
```sh
```bash
mv /var/opt/gitlab/gitlab-rails/working /var/opt/gitlab/gitlab-rails/working.old
mkdir /var/opt/gitlab/gitlab-rails/working
chmod 700 /var/opt/gitlab/gitlab-rails/working
......@@ -218,7 +218,7 @@ You may delete `/var/opt/gitlab/gitlab-rails/working.old` any time.
Once this is done, we advise restarting GitLab on the secondary nodes for the
new working directory to be used:
```
```bash
sudo gitlab-ctl restart
```
......@@ -267,7 +267,7 @@ is prepended with the relevant node for better clarity:
1. **[secondary]** Make a backup of the `recovery.conf` file on **all**
secondary nodes to preserve PostgreSQL's credentials:
```
```bash
sudo cp /var/opt/gitlab/postgresql/data/recovery.conf /var/opt/gitlab/
```
......@@ -279,7 +279,7 @@ is prepended with the relevant node for better clarity:
stop all services except `postgresql` as we will use it to re-initialize the
secondary node's database:
```
```bash
sudo gitlab-ctl stop
sudo gitlab-ctl start postgresql
```
......@@ -288,19 +288,19 @@ is prepended with the relevant node for better clarity:
1. **[secondary]** Stop all services:
```
```bash
sudo gitlab-ctl stop
```
1. **[secondary]** Prevent running database migrations:
```
```bash
sudo touch /etc/gitlab/skip-auto-migrations
```
1. **[secondary]** Move the old database to another directory:
```
```bash
sudo mv /var/opt/gitlab/postgresql{,.bak}
```
......@@ -309,26 +309,26 @@ is prepended with the relevant node for better clarity:
1. **[secondary]** Make sure all services are up:
```
```bash
sudo gitlab-ctl start
```
1. **[secondary]** Reconfigure GitLab:
```
```bash
sudo gitlab-ctl reconfigure
```
1. **[secondary]** Run the PostgreSQL upgrade command:
```
sudo gitlab-ctl pg-upgrade
```
```bash
sudo gitlab-ctl pg-upgrade
```
1. **[secondary]** See the stored credentials for the database that you will
need to re-initialize the replication:
```
```bash
sudo grep -s primary_conninfo /var/opt/gitlab/recovery.conf
```
......@@ -338,19 +338,19 @@ is prepended with the relevant node for better clarity:
1. **[secondary]** Run the recovery script using the credentials from the
previous step:
```
```bash
sudo bash /tmp/replica.sh
```
1. **[secondary]** Reconfigure GitLab:
```
```bash
sudo gitlab-ctl reconfigure
```
1. **[secondary]** Start all services:
```
```bash
sudo gitlab-ctl start
```
......@@ -359,7 +359,7 @@ is prepended with the relevant node for better clarity:
1. **[primary]** After all secondaries are updated, start all services in
primary:
```
```bash
sudo gitlab-ctl start
```
......@@ -370,7 +370,7 @@ everything is working correctly:
1. Run the Geo raketask on all nodes, everything should be green:
```
```bash
sudo gitlab-rake gitlab:geo:check
```
......@@ -386,7 +386,7 @@ and it is required since 10.0.
1. Run database migrations on tracking database
```
```bash
sudo gitlab-rake geo:db:migrate
```
......
......@@ -17,13 +17,21 @@ This task will schedule all your existing projects and attachments associated wi
**Omnibus Installation**
```bash
gitlab-rake gitlab:storage:migrate_to_hashed
sudo gitlab-rake gitlab:storage:migrate_to_hashed
```
**Source Installation**
```bash
rake gitlab:storage:migrate_to_hashed
sudo -u git -H bundle exec rake gitlab:storage:migrate_to_hashed RAILS_ENV=production
```
They both also accept a range as environment variable:
```bash
# to migrate any non migrated project from ID 20 to 50.
export ID_FROM=20
export ID_TO=50
```
You can monitor the progress in the _Admin > Monitoring > Background jobs_ screen.
......@@ -44,13 +52,13 @@ To have a simple summary of projects using **Legacy** storage:
**Omnibus Installation**
```bash
gitlab-rake gitlab:storage:legacy_projects
sudo gitlab-rake gitlab:storage:legacy_projects
```
**Source Installation**
```bash
rake gitlab:storage:legacy_projects
sudo -u git -H bundle exec rake gitlab:storage:legacy_projects RAILS_ENV=production
```
------
......@@ -60,13 +68,13 @@ To list projects using **Legacy** storage:
**Omnibus Installation**
```bash
gitlab-rake gitlab:storage:list_legacy_projects
sudo gitlab-rake gitlab:storage:list_legacy_projects
```
**Source Installation**
```bash
rake gitlab:storage:list_legacy_projects
sudo -u git -H bundle exec rake gitlab:storage:list_legacy_projects RAILS_ENV=production
```
......@@ -77,13 +85,13 @@ To have a simple summary of projects using **Hashed** storage:
**Omnibus Installation**
```bash
gitlab-rake gitlab:storage:hashed_projects
sudo gitlab-rake gitlab:storage:hashed_projects
```
**Source Installation**
```bash
rake gitlab:storage:hashed_projects
sudo -u git -H bundle exec rake gitlab:storage:hashed_projects RAILS_ENV=production
```
------
......@@ -93,14 +101,13 @@ To list projects using **Hashed** storage:
**Omnibus Installation**
```bash
gitlab-rake gitlab:storage:list_hashed_projects
sudo gitlab-rake gitlab:storage:list_hashed_projects
```
**Source Installation**
```bash
rake gitlab:storage:list_hashed_projects
sudo -u git -H bundle exec rake gitlab:storage:list_hashed_projects RAILS_ENV=production
```
## List attachments on Legacy storage
......@@ -110,13 +117,13 @@ To have a simple summary of project attachments using **Legacy** storage:
**Omnibus Installation**
```bash
gitlab-rake gitlab:storage:legacy_attachments
sudo gitlab-rake gitlab:storage:legacy_attachments
```
**Source Installation**
```bash
rake gitlab:storage:legacy_attachments
sudo -u git -H bundle exec rake gitlab:storage:legacy_attachments RAILS_ENV=production
```
------
......@@ -126,13 +133,13 @@ To list project attachments using **Legacy** storage:
**Omnibus Installation**
```bash
gitlab-rake gitlab:storage:list_legacy_attachments
sudo gitlab-rake gitlab:storage:list_legacy_attachments
```
**Source Installation**
```bash
rake gitlab:storage:list_legacy_attachments
sudo -u git -H bundle exec rake gitlab:storage:list_legacy_attachments RAILS_ENV=production
```
## List attachments on Hashed storage
......@@ -142,13 +149,13 @@ To have a simple summary of project attachments using **Hashed** storage:
**Omnibus Installation**
```bash
gitlab-rake gitlab:storage:hashed_attachments
sudo gitlab-rake gitlab:storage:hashed_attachments
```
**Source Installation**
```bash
rake gitlab:storage:hashed_attachments
sudo -u git -H bundle exec rake gitlab:storage:hashed_attachments RAILS_ENV=production
```
------
......@@ -158,13 +165,13 @@ To list project attachments using **Hashed** storage:
**Omnibus Installation**
```bash
gitlab-rake gitlab:storage:list_hashed_attachments
sudo gitlab-rake gitlab:storage:list_hashed_attachments
```
**Source Installation**
```bash
rake gitlab:storage:list_hashed_attachments
sudo -u git -H bundle exec rake gitlab:storage:list_hashed_attachments RAILS_ENV=production
```
[storage-types]: ../repository_storage_types.md
......
......@@ -114,7 +114,7 @@ Let's now see how that information is exposed within GitLab.
## Viewing the current status of an environment
The environment list under your project's **Pipelines ➔ Environments**, is
The environment list under your project's **Operations > Environments**, is
where you can find information of the last deployment status of an environment.
Here's how the Environments page looks so far.
......@@ -167,7 +167,7 @@ that works.
You can't control everything, so sometimes things go wrong. When that unfortunate
time comes GitLab has you covered. Simply by clicking the **Rollback** button
that can be found in the deployments page
(**Pipelines ➔ Environments ➔ `environment name`**) you can relaunch the
(**Operations > Environments > `environment name`**) you can relaunch the
job with the commit associated with it.
>**Note:**
......
......@@ -213,11 +213,11 @@ full use of Auto DevOps. If this is your fist time, we recommend you follow the
To enable Auto DevOps to your project:
1. Check that your project doesn't have a `.gitlab-ci.yml`, or remove it otherwise
1. Go to your project's **Settings > CI/CD > General pipelines settings** and
find the Auto DevOps section
1. Go to your project's **Settings > CI/CD > Auto DevOps**
1. Select "Enable Auto DevOps"
1. Optionally, but recommended, add in the [base domain](#auto-devops-base-domain)
that will be used by Kubernetes to deploy your application
that will be used by Kubernetes to [deploy your application](#auto-deploy)
and choose the [deployment strategy](#deployment-strategy)
1. Hit **Save changes** for the changes to take effect
Once saved, an Auto DevOps pipeline will be triggered on the default branch.
......@@ -234,6 +234,24 @@ in **Admin Area > Settings > Continuous Integration and Deployment**. Doing that
all the projects that haven't explicitly set an option will have Auto DevOps
enabled by default.
### Deployment strategy
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/38542) in GitLab 11.0.
You can change the deployment strategy used by Auto DevOps by going to your
project's **Settings > CI/CD > Auto DevOps**.
The available options are:
- **Continuous deployment to production** - enables [Auto Deploy](#auto-deploy)
by setting the [`STAGING_ENABLED`](#deploy-policy-for-staging-and-production-environments) and
[`INCREMENTAL_ROLLOUT_ENABLED`](#incremental-rollout-to-production) variables
to false.
- **Automatic deployment to staging, manual deployment to production** - sets the
[`STAGING_ENABLED`](#deploy-policy-for-staging-and-production-environments) and
[`INCREMENTAL_ROLLOUT_ENABLED`](#incremental-rollout-to-production) variables
to true, and the user is responsible for manually deploying to staging and production.
## Stages of Auto DevOps
The following sections describe the stages of Auto DevOps. Read them carefully
......@@ -426,6 +444,15 @@ no longer be valid as soon as the deployment job finishes. This means that
Kubernetes can run the application, but in case it should be restarted or
executed somewhere else, it cannot be accessed again.
> [Introduced][ce-19507] in GitLab 11.0.
For internal and private projects a [GitLab Deploy Token](../../user/project/deploy_tokens/index.md###gitlab-deploy-token)
will be automatically created, when Auto DevOps is enabled and the Auto DevOps settings are saved. This Deploy Token
can be used for permanent access to the registry.
Note: **Note**
When the GitLab Deploy Token has been manually revoked, it won't be automatically created.
### Auto Monitoring
NOTE: **Note:**
......@@ -503,7 +530,7 @@ repo or by specifying a project variable:
file in it, Auto DevOps will detect the chart and use it instead of the [default
one](https://gitlab.com/charts/charts.gitlab.io/tree/master/charts/auto-deploy-app).
This can be a great way to control exactly how your application is deployed.
- **Project variable** - Create a [variable](../../ci/variables/README.md#variables)
- **Project variable** - Create a [project variable](../../ci/variables/README.md#secret-variables)
`AUTO_DEVOPS_CHART` with the URL of a custom chart to use.
### Customizing `.gitlab-ci.yml`
......@@ -565,18 +592,17 @@ also be customized, and you can easily use a [custom buildpack](#custom-buildpac
| `CANARY_ENABLED` | From GitLab 11.0, this variable can be used to define a [deploy policy for canary environments](#deploy-policy-for-canary-environments). |
| `INCREMENTAL_ROLLOUT_ENABLED`| From GitLab 10.8, this variable can be used to enable an [incremental rollout](#incremental-rollout-to-production) of your application for the production environment. |
| `TEST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `test` job. If the variable is present, the job will not be created. |
| `CODE_QUALITY_DISABLED` | From GitLab 11.0, this variable can be used to disable the `code_quality` job. If the variable is present, the job will not be created. |
| `LICENSE_MANAGEMENT_DISABLED` | From GitLab 11.0, this variable can be used to disable the `license_management` job. If the variable is present, the job will not be created. |
| `CODEQUALITY_DISABLED` | From GitLab 11.0, this variable can be used to disable the `codequality` job. If the variable is present, the job will not be created. |
| `SAST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `sast` job. If the variable is present, the job will not be created. |
| `DEPENDENCY_SCANNING_DISABLED` | From GitLab 11.0, this variable can be used to disable the `dependency_scanning` job. If the variable is present, the job will not be created. |
| `CONTAINER_SCANNING_DISABLED` | From GitLab 11.0, this variable can be used to disable the `container_scanning` job. If the variable is present, the job will not be created. |
| `CONTAINER_SCANNING_DISABLED` | From GitLab 11.0, this variable can be used to disable the `sast:container` job. If the variable is present, the job will not be created. |
| `REVIEW_DISABLED` | From GitLab 11.0, this variable can be used to disable the `review` and the manual `review:stop` job. If the variable is present, these jobs 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. |
TIP: **Tip:**
Set up the replica variables using a
[project variable](../../ci/variables/README.md#variables)
[project variable](../../ci/variables/README.md#secret-variables)
and scale your application by just redeploying it!
CAUTION: **Caution:**
......@@ -644,6 +670,9 @@ service:
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ci-yml/merge_requests/160)
in GitLab 10.8.
TIP: **Tip:**
You can also set this inside your [project's settings](#deployment-strategy).
The normal behavior of Auto DevOps is to use Continuous Deployment, pushing
automatically to the `production` environment every time a new pipeline is run
on the default branch. However, there are cases where you might want to use a
......@@ -651,7 +680,7 @@ staging environment and deploy to production manually. For this scenario, the
`STAGING_ENABLED` environment variable was introduced.
If `STAGING_ENABLED` is defined in your project (e.g., set `STAGING_ENABLED` to
`1` as a variable), then the application will be automatically deployed
`1` as a secret variable), then the application will be automatically deployed
to a `staging` environment, and a `production_manual` job will be created for
you when you're ready to manually deploy to production.
......@@ -664,7 +693,7 @@ A [canary environment](https://docs.gitlab.com/ee/user/project/canary_deployment
before any changes are deployed to production.
If `CANARY_ENABLED` is defined in your project (e.g., set `CANARY_ENABLED` to
`1` as a variable) then two manual jobs will be created:
`1` as a secret variable) then two manual jobs will be created:
- `canary` which will deploy the application to the canary environment
- `production_manual` which is to be used by you when you're ready to manually
......@@ -674,13 +703,16 @@ If `CANARY_ENABLED` is defined in your project (e.g., set `CANARY_ENABLED` to
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/5415) in GitLab 10.8.
TIP: **Tip:**
You can also set this inside your [project's settings](#deployment-strategy).
When you have a new version of your app to deploy in production, you may want
to use an incremental rollout to replace just a few pods with the latest code.
This will allow you to first check how the app is behaving, and later manually
increasing the rollout up to 100%.
If `INCREMENTAL_ROLLOUT_ENABLED` is defined in your project (e.g., set
`INCREMENTAL_ROLLOUT_ENABLED` to `1` as a variable), then instead of the
`INCREMENTAL_ROLLOUT_ENABLED` to `1` as a secret variable), then instead of the
standard `production` job, 4 different
[manual jobs](../../ci/pipelines.md#manual-actions-from-the-pipeline-graph)
will be created:
......@@ -810,3 +842,4 @@ curl --data "value=true" --header "PRIVATE-TOKEN: personal_access_token" https:/
[Auto DevOps template]: https://gitlab.com/gitlab-org/gitlab-ci-yml/blob/master/Auto-DevOps.gitlab-ci.yml
[GitLab Omnibus Helm Chart]: ../../install/kubernetes/gitlab_omnibus.md
[ee]: https://about.gitlab.com/products/
[ce-19507]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/19507
......@@ -557,10 +557,6 @@ Software that is hosted centrally and accessed on-demand (i.e. whenever you want
This term is often used by people when they mean "Version Control."
#### SCLAU
Abbreviation for SQO Count [Large And Up](https://about.gitlab.com/handbook/sales/#market-segmentation). This is the number of opportunities in large and strategic organizations passed from marketing to sales.
### Scrum
An Agile [framework](https://www.scrum.org/Resources/What-is-Scrum) designed to typically help complete complex software projects. It's made up of several parts: product requirements backlog, sprint planning, sprint (development), sprint review, and retrospec (analyzing the sprint). The goal is to end up with potentially shippable products.
......
......@@ -5,15 +5,15 @@ update guides.
There are currently 3 official ways to install GitLab:
- Omnibus packages
- Source installation
- Docker installation
- [Omnibus packages](#omnibus-packages)
- [Source installation](#installation-from-source)
- [Docker installation](#installation-using-docker)
Based on your installation, choose a section below that fits your needs.
## Omnibus Packages
- The [Omnibus update guide](http://docs.gitlab.com/omnibus/update/README.html)
- The [Omnibus update guide][omni-update]
contains the steps needed to update an Omnibus GitLab package.
## Installation from source
......@@ -30,7 +30,7 @@ Based on your installation, choose a section below that fits your needs.
GitLab provides official Docker images for both Community and Enterprise
editions. They are based on the Omnibus package and instructions on how to
update them are in [a separate document][omnidocker].
update them are in [a separate document][omni-docker].
## Upgrading without downtime
......@@ -96,6 +96,10 @@ migrations this could potentially lead to hours of downtime, depending on the
size of your database. To work around this you will have to use PostgreSQL and
meet the other online upgrade requirements mentioned above.
### Steps
Steps to [upgrade without downtime][omni-zero-downtime].
## Upgrading between editions
GitLab comes in two flavors: [Community Edition][ce] which is MIT licensed,
......@@ -136,10 +140,12 @@ possible.
- [Upgrading PostgreSQL Using Slony](upgrading_postgresql_using_slony.md), for
upgrading a PostgreSQL database with minimal downtime.
[omnidocker]: http://docs.gitlab.com/omnibus/docker/README.html
[source-ee]: https://gitlab.com/gitlab-org/gitlab-ee/tree/master/doc/update
[source-ce]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update
[ee-ce]: ../downgrade_ee_to_ce/README.md
[ce]: https://about.gitlab.com/features/#community
[ee]: https://about.gitlab.com/features/#enterprise
[omni-ce-ee]: http://docs.gitlab.com/omnibus/update/README.html#from-community-edition-to-enterprise-edition
[omni-ce-ee]: https://docs.gitlab.com/omnibus/update/README.html#updating-community-edition-to-enterprise-edition
[omni-docker]: https://docs.gitlab.com/omnibus/docker/README.html
[omni-update]: https://docs.gitlab.com/omnibus/update/README.html
[omni-zero-downtime]: https://docs.gitlab.com/omnibus/update/README.html#zero-downtime-updates
......@@ -20,6 +20,19 @@ When an admin enables this feature, they will automattically be
directed to the page to accept the terms themselves. After they
accept, they will be directed back to the settings page.
## New registrations
When this feature is enabled, a checkbox will be available in the
sign-up form.
![Sign up form](img/sign_up_terms.png)
This checkbox will be required during sign up.
Users can review the terms entered in the admin panel before
accepting. The page will be opened in a new window so they can
continue their registration afterwards.
## Accepting terms
When this feature was enabled, the users that have not accepted the
......
......@@ -60,6 +60,7 @@ Below are the current settings regarding [GitLab CI/CD](../../ci/README.md).
| Setting | GitLab.com | Default |
| ----------- | ----------------- | ------------- |
| Artifacts maximum size | 1G | 100M |
| Artifacts [expiry time](../../ci/yaml/README.md#artifacts-expire_in) | kept forever | deleted after 30 days unless otherwise specified |
## Repository size limit
......
......@@ -17,8 +17,7 @@ under the URL `/groups/<groupname>/analytics`.
- Analyze your team's contributions along a period of time and offer a bonus
for the top contributors
- If you are unhappy with a particular team member, you can use the analytics
as argument for demanding efficiency from this person
- Identify opportunities for improvement with group members who may benefit from additional support
## Using Contribution Analytics
......
......@@ -162,13 +162,13 @@ such as Trello, JIRA, etc.
## Webhooks
Configure [webhooks](project/integrations/webhooks.html) to listen for
Configure [webhooks](project/integrations/webhooks.md) to listen for
specific events like pushes, issues or merge requests. GitLab will send a
POST request with data to the webhook URL.
## API
Automate GitLab via [API](../api/README.html).
Automate GitLab via [API](../api/README.md).
## Git and GitLab
......
......@@ -35,7 +35,7 @@ https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#newline
GFM honors the markdown specification in how [paragraphs and line breaks are handled](https://daringfireball.net/projects/markdown/syntax#p).
A paragraph is simply one or more consecutive lines of text, separated by one or more blank lines.
Line-breaks, or softreturns, are rendered if you end a line with two or more spaces:
Line-breaks, or soft returns, are rendered if you end a line with two or more spaces:
[//]: # (Do *NOT* remove the two ending whitespaces in the following line.)
[//]: # (They are needed for the Markdown text to render correctly.)
......@@ -198,7 +198,7 @@ https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#inline-
With inline diffs tags you can display {+ additions +} or [- deletions -].
The wrapping tags can be either curly braces or square brackets [+ additions +] or {- deletions -}.
The wrapping tags can be either curly braces or square brackets: [+ additions +] or {- deletions -}.
Examples:
......@@ -229,7 +229,7 @@ https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#emoji
You can use it to point out a :bug: or warn about :speak_no_evil: patches. And if someone improves your really :snail: code, send them some :birthday:. People will :heart: you for that.
If you are new to this, don't be :fearful:. You can easily join the emoji :family:. All you need to do is to look up on the supported codes.
If you are new to this, don't be :fearful:. You can easily join the emoji :family:. All you need to do is to look up one of the supported codes.
Consult the [Emoji Cheat Sheet](https://www.emojicopy.com) for a list of all supported emoji codes. :thumbsup:
......@@ -239,7 +239,7 @@ Sometimes you want to :monkey: around a bit and add some :star2: to your :speech
You can use it to point out a :bug: or warn about :speak_no_evil: patches. And if someone improves your really :snail: code, send them some :birthday:. People will :heart: you for that.
If you are new to this, don't be :fearful:. You can easily join the emoji :family:. All you need to do is to look up on the supported codes.
If you are new to this, don't be :fearful:. You can easily join the emoji :family:. All you need to do is to look up one of the supported codes.
Consult the [Emoji Cheat Sheet](https://www.emojicopy.com) for a list of all supported emoji codes. :thumbsup:
......@@ -407,7 +407,7 @@ Examples:
`HSL(540,70%,50%)`
`HSLA(540,70%,50%,0.7)`
Becomes:
Become:
`#F00`
`#F00A`
......@@ -484,14 +484,14 @@ Alt-H2
All Markdown-rendered headers automatically get IDs, except in comments.
On hover a link to those IDs becomes visible to make it easier to copy the link to the header to give it to someone else.
On hover, a link to those IDs becomes visible to make it easier to copy the link to the header to give it to someone else.
The IDs are generated from the content of the header according to the following rules:
1. All text is converted to lowercase
1. All non-word text (e.g., punctuation, HTML) is removed
1. All spaces are converted to hyphens
1. Two or more hyphens in a row are converted to one
1. All text is converted to lowercase.
1. All non-word text (e.g., punctuation, HTML) is removed.
1. All spaces are converted to hyphens.
1. Two or more hyphens in a row are converted to one.
1. If a header with the same ID has already been generated, a unique
incrementing number is appended, starting at 1.
......@@ -519,6 +519,8 @@ Note that the Emoji processing happens before the header IDs are generated, so t
### Emphasis
Examples:
```no-highlight
Emphasis, aka italics, with *asterisks* or _underscores_.
......@@ -529,6 +531,8 @@ Combined emphasis with **asterisks and _underscores_**.
Strikethrough uses two tildes. ~~Scratch this.~~
```
Become:
Emphasis, aka italics, with *asterisks* or _underscores_.
Strong emphasis, aka bold, with **asterisks** or __underscores__.
......@@ -539,6 +543,8 @@ Strikethrough uses two tildes. ~~Scratch this.~~
### Lists
Examples:
```no-highlight
1. First ordered list item
2. Another item
......@@ -552,6 +558,8 @@ Strikethrough uses two tildes. ~~Scratch this.~~
+ Or pluses
```
Become:
1. First ordered list item
2. Another item
* Unordered sub-list.
......@@ -566,6 +574,8 @@ Strikethrough uses two tildes. ~~Scratch this.~~
If a list item contains multiple paragraphs,
each subsequent paragraph should be indented with four spaces.
Example:
```no-highlight
1. First ordered list item
......@@ -573,6 +583,8 @@ each subsequent paragraph should be indented with four spaces.
2. Another item
```
Becomes:
1. First ordered list item
Second paragraph of first item.
......@@ -581,6 +593,8 @@ each subsequent paragraph should be indented with four spaces.
If the second paragraph isn't indented with four spaces,
the second list item will be incorrectly labeled as `1`.
Example:
```no-highlight
1. First ordered list item
......@@ -588,6 +602,8 @@ the second list item will be incorrectly labeled as `1`.
2. Another item
```
Becomes:
1. First ordered list item
Second paragraph of first item.
......@@ -625,6 +641,8 @@ will point the link to `wikis/style` when the link is inside of a wiki markdown
### Images
Examples:
Here's our logo (hover to see the title text):
Inline-style:
......@@ -635,6 +653,8 @@ will point the link to `wikis/style` when the link is inside of a wiki markdown
[logo]: img/markdown_logo.png
Become:
Here's our logo:
Inline-style:
......@@ -649,6 +669,8 @@ Reference-style:
### Blockquotes
Examples:
```no-highlight
> Blockquotes are very handy in email to emulate reply text.
> This line is part of the same quote.
......@@ -658,6 +680,8 @@ Quote break.
> This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can *put* **Markdown** into a blockquote.
```
Become:
> Blockquotes are very handy in email to emulate reply text.
> This line is part of the same quote.
......@@ -671,6 +695,8 @@ You can also use raw HTML in your Markdown, and it'll mostly work pretty well.
See the documentation for HTML::Pipeline's [SanitizationFilter](http://www.rubydoc.info/gems/html-pipeline/1.11.0/HTML/Pipeline/SanitizationFilter#WHITELIST-constant) class for the list of allowed HTML tags and attributes. In addition to the default `SanitizationFilter` whitelist, GitLab allows `span`, `abbr`, `details` and `summary` elements.
Examples:
```no-highlight
<dl>
<dt>Definition list</dt>
......@@ -681,6 +707,8 @@ See the documentation for HTML::Pipeline's [SanitizationFilter](http://www.rubyd
</dl>
```
Become:
<dl>
<dt>Definition list</dt>
<dd>Is something people use sometimes.</dd>
......@@ -715,6 +743,8 @@ These details will remain hidden until expanded.
### Horizontal Rule
Examples:
```
Three or more...
......@@ -731,6 +761,8 @@ ___
Underscores
```
Become:
Three or more...
---
......@@ -751,6 +783,8 @@ My basic recommendation for learning how line breaks work is to experiment and d
Here are some things to try out:
Examples:
```
Here's a line for us to start with.
......@@ -765,6 +799,8 @@ This line is *on its own line*, because the previous line ends with two spaces.
spaces.
```
Become:
Here's a line for us to start with.
This line is separated from the one above by two newlines, so it will be a *separate paragraph*.
......@@ -781,6 +817,8 @@ spaces.
Tables aren't part of the core Markdown spec, but they are part of GFM and Markdown Here supports them.
Example:
```
| header 1 | header 2 |
| -------- | -------- |
......@@ -788,7 +826,7 @@ Tables aren't part of the core Markdown spec, but they are part of GFM and Markd
| cell 3 | cell 4 |
```
Code above produces next output:
Becomes:
| header 1 | header 2 |
| -------- | -------- |
......@@ -799,7 +837,9 @@ Code above produces next output:
The row of dashes between the table header and body must have at least three dashes in each column.
By including colons in the header row, you can align the text within that column:
By including colons in the header row, you can align the text within that column.
Example:
```
| Left Aligned | Centered | Right Aligned | Left Aligned | Centered | Right Aligned |
......@@ -808,6 +848,8 @@ By including colons in the header row, you can align the text within that column
| Cell 7 | Cell 8 | Cell 9 | Cell 10 | Cell 11 | Cell 12 |
```
Becomes:
| Left Aligned | Centered | Right Aligned | Left Aligned | Centered | Right Aligned |
| :----------- | :------: | ------------: | :----------- | :------: | ------------: |
| Cell 1 | Cell 2 | Cell 3 | Cell 4 | Cell 5 | Cell 6 |
......@@ -815,11 +857,15 @@ By including colons in the header row, you can align the text within that column
### Footnotes
Example:
```
You can add footnotes to your text as follows.[^2]
[^2]: This is my awesome footnote.
```
Becomes:
You can add footnotes to your text as follows.[^2]
## Wiki-specific Markdown
......
......@@ -38,7 +38,7 @@ Give the project a name, and then select `Create project`.
## Connecting the EKS cluster
From the left side bar, hover over `CI/CD` and select `Kubernetes`, then click on `Add Kubernetes cluster`, and finally `Add an existing Kubernetes cluster`.
From the left side bar, hover over `Operations` and select `Kubernetes`, then click on `Add Kubernetes cluster`, and finally `Add an existing Kubernetes cluster`.
A few details from the EKS cluster will be required to connect it to GitLab.
......
......@@ -39,7 +39,7 @@ Before proceeding, make sure the following requirements are met:
If all of the above requirements are met, you can proceed to create and add a
new Kubernetes cluster that will be hosted on GKE to your project:
1. Navigate to your project's **CI/CD > Kubernetes** page.
1. Navigate to your project's **Operations > Kubernetes** page.
1. Click on **Add Kubernetes cluster**.
1. Click on **Create with GKE**.
1. Connect your Google account if you haven't done already by clicking the
......@@ -70,7 +70,7 @@ You need Maintainer [permissions] and above to access the Kubernetes page.
To add an existing Kubernetes cluster to your project:
1. Navigate to your project's **CI/CD > Kubernetes** page.
1. Navigate to your project's **Operations > Kubernetes** page.
1. Click on **Add Kubernetes cluster**.
1. Click on **Add an existing Kubernetes cluster** and fill in the details:
- **Kubernetes cluster name** (required) - The name you wish to give the cluster.
......
......@@ -29,7 +29,9 @@ The following aspects of a project are imported:
* Regular issue and pull request comments
References to pull requests and issues are preserved (GitLab.com & 8.7+), and
each imported repository defaults to `private` but [can be made public](../settings/index.md#sharing-and-permissions), as needed.
each imported repository maintains visibility level unless that [visibility
level is restricted](../../../public_access/public_access.md#restricting-the-use-of-public-or-internal-projects),
in which case it defaults to the default project visibility.
## How it works
......
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.
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