Commit 6c9844c1 authored by Phil Hughes's avatar Phil Hughes

Merge branch 'master' into ide-jobs-list-components

parents e6e9706e fd79df64
...@@ -6,7 +6,7 @@ image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.7-golang-1.9-git ...@@ -6,7 +6,7 @@ image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.7-golang-1.9-git
- gitlab-org - gitlab-org
.default-cache: &default-cache .default-cache: &default-cache
key: "ruby-2.3.7-with-yarn" key: "ruby-2.3.7-debian-stretch-with-yarn"
paths: paths:
- vendor/ruby - vendor/ruby
- .yarn-cache/ - .yarn-cache/
...@@ -550,7 +550,7 @@ static-analysis: ...@@ -550,7 +550,7 @@ static-analysis:
script: script:
- scripts/static-analysis - scripts/static-analysis
cache: cache:
key: "ruby-2.3.7-with-yarn-and-rubocop" key: "ruby-2.3.7-debian-stretch-with-yarn-and-rubocop"
paths: paths:
- vendor/ruby - vendor/ruby
- .yarn-cache/ - .yarn-cache/
......
...@@ -2,6 +2,15 @@ ...@@ -2,6 +2,15 @@
documentation](doc/development/changelog.md) for instructions on adding your own documentation](doc/development/changelog.md) for instructions on adding your own
entry. entry.
## 10.8.2 (2018-05-28)
### Security (3 changes)
- Prevent user passwords from being changed without providing the previous password.
- Fix API to remove deploy key from project instead of deleting it entirely.
- Fixed bug that allowed importing arbitrary project attributes.
## 10.8.1 (2018-05-23) ## 10.8.1 (2018-05-23)
### Fixed (9 changes) ### Fixed (9 changes)
...@@ -193,6 +202,15 @@ entry. ...@@ -193,6 +202,15 @@ entry.
- Gitaly handles repository forks by default. - Gitaly handles repository forks by default.
## 10.7.5 (2018-05-28)
### Security (3 changes)
- Prevent user passwords from being changed without providing the previous password.
- Fix API to remove deploy key from project instead of deleting it entirely.
- Fixed bug that allowed importing arbitrary project attributes.
## 10.7.4 (2018-05-21) ## 10.7.4 (2018-05-21)
### Fixed (1 change) ### Fixed (1 change)
...@@ -457,6 +475,16 @@ entry. ...@@ -457,6 +475,16 @@ entry.
- Upgrade Gitaly to upgrade its charlock_holmes. - Upgrade Gitaly to upgrade its charlock_holmes.
## 10.6.6 (2018-05-28)
### Security (4 changes)
- Do not allow non-members to create MRs via forked projects when MRs are private.
- Prevent user passwords from being changed without providing the previous password.
- Fix API to remove deploy key from project instead of deleting it entirely.
- Fixed bug that allowed importing arbitrary project attributes.
## 10.6.5 (2018-04-24) ## 10.6.5 (2018-04-24)
### Security (1 change) ### Security (1 change)
......
...@@ -133,7 +133,7 @@ gem 'gitlab-markup', '~> 1.6.2' ...@@ -133,7 +133,7 @@ gem 'gitlab-markup', '~> 1.6.2'
gem 'redcarpet', '~> 3.4' gem 'redcarpet', '~> 3.4'
gem 'commonmarker', '~> 0.17' gem 'commonmarker', '~> 0.17'
gem 'RedCloth', '~> 4.3.2' gem 'RedCloth', '~> 4.3.2'
gem 'rdoc', '~> 4.2' gem 'rdoc', '~> 6.0'
gem 'org-ruby', '~> 0.9.12' gem 'org-ruby', '~> 0.9.12'
gem 'creole', '~> 0.5.0' gem 'creole', '~> 0.5.0'
gem 'wikicloth', '0.8.1' gem 'wikicloth', '0.8.1'
...@@ -320,7 +320,7 @@ group :development, :test do ...@@ -320,7 +320,7 @@ group :development, :test do
gem 'pry-byebug', '~> 3.4.1', platform: :mri gem 'pry-byebug', '~> 3.4.1', platform: :mri
gem 'pry-rails', '~> 0.3.4' gem 'pry-rails', '~> 0.3.4'
gem 'awesome_print', '~> 1.2.0', require: false gem 'awesome_print', '~> 1.8.0', require: false
gem 'fuubar', '~> 2.2.0' gem 'fuubar', '~> 2.2.0'
gem 'database_cleaner', '~> 1.5.0' gem 'database_cleaner', '~> 1.5.0'
......
...@@ -69,7 +69,7 @@ GEM ...@@ -69,7 +69,7 @@ GEM
attr_encrypted (3.1.0) attr_encrypted (3.1.0)
encryptor (~> 3.0.0) encryptor (~> 3.0.0)
attr_required (1.0.0) attr_required (1.0.0)
awesome_print (1.2.0) awesome_print (1.8.0)
axiom-types (0.1.1) axiom-types (0.1.1)
descendants_tracker (~> 0.0.4) descendants_tracker (~> 0.0.4)
ice_nine (~> 0.11.0) ice_nine (~> 0.11.0)
...@@ -694,8 +694,7 @@ GEM ...@@ -694,8 +694,7 @@ GEM
ffi ffi
rbnacl-libsodium (1.0.11) rbnacl-libsodium (1.0.11)
rbnacl (>= 3.0.1) rbnacl (>= 3.0.1)
rdoc (4.2.2) rdoc (6.0.4)
json (~> 1.4)
re2 (1.1.1) re2 (1.1.1)
recaptcha (3.0.0) recaptcha (3.0.0)
json json
...@@ -801,7 +800,7 @@ GEM ...@@ -801,7 +800,7 @@ GEM
rubyzip (1.2.1) rubyzip (1.2.1)
rufus-scheduler (3.4.0) rufus-scheduler (3.4.0)
et-orbi (~> 1.0) et-orbi (~> 1.0)
rugged (0.27.0) rugged (0.27.1)
safe_yaml (1.0.4) safe_yaml (1.0.4)
sanitize (2.1.0) sanitize (2.1.0)
nokogiri (>= 1.4.4) nokogiri (>= 1.4.4)
...@@ -978,7 +977,7 @@ DEPENDENCIES ...@@ -978,7 +977,7 @@ DEPENDENCIES
asciidoctor-plantuml (= 0.0.8) asciidoctor-plantuml (= 0.0.8)
asset_sync (~> 2.4) asset_sync (~> 2.4)
attr_encrypted (~> 3.1.0) attr_encrypted (~> 3.1.0)
awesome_print (~> 1.2.0) awesome_print (~> 1.8.0)
babosa (~> 1.0.2) babosa (~> 1.0.2)
base32 (~> 0.3.0) base32 (~> 0.3.0)
batch-loader (~> 1.2.1) batch-loader (~> 1.2.1)
...@@ -1124,7 +1123,7 @@ DEPENDENCIES ...@@ -1124,7 +1123,7 @@ DEPENDENCIES
rblineprof (~> 0.3.6) rblineprof (~> 0.3.6)
rbnacl (~> 4.0) rbnacl (~> 4.0)
rbnacl-libsodium rbnacl-libsodium
rdoc (~> 4.2) rdoc (~> 6.0)
re2 (~> 1.1.1) re2 (~> 1.1.1)
recaptcha (~> 3.0) recaptcha (~> 3.0)
redcarpet (~> 3.4) redcarpet (~> 3.4)
......
...@@ -160,7 +160,7 @@ export default { ...@@ -160,7 +160,7 @@ export default {
@input="debouncedPreview" @input="debouncedPreview"
/> />
<span <span
class="help-block" class="form-text text-muted"
v-html="helpText" v-html="helpText"
></span> ></span>
</div> </div>
...@@ -176,7 +176,7 @@ export default { ...@@ -176,7 +176,7 @@ export default {
@input="debouncedPreview" @input="debouncedPreview"
/> />
<span <span
class="help-block" class="form-text text-muted"
v-html="helpText" v-html="helpText"
></span> ></span>
</div> </div>
......
...@@ -43,7 +43,7 @@ export default { ...@@ -43,7 +43,7 @@ export default {
return `${this.changedIcon}-solid`; return `${this.changedIcon}-solid`;
}, },
changedIconClass() { changedIconClass() {
return `multi-${this.changedIcon} pull-left`; return `multi-${this.changedIcon} float-left`;
}, },
tooltipTitle() { tooltipTitle() {
if (!this.showTooltip) return undefined; if (!this.showTooltip) return undefined;
......
...@@ -144,14 +144,14 @@ export default { ...@@ -144,14 +144,14 @@ export default {
<loading-button <loading-button
:loading="submitCommitLoading" :loading="submitCommitLoading"
:disabled="commitButtonDisabled" :disabled="commitButtonDisabled"
container-class="btn btn-success btn-sm pull-left" container-class="btn btn-success btn-sm float-left"
:label="__('Commit')" :label="__('Commit')"
@click="commitChanges" @click="commitChanges"
/> />
<button <button
v-if="!discardDraftButtonDisabled" v-if="!discardDraftButtonDisabled"
type="button" type="button"
class="btn btn-default btn-sm pull-right" class="btn btn-default btn-sm float-right"
@click="discardDraft" @click="discardDraft"
> >
{{ __('Discard draft') }} {{ __('Discard draft') }}
...@@ -159,7 +159,7 @@ export default { ...@@ -159,7 +159,7 @@ export default {
<button <button
v-else v-else
type="button" type="button"
class="btn btn-default btn-sm pull-right" class="btn btn-default btn-sm float-right"
@click="toggleIsSmall" @click="toggleIsSmall"
> >
{{ __('Collapse') }} {{ __('Collapse') }}
......
...@@ -120,7 +120,7 @@ export default { ...@@ -120,7 +120,7 @@ export default {
</ul> </ul>
<p <p
v-else v-else
class="multi-file-commit-list help-block" class="multi-file-commit-list form-text text-muted"
> >
{{ __('No changes') }} {{ __('No changes') }}
</p> </p>
......
...@@ -80,7 +80,7 @@ export default { ...@@ -80,7 +80,7 @@ export default {
{{ __('Commit Message') }} {{ __('Commit Message') }}
<span <span
v-popover="$options.popoverOptions" v-popover="$options.popoverOptions"
class="help-block prepend-left-10" class="form-text text-muted prepend-left-10"
> >
<icon <icon
name="question" name="question"
......
...@@ -72,21 +72,19 @@ export default { ...@@ -72,21 +72,19 @@ export default {
<form <form
slot="body" slot="body"
@submit.prevent="createEntryInStore" @submit.prevent="createEntryInStore"
class="form-group row append-bottom-0" class="form-group row"
> >
<fieldset class="form-group append-bottom-0"> <label class="label-light col-form-label col-sm-3">
<label class="label-light col-form-label col-sm-3 ide-new-modal-label"> {{ __('Name') }}
{{ __('Name') }} </label>
</label> <div class="col-sm-9">
<div class="col-sm-9"> <input
<input type="text"
type="text" class="form-control"
class="form-control" v-model="entryName"
v-model="entryName" ref="fieldName"
ref="fieldName" />
/> </div>
</div>
</fieldset>
</form> </form>
</deprecated-modal> </deprecated-modal>
</template> </template>
...@@ -169,7 +169,7 @@ export default { ...@@ -169,7 +169,7 @@ export default {
:show-tooltip="true" :show-tooltip="true"
:show-staged-icon="true" :show-staged-icon="true"
:force-modified-icon="true" :force-modified-icon="true"
class="pull-right" class="float-right"
/> />
</span> </span>
<new-dropdown <new-dropdown
......
...@@ -362,7 +362,7 @@ export default class MergeRequestTabs { ...@@ -362,7 +362,7 @@ export default class MergeRequestTabs {
// //
// status - Boolean, true to show, false to hide // status - Boolean, true to show, false to hide
toggleLoading(status) { toggleLoading(status) {
$('.mr-loading-status .loading').toggleClass('hidden', status); $('.mr-loading-status .loading').toggleClass('hidden', !status);
} }
diffViewType() { diffViewType() {
......
...@@ -213,7 +213,7 @@ ...@@ -213,7 +213,7 @@
</i> </i>
</div> </div>
</div> </div>
<span class="help-block">{{ visibilityLevelDescription }}</span> <span class="form-text text-muted">{{ visibilityLevelDescription }}</span>
<label <label
v-if="visibilityLevel !== visibilityOptions.PRIVATE" v-if="visibilityLevel !== visibilityOptions.PRIVATE"
class="request-access" class="request-access"
......
...@@ -77,10 +77,9 @@ export default class UserTabs { ...@@ -77,10 +77,9 @@ export default class UserTabs {
this.action = action || this.defaultAction; this.action = action || this.defaultAction;
this.$parentEl = $(parentEl) || $(document); this.$parentEl = $(parentEl) || $(document);
this.windowLocation = window.location; this.windowLocation = window.location;
this.$parentEl.find('.nav-links a') this.$parentEl.find('.nav-links a').each((i, navLink) => {
.each((i, navLink) => { this.loaded[$(navLink).attr('data-action')] = false;
this.loaded[$(navLink).attr('data-action')] = false; });
});
this.actions = Object.keys(this.loaded); this.actions = Object.keys(this.loaded);
this.bindEvents(); this.bindEvents();
...@@ -116,8 +115,7 @@ export default class UserTabs { ...@@ -116,8 +115,7 @@ export default class UserTabs {
} }
activateTab(action) { activateTab(action) {
return this.$parentEl.find(`.nav-links .js-${action}-tab a`) return this.$parentEl.find(`.nav-links .js-${action}-tab a`).tab('show');
.tab('show');
} }
setTab(action, endpoint) { setTab(action, endpoint) {
...@@ -137,7 +135,8 @@ export default class UserTabs { ...@@ -137,7 +135,8 @@ export default class UserTabs {
loadTab(action, endpoint) { loadTab(action, endpoint) {
this.toggleLoading(true); this.toggleLoading(true);
return axios.get(endpoint) return axios
.get(endpoint)
.then(({ data }) => { .then(({ data }) => {
const tabSelector = `div#${action}`; const tabSelector = `div#${action}`;
this.$parentEl.find(tabSelector).html(data.html); this.$parentEl.find(tabSelector).html(data.html);
...@@ -161,10 +160,11 @@ export default class UserTabs { ...@@ -161,10 +160,11 @@ export default class UserTabs {
const utcOffset = $calendarWrap.data('utcOffset'); const utcOffset = $calendarWrap.data('utcOffset');
let utcFormatted = 'UTC'; let utcFormatted = 'UTC';
if (utcOffset !== 0) { if (utcOffset !== 0) {
utcFormatted = `UTC${utcOffset > 0 ? '+' : ''}${(utcOffset / 3600)}`; utcFormatted = `UTC${utcOffset > 0 ? '+' : ''}${utcOffset / 3600}`;
} }
axios.get(calendarPath) axios
.get(calendarPath)
.then(({ data }) => { .then(({ data }) => {
$calendarWrap.html(CALENDAR_TEMPLATE); $calendarWrap.html(CALENDAR_TEMPLATE);
$calendarWrap.find('.calendar-hint').append(`(Timezone: ${utcFormatted})`); $calendarWrap.find('.calendar-hint').append(`(Timezone: ${utcFormatted})`);
...@@ -180,17 +180,20 @@ export default class UserTabs { ...@@ -180,17 +180,20 @@ export default class UserTabs {
} }
toggleLoading(status) { toggleLoading(status) {
return this.$parentEl.find('.loading-status .loading') return this.$parentEl.find('.loading-status .loading').toggleClass('hidden', !status);
.toggleClass('hidden', status);
} }
setCurrentAction(source) { setCurrentAction(source) {
let newState = source; let newState = source;
newState = newState.replace(/\/+$/, ''); newState = newState.replace(/\/+$/, '');
newState += this.windowLocation.search + this.windowLocation.hash; newState += this.windowLocation.search + this.windowLocation.hash;
history.replaceState({ history.replaceState(
url: newState, {
}, document.title, newState); url: newState,
},
document.title,
newState,
);
return newState; return newState;
} }
......
...@@ -99,7 +99,7 @@ Please update your Git repository remotes as soon as possible.`), ...@@ -99,7 +99,7 @@ Please update your Git repository remotes as soon as possible.`),
:disabled="isRequestPending" :disabled="isRequestPending"
/> />
</div> </div>
<p class="help-block"> <p class="form-text text-muted">
{{ path }} {{ path }}
</p> </p>
</div> </div>
......
...@@ -132,7 +132,7 @@ export default { ...@@ -132,7 +132,7 @@ export default {
</div> </div>
</div> </div>
<span <span
class="help-block" class="form-text text-muted"
:class="{ 'gl-field-error': hasErrors }" :class="{ 'gl-field-error': hasErrors }"
v-if="hasErrors" v-if="hasErrors"
> >
......
...@@ -193,7 +193,7 @@ export default { ...@@ -193,7 +193,7 @@ export default {
</div> </div>
</div> </div>
<span <span
class="help-block" class="form-text text-muted"
:class="{ 'gl-field-error': hasErrors }" :class="{ 'gl-field-error': hasErrors }"
v-html="helpText" v-html="helpText"
></span> ></span>
......
...@@ -106,7 +106,7 @@ export default { ...@@ -106,7 +106,7 @@ export default {
</div> </div>
</div> </div>
<span <span
class="help-block" class="form-text text-muted"
:class="{ 'gl-field-error': hasErrors }" :class="{ 'gl-field-error': hasErrors }"
v-if="hasErrors" v-if="hasErrors"
> >
......
/*
The squash-before-merge button is EE only, but it's located right in the middle
of the readyToMerge state component template.
If we didn't declare this component in CE, we'd need to maintain a separate copy
of the readyToMergeState template in EE, which is pretty big and likely to change.
Instead, in CE, we declare the component, but it's hidden and is configured to do nothing.
In EE, the configuration extends this object to add a functioning squash-before-merge
button.
*/
<script> <script>
export default {}; import Icon from '~/vue_shared/components/icon.vue';
import eventHub from '~/vue_merge_request_widget/event_hub';
import tooltip from '~/vue_shared/directives/tooltip';
export default {
components: {
Icon,
},
directives: {
tooltip,
},
props: {
mr: {
type: Object,
required: true,
},
isMergeButtonDisabled: {
type: Boolean,
required: true,
},
},
data() {
return {
squashBeforeMerge: this.mr.squash,
};
},
methods: {
updateSquashModel() {
eventHub.$emit('MRWidgetUpdateSquash', this.squashBeforeMerge);
},
},
};
</script> </script>
<template>
<div class="accept-control inline">
<label class="merge-param-checkbox">
<input
type="checkbox"
name="squash"
class="qa-squash-checkbox"
:disabled="isMergeButtonDisabled"
v-model="squashBeforeMerge"
@change="updateSquashModel"
/>
{{ __('Squash commits') }}
</label>
<a
:href="mr.squashBeforeMergeHelpPath"
data-title="About this feature"
data-placement="bottom"
target="_blank"
rel="noopener noreferrer nofollow"
data-container="body"
v-tooltip
>
<icon
name="question-o"
/>
</a>
</div>
</template>
...@@ -6,11 +6,13 @@ import MergeRequest from '../../../merge_request'; ...@@ -6,11 +6,13 @@ import MergeRequest from '../../../merge_request';
import Flash from '../../../flash'; import Flash from '../../../flash';
import statusIcon from '../mr_widget_status_icon.vue'; import statusIcon from '../mr_widget_status_icon.vue';
import eventHub from '../../event_hub'; import eventHub from '../../event_hub';
import SquashBeforeMerge from './mr_widget_squash_before_merge.vue';
export default { export default {
name: 'ReadyToMerge', name: 'ReadyToMerge',
components: { components: {
statusIcon, statusIcon,
'squash-before-merge': SquashBeforeMerge,
}, },
props: { props: {
mr: { type: Object, required: true }, mr: { type: Object, required: true },
...@@ -101,6 +103,12 @@ export default { ...@@ -101,6 +103,12 @@ export default {
return enableSquashBeforeMerge && commitsCount > 1; return enableSquashBeforeMerge && commitsCount > 1;
}, },
}, },
created() {
eventHub.$on('MRWidgetUpdateSquash', this.handleUpdateSquash);
},
beforeDestroy() {
eventHub.$off('MRWidgetUpdateSquash', this.handleUpdateSquash);
},
methods: { methods: {
shouldShowMergeControls() { shouldShowMergeControls() {
return this.mr.isMergeAllowed || this.shouldShowMergeWhenPipelineSucceedsText; return this.mr.isMergeAllowed || this.shouldShowMergeWhenPipelineSucceedsText;
...@@ -128,13 +136,9 @@ export default { ...@@ -128,13 +136,9 @@ export default {
commit_message: this.commitMessage, commit_message: this.commitMessage,
merge_when_pipeline_succeeds: this.setToMergeWhenPipelineSucceeds, merge_when_pipeline_succeeds: this.setToMergeWhenPipelineSucceeds,
should_remove_source_branch: this.removeSourceBranch === true, should_remove_source_branch: this.removeSourceBranch === true,
squash: this.mr.squash,
}; };
// Only truthy in EE extension of this component
if (this.setAdditionalParams) {
this.setAdditionalParams(options);
}
this.isMakingRequest = true; this.isMakingRequest = true;
this.service.merge(options) this.service.merge(options)
.then(res => res.data) .then(res => res.data)
...@@ -154,6 +158,9 @@ export default { ...@@ -154,6 +158,9 @@ export default {
new Flash('Something went wrong. Please try again.'); // eslint-disable-line new Flash('Something went wrong. Please try again.'); // eslint-disable-line
}); });
}, },
handleUpdateSquash(val) {
this.mr.squash = val;
},
initiateMergePolling() { initiateMergePolling() {
simplePoll((continuePolling, stopPolling) => { simplePoll((continuePolling, stopPolling) => {
this.handleMergePolling(continuePolling, stopPolling); this.handleMergePolling(continuePolling, stopPolling);
......
...@@ -15,6 +15,11 @@ export default class MergeRequestStore { ...@@ -15,6 +15,11 @@ export default class MergeRequestStore {
const currentUser = data.current_user; const currentUser = data.current_user;
const pipelineStatus = data.pipeline ? data.pipeline.details.status : null; const pipelineStatus = data.pipeline ? data.pipeline.details.status : null;
this.squash = data.squash;
this.squashBeforeMergeHelpPath = this.squashBeforeMergeHelpPath ||
data.squash_before_merge_help_path;
this.enableSquashBeforeMerge = this.enableSquashBeforeMerge || true;
this.title = data.title; this.title = data.title;
this.targetBranch = data.target_branch; this.targetBranch = data.target_branch;
this.sourceBranch = data.source_branch; this.sourceBranch = data.source_branch;
......
...@@ -131,6 +131,10 @@ table { ...@@ -131,6 +131,10 @@ table {
} }
.card { .card {
.card-title {
margin-bottom: 0;
}
&.card-without-border { &.card-without-border {
@extend .border-0; @extend .border-0;
} }
...@@ -147,3 +151,7 @@ table { ...@@ -147,3 +151,7 @@ table {
.nav-tabs .nav-link { .nav-tabs .nav-link {
border: 0; border: 0;
} }
pre code {
white-space: pre-wrap;
}
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* Well styled list * Well styled list
* *
*/ */
.card-body-list { .hover-list {
position: relative; position: relative;
margin: 0; margin: 0;
padding: 0; padding: 0;
......
...@@ -405,7 +405,7 @@ table.u2f-registrations { ...@@ -405,7 +405,7 @@ table.u2f-registrations {
margin-right: $gl-padding / 4; margin-right: $gl-padding / 4;
} }
.label-verification-status { .badge-verification-status {
border-width: 1px; border-width: 1px;
border-style: solid; border-style: solid;
......
...@@ -546,7 +546,7 @@ ...@@ -546,7 +546,7 @@
margin-right: 0; margin-right: 0;
} }
&.help-block { &.form-text.text-muted {
margin-left: 0; margin-left: 0;
right: 0; right: 0;
} }
...@@ -962,7 +962,7 @@ ...@@ -962,7 +962,7 @@
height: 30px; height: 30px;
} }
.help-block { .form-text.text-muted {
margin-top: 2px; margin-top: 2px;
color: $blue-500; color: $blue-500;
cursor: pointer; cursor: pointer;
...@@ -1098,10 +1098,6 @@ ...@@ -1098,10 +1098,6 @@
font-size: 12px; font-size: 12px;
} }
.ide-new-modal-label {
line-height: 34px;
}
.multi-file-commit-panel-success-message { .multi-file-commit-panel-success-message {
position: absolute; position: absolute;
top: 61px; top: 61px;
......
...@@ -93,8 +93,6 @@ class ProfilesController < Profiles::ApplicationController ...@@ -93,8 +93,6 @@ class ProfilesController < Profiles::ApplicationController
:linkedin, :linkedin,
:location, :location,
:name, :name,
:password,
:password_confirmation,
:public_email, :public_email,
:skype, :skype,
:twitter, :twitter,
......
...@@ -71,19 +71,6 @@ class Projects::ClustersController < Projects::ApplicationController ...@@ -71,19 +71,6 @@ class Projects::ClustersController < Projects::ApplicationController
.present(current_user: current_user) .present(current_user: current_user)
end end
def create_params
params.require(:cluster).permit(
:enabled,
:name,
:provider_type,
provider_gcp_attributes: [
:gcp_project_id,
:zone,
:num_nodes,
:machine_type
])
end
def update_params def update_params
if cluster.managed? if cluster.managed?
params.require(:cluster).permit( params.require(:cluster).permit(
......
...@@ -7,6 +7,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController ...@@ -7,6 +7,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController
before_action :authorize_admin_environment!, only: [:terminal, :terminal_websocket_authorize] before_action :authorize_admin_environment!, only: [:terminal, :terminal_websocket_authorize]
before_action :environment, only: [:show, :edit, :update, :stop, :terminal, :terminal_websocket_authorize, :metrics] before_action :environment, only: [:show, :edit, :update, :stop, :terminal, :terminal_websocket_authorize, :metrics]
before_action :verify_api_request!, only: :terminal_websocket_authorize before_action :verify_api_request!, only: :terminal_websocket_authorize
before_action :expire_etag_cache, only: [:index]
def index def index
@environments = project.environments @environments = project.environments
...@@ -148,6 +149,15 @@ class Projects::EnvironmentsController < Projects::ApplicationController ...@@ -148,6 +149,15 @@ class Projects::EnvironmentsController < Projects::ApplicationController
Gitlab::Workhorse.verify_api_request!(request.headers) Gitlab::Workhorse.verify_api_request!(request.headers)
end end
def expire_etag_cache
return if request.format.json?
# this forces to reload json content
Gitlab::EtagCaching::Store.new.tap do |store|
store.touch(project_environments_path(project, format: :json))
end
end
def environment_params def environment_params
params.require(:environment).permit(:name, :external_url) params.require(:environment).permit(:name, :external_url)
end end
......
...@@ -24,6 +24,7 @@ class Projects::MergeRequests::ApplicationController < Projects::ApplicationCont ...@@ -24,6 +24,7 @@ class Projects::MergeRequests::ApplicationController < Projects::ApplicationCont
:source_branch, :source_branch,
:source_project_id, :source_project_id,
:state_event, :state_event,
:squash,
:target_branch, :target_branch,
:target_project_id, :target_project_id,
:task_num, :task_num,
......
...@@ -253,7 +253,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo ...@@ -253,7 +253,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
end end
def merge_params_attributes def merge_params_attributes
[:should_remove_source_branch, :commit_message] [:should_remove_source_branch, :commit_message, :squash]
end end
def merge_when_pipeline_succeeds_active? def merge_when_pipeline_succeeds_active?
...@@ -282,7 +282,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo ...@@ -282,7 +282,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
return :sha_mismatch if params[:sha] != @merge_request.diff_head_sha return :sha_mismatch if params[:sha] != @merge_request.diff_head_sha
@merge_request.update(merge_error: nil) @merge_request.update(merge_error: nil, squash: merge_params.fetch(:squash, false))
if params[:merge_when_pipeline_succeeds].present? if params[:merge_when_pipeline_succeeds].present?
return :failed unless @merge_request.actual_head_pipeline return :failed unless @merge_request.actual_head_pipeline
......
...@@ -204,7 +204,7 @@ module ApplicationSettingsHelper ...@@ -204,7 +204,7 @@ module ApplicationSettingsHelper
:pages_domain_verification_enabled, :pages_domain_verification_enabled,
:password_authentication_enabled_for_web, :password_authentication_enabled_for_web,
:password_authentication_enabled_for_git, :password_authentication_enabled_for_git,
:performance_bar_allowed_group_id, :performance_bar_allowed_group_path,
:performance_bar_enabled, :performance_bar_enabled,
:plantuml_enabled, :plantuml_enabled,
:plantuml_url, :plantuml_url,
......
...@@ -97,8 +97,9 @@ module MergeRequestsHelper ...@@ -97,8 +97,9 @@ module MergeRequestsHelper
{ {
merge_when_pipeline_succeeds: true, merge_when_pipeline_succeeds: true,
should_remove_source_branch: true, should_remove_source_branch: true,
sha: merge_request.diff_head_sha sha: merge_request.diff_head_sha,
}.merge(merge_params_ee(merge_request)) squash: merge_request.squash
}
end end
def tab_link_for(merge_request, tab, options = {}, &block) def tab_link_for(merge_request, tab, options = {}, &block)
...@@ -149,8 +150,4 @@ module MergeRequestsHelper ...@@ -149,8 +150,4 @@ module MergeRequestsHelper
current_user.fork_of(project) current_user.fork_of(project)
end end
end end
def merge_params_ee(merge_request)
{}
end
end end
...@@ -230,6 +230,7 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -230,6 +230,7 @@ class ApplicationSetting < ActiveRecord::Base
after_commit do after_commit do
reset_memoized_terms reset_memoized_terms
end end
after_commit :expire_performance_bar_allowed_user_ids_cache, if: -> { previous_changes.key?('performance_bar_allowed_group_id') }
def self.defaults def self.defaults
{ {
...@@ -386,31 +387,6 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -386,31 +387,6 @@ class ApplicationSetting < ActiveRecord::Base
super(levels.map { |level| Gitlab::VisibilityLevel.level_value(level) }) super(levels.map { |level| Gitlab::VisibilityLevel.level_value(level) })
end end
def performance_bar_allowed_group_id=(group_full_path)
group_full_path = nil if group_full_path.blank?
if group_full_path.nil?
if group_full_path != performance_bar_allowed_group_id
super(group_full_path)
Gitlab::PerformanceBar.expire_allowed_user_ids_cache
end
return
end
group = Group.find_by_full_path(group_full_path)
if group
if group.id != performance_bar_allowed_group_id
super(group.id)
Gitlab::PerformanceBar.expire_allowed_user_ids_cache
end
else
super(nil)
Gitlab::PerformanceBar.expire_allowed_user_ids_cache
end
end
def performance_bar_allowed_group def performance_bar_allowed_group
Group.find_by_id(performance_bar_allowed_group_id) Group.find_by_id(performance_bar_allowed_group_id)
end end
...@@ -420,15 +396,6 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -420,15 +396,6 @@ class ApplicationSetting < ActiveRecord::Base
performance_bar_allowed_group_id.present? performance_bar_allowed_group_id.present?
end end
# - If `enable` is true, we early return since the actual attribute that holds
# the enabling/disabling is `performance_bar_allowed_group_id`
# - If `enable` is false, we set `performance_bar_allowed_group_id` to `nil`
def performance_bar_enabled=(enable)
return if Gitlab::Utils.to_boolean(enable)
self.performance_bar_allowed_group_id = nil
end
# Choose one of the available repository storage options. Currently all have # Choose one of the available repository storage options. Currently all have
# equal weighting. # equal weighting.
def pick_repository_storage def pick_repository_storage
...@@ -506,4 +473,8 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -506,4 +473,8 @@ class ApplicationSetting < ActiveRecord::Base
errors.add(:terms, "You need to set terms to be enforced") unless terms.present? errors.add(:terms, "You need to set terms to be enforced") unless terms.present?
end end
def expire_performance_bar_allowed_user_ids_cache
Gitlab::PerformanceBar.expire_allowed_user_ids_cache
end
end end
# Provides a way to work around Rails issue where dependent objects are all
# loaded into memory before destroyed: https://github.com/rails/rails/issues/22510.
#
# This concern allows an ActiveRecord module to destroy all its dependent
# associations in batches. The idea is borrowed from https://github.com/thisismydesign/batch_dependent_associations.
#
# The differences here with that gem:
#
# 1. We allow excluding certain associations.
# 2. We don't need to support delete_all since we can use the EachBatch concern.
module BatchDestroyDependentAssociations
extend ActiveSupport::Concern
DEPENDENT_ASSOCIATIONS_BATCH_SIZE = 1000
def dependent_associations_to_destroy
self.class.reflect_on_all_associations(:has_many).select { |assoc| assoc.options[:dependent] == :destroy }
end
def destroy_dependent_associations_in_batches(exclude: [])
dependent_associations_to_destroy.each do |association|
next if exclude.include?(association.name)
# rubocop:disable GitlabSecurity/PublicSend
public_send(association.name).find_each(batch_size: DEPENDENT_ASSOCIATIONS_BATCH_SIZE, &:destroy)
end
end
end
...@@ -40,6 +40,7 @@ class Event < ActiveRecord::Base ...@@ -40,6 +40,7 @@ class Event < ActiveRecord::Base
).freeze ).freeze
RESET_PROJECT_ACTIVITY_INTERVAL = 1.hour RESET_PROJECT_ACTIVITY_INTERVAL = 1.hour
REPOSITORY_UPDATED_AT_INTERVAL = 5.minutes
delegate :name, :email, :public_email, :username, to: :author, prefix: true, allow_nil: true delegate :name, :email, :public_email, :username, to: :author, prefix: true, allow_nil: true
delegate :title, to: :issue, prefix: true, allow_nil: true delegate :title, to: :issue, prefix: true, allow_nil: true
...@@ -391,6 +392,7 @@ class Event < ActiveRecord::Base ...@@ -391,6 +392,7 @@ class Event < ActiveRecord::Base
def set_last_repository_updated_at def set_last_repository_updated_at
Project.unscoped.where(id: project_id) Project.unscoped.where(id: project_id)
.where("last_repository_updated_at < ? OR last_repository_updated_at IS NULL", REPOSITORY_UPDATED_AT_INTERVAL.ago)
.update_all(last_repository_updated_at: created_at) .update_all(last_repository_updated_at: created_at)
end end
......
...@@ -1140,4 +1140,11 @@ class MergeRequest < ActiveRecord::Base ...@@ -1140,4 +1140,11 @@ class MergeRequest < ActiveRecord::Base
maintainer_push_possible? && maintainer_push_possible? &&
Ability.allowed?(user, :push_code, source_project) Ability.allowed?(user, :push_code, source_project)
end end
def squash_in_progress?
# The source project can be deleted
return false unless source_project
source_project.repository.squash_in_progress?(id)
end
end end
...@@ -24,6 +24,7 @@ class Project < ActiveRecord::Base ...@@ -24,6 +24,7 @@ class Project < ActiveRecord::Base
include ChronicDurationAttribute include ChronicDurationAttribute
include FastDestroyAll::Helpers include FastDestroyAll::Helpers
include WithUploads include WithUploads
include BatchDestroyDependentAssociations
extend Gitlab::ConfigHelper extend Gitlab::ConfigHelper
......
...@@ -957,6 +957,14 @@ class Repository ...@@ -957,6 +957,14 @@ class Repository
remote_branch: merge_request.target_branch) remote_branch: merge_request.target_branch)
end end
def squash(user, merge_request)
raw.squash(user, merge_request.id, branch: merge_request.target_branch,
start_sha: merge_request.diff_start_sha,
end_sha: merge_request.diff_head_sha,
author: merge_request.author,
message: merge_request.title)
end
private private
# TODO Generice finder, later split this on finders by Ref or Oid # TODO Generice finder, later split this on finders by Ref or Oid
......
...@@ -10,6 +10,7 @@ class MergeRequestWidgetEntity < IssuableEntity ...@@ -10,6 +10,7 @@ class MergeRequestWidgetEntity < IssuableEntity
expose :merge_when_pipeline_succeeds expose :merge_when_pipeline_succeeds
expose :source_branch expose :source_branch
expose :source_project_id expose :source_project_id
expose :squash
expose :target_branch expose :target_branch
expose :target_project_id expose :target_project_id
expose :allow_maintainer_to_push expose :allow_maintainer_to_push
......
...@@ -3,6 +3,10 @@ module ApplicationSettings ...@@ -3,6 +3,10 @@ module ApplicationSettings
def execute def execute
update_terms(@params.delete(:terms)) update_terms(@params.delete(:terms))
if params.key?(:performance_bar_allowed_group_path)
params[:performance_bar_allowed_group_id] = performance_bar_allowed_group_id
end
@application_setting.update(@params) @application_setting.update(@params)
end end
...@@ -18,5 +22,13 @@ module ApplicationSettings ...@@ -18,5 +22,13 @@ module ApplicationSettings
ApplicationSetting::Term.create(terms: terms) ApplicationSetting::Term.create(terms: terms)
@application_setting.reset_memoized_terms @application_setting.reset_memoized_terms
end end
def performance_bar_allowed_group_id
performance_bar_enabled = !params.key?(:performance_bar_enabled) || params.delete(:performance_bar_enabled)
group_full_path = params.delete(:performance_bar_allowed_group_path)
return nil unless Gitlab::Utils.to_boolean(performance_bar_enabled)
Group.find_by_full_path(group_full_path)&.id if group_full_path.present?
end
end end
end end
...@@ -34,6 +34,19 @@ module MergeRequests ...@@ -34,6 +34,19 @@ module MergeRequests
handle_merge_error(log_message: e.message, save_message_on_model: true) handle_merge_error(log_message: e.message, save_message_on_model: true)
end end
def source
return merge_request.diff_head_sha unless merge_request.squash
squash_result = ::MergeRequests::SquashService.new(project, current_user, params).execute(merge_request)
case squash_result[:status]
when :success
squash_result[:squash_sha]
when :error
raise ::MergeRequests::MergeService::MergeError, squash_result[:message]
end
end
private private
def error_check! def error_check!
...@@ -116,9 +129,5 @@ module MergeRequests ...@@ -116,9 +129,5 @@ module MergeRequests
def merge_request_info def merge_request_info
merge_request.to_reference(full: true) merge_request.to_reference(full: true)
end end
def source
@source ||= @merge_request.diff_head_sha
end
end end
end end
module MergeRequests
class SquashService < MergeRequests::WorkingCopyBaseService
def execute(merge_request)
@merge_request = merge_request
@repository = target_project.repository
squash || error('Failed to squash. Should be done manually.')
end
def squash
if merge_request.commits_count < 2
return success(squash_sha: merge_request.diff_head_sha)
end
if merge_request.squash_in_progress?
return error('Squash task canceled: another squash is already in progress.')
end
squash_sha = repository.squash(current_user, merge_request)
success(squash_sha: squash_sha)
rescue => e
log_error("Failed to squash merge request #{merge_request.to_reference(full: true)}:")
log_error(e.message)
false
end
end
end
...@@ -137,7 +137,13 @@ module Projects ...@@ -137,7 +137,13 @@ module Projects
trash_repositories! trash_repositories!
project.team.truncate # Rails attempts to load all related records into memory before
# destroying: https://github.com/rails/rails/issues/22510
# This ensures we delete records in batches.
#
# Exclude container repositories because its before_destroy would be
# called multiple times, and it doesn't destroy any database records.
project.destroy_dependent_associations_in_batches(exclude: [:container_repositories])
project.destroy! project.destroy!
end end
end end
......
...@@ -9,8 +9,8 @@ ...@@ -9,8 +9,8 @@
= f.check_box :performance_bar_enabled = f.check_box :performance_bar_enabled
Enable the Performance Bar Enable the Performance Bar
.form-group.row .form-group.row
= f.label :performance_bar_allowed_group_id, 'Allowed group', class: 'col-form-label col-sm-2' = f.label :performance_bar_allowed_group_path, 'Allowed group', class: 'col-form-label col-sm-2'
.col-sm-10 .col-sm-10
= f.text_field :performance_bar_allowed_group_id, class: 'form-control', placeholder: 'my-org/my-group', value: @application_setting.performance_bar_allowed_group&.full_path = f.text_field :performance_bar_allowed_group_path, class: 'form-control', placeholder: 'my-org/my-group', value: @application_setting.performance_bar_allowed_group&.full_path
= f.submit 'Save changes', class: "btn btn-success" = f.submit 'Save changes', class: "btn btn-success"
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
= f.label :mirror_available do = f.label :mirror_available do
= f.check_box :mirror_available = f.check_box :mirror_available
Allow mirrors to be setup for projects Allow mirrors to be setup for projects
%span.help-block %span.form-text.text-muted
If disabled, only admins will be able to setup mirrors in projects. If disabled, only admins will be able to setup mirrors in projects.
= link_to icon('question-circle'), help_page_path('workflow/repository_mirroring') = link_to icon('question-circle'), help_page_path('workflow/repository_mirroring')
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
must be used to authenticate. must be used to authenticate.
- if omniauth_enabled? && button_based_providers.any? - if omniauth_enabled? && button_based_providers.any?
.form-group.row .form-group.row
= f.label :enabled_oauth_sign_in_sources, 'Enabled OAuth sign-in sources', class: 'control-label col-sm-2' = f.label :enabled_oauth_sign_in_sources, 'Enabled OAuth sign-in sources', class: 'col-form-label col-sm-2'
= hidden_field_tag 'application_setting[enabled_oauth_sign_in_sources][]' = hidden_field_tag 'application_setting[enabled_oauth_sign_in_sources][]'
.col-sm-10 .col-sm-10
.btn-group{ data: { toggle: 'buttons' } } .btn-group{ data: { toggle: 'buttons' } }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
= f.label :enforce_terms do = f.label :enforce_terms do
= f.check_box :enforce_terms = f.check_box :enforce_terms
= _("Require all users to accept Terms of Service when they access GitLab.") = _("Require all users to accept Terms of Service when they access GitLab.")
.help-block .form-text.text-muted
= _("When enabled, users cannot use GitLab until the terms have been accepted.") = _("When enabled, users cannot use GitLab until the terms have been accepted.")
.form-group .form-group
.col-sm-12 .col-sm-12
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
= _("Terms of Service Agreement") = _("Terms of Service Agreement")
.col-sm-12 .col-sm-12
= f.text_area :terms, class: 'form-control', rows: 8 = f.text_area :terms, class: 'form-control', rows: 8
.help-block .form-text.text-muted
= _("Markdown enabled") = _("Markdown enabled")
= f.submit _("Save changes"), class: "btn btn-success" = f.submit _("Save changes"), class: "btn btn-success"
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
.card .card
.card-header .card-header
Group info: Group info:
%ul.well-list %ul.content-list
%li %li
.avatar-container.s60 .avatar-container.s60
= group_icon(@group, class: "avatar s60") = group_icon(@group, class: "avatar s60")
...@@ -64,7 +64,7 @@ ...@@ -64,7 +64,7 @@
Projects Projects
%span.badge.badge-pill %span.badge.badge-pill
#{@group.projects.count} #{@group.projects.count}
%ul.well-list %ul.content-list
- @projects.each do |project| - @projects.each do |project|
%li %li
%strong %strong
...@@ -82,7 +82,7 @@ ...@@ -82,7 +82,7 @@
Projects shared with #{@group.name} Projects shared with #{@group.name}
%span.badge.badge-pill %span.badge.badge-pill
#{@group.shared_projects.count} #{@group.shared_projects.count}
%ul.well-list %ul.content-list
- @group.shared_projects.sort_by(&:name).each do |project| - @group.shared_projects.sort_by(&:name).each do |project|
%li %li
%strong %strong
...@@ -118,7 +118,7 @@ ...@@ -118,7 +118,7 @@
%span.badge.badge-pill= @group.members.size %span.badge.badge-pill= @group.members.size
.float-right .float-right
= link_to icon('pencil-square-o', text: 'Manage access'), polymorphic_url([@group, :members]), class: "btn btn-sm" = link_to icon('pencil-square-o', text: 'Manage access'), polymorphic_url([@group, :members]), class: "btn btn-sm"
%ul.well-list.group-users-list.content-list.members-list %ul.content-list.group-users-list.content-list.members-list
= render partial: 'shared/members/member', collection: @members, as: :member, locals: { show_controls: false } = render partial: 'shared/members/member', collection: @members, as: :member, locals: { show_controls: false }
.card-footer .card-footer
= paginate @members, param_name: 'members_page', theme: 'gitlab' = paginate @members, param_name: 'members_page', theme: 'gitlab'
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
.card .card
.card-header .card-header
Project info: Project info:
%ul.well-list %ul.content-list
%li %li
%span.light Name: %span.light Name:
%strong %strong
...@@ -166,7 +166,7 @@ ...@@ -166,7 +166,7 @@
.float-right .float-right
= link_to admin_group_path(@group), class: 'btn btn-sm' do = link_to admin_group_path(@group), class: 'btn btn-sm' do
= icon('pencil-square-o', text: 'Manage access') = icon('pencil-square-o', text: 'Manage access')
%ul.well-list.content-list.members-list %ul.content-list.members-list
= render partial: 'shared/members/member', collection: @group_members, as: :member, locals: { show_controls: false } = render partial: 'shared/members/member', collection: @group_members, as: :member, locals: { show_controls: false }
.card-footer .card-footer
= paginate @group_members, param_name: 'group_members_page', theme: 'gitlab' = paginate @group_members, param_name: 'group_members_page', theme: 'gitlab'
...@@ -180,7 +180,7 @@ ...@@ -180,7 +180,7 @@
%span.badge.badge-pill= @project.users.size %span.badge.badge-pill= @project.users.size
.float-right .float-right
= link_to icon('pencil-square-o', text: 'Manage access'), polymorphic_url([@project, :members]), class: "btn btn-sm" = link_to icon('pencil-square-o', text: 'Manage access'), polymorphic_url([@project, :members]), class: "btn btn-sm"
%ul.well-list.project_members.content-list.members-list %ul.content-list.project_members.members-list
= render partial: 'shared/members/member', collection: @project_members, as: :member, locals: { show_controls: false } = render partial: 'shared/members/member', collection: @project_members, as: :member, locals: { show_controls: false }
.card-footer .card-footer
= paginate @project_members, param_name: 'project_members_page', theme: 'gitlab' = paginate @project_members, param_name: 'project_members_page', theme: 'gitlab'
.card .card
.card-header .card-header
Profile Profile
%ul.well-list %ul.content-list
%li %li
%span.light Member since %span.light Member since
%strong= user.created_at.to_s(:medium) %strong= user.created_at.to_s(:medium)
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
- if @user.groups.any? - if @user.groups.any?
.card .card
.card-header Group projects .card-header Group projects
%ul.card-body-list %ul.hover-list
- @user.group_members.includes(:source).each do |group_member| - @user.group_members.includes(:source).each do |group_member|
- group = group_member.group - group = group_member.group
%li.group_member %li.group_member
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
.col-md-6 .col-md-6
.card .card
.card-header Joined projects (#{@joined_projects.count}) .card-header Joined projects (#{@joined_projects.count})
%ul.card-body-list %ul.hover-list
- @joined_projects.sort_by(&:full_name).each do |project| - @joined_projects.sort_by(&:full_name).each do |project|
- member = project.team.find_member(@user.id) - member = project.team.find_member(@user.id)
%li.project_member %li.project_member
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
.card .card
.card-header .card-header
= @user.name = @user.name
%ul.well-list %ul.content-list
%li %li
= image_tag avatar_icon_for_user(@user, 60), class: "avatar s60" = image_tag avatar_icon_for_user(@user, 60), class: "avatar s60"
%li %li
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
.card .card
.card-header .card-header
Account: Account:
%ul.well-list %ul.content-list
%li %li
%span.light Name: %span.light Name:
%strong= @user.name %strong= @user.name
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
- if event.push_with_commits? - if event.push_with_commits?
.event-body .event-body
%ul.well-list.event_commits %ul.content-list.event_commits
= render "events/commit", project: project, event: event = render "events/commit", project: project, event: event
- create_mr = event.new_ref? && create_mr_button?(project.default_branch, event.ref_name, project) && event.authored_by?(current_user) - create_mr = event.new_ref? && create_mr_button?(project.default_branch, event.ref_name, project) && event.authored_by?(current_user)
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
.controls .controls
= link_to new_project_path(namespace_id: @group.id), class: "btn btn-sm btn-success" do = link_to new_project_path(namespace_id: @group.id), class: "btn btn-sm btn-success" do
New project New project
%ul.well-list %ul.content-list
- @projects.each do |project| - @projects.each do |project|
%li %li
.list-item-name .list-item-name
......
...@@ -8,13 +8,13 @@ ...@@ -8,13 +8,13 @@
= link_to edit_group_runner_path(@group, runner) do = link_to edit_group_runner_path(@group, runner) do
= icon('edit') = icon('edit')
.pull-right .float-right
- if runner.active? - if runner.active?
= link_to _('Pause'), pause_group_runner_path(@group, runner), method: :post, class: 'btn btn-sm btn-danger', data: { confirm: _("Are you sure?") } = link_to _('Pause'), pause_group_runner_path(@group, runner), method: :post, class: 'btn btn-sm btn-danger', data: { confirm: _("Are you sure?") }
- else - else
= link_to _('Resume'), resume_group_runner_path(@group, runner), method: :post, class: 'btn btn-success btn-sm' = link_to _('Resume'), resume_group_runner_path(@group, runner), method: :post, class: 'btn btn-success btn-sm'
= link_to _('Remove Runner'), group_runner_path(@group, runner), data: { confirm: _("Are you sure?") }, method: :delete, class: 'btn btn-danger btn-sm' = link_to _('Remove Runner'), group_runner_path(@group, runner), data: { confirm: _("Are you sure?") }, method: :delete, class: 'btn btn-danger btn-sm'
.pull-right .float-right
%small.light %small.light
\##{runner.id} \##{runner.id}
- if runner.description.present? - if runner.description.present?
......
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
.card .card
.card-header .card-header
Quick help Quick help
%ul.well-list %ul.content-list
%li= link_to 'See our website for getting help', support_url %li= link_to 'See our website for getting help', support_url
%li %li
%button.btn-blank.btn-link.js-trigger-search-bar{ type: 'button' } %button.btn-blank.btn-link.js-trigger-search-bar{ type: 'button' }
......
...@@ -116,9 +116,9 @@ ...@@ -116,9 +116,9 @@
.lead .lead
List with hover effect List with hover effect
%code .well-list %code .hover-list
.example .example
%ul.well-list %ul.hover-list
%li %li
One item One item
%li %li
...@@ -131,7 +131,7 @@ ...@@ -131,7 +131,7 @@
.example .example
.card .card
.card-header Your list .card-header Your list
%ul.well-list %ul.content-list
%li %li
One item One item
%li %li
......
%h5.prepend-top-0 %h5.prepend-top-0
History of authentications History of authentications
%ul.well-list %ul.content-list
- events.each do |event| - events.each do |event|
%li %li
%span.description %span.description
......
- is_current_session = active_session.current?(session) - is_current_session = active_session.current?(session)
%li.list-group-item %li.list-group-item
.pull-left.append-right-10{ data: { toggle: 'tooltip' }, title: active_session.human_device_type } .float-left.append-right-10{ data: { toggle: 'tooltip' }, title: active_session.human_device_type }
= active_session_device_type_icon(active_session) = active_session_device_type_icon(active_session)
.description.pull-left .description.float-left
%div %div
%strong= active_session.ip_address %strong= active_session.ip_address
- if is_current_session - if is_current_session
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
= l(active_session.created_at, format: :short) = l(active_session.created_at, format: :short)
- unless is_current_session - unless is_current_session
.pull-right .float-right
= link_to profile_active_session_path(active_session.session_id), data: { confirm: 'Are you sure? The device will be signed out of GitLab.' }, method: :delete, class: "btn btn-danger prepend-left-10" do = link_to profile_active_session_path(active_session.session_id), data: { confirm: 'Are you sure? The device will be signed out of GitLab.' }, method: :delete, class: "btn btn-danger prepend-left-10" do
%span.sr-only Revoke %span.sr-only Revoke
Revoke Revoke
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
Your Public Email will be displayed on your public profile. Your Public Email will be displayed on your public profile.
%li %li
All email addresses will be used to identify your commits. All email addresses will be used to identify your commits.
%ul.well-list %ul.content-list
%li %li
= render partial: 'shared/email_with_badge', locals: { email: @primary_email, verified: current_user.confirmed? } = render partial: 'shared/email_with_badge', locals: { email: @primary_email, verified: current_user.confirmed? }
%span.float-right %span.float-right
......
- is_admin = local_assigns.fetch(:admin, false) - is_admin = local_assigns.fetch(:admin, false)
- if @gpg_keys.any? - if @gpg_keys.any?
%ul.well-list %ul.content-list
= render partial: 'profiles/gpg_keys/key', collection: @gpg_keys, locals: { is_admin: is_admin } = render partial: 'profiles/gpg_keys/key', collection: @gpg_keys, locals: { is_admin: is_admin }
- else - else
%p.settings-message.text-center %p.settings-message.text-center
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
.card .card
.card-header .card-header
SSH Key SSH Key
%ul.well-list %ul.content-list
%li %li
%span.light Title: %span.light Title:
%strong= @key.title %strong= @key.title
......
- is_admin = local_assigns.fetch(:admin, false) - is_admin = local_assigns.fetch(:admin, false)
- if @keys.any? - if @keys.any?
%ul.well-list %ul.content-list
= render partial: 'profiles/keys/key', collection: @keys, locals: { is_admin: is_admin } = render partial: 'profiles/keys/key', collection: @keys, locals: { is_admin: is_admin }
- else - else
%p.settings-message.text-center %p.settings-message.text-center
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
= s_('Branches|Cant find HEAD commit for this branch') = s_('Branches|Cant find HEAD commit for this branch')
- if branch.name != @repository.root_ref - if branch.name != @repository.root_ref
.divergence-graph.d-none.d-sm-block{ title: s_('%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead') % { number_commits_behind: diverging_count_label(number_commits_behind), .divergence-graph.d-none.d-md-block{ title: s_('%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead') % { number_commits_behind: diverging_count_label(number_commits_behind),
default_branch: @repository.root_ref, default_branch: @repository.root_ref,
number_commits_ahead: diverging_count_label(number_commits_ahead) } } number_commits_ahead: diverging_count_label(number_commits_ahead) } }
.graph-side .graph-side
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
.bar.bar-ahead{ style: "width: #{number_commits_ahead * bar_graph_width_factor}%" } .bar.bar-ahead{ style: "width: #{number_commits_ahead * bar_graph_width_factor}%" }
%span.count.count-ahead= diverging_count_label(number_commits_ahead) %span.count.count-ahead= diverging_count_label(number_commits_ahead)
.controls.d-none.d-sm-block< .controls.d-none.d-md-block<
- if merge_project && create_mr_button?(@repository.root_ref, branch.name) - if merge_project && create_mr_button?(@repository.root_ref, branch.name)
= link_to create_mr_path(@repository.root_ref, branch.name), class: 'btn btn-default' do = link_to create_mr_path(@repository.root_ref, branch.name), class: 'btn btn-default' do
= _('Merge request') = _('Merge request')
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
%span.dropdown-toggle-text %span.dropdown-toggle-text
= _('Select project') = _('Select project')
= icon('chevron-down') = icon('chevron-down')
%span.help-block &nbsp; %span.form-text.text-muted &nbsp;
.form-group .form-group
= provider_gcp_field.label :zone, s_('ClusterIntegration|Zone') = provider_gcp_field.label :zone, s_('ClusterIntegration|Zone')
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
%h5.prepend-top-default %h5.prepend-top-default
Webhooks (#{@hooks.count}) Webhooks (#{@hooks.count})
- if @hooks.any? - if @hooks.any?
%ul.well-list %ul.content-list
- @hooks.each do |hook| - @hooks.each do |hook|
= render 'project_hook', hook: hook = render 'project_hook', hook: hook
- else - else
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
%span= time_ago_with_tooltip @build.artifacts_expire_at %span= time_ago_with_tooltip @build.artifacts_expire_at
- if @build.artifacts? - if @build.artifacts?
.btn-group.btn-group.d-flex{ role: :group } .btn-group.d-flex{ role: :group }
- if @build.has_expiring_artifacts? && can?(current_user, :update_build, @build) - if @build.has_expiring_artifacts? && can?(current_user, :update_build, @build)
= link_to keep_project_job_artifacts_path(@project, @build), class: 'btn btn-sm btn-default', method: :post do = link_to keep_project_job_artifacts_path(@project, @build), class: 'btn btn-sm btn-default', method: :post do
Keep Keep
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
- if @build.trigger_variables.any? - if @build.trigger_variables.any?
%p %p
%button.btn.group.btn-group.js-reveal-variables Reveal Variables %button.btn.group.js-reveal-variables Reveal Variables
%dl.js-build-variables.trigger-build-variables.hide %dl.js-build-variables.trigger-build-variables.hide
- @build.trigger_variables.each do |trigger_variable| - @build.trigger_variables.each do |trigger_variable|
......
...@@ -20,6 +20,8 @@ ...@@ -20,6 +20,8 @@
window.gl = window.gl || {}; window.gl = window.gl || {};
window.gl.mrWidgetData = #{serialize_issuable(@merge_request, serializer: 'widget')} window.gl.mrWidgetData = #{serialize_issuable(@merge_request, serializer: 'widget')}
window.gl.mrWidgetData.squash_before_merge_help_path = '#{help_page_path("user/project/merge_requests/squash_and_merge")}';
#js-vue-mr-widget.mr-widget #js-vue-mr-widget.mr-widget
.content-block.content-block-small.emoji-list-container.js-noteable-awards .content-block.content-block-small.emoji-list-container.js-noteable-awards
......
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
#{h(@remote_mirror.last_error.strip)} #{h(@remote_mirror.last_error.strip)}
= f.fields_for :remote_mirrors, @remote_mirror do |rm_form| = f.fields_for :remote_mirrors, @remote_mirror do |rm_form|
.form-group .form-group
= rm_form.check_box :enabled, class: "pull-left" = rm_form.check_box :enabled, class: "float-left"
.prepend-left-20 .prepend-left-20
= rm_form.label :enabled, "Remote mirror repository", class: "label-light append-bottom-0" = rm_form.label :enabled, "Remote mirror repository", class: "label-light append-bottom-0"
%p.light.append-bottom-0 %p.light.append-bottom-0
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
= render "projects/mirrors/instructions" = render "projects/mirrors/instructions"
.form-group .form-group
= rm_form.check_box :only_protected_branches, class: 'pull-left' = rm_form.check_box :only_protected_branches, class: 'float-left'
.prepend-left-20 .prepend-left-20
= rm_form.label :only_protected_branches, class: 'label-light' = rm_form.label :only_protected_branches, class: 'label-light'
= link_to icon('question-circle'), help_page_path('user/project/protected_branches') = link_to icon('question-circle'), help_page_path('user/project/protected_branches')
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
.card .card
.card-header .card-header
Domains (#{@domains.count}) Domains (#{@domains.count})
%ul.well-list.pages-domain-list{ class: ("has-verification-status" if verification_enabled) } %ul.content-list.pages-domain-list{ class: ("has-verification-status" if verification_enabled) }
- @domains.each do |domain| - @domains.each do |domain|
%li.pages-domain-list-item.unstyled %li.pages-domain-list-item.unstyled
- if verification_enabled - if verification_enabled
......
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
.form-actions .form-actions
= f.submit s_('Pipeline|Create pipeline'), class: 'btn btn-success js-variables-save-button', tabindex: 3 = f.submit s_('Pipeline|Create pipeline'), class: 'btn btn-success js-variables-save-button', tabindex: 3
= link_to 'Cancel', project_pipelines_path(@project), class: 'btn btn-default pull-right' = link_to 'Cancel', project_pipelines_path(@project), class: 'btn btn-default float-right'
-# haml-lint:disable InlineJavaScript -# haml-lint:disable InlineJavaScript
%script#availableRefs{ type: "application/json" }= @project.repository.ref_names.to_json.html_safe %script#availableRefs{ type: "application/json" }= @project.repository.ref_names.to_json.html_safe
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
= form.label :domain, class:"prepend-top-10" do = form.label :domain, class:"prepend-top-10" do
= _('Domain') = _('Domain')
= form.text_field :domain, class: 'form-control', placeholder: 'domain.com' = form.text_field :domain, class: 'form-control', placeholder: 'domain.com'
.help-block .form-text.text-muted
= s_('CICD|A domain is required to use Auto Review Apps and Auto Deploy Stages.') = s_('CICD|A domain is required to use Auto Review Apps and Auto Deploy Stages.')
- if cluster_ingress_ip = cluster_ingress_ip(@project) - if cluster_ingress_ip = cluster_ingress_ip(@project)
= s_('%{nip_domain} can be used as an alternative to a custom domain.').html_safe % { nip_domain: "<code>#{cluster_ingress_ip}.nip.io</code>".html_safe } = s_('%{nip_domain} can be used as an alternative to a custom domain.').html_safe % { nip_domain: "<code>#{cluster_ingress_ip}.nip.io</code>".html_safe }
......
- css_classes = %w(label label-verification-status) - css_classes = %w(badge badge-verification-status)
- css_classes << (verified ? 'verified': 'unverified') - css_classes << (verified ? 'verified': 'unverified')
- text = verified ? 'Verified' : 'Unverified' - text = verified ? 'Verified' : 'Unverified'
......
- with_label = local_assigns.fetch(:with_label, true) - with_label = local_assigns.fetch(:with_label, true)
.form-group.visibility-level-setting .form-group.row.visibility-level-setting
- if with_label - if with_label
= f.label :visibility_level, class: 'col-form-label col-sm-2' do = f.label :visibility_level, class: 'col-form-label col-sm-2' do
Visibility Level Visibility Level
......
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
= link_to _('Edit'), '#', class: 'js-sidebar-dropdown-toggle edit-link float-right' = link_to _('Edit'), '#', class: 'js-sidebar-dropdown-toggle edit-link float-right'
.value.hide-collapsed .value.hide-collapsed
- if issuable.milestone - if issuable.milestone
= link_to issuable.milestone.title, milestone_path(issuable.milestone), class: "bold has-tooltip", title: milestone_tooltip_due_date(issuable.milestone), data: { container: "body", html: 'true' } = link_to issuable.milestone.title, milestone_path(issuable.milestone), class: "bold has-tooltip", title: milestone_tooltip_due_date(issuable.milestone), data: { container: "body", html: 'true', boundary: 'viewport' }
- else - else
%span.no-value %span.no-value
= _('None') = _('None')
......
...@@ -15,3 +15,12 @@ ...@@ -15,3 +15,12 @@
= hidden_field_tag 'merge_request[force_remove_source_branch]', '0', id: nil = hidden_field_tag 'merge_request[force_remove_source_branch]', '0', id: nil
= check_box_tag 'merge_request[force_remove_source_branch]', '1', issuable.force_remove_source_branch? = check_box_tag 'merge_request[force_remove_source_branch]', '1', issuable.force_remove_source_branch?
Remove source branch when merge request is accepted. Remove source branch when merge request is accepted.
.form-group
.col-sm-10.col-sm-offset-2
.checkbox
= label_tag 'merge_request[squash]' do
= hidden_field_tag 'merge_request[squash]', '0', id: nil
= check_box_tag 'merge_request[squash]', '1', issuable.squash
Squash commits when merge request is accepted.
= link_to 'About this feature', help_page_path('user/project/merge_requests/squash_and_merge')
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
= number_with_delimiter(issuables.length) = number_with_delimiter(issuables.length)
- class_prefix = dom_class(issuables).pluralize - class_prefix = dom_class(issuables).pluralize
%ul{ class: "well-list milestone-#{class_prefix}-list", id: "#{class_prefix}-list-#{id}" } %ul{ class: "content-list milestone-#{class_prefix}-list", id: "#{class_prefix}-list-#{id}" }
= render partial: 'shared/milestones/issuable', = render partial: 'shared/milestones/issuable',
collection: issuables, collection: issuables,
as: :issuable, as: :issuable,
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
= link_to user_snippets_path(snippet.author) do = link_to user_snippets_path(snippet.author) do
= snippet.author_name = snippet.author_name
- if link_project && snippet.project_id? - if link_project && snippet.project_id?
%span.d-none.d-sm-block %span.d-none.d-sm-inline-block
in in
= link_to project_path(snippet.project) do = link_to project_path(snippet.project) do
= snippet.project.full_name = snippet.project.full_name
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
.card-header .card-header
%strong %strong
= t('sherlock.application_backtrace') = t('sherlock.application_backtrace')
%ul.well-list %ul.content-list
- @query.application_backtrace.each do |location| - @query.application_backtrace.each do |location|
%li %li
%strong %strong
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
.card-header .card-header
%strong %strong
= t('sherlock.full_backtrace') = t('sherlock.full_backtrace')
%ul.well-list %ul.content-list
- @query.backtrace.each do |location| - @query.backtrace.each do |location|
%li %li
- if location.application? - if location.application?
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
.card-header .card-header
%strong %strong
= t('sherlock.general') = t('sherlock.general')
%ul.well-list %ul.content-list
%li %li
%span.light %span.light
#{t('sherlock.time')}: #{t('sherlock.time')}:
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
= @query.formatted_query = @query.formatted_query
%strong %strong
= t('sherlock.query') = t('sherlock.query')
%ul.well-list %ul.content-list
%li %li
.code.js-syntax-highlight.sherlock-code .code.js-syntax-highlight.sherlock-code
:preserve :preserve
...@@ -47,7 +47,7 @@ ...@@ -47,7 +47,7 @@
= @query.explain = @query.explain
%strong %strong
= t('sherlock.query_plan') = t('sherlock.query_plan')
%ul.well-list %ul.content-list
%li %li
.code.js-syntax-highlight.sherlock-code .code.js-syntax-highlight.sherlock-code
%pre %pre
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
.card-header .card-header
%strong %strong
= t('sherlock.general') = t('sherlock.general')
%ul.well-list %ul.content-list
%li %li
%span.light %span.light
#{t('sherlock.id')}: #{t('sherlock.id')}:
......
...@@ -4,10 +4,10 @@ ...@@ -4,10 +4,10 @@
= markdown_field(@term, :terms) = markdown_field(@term, :terms)
.row-content-block.footer-block.clearfix .row-content-block.footer-block.clearfix
- if can?(current_user, :accept_terms, @term) - if can?(current_user, :accept_terms, @term)
.pull-right .float-right
= button_to accept_term_path(@term, redirect_params), class: 'btn btn-success prepend-left-8' do = button_to accept_term_path(@term, redirect_params), class: 'btn btn-success prepend-left-8' do
= _('Accept terms') = _('Accept terms')
- if can?(current_user, :decline_terms, @term) - if can?(current_user, :decline_terms, @term)
.pull-right .float-right
= button_to decline_term_path(@term, redirect_params), class: 'btn btn-default prepend-left-8' do = button_to decline_term_path(@term, redirect_params), class: 'btn btn-default prepend-left-8' do
= _('Decline and sign out') = _('Decline and sign out')
---
title: Update awesome_print to 1.8.0
merge_request: 19163
author: Takuya Noguchi
type: other
---
title: Update rdoc to 6.0.4
merge_request: 19167
author: Takuya Noguchi
type: other
---
title: Throttle updates to Project#last_repository_updated_at.
merge_request: 19183
author:
type: performance
---
title: Add `Squash and merge` to GitLab Core (CE)
merge_request: 18956
author: "@blackst0ne"
type: added
---
title: Fix API to remove deploy key from project instead of deleting it entirely
merge_request:
author:
type: security
---
title: Fixed bug that allowed importing arbitrary project attributes
merge_request:
author:
type: security
---
title: Prevent user passwords from being changed without providing the previous password
merge_request:
author:
type: security
---
title: Fix project destruction failing due to idle in transaction timeouts
merge_request:
author:
type: fixed
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddSquashToMergeRequests < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
DOWNTIME = false
def up
unless column_exists?(:merge_requests, :squash)
add_column_with_default :merge_requests, :squash, :boolean, default: false, allow_null: false
end
end
def down
remove_column :merge_requests, :squash if column_exists?(:merge_requests, :squash)
end
end
...@@ -1217,6 +1217,7 @@ ActiveRecord::Schema.define(version: 20180529093006) do ...@@ -1217,6 +1217,7 @@ ActiveRecord::Schema.define(version: 20180529093006) do
t.integer "latest_merge_request_diff_id" t.integer "latest_merge_request_diff_id"
t.string "rebase_commit_sha" t.string "rebase_commit_sha"
t.boolean "allow_maintainer_to_push" t.boolean "allow_maintainer_to_push"
t.boolean "squash", default: false, null: false
end end
add_index "merge_requests", ["assignee_id"], name: "index_merge_requests_on_assignee_id", using: :btree add_index "merge_requests", ["assignee_id"], name: "index_merge_requests_on_assignee_id", using: :btree
......
...@@ -107,6 +107,7 @@ Parameters: ...@@ -107,6 +107,7 @@ Parameters:
"changes_count": "1", "changes_count": "1",
"should_remove_source_branch": true, "should_remove_source_branch": true,
"force_remove_source_branch": false, "force_remove_source_branch": false,
"squash": false,
"web_url": "http://example.com/example/example/merge_requests/1", "web_url": "http://example.com/example/example/merge_requests/1",
"time_stats": { "time_stats": {
"time_estimate": 0, "time_estimate": 0,
...@@ -226,6 +227,7 @@ Parameters: ...@@ -226,6 +227,7 @@ Parameters:
"changes_count": "1", "changes_count": "1",
"should_remove_source_branch": true, "should_remove_source_branch": true,
"force_remove_source_branch": false, "force_remove_source_branch": false,
"squash": false,
"web_url": "http://example.com/example/example/merge_requests/1", "web_url": "http://example.com/example/example/merge_requests/1",
"discussion_locked": false, "discussion_locked": false,
"time_stats": { "time_stats": {
...@@ -305,6 +307,7 @@ Parameters: ...@@ -305,6 +307,7 @@ Parameters:
"changes_count": "1", "changes_count": "1",
"should_remove_source_branch": true, "should_remove_source_branch": true,
"force_remove_source_branch": false, "force_remove_source_branch": false,
"squash": false,
"web_url": "http://example.com/example/example/merge_requests/1", "web_url": "http://example.com/example/example/merge_requests/1",
"discussion_locked": false, "discussion_locked": false,
"time_stats": { "time_stats": {
...@@ -541,7 +544,8 @@ POST /projects/:id/merge_requests ...@@ -541,7 +544,8 @@ POST /projects/:id/merge_requests
| `labels` | string | no | Labels for MR as a comma-separated list | | `labels` | string | no | Labels for MR as a comma-separated list |
| `milestone_id` | integer | no | The global ID of a milestone | | `milestone_id` | integer | no | The global ID of a milestone |
| `remove_source_branch` | boolean | no | Flag indicating if a merge request should remove the source branch when merging | | `remove_source_branch` | boolean | no | Flag indicating if a merge request should remove the source branch when merging |
| `allow_maintainer_to_push` | boolean | no | Whether or not a maintainer of the target project can push to the source branch | | `allow_maintainer_to_push` | boolean | no | Whether or not a maintainer of the target project can push to the source branch |
| `squash` | boolean | no | Squash commits into a single commit when merging |
```json ```json
{ {
...@@ -595,6 +599,7 @@ POST /projects/:id/merge_requests ...@@ -595,6 +599,7 @@ POST /projects/:id/merge_requests
"changes_count": "1", "changes_count": "1",
"should_remove_source_branch": true, "should_remove_source_branch": true,
"force_remove_source_branch": false, "force_remove_source_branch": false,
"squash": false,
"web_url": "http://example.com/example/example/merge_requests/1", "web_url": "http://example.com/example/example/merge_requests/1",
"discussion_locked": false, "discussion_locked": false,
"allow_maintainer_to_push": false, "allow_maintainer_to_push": false,
...@@ -627,6 +632,7 @@ PUT /projects/:id/merge_requests/:merge_request_iid ...@@ -627,6 +632,7 @@ PUT /projects/:id/merge_requests/:merge_request_iid
| `description` | string | no | Description of MR | | `description` | string | no | Description of MR |
| `state_event` | string | no | New state (close/reopen) | | `state_event` | string | no | New state (close/reopen) |
| `remove_source_branch` | boolean | no | Flag indicating if a merge request should remove the source branch when merging | | `remove_source_branch` | boolean | no | Flag indicating if a merge request should remove the source branch when merging |
| `squash` | boolean | no | Squash commits into a single commit when merging |
| `discussion_locked` | boolean | no | Flag indicating if the merge request's discussion is locked. If the discussion is locked only project members can add, edit or resolve comments. | | `discussion_locked` | boolean | no | Flag indicating if the merge request's discussion is locked. If the discussion is locked only project members can add, edit or resolve comments. |
| `allow_maintainer_to_push` | boolean | no | Whether or not a maintainer of the target project can push to the source branch | | `allow_maintainer_to_push` | boolean | no | Whether or not a maintainer of the target project can push to the source branch |
...@@ -683,6 +689,7 @@ Must include at least one non-required attribute from above. ...@@ -683,6 +689,7 @@ Must include at least one non-required attribute from above.
"changes_count": "1", "changes_count": "1",
"should_remove_source_branch": true, "should_remove_source_branch": true,
"force_remove_source_branch": false, "force_remove_source_branch": false,
"squash": false,
"web_url": "http://example.com/example/example/merge_requests/1", "web_url": "http://example.com/example/example/merge_requests/1",
"discussion_locked": false, "discussion_locked": false,
"allow_maintainer_to_push": false, "allow_maintainer_to_push": false,
...@@ -790,6 +797,7 @@ Parameters: ...@@ -790,6 +797,7 @@ Parameters:
"changes_count": "1", "changes_count": "1",
"should_remove_source_branch": true, "should_remove_source_branch": true,
"force_remove_source_branch": false, "force_remove_source_branch": false,
"squash": false,
"web_url": "http://example.com/example/example/merge_requests/1", "web_url": "http://example.com/example/example/merge_requests/1",
"discussion_locked": false, "discussion_locked": false,
"time_stats": { "time_stats": {
...@@ -868,6 +876,7 @@ Parameters: ...@@ -868,6 +876,7 @@ Parameters:
"changes_count": "1", "changes_count": "1",
"should_remove_source_branch": true, "should_remove_source_branch": true,
"force_remove_source_branch": false, "force_remove_source_branch": false,
"squash": false,
"web_url": "http://example.com/example/example/merge_requests/1", "web_url": "http://example.com/example/example/merge_requests/1",
"discussion_locked": false, "discussion_locked": false,
"time_stats": { "time_stats": {
...@@ -1200,6 +1209,7 @@ Example response: ...@@ -1200,6 +1209,7 @@ Example response:
"changes_count": "1", "changes_count": "1",
"should_remove_source_branch": true, "should_remove_source_branch": true,
"force_remove_source_branch": false, "force_remove_source_branch": false,
"squash": false,
"web_url": "http://example.com/example/example/merge_requests/1" "web_url": "http://example.com/example/example/merge_requests/1"
}, },
"target_url": "https://gitlab.example.com/gitlab-org/gitlab-ci/merge_requests/7", "target_url": "https://gitlab.example.com/gitlab-org/gitlab-ci/merge_requests/7",
......
...@@ -55,6 +55,7 @@ Example response: ...@@ -55,6 +55,7 @@ Example response:
"ed25519_key_restriction": 0, "ed25519_key_restriction": 0,
"enforce_terms": true, "enforce_terms": true,
"terms": "Hello world!", "terms": "Hello world!",
"performance_bar_allowed_group_id": 42
} }
``` ```
...@@ -120,8 +121,9 @@ PUT /application/settings ...@@ -120,8 +121,9 @@ PUT /application/settings
| `metrics_timeout` | integer | yes (if `metrics_enabled` is `true`) | The amount of seconds after which InfluxDB will time out. | | `metrics_timeout` | integer | yes (if `metrics_enabled` is `true`) | The amount of seconds after which InfluxDB will time out. |
| `password_authentication_enabled_for_web` | boolean | no | Enable authentication for the web interface via a GitLab account password. Default is `true`. | | `password_authentication_enabled_for_web` | boolean | no | Enable authentication for the web interface via a GitLab account password. Default is `true`. |
| `password_authentication_enabled_for_git` | boolean | no | Enable authentication for Git over HTTP(S) via a GitLab account password. Default is `true`. | | `password_authentication_enabled_for_git` | boolean | no | Enable authentication for Git over HTTP(S) via a GitLab account password. Default is `true`. |
| `performance_bar_allowed_group_id` | string | no | The group that is allowed to enable the performance bar | | `performance_bar_allowed_group_path` | string | no | Path of the group that is allowed to toggle the performance bar |
| `performance_bar_enabled` | boolean | no | Allow enabling the performance bar | | `performance_bar_allowed_group_id` | string | no | Deprecated: Use `performance_bar_allowed_group_path` instead. Path of the group that is allowed to toggle the performance bar |
| `performance_bar_enabled` | boolean | no | Deprecated: Pass `performance_bar_allowed_group_path: nil` instead. Allow enabling the performance bar |
| `plantuml_enabled` | boolean | no | Enable PlantUML integration. Default is `false`. | | `plantuml_enabled` | boolean | no | Enable PlantUML integration. Default is `false`. |
| `plantuml_url` | string | yes (if `plantuml_enabled` is `true`) | The PlantUML instance URL for integration. | | `plantuml_url` | string | yes (if `plantuml_enabled` is `true`) | The PlantUML instance URL for integration. |
| `polling_interval_multiplier` | decimal | no | Interval multiplier used by endpoints that perform polling. Set to 0 to disable polling. | | `polling_interval_multiplier` | decimal | no | Interval multiplier used by endpoints that perform polling. Set to 0 to disable polling. |
...@@ -201,5 +203,6 @@ Example response: ...@@ -201,5 +203,6 @@ Example response:
"ed25519_key_restriction": 0, "ed25519_key_restriction": 0,
"enforce_terms": true, "enforce_terms": true,
"terms": "Hello world!", "terms": "Hello world!",
"performance_bar_allowed_group_id": 42
} }
``` ```
...@@ -19,7 +19,7 @@ Here's some info we've gathered to get you started. ...@@ -19,7 +19,7 @@ Here's some info we've gathered to get you started.
The first steps towards your GitLab CI/CD journey. The first steps towards your GitLab CI/CD journey.
- [Getting started with GitLab CI/CD](quick_start/README.md): understand how GitLab CI/CD works. - [Getting started with GitLab CI/CD](quick_start/README.md): understand how GitLab CI/CD works.
- GitLab CI/CD configuration file: [`.gitlab-ci.yml`](yaml/README.md) - Learn all about the ins and outs of `.gitlab-ci.yml`. - [GitLab CI/CD configuration file: `.gitlab-ci.yml`](yaml/README.md) - Learn all about the ins and outs of `.gitlab-ci.yml`.
- [Pipelines and jobs](pipelines.md): configure your GitLab CI/CD pipelines to build, test, and deploy your application. - [Pipelines and jobs](pipelines.md): configure your GitLab CI/CD pipelines to build, test, and deploy your application.
- Runners: The [GitLab Runner](https://docs.gitlab.com/runner/) is responsible by running the jobs in your CI/CD pipeline. On GitLab.com, Shared Runners are enabled by default, so - Runners: The [GitLab Runner](https://docs.gitlab.com/runner/) is responsible by running the jobs in your CI/CD pipeline. On GitLab.com, Shared Runners are enabled by default, so
you don't need to set up anything to start to use them with GitLab CI/CD. you don't need to set up anything to start to use them with GitLab CI/CD.
...@@ -46,7 +46,9 @@ you don't need to set up anything to start to use them with GitLab CI/CD. ...@@ -46,7 +46,9 @@ you don't need to set up anything to start to use them with GitLab CI/CD.
## Exploring GitLab CI/CD ## Exploring GitLab CI/CD
- [CI/CD Variables](variables/README.md) - Learn how to use variables defined in - [CI/CD Variables](variables/README.md) - Learn how to use variables defined in
your `.gitlab-ci.yml` or secured ones defined in your project's settings your `.gitlab-ci.yml` or the ones defined in your project's settings
- [Where variables can be used](variables/where_variables_can_be_used.md) - A
deeper look on where and how the CI/CD variables can be used
- **The permissions model** - Learn about the access levels a user can have for - **The permissions model** - Learn about the access levels a user can have for
performing certain CI actions performing certain CI actions
- [User permissions](../user/permissions.md#gitlab-ci) - [User permissions](../user/permissions.md#gitlab-ci)
......
...@@ -246,23 +246,14 @@ As the name suggests, it is possible to create environments on the fly by just ...@@ -246,23 +246,14 @@ As the name suggests, it is possible to create environments on the fly by just
declaring their names dynamically in `.gitlab-ci.yml`. Dynamic environments is declaring their names dynamically in `.gitlab-ci.yml`. Dynamic environments is
the basis of [Review apps](review_apps/index.md). the basis of [Review apps](review_apps/index.md).
>**Note:** NOTE: **Note:**
The `name` and `url` parameters can use most of the defined CI variables, The `name` and `url` parameters can use most of the CI/CD variables,
including predefined, secure variables and `.gitlab-ci.yml` including [predefined](variables/README.md#predefined-variables-environment-variables),
[`variables`](yaml/README.md#variables). You however cannot use variables [secret](variables/README.md#secret-variables) and
defined under `script` or on the Runner's side. There are other variables that [`.gitlab-ci.yml` variables](yaml/README.md#variables). You however cannot use variables
are unsupported in environment name context: defined under `script` or on the Runner's side. There are also other variables that
- `CI_PIPELINE_ID` are unsupported in the context of `environment:name`. You can read more about
- `CI_JOB_ID` [where variables can be used](variables/where_variables_can_be_used.md).
- `CI_JOB_TOKEN`
- `CI_BUILD_ID`
- `CI_BUILD_TOKEN`
- `CI_REGISTRY_USER`
- `CI_REGISTRY_PASSWORD`
- `CI_REPOSITORY_URL`
- `CI_ENVIRONMENT_URL`
- `CI_DEPLOY_USER`
- `CI_DEPLOY_PASSWORD`
GitLab Runner exposes various [environment variables][variables] when a job runs, GitLab Runner exposes various [environment variables][variables] when a job runs,
and as such, you can use them as environment names. Let's add another job in and as such, you can use them as environment names. Let's add another job in
......
...@@ -22,6 +22,12 @@ For example, if you define `API_TOKEN=secure` as a secret variable and ...@@ -22,6 +22,12 @@ For example, if you define `API_TOKEN=secure` as a secret variable and
`API_TOKEN=yaml` in your `.gitlab-ci.yml`, the `API_TOKEN` will take the value `API_TOKEN=yaml` in your `.gitlab-ci.yml`, the `API_TOKEN` will take the value
`secure` as the secret variables are higher in the chain. `secure` as the secret variables are higher in the chain.
## Unsupported variables
There are cases where some variables cannot be used in the context of a
`.gitlab-ci.yml` definition (for example under `script`). Read more
about which variables are [not supported](where_variables_can_be_used.md).
## Predefined variables (Environment variables) ## Predefined variables (Environment variables)
Some of the predefined environment variables are available only if a minimum Some of the predefined environment variables are available only if a minimum
...@@ -36,6 +42,7 @@ future GitLab releases.** ...@@ -36,6 +42,7 @@ future GitLab releases.**
| Variable | GitLab | Runner | Description | | Variable | GitLab | Runner | Description |
|-------------------------------- |--------|--------|-------------| |-------------------------------- |--------|--------|-------------|
| **ARTIFACT_DOWNLOAD_ATTEMPTS** | 8.15 | 1.9 | Number of attempts to download artifacts running a job |
| **CI** | all | 0.4 | Mark that job is executed in CI environment | | **CI** | all | 0.4 | Mark that job is executed in CI environment |
| **CI_COMMIT_REF_NAME** | 9.0 | all | The branch or tag name for which project is built | | **CI_COMMIT_REF_NAME** | 9.0 | all | The branch or tag name for which project is built |
| **CI_COMMIT_REF_SLUG** | 9.0 | all | `$CI_COMMIT_REF_NAME` lowercased, shortened to 63 bytes, and with everything except `0-9` and `a-z` replaced with `-`. No leading / trailing `-`. Use in URLs, host names and domain names. | | **CI_COMMIT_REF_SLUG** | 9.0 | all | `$CI_COMMIT_REF_NAME` lowercased, shortened to 63 bytes, and with everything except `0-9` and `a-z` replaced with `-`. No leading / trailing `-`. Use in URLs, host names and domain names. |
...@@ -46,6 +53,8 @@ future GitLab releases.** ...@@ -46,6 +53,8 @@ future GitLab releases.**
| **CI_COMMIT_DESCRIPTION** | 10.8 | all | The description of the commit: the message without first line, if the title is shorter than 100 characters; full message in other case. | | **CI_COMMIT_DESCRIPTION** | 10.8 | all | The description of the commit: the message without first line, if the title is shorter than 100 characters; full message in other case. |
| **CI_CONFIG_PATH** | 9.4 | 0.5 | The path to CI config file. Defaults to `.gitlab-ci.yml` | | **CI_CONFIG_PATH** | 9.4 | 0.5 | The path to CI config file. Defaults to `.gitlab-ci.yml` |
| **CI_DEBUG_TRACE** | all | 1.7 | Whether [debug tracing](#debug-tracing) is enabled | | **CI_DEBUG_TRACE** | all | 1.7 | Whether [debug tracing](#debug-tracing) is enabled |
| **CI_DEPLOY_USER** | 10.8 | all | Authentication username of the [GitLab Deploy Token][gitlab-deploy-token], only present if the Project has one related.|
| **CI_DEPLOY_PASSWORD** | 10.8 | all | Authentication password of the [GitLab Deploy Token][gitlab-deploy-token], only present if the Project has one related.|
| **CI_DISPOSABLE_ENVIRONMENT** | all | 10.1 | Marks that the job is executed in a disposable environment (something that is created only for this job and disposed of/destroyed after the execution - all executors except `shell` and `ssh`). If the environment is disposable, it is set to true, otherwise it is not defined at all. | | **CI_DISPOSABLE_ENVIRONMENT** | all | 10.1 | Marks that the job is executed in a disposable environment (something that is created only for this job and disposed of/destroyed after the execution - all executors except `shell` and `ssh`). If the environment is disposable, it is set to true, otherwise it is not defined at all. |
| **CI_ENVIRONMENT_NAME** | 8.15 | all | The name of the environment for this job | | **CI_ENVIRONMENT_NAME** | 8.15 | all | The name of the environment for this job |
| **CI_ENVIRONMENT_SLUG** | 8.15 | all | A simplified version of the environment name, suitable for inclusion in DNS, URLs, Kubernetes labels, etc. | | **CI_ENVIRONMENT_SLUG** | 8.15 | all | A simplified version of the environment name, suitable for inclusion in DNS, URLs, Kubernetes labels, etc. |
...@@ -82,16 +91,13 @@ future GitLab releases.** ...@@ -82,16 +91,13 @@ future GitLab releases.**
| **CI_SERVER_REVISION** | all | all | GitLab revision that is used to schedule jobs | | **CI_SERVER_REVISION** | all | all | GitLab revision that is used to schedule jobs |
| **CI_SERVER_VERSION** | all | all | GitLab version that is used to schedule jobs | | **CI_SERVER_VERSION** | all | all | GitLab version that is used to schedule jobs |
| **CI_SHARED_ENVIRONMENT** | all | 10.1 | Marks that the job is executed in a shared environment (something that is persisted across CI invocations like `shell` or `ssh` executor). If the environment is shared, it is set to true, otherwise it is not defined at all. | | **CI_SHARED_ENVIRONMENT** | all | 10.1 | Marks that the job is executed in a shared environment (something that is persisted across CI invocations like `shell` or `ssh` executor). If the environment is shared, it is set to true, otherwise it is not defined at all. |
| **ARTIFACT_DOWNLOAD_ATTEMPTS** | 8.15 | 1.9 | Number of attempts to download artifacts running a job |
| **GET_SOURCES_ATTEMPTS** | 8.15 | 1.9 | Number of attempts to fetch sources running a job | | **GET_SOURCES_ATTEMPTS** | 8.15 | 1.9 | Number of attempts to fetch sources running a job |
| **GITLAB_CI** | all | all | Mark that job is executed in GitLab CI environment | | **GITLAB_CI** | all | all | Mark that job is executed in GitLab CI environment |
| **GITLAB_USER_ID** | 8.12 | all | The id of the user who started the job |
| **GITLAB_USER_EMAIL** | 8.12 | all | The email of the user who started the job | | **GITLAB_USER_EMAIL** | 8.12 | all | The email of the user who started the job |
| **GITLAB_USER_ID** | 8.12 | all | The id of the user who started the job |
| **GITLAB_USER_LOGIN** | 10.0 | all | The login username of the user who started the job | | **GITLAB_USER_LOGIN** | 10.0 | all | The login username of the user who started the job |
| **GITLAB_USER_NAME** | 10.0 | all | The real name of the user who started the job | | **GITLAB_USER_NAME** | 10.0 | all | The real name of the user who started the job |
| **RESTORE_CACHE_ATTEMPTS** | 8.15 | 1.9 | Number of attempts to restore the cache running a job | | **RESTORE_CACHE_ATTEMPTS** | 8.15 | 1.9 | Number of attempts to restore the cache running a job |
| **CI_DEPLOY_USER** | 10.8 | all | Authentication username of the [GitLab Deploy Token][gitlab-deploy-token], only present if the Project has one related.|
| **CI_DEPLOY_PASSWORD** | 10.8 | all | Authentication password of the [GitLab Deploy Token][gitlab-deploy-token], only present if the Project has one related.|
## 9.0 Renaming ## 9.0 Renaming
...@@ -540,34 +546,6 @@ Below you can find supported syntax reference: ...@@ -540,34 +546,6 @@ Below you can find supported syntax reference:
Pattern matching is case-sensitive by default. Use `i` flag modifier, like Pattern matching is case-sensitive by default. Use `i` flag modifier, like
`/pattern/i` to make a pattern case-insensitive. `/pattern/i` to make a pattern case-insensitive.
### Unsupported predefined variables
Because GitLab evaluates variables before creating jobs, we do not support a
few variables that depend on persistence layer, like `$CI_JOB_ID`.
Environments (like `production` or `staging`) are also being created based on
what jobs pipeline consists of, thus some environment-specific variables are
not supported as well.
We do not support variables containing tokens because of security reasons.
You can find a full list of unsupported variables below:
- `CI_PIPELINE_ID`
- `CI_JOB_ID`
- `CI_JOB_TOKEN`
- `CI_BUILD_ID`
- `CI_BUILD_TOKEN`
- `CI_REGISTRY_USER`
- `CI_REGISTRY_PASSWORD`
- `CI_REPOSITORY_URL`
- `CI_ENVIRONMENT_URL`
- `CI_DEPLOY_USER`
- `CI_DEPLOY_PASSWORD`
These variables are also not supported in a context of a
[dynamic environment name][dynamic-environments].
[ce-13784]: https://gitlab.com/gitlab-org/gitlab-ce/issues/13784 "Simple protection of CI secret variables" [ce-13784]: https://gitlab.com/gitlab-org/gitlab-ce/issues/13784 "Simple protection of CI secret variables"
[eep]: https://about.gitlab.com/products/ "Available only in GitLab Premium" [eep]: https://about.gitlab.com/products/ "Available only in GitLab Premium"
[envs]: ../environments.md [envs]: ../environments.md
...@@ -579,5 +557,4 @@ These variables are also not supported in a context of a ...@@ -579,5 +557,4 @@ These variables are also not supported in a context of a
[triggers]: ../triggers/README.md#pass-job-variables-to-a-trigger [triggers]: ../triggers/README.md#pass-job-variables-to-a-trigger
[subgroups]: ../../user/group/subgroups/index.md [subgroups]: ../../user/group/subgroups/index.md
[builds-policies]: ../yaml/README.md#only-and-except-complex [builds-policies]: ../yaml/README.md#only-and-except-complex
[dynamic-environments]: ../environments.md#dynamic-environments
[gitlab-deploy-token]: ../../user/project/deploy_tokens/index.md#gitlab-deploy-token [gitlab-deploy-token]: ../../user/project/deploy_tokens/index.md#gitlab-deploy-token
# Where variables can be used
As it's described in the [CI/CD variables](README.md) docs, you can
define many different variables. Some of them can be used for all GitLab CI/CD
features, but some of them are more or less limited.
This document describes where and how the different types of variables can be used.
## Variables usage
There are basically two places where you can use any defined variables:
1. On GitLab's side there's `.gitlab-ci.yml`
1. On the Runner's side there's `config.toml`
### `.gitlab-ci.yml` file
| Definition | Can be expanded? | Expansion place | Description |
|--------------------------------------|-------------------|-----------------|--------------|
| `environment:url` | yes | GitLab | The variable expansion is made by GitLab's [internal variable expansion mechanism](#gitlab-internal-variable-expansion-mechanism).<ul><li>**Supported:** all variables defined for a job (secret variables, variables from `.gitlab-ci.yml`, variables from triggers, variables from pipeline schedules)</li><li>**Not suported:** variables defined in Runner's `config.toml` and variables created in job's `script`</li></ul> |
| `environment:name` | yes | GitLab | Similar to `environment:url`, but the variables expansion **doesn't support**: <ul><li>variables that are based on the environment's name (`CI_ENVIRONMENT_NAME`, `CI_ENVIRONMENT_SLUG`)</li><li>any other variables related to environment (currently only `CI_ENVIRONMENT_URL`)</li><li>[persisted variables](#persisted-variables)</li></ul> |
| `variables` | yes | Runner | The variable expansion is made by GitLab Runner's [internal variable expansion mechanism](#gitlab-runner-internal-variable-expansion-mechanism) |
| `image` | yes | Runner | The variable expansion is made by GitLab Runner's [internal variable expansion mechanism](#gitlab-runner-internal-variable-expansion-mechanism) |
| `services:[]` | yes | Runner | The variable expansion is made by GitLab Runner's [internal variable expansion mechanism](#gitlab-runner-internal-variable-expansion-mechanism) |
| `services:[]:name` | yes | Runner | The variable expansion is made by GitLab Runner's [internal variable expansion mechanism](#gitlab-runner-internal-variable-expansion-mechanism) |
| `cache:key` | yes | Runner | The variable expansion is made by GitLab Runner's [internal variable expansion mechanism](#gitlab-runner-internal-variable-expansion-mechanism) |
| `artifacts:name` | yes | Runner | The variable expansion is made by GitLab Runner's shell environment |
| `script`, `before_script`, `after_script` | yes | Script execution shell | The variable expansion is made by the [execution shell environment](#execution-shell-environment) |
| `only:variables:[]`, `except:variables:[]` | no | n/a | The variable must be in the form of `$variable`.<br/>**Not supported:**<ul><li>variables that are based on the environment's name (`CI_ENVIRONMENT_NAME`, `CI_ENVIRONMENT_SLUG`)</li><li>any other variables related to environment (currently only `CI_ENVIRONMENT_URL`)</li><li>[persisted variables](#persisted-variables)</li></ul> |
### `config.toml` file
NOTE: **Note:**
You can read more about `config.toml` in the [Runner's docs](https://docs.gitlab.com/runner/configuration/advanced-configuration.html).
| Definition | Can be expanded? | Description |
|--------------------------------------|------------------|-------------|
| `runners.environment` | yes | The variable expansion is made by the Runner's [internal variable expansion mechanism](#gitlab-runner-internal-variable-expansion-mechanism) |
| `runners.kubernetes.pod_labels` | yes | The Variable expansion is made by the Runner's [internal variable expansion mechanism](#gitlab-runner-internal-variable-expansion-mechanism) |
| `runners.kubernetes.pod_annotations` | yes | The Variable expansion is made by the Runner's [internal variable expansion mechanism](#gitlab-runner-internal-variable-expansion-mechanism) |
## Expansion mechanisms
There are three expansion mechanisms:
- GitLab
- GitLab Runner
- Execution shell environment
### GitLab internal variable expansion mechanism
The expanded part needs to be in a form of `$variable`, or `${variable}` or `%variable%`.
Each form is handled in the same way, no matter which OS/shell will finally handle the job,
since the expansion is done in GitLab before any Runner will get the job.
### GitLab Runner internal variable expansion mechanism
- **Supported:** secret variables, `.gitlab-ci.yml` variables, `config.toml` variables, and
variables from triggers and pipeline schedules
- **Not supported:** variables defined inside of scripts (e.g., `export MY_VARIABLE="test"`)
The Runner uses Go's `os.Expand()` method for variable expansion. It means that it will handle
only variables defined as `$variable` and `${variable}`. What's also important, is that
the expansion is done only once, so nested variables may or may not work, depending on the
ordering of variables definitions.
### Execution shell environment
This is an expansion that takes place during the `script` execution.
How it works depends on the used shell (bash/sh/cmd/PowerShell). For example, if the job's
`script` contains a line `echo $MY_VARIABLE-${MY_VARIABLE_2}`, it should be properly handled
by bash/sh (leaving empty strings or some values depending whether the variables were
defined or not), but will not work with Windows' cmd/PowerShell, since these shells
are using a different variables syntax.
**Supported:**
- The `script` may use all available variables that are default for the shell (e.g., `$PATH` which
should be present in all bash/sh shells) and all variables defined by GitLab CI/CD (secret variables,
`.gitlab-ci.yml` variables, `config.toml` variables, and variables from triggers and pipeline schedules).
- The `script` may also use all variables defined in the lines before. So, for example, if you define
a variable `export MY_VARIABLE="test"`:
- in `before_script`, it will work in the following lines of `before_script` and
all lines of the related `script`
- in `script`, it will work in the following lines of `script`
- in `after_script`, it will work in following lines of `after_script`
## Persisted variables
NOTE: **Note:**
Some of the persisted variables contain tokens and cannot be used by some definitions
due to security reasons.
The following variables are known as "persisted":
- `CI_PIPELINE_ID`
- `CI_JOB_ID`
- `CI_JOB_TOKEN`
- `CI_BUILD_ID`
- `CI_BUILD_TOKEN`
- `CI_REGISTRY_USER`
- `CI_REGISTRY_PASSWORD`
- `CI_REPOSITORY_URL`
- `CI_DEPLOY_USER`
- `CI_DEPLOY_PASSWORD`
They are:
- **supported** for all definitions as [described in the table](#gitlab-ci-yml-file) where the "Expansion place" is "Runner"
- **not supported:**
- by the definitions [described in the table](#gitlab-ci-yml-file) where the "Expansion place" is "GitLab"
- in the `only` and `except` [variables expressions](README.md#variables-expressions)
...@@ -193,7 +193,7 @@ List with avatar, title and description using .content-list ...@@ -193,7 +193,7 @@ List with avatar, title and description using .content-list
![List with avatar](img/components-listwithavatar.png) ![List with avatar](img/components-listwithavatar.png)
List with hover effect .well-list List with hover effect .content-list
![List with hover effect](img/components-listwithhover.png) ![List with hover effect](img/components-listwithhover.png)
......
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