Commit 49d35a02 authored by Stan Hu's avatar Stan Hu

Merge branch 'ce-to-ee-2018-11-07' into 'master'

CE upstream - 2018-11-07 21:57 UTC

Closes gitlab-ce#52649, gitlab-ce#52639, gitlab-ce#48956, #6070, gitlab-ce#51737, omnibus-gitlab#3872, gitlab-runner#3515, gitlab-org/quality/nightly#32, and #8234

See merge request gitlab-org/gitlab-ee!8306
parents f26de199 33dcfab2
...@@ -431,7 +431,7 @@ group :ed25519 do ...@@ -431,7 +431,7 @@ group :ed25519 do
end end
# Gitaly GRPC client # Gitaly GRPC client
gem 'gitaly-proto', '~> 0.118.1', require: 'gitaly' gem 'gitaly-proto', '~> 0.123.0', require: 'gitaly'
gem 'grpc', '~> 1.15.0' gem 'grpc', '~> 1.15.0'
gem 'google-protobuf', '~> 3.6' gem 'google-protobuf', '~> 3.6'
...@@ -446,6 +446,3 @@ gem 'flipper-active_support_cache_store', '~> 0.13.0' ...@@ -446,6 +446,3 @@ gem 'flipper-active_support_cache_store', '~> 0.13.0'
# Structured logging # Structured logging
gem 'lograge', '~> 0.5' gem 'lograge', '~> 0.5'
gem 'grape_logging', '~> 1.7' gem 'grape_logging', '~> 1.7'
# Asset synchronization
gem 'asset_sync', '~> 2.4'
...@@ -58,11 +58,6 @@ GEM ...@@ -58,11 +58,6 @@ GEM
asciidoctor (1.5.6.2) asciidoctor (1.5.6.2)
asciidoctor-plantuml (0.0.8) asciidoctor-plantuml (0.0.8)
asciidoctor (~> 1.5) asciidoctor (~> 1.5)
asset_sync (2.4.0)
activemodel (>= 4.1.0)
fog-core
mime-types (>= 2.99)
unf
ast (2.4.0) ast (2.4.0)
atomic (1.1.99) atomic (1.1.99)
attr_encrypted (3.1.0) attr_encrypted (3.1.0)
...@@ -298,9 +293,8 @@ GEM ...@@ -298,9 +293,8 @@ GEM
gettext_i18n_rails (>= 0.7.1) gettext_i18n_rails (>= 0.7.1)
po_to_json (>= 1.0.0) po_to_json (>= 1.0.0)
rails (>= 3.2.0) rails (>= 3.2.0)
gitaly-proto (0.118.1) gitaly-proto (0.123.0)
google-protobuf (~> 3.1) grpc (~> 1.0)
grpc (~> 1.10)
github-markup (1.7.0) github-markup (1.7.0)
gitlab-license (1.0.0) gitlab-license (1.0.0)
gitlab-markup (1.6.4) gitlab-markup (1.6.4)
...@@ -968,7 +962,6 @@ DEPENDENCIES ...@@ -968,7 +962,6 @@ DEPENDENCIES
asana (~> 0.6.0) asana (~> 0.6.0)
asciidoctor (~> 1.5.6) asciidoctor (~> 1.5.6)
asciidoctor-plantuml (= 0.0.8) asciidoctor-plantuml (= 0.0.8)
asset_sync (~> 2.4)
attr_encrypted (~> 3.1.0) attr_encrypted (~> 3.1.0)
awesome_print awesome_print
aws-sdk aws-sdk
...@@ -1034,7 +1027,7 @@ DEPENDENCIES ...@@ -1034,7 +1027,7 @@ DEPENDENCIES
gettext (~> 3.2.2) gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3) gettext_i18n_rails_js (~> 1.3)
gitaly-proto (~> 0.118.1) gitaly-proto (~> 0.123.0)
github-markup (~> 1.7.0) github-markup (~> 1.7.0)
gitlab-license (~> 1.0) gitlab-license (~> 1.0)
gitlab-markup (~> 1.6.4) gitlab-markup (~> 1.6.4)
...@@ -1195,4 +1188,4 @@ DEPENDENCIES ...@@ -1195,4 +1188,4 @@ DEPENDENCIES
wikicloth (= 0.8.1) wikicloth (= 0.8.1)
BUNDLED WITH BUNDLED WITH
1.16.6 1.17.1
...@@ -61,11 +61,6 @@ GEM ...@@ -61,11 +61,6 @@ GEM
asciidoctor (1.5.6.2) asciidoctor (1.5.6.2)
asciidoctor-plantuml (0.0.8) asciidoctor-plantuml (0.0.8)
asciidoctor (~> 1.5) asciidoctor (~> 1.5)
asset_sync (2.4.0)
activemodel (>= 4.1.0)
fog-core
mime-types (>= 2.99)
unf
ast (2.4.0) ast (2.4.0)
atomic (1.1.99) atomic (1.1.99)
attr_encrypted (3.1.0) attr_encrypted (3.1.0)
...@@ -301,9 +296,8 @@ GEM ...@@ -301,9 +296,8 @@ GEM
gettext_i18n_rails (>= 0.7.1) gettext_i18n_rails (>= 0.7.1)
po_to_json (>= 1.0.0) po_to_json (>= 1.0.0)
rails (>= 3.2.0) rails (>= 3.2.0)
gitaly-proto (0.118.1) gitaly-proto (0.123.0)
google-protobuf (~> 3.1) grpc (~> 1.0)
grpc (~> 1.10)
github-markup (1.7.0) github-markup (1.7.0)
gitlab-license (1.0.0) gitlab-license (1.0.0)
gitlab-markup (1.6.4) gitlab-markup (1.6.4)
...@@ -977,7 +971,6 @@ DEPENDENCIES ...@@ -977,7 +971,6 @@ DEPENDENCIES
asana (~> 0.6.0) asana (~> 0.6.0)
asciidoctor (~> 1.5.6) asciidoctor (~> 1.5.6)
asciidoctor-plantuml (= 0.0.8) asciidoctor-plantuml (= 0.0.8)
asset_sync (~> 2.4)
attr_encrypted (~> 3.1.0) attr_encrypted (~> 3.1.0)
awesome_print awesome_print
aws-sdk aws-sdk
...@@ -1043,7 +1036,7 @@ DEPENDENCIES ...@@ -1043,7 +1036,7 @@ DEPENDENCIES
gettext (~> 3.2.2) gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0) gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3) gettext_i18n_rails_js (~> 1.3)
gitaly-proto (~> 0.118.1) gitaly-proto (~> 0.123.0)
github-markup (~> 1.7.0) github-markup (~> 1.7.0)
gitlab-license (~> 1.0) gitlab-license (~> 1.0)
gitlab-markup (~> 1.6.4) gitlab-markup (~> 1.6.4)
...@@ -1204,4 +1197,4 @@ DEPENDENCIES ...@@ -1204,4 +1197,4 @@ DEPENDENCIES
wikicloth (= 0.8.1) wikicloth (= 0.8.1)
BUNDLED WITH BUNDLED WITH
1.16.6 1.17.1
...@@ -41,8 +41,7 @@ export default { ...@@ -41,8 +41,7 @@ export default {
placement="bottom" placement="bottom"
class="js-issue-time-estimate" class="js-issue-time-estimate"
> >
<span class="bold">{{ __('Time estimate') }}</span> <span class="bold d-block">{{ __('Time estimate') }}</span>
<br />
{{ title }} {{ title }}
</gl-tooltip> </gl-tooltip>
</span> </span>
......
...@@ -28,6 +28,7 @@ export default class Clusters { ...@@ -28,6 +28,7 @@ export default class Clusters {
installIngressPath, installIngressPath,
installRunnerPath, installRunnerPath,
installJupyterPath, installJupyterPath,
installKnativePath,
installPrometheusPath, installPrometheusPath,
managePrometheusPath, managePrometheusPath,
clusterStatus, clusterStatus,
...@@ -49,6 +50,7 @@ export default class Clusters { ...@@ -49,6 +50,7 @@ export default class Clusters {
installRunnerEndpoint: installRunnerPath, installRunnerEndpoint: installRunnerPath,
installPrometheusEndpoint: installPrometheusPath, installPrometheusEndpoint: installPrometheusPath,
installJupyterEndpoint: installJupyterPath, installJupyterEndpoint: installJupyterPath,
installKnativeEndpoint: installKnativePath,
}); });
this.installApplication = this.installApplication.bind(this); this.installApplication = this.installApplication.bind(this);
......
...@@ -7,6 +7,7 @@ import helmLogo from 'images/cluster_app_logos/helm.png'; ...@@ -7,6 +7,7 @@ import helmLogo from 'images/cluster_app_logos/helm.png';
import jeagerLogo from 'images/cluster_app_logos/jeager.png'; import jeagerLogo from 'images/cluster_app_logos/jeager.png';
import jupyterhubLogo from 'images/cluster_app_logos/jupyterhub.png'; import jupyterhubLogo from 'images/cluster_app_logos/jupyterhub.png';
import kubernetesLogo from 'images/cluster_app_logos/kubernetes.png'; import kubernetesLogo from 'images/cluster_app_logos/kubernetes.png';
import knativeLogo from 'images/cluster_app_logos/knative.png';
import meltanoLogo from 'images/cluster_app_logos/meltano.png'; import meltanoLogo from 'images/cluster_app_logos/meltano.png';
import prometheusLogo from 'images/cluster_app_logos/prometheus.png'; import prometheusLogo from 'images/cluster_app_logos/prometheus.png';
import { s__, sprintf } from '../../locale'; import { s__, sprintf } from '../../locale';
...@@ -53,6 +54,7 @@ export default { ...@@ -53,6 +54,7 @@ export default {
jeagerLogo, jeagerLogo,
jupyterhubLogo, jupyterhubLogo,
kubernetesLogo, kubernetesLogo,
knativeLogo,
meltanoLogo, meltanoLogo,
prometheusLogo, prometheusLogo,
}), }),
...@@ -136,6 +138,9 @@ export default { ...@@ -136,6 +138,9 @@ export default {
jupyterHostname() { jupyterHostname() {
return this.applications.jupyter.hostname; return this.applications.jupyter.hostname;
}, },
knativeInstalled() {
return this.applications.knative.status === APPLICATION_STATUS.INSTALLED;
},
}, },
created() { created() {
this.helmInstallIllustration = helmInstallIllustration; this.helmInstallIllustration = helmInstallIllustration;
...@@ -321,7 +326,6 @@ export default { ...@@ -321,7 +326,6 @@ export default {
:request-reason="applications.jupyter.requestReason" :request-reason="applications.jupyter.requestReason"
:install-application-request-params="{ hostname: applications.jupyter.hostname }" :install-application-request-params="{ hostname: applications.jupyter.hostname }"
:disabled="!helmInstalled" :disabled="!helmInstalled"
class="hide-bottom-border rounded-bottom"
title-link="https://jupyterhub.readthedocs.io/en/stable/" title-link="https://jupyterhub.readthedocs.io/en/stable/"
> >
<div slot="description"> <div slot="description">
...@@ -371,6 +375,58 @@ export default { ...@@ -371,6 +375,58 @@ export default {
</template> </template>
</div> </div>
</application-row> </application-row>
<application-row
id="knative"
:logo-url="knativeLogo"
:title="applications.knative.title"
:status="applications.knative.status"
:status-reason="applications.knative.statusReason"
:request-status="applications.knative.requestStatus"
:request-reason="applications.knative.requestReason"
:install-application-request-params="{ hostname: applications.knative.hostname}"
:disabled="!helmInstalled"
class="hide-bottom-border rounded-bottom"
title-link="https://github.com/knative/docs"
>
<div slot="description">
<p>
{{ s__(`ClusterIntegration|A Knative build extends Kubernetes
and utilizes existing Kubernetes primitives to provide you with
the ability to run on-cluster container builds from source.
For example, you can write a build that uses Kubernetes-native
resources to obtain your source code from a repository,
build it into container a image, and then run that image.`) }}
</p>
<template v-if="knativeInstalled">
<div class="form-group">
<label for="knative-domainname">
{{ s__('ClusterIntegration|Knative Domain Name:') }}
</label>
<input
id="knative-domainname"
v-model="applications.knative.hostname"
type="text"
class="form-control js-domainname"
readonly
/>
</div>
</template>
<template v-else>
<div class="form-group">
<label for="knative-domainname">
{{ s__('ClusterIntegration|Knative Domain Name:') }}
</label>
<input
id="knative-domainname"
v-model="applications.knative.hostname"
type="text"
class="form-control js-domainname"
/>
</div>
</template>
</div>
</application-row>
</div> </div>
</section> </section>
</template> </template>
...@@ -16,3 +16,4 @@ export const REQUEST_SUCCESS = 'request-success'; ...@@ -16,3 +16,4 @@ export const REQUEST_SUCCESS = 'request-success';
export const REQUEST_FAILURE = 'request-failure'; export const REQUEST_FAILURE = 'request-failure';
export const INGRESS = 'ingress'; export const INGRESS = 'ingress';
export const JUPYTER = 'jupyter'; export const JUPYTER = 'jupyter';
export const KNATIVE = 'knative';
...@@ -9,6 +9,7 @@ export default class ClusterService { ...@@ -9,6 +9,7 @@ export default class ClusterService {
runner: this.options.installRunnerEndpoint, runner: this.options.installRunnerEndpoint,
prometheus: this.options.installPrometheusEndpoint, prometheus: this.options.installPrometheusEndpoint,
jupyter: this.options.installJupyterEndpoint, jupyter: this.options.installJupyterEndpoint,
knative: this.options.installKnativeEndpoint,
}; };
} }
......
import { s__ } from '../../locale'; import { s__ } from '../../locale';
import { INGRESS, JUPYTER } from '../constants'; import { INGRESS, JUPYTER, KNATIVE } from '../constants';
export default class ClusterStore { export default class ClusterStore {
constructor() { constructor() {
...@@ -46,6 +46,14 @@ export default class ClusterStore { ...@@ -46,6 +46,14 @@ export default class ClusterStore {
requestReason: null, requestReason: null,
hostname: null, hostname: null,
}, },
knative: {
title: s__('ClusterIntegration|Knative'),
status: null,
statusReason: null,
requestStatus: null,
requestReason: null,
hostname: null,
},
}, },
}; };
} }
...@@ -93,6 +101,9 @@ export default class ClusterStore { ...@@ -93,6 +101,9 @@ export default class ClusterStore {
(this.state.applications.ingress.externalIp (this.state.applications.ingress.externalIp
? `jupyter.${this.state.applications.ingress.externalIp}.nip.io` ? `jupyter.${this.state.applications.ingress.externalIp}.nip.io`
: ''); : '');
} else if (appId === KNATIVE) {
this.state.applications.knative.hostname =
serverAppEntry.hostname || this.state.applications.knative.hostname;
} }
}); });
} }
......
...@@ -36,7 +36,7 @@ export default { ...@@ -36,7 +36,7 @@ export default {
}, },
computed: { computed: {
...mapState('diffs', ['commit', 'showTreeList']), ...mapState('diffs', ['commit', 'showTreeList']),
...mapGetters('diffs', ['isInlineView', 'isParallelView', 'areAllFilesCollapsed']), ...mapGetters('diffs', ['isInlineView', 'isParallelView', 'hasCollapsedFile']),
comparableDiffs() { comparableDiffs() {
return this.mergeRequestDiffs.slice(1); return this.mergeRequestDiffs.slice(1);
}, },
...@@ -113,8 +113,8 @@ export default { ...@@ -113,8 +113,8 @@ export default {
class="inline-parallel-buttons d-none d-md-flex ml-auto" class="inline-parallel-buttons d-none d-md-flex ml-auto"
> >
<a <a
v-if="areAllFilesCollapsed" v-show="hasCollapsedFile"
class="btn btn-default" class="btn btn-default append-right-8"
@click="expandAllFiles" @click="expandAllFiles"
> >
{{ __('Expand all') }} {{ __('Expand all') }}
......
...@@ -55,11 +55,6 @@ export default { ...@@ -55,11 +55,6 @@ export default {
required: false, required: false,
default: false, default: false,
}, },
isContextLine: {
type: Boolean,
required: false,
default: false,
},
isHover: { isHover: {
type: Boolean, type: Boolean,
required: false, required: false,
...@@ -81,7 +76,6 @@ export default { ...@@ -81,7 +76,6 @@ export default {
this.showCommentButton && this.showCommentButton &&
this.isHover && this.isHover &&
!this.isMatchLine && !this.isMatchLine &&
!this.isContextLine &&
!this.isMetaLine && !this.isMetaLine &&
!this.hasDiscussions !this.hasDiscussions
); );
......
...@@ -3,7 +3,6 @@ import { mapGetters } from 'vuex'; ...@@ -3,7 +3,6 @@ import { mapGetters } from 'vuex';
import DiffLineGutterContent from './diff_line_gutter_content.vue'; import DiffLineGutterContent from './diff_line_gutter_content.vue';
import { import {
MATCH_LINE_TYPE, MATCH_LINE_TYPE,
CONTEXT_LINE_TYPE,
EMPTY_CELL_TYPE, EMPTY_CELL_TYPE,
OLD_LINE_TYPE, OLD_LINE_TYPE,
OLD_NO_NEW_LINE_TYPE, OLD_NO_NEW_LINE_TYPE,
...@@ -71,9 +70,6 @@ export default { ...@@ -71,9 +70,6 @@ export default {
isMatchLine() { isMatchLine() {
return this.line.type === MATCH_LINE_TYPE; return this.line.type === MATCH_LINE_TYPE;
}, },
isContextLine() {
return this.line.type === CONTEXT_LINE_TYPE;
},
isMetaLine() { isMetaLine() {
const { type } = this.line; const { type } = this.line;
...@@ -88,11 +84,7 @@ export default { ...@@ -88,11 +84,7 @@ export default {
[type]: type, [type]: type,
[LINE_UNFOLD_CLASS_NAME]: this.isMatchLine, [LINE_UNFOLD_CLASS_NAME]: this.isMatchLine,
[LINE_HOVER_CLASS_NAME]: [LINE_HOVER_CLASS_NAME]:
this.isLoggedIn && this.isLoggedIn && this.isHover && !this.isMatchLine && !this.isMetaLine,
this.isHover &&
!this.isMatchLine &&
!this.isContextLine &&
!this.isMetaLine,
}; };
}, },
lineNumber() { lineNumber() {
......
...@@ -4,8 +4,6 @@ import DiffTableCell from './diff_table_cell.vue'; ...@@ -4,8 +4,6 @@ import DiffTableCell from './diff_table_cell.vue';
import { import {
NEW_LINE_TYPE, NEW_LINE_TYPE,
OLD_LINE_TYPE, OLD_LINE_TYPE,
CONTEXT_LINE_TYPE,
CONTEXT_LINE_CLASS_NAME,
PARALLEL_DIFF_VIEW_TYPE, PARALLEL_DIFF_VIEW_TYPE,
LINE_POSITION_LEFT, LINE_POSITION_LEFT,
LINE_POSITION_RIGHT, LINE_POSITION_RIGHT,
...@@ -41,13 +39,9 @@ export default { ...@@ -41,13 +39,9 @@ export default {
}, },
computed: { computed: {
...mapGetters('diffs', ['isInlineView']), ...mapGetters('diffs', ['isInlineView']),
isContextLine() {
return this.line.type === CONTEXT_LINE_TYPE;
},
classNameMap() { classNameMap() {
return { return {
[this.line.type]: this.line.type, [this.line.type]: this.line.type,
[CONTEXT_LINE_CLASS_NAME]: this.isContextLine,
[PARALLEL_DIFF_VIEW_TYPE]: this.isParallelView, [PARALLEL_DIFF_VIEW_TYPE]: this.isParallelView,
}; };
}, },
......
...@@ -5,8 +5,6 @@ import DiffTableCell from './diff_table_cell.vue'; ...@@ -5,8 +5,6 @@ import DiffTableCell from './diff_table_cell.vue';
import { import {
NEW_LINE_TYPE, NEW_LINE_TYPE,
OLD_LINE_TYPE, OLD_LINE_TYPE,
CONTEXT_LINE_TYPE,
CONTEXT_LINE_CLASS_NAME,
OLD_NO_NEW_LINE_TYPE, OLD_NO_NEW_LINE_TYPE,
PARALLEL_DIFF_VIEW_TYPE, PARALLEL_DIFF_VIEW_TYPE,
NEW_NO_NEW_LINE_TYPE, NEW_NO_NEW_LINE_TYPE,
...@@ -43,12 +41,8 @@ export default { ...@@ -43,12 +41,8 @@ export default {
}; };
}, },
computed: { computed: {
isContextLine() {
return this.line.left && this.line.left.type === CONTEXT_LINE_TYPE;
},
classNameMap() { classNameMap() {
return { return {
[CONTEXT_LINE_CLASS_NAME]: this.isContextLine,
[PARALLEL_DIFF_VIEW_TYPE]: true, [PARALLEL_DIFF_VIEW_TYPE]: true,
}; };
}, },
......
...@@ -3,7 +3,6 @@ export const PARALLEL_DIFF_VIEW_TYPE = 'parallel'; ...@@ -3,7 +3,6 @@ export const PARALLEL_DIFF_VIEW_TYPE = 'parallel';
export const MATCH_LINE_TYPE = 'match'; export const MATCH_LINE_TYPE = 'match';
export const OLD_NO_NEW_LINE_TYPE = 'old-nonewline'; export const OLD_NO_NEW_LINE_TYPE = 'old-nonewline';
export const NEW_NO_NEW_LINE_TYPE = 'new-nonewline'; export const NEW_NO_NEW_LINE_TYPE = 'new-nonewline';
export const CONTEXT_LINE_TYPE = 'context';
export const EMPTY_CELL_TYPE = 'empty-cell'; export const EMPTY_CELL_TYPE = 'empty-cell';
export const COMMENT_FORM_TYPE = 'commentForm'; export const COMMENT_FORM_TYPE = 'commentForm';
export const DIFF_NOTE_TYPE = 'DiffNote'; export const DIFF_NOTE_TYPE = 'DiffNote';
...@@ -22,7 +21,6 @@ export const LINE_SIDE_RIGHT = 'right-side'; ...@@ -22,7 +21,6 @@ export const LINE_SIDE_RIGHT = 'right-side';
export const DIFF_VIEW_COOKIE_NAME = 'diff_view'; export const DIFF_VIEW_COOKIE_NAME = 'diff_view';
export const LINE_HOVER_CLASS_NAME = 'is-over'; export const LINE_HOVER_CLASS_NAME = 'is-over';
export const LINE_UNFOLD_CLASS_NAME = 'unfold js-unfold'; export const LINE_UNFOLD_CLASS_NAME = 'unfold js-unfold';
export const CONTEXT_LINE_CLASS_NAME = 'diff-expanded';
export const UNFOLD_COUNT = 20; export const UNFOLD_COUNT = 20;
export const COUNT_OF_AVATARS_IN_GUTTER = 3; export const COUNT_OF_AVATARS_IN_GUTTER = 3;
......
...@@ -5,7 +5,7 @@ export const isParallelView = state => state.diffViewType === PARALLEL_DIFF_VIEW ...@@ -5,7 +5,7 @@ export const isParallelView = state => state.diffViewType === PARALLEL_DIFF_VIEW
export const isInlineView = state => state.diffViewType === INLINE_DIFF_VIEW_TYPE; export const isInlineView = state => state.diffViewType === INLINE_DIFF_VIEW_TYPE;
export const areAllFilesCollapsed = state => state.diffFiles.every(file => file.collapsed); export const hasCollapsedFile = state => state.diffFiles.some(file => file.collapsed);
export const commitId = state => (state.commit && state.commit.id ? state.commit.id : null); export const commitId = state => (state.commit && state.commit.id ? state.commit.id : null);
......
...@@ -65,7 +65,13 @@ export default { ...@@ -65,7 +65,13 @@ export default {
const { highlightedDiffLines, parallelDiffLines } = diffFile; const { highlightedDiffLines, parallelDiffLines } = diffFile;
removeMatchLine(diffFile, lineNumbers, bottom); removeMatchLine(diffFile, lineNumbers, bottom);
const lines = addLineReferences(contextLines, lineNumbers, bottom);
const lines = addLineReferences(contextLines, lineNumbers, bottom).map(line => ({
...line,
lineCode: line.lineCode || `${fileHash}_${line.oldLine}_${line.newLine}`,
discussions: line.discussions || [],
}));
addContextLines({ addContextLines({
inlineLines: highlightedDiffLines, inlineLines: highlightedDiffLines,
parallelLines: parallelDiffLines, parallelLines: parallelDiffLines,
......
...@@ -19,6 +19,8 @@ import Log from './job_log.vue'; ...@@ -19,6 +19,8 @@ import Log from './job_log.vue';
import LogTopBar from './job_log_controllers.vue'; import LogTopBar from './job_log_controllers.vue';
import StuckBlock from './stuck_block.vue'; import StuckBlock from './stuck_block.vue';
import Sidebar from './sidebar.vue'; import Sidebar from './sidebar.vue';
import { sprintf } from '~/locale';
import delayedJobMixin from '../mixins/delayed_job_mixin';
export default { export default {
name: 'JobPageApp', name: 'JobPageApp',
...@@ -37,6 +39,7 @@ export default { ...@@ -37,6 +39,7 @@ export default {
SharedRunner, SharedRunner,
Sidebar, Sidebar,
}, },
mixins: [delayedJobMixin],
props: { props: {
runnerSettingsUrl: { runnerSettingsUrl: {
type: String, type: String,
...@@ -97,6 +100,17 @@ export default { ...@@ -97,6 +100,17 @@ export default {
shouldRenderContent() { shouldRenderContent() {
return !this.isLoading && !this.hasError; return !this.isLoading && !this.hasError;
}, },
emptyStateTitle() {
const { emptyStateIllustration, remainingTime } = this;
const { title } = emptyStateIllustration;
if (this.isDelayedJob) {
return sprintf(title, { remainingTime });
}
return title;
},
}, },
watch: { watch: {
// Once the job log is loaded, // Once the job log is loaded,
...@@ -285,7 +299,7 @@ export default { ...@@ -285,7 +299,7 @@ export default {
class="js-job-empty-state" class="js-job-empty-state"
:illustration-path="emptyStateIllustration.image" :illustration-path="emptyStateIllustration.image"
:illustration-size-class="emptyStateIllustration.size" :illustration-size-class="emptyStateIllustration.size"
:title="emptyStateIllustration.title" :title="emptyStateTitle"
:content="emptyStateIllustration.content" :content="emptyStateIllustration.content"
:action="emptyStateAction" :action="emptyStateAction"
/> />
......
<script> <script>
import { GlTooltipDirective, GlLink } from '@gitlab-org/gitlab-ui'; import { GlLink } from '@gitlab-org/gitlab-ui';
import tooltip from '~/vue_shared/directives/tooltip';
import CiIcon from '~/vue_shared/components/ci_icon.vue'; import CiIcon from '~/vue_shared/components/ci_icon.vue';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import delayedJobMixin from '~/jobs/mixins/delayed_job_mixin';
import { sprintf } from '~/locale';
export default { export default {
components: { components: {
...@@ -10,8 +13,9 @@ export default { ...@@ -10,8 +13,9 @@ export default {
GlLink, GlLink,
}, },
directives: { directives: {
GlTooltip: GlTooltipDirective, tooltip,
}, },
mixins: [delayedJobMixin],
props: { props: {
job: { job: {
type: Object, type: Object,
...@@ -24,7 +28,14 @@ export default { ...@@ -24,7 +28,14 @@ export default {
}, },
computed: { computed: {
tooltipText() { tooltipText() {
return `${this.job.name} - ${this.job.status.tooltip}`; const { name, status } = this.job;
const text = `${name} - ${status.tooltip}`;
if (this.isDelayedJob) {
return sprintf(text, { remainingTime: this.remainingTime });
}
return text;
}, },
}, },
}; };
...@@ -39,7 +50,7 @@ export default { ...@@ -39,7 +50,7 @@ export default {
}" }"
> >
<gl-link <gl-link
v-gl-tooltip v-tooltip
:href="job.status.details_path" :href="job.status.details_path"
:title="tooltipText" :title="tooltipText"
data-boundary="viewport" data-boundary="viewport"
......
import { calculateRemainingMilliseconds, formatTime } from '~/lib/utils/datetime_utility';
export default {
data() {
return {
remainingTime: formatTime(0),
remainingTimeIntervalId: null,
};
},
mounted() {
this.startRemainingTimeInterval();
},
beforeDestroy() {
if (this.remainingTimeIntervalId) {
clearInterval(this.remainingTimeIntervalId);
}
},
computed: {
isDelayedJob() {
return this.job && this.job.scheduled;
},
},
watch: {
isDelayedJob() {
this.startRemainingTimeInterval();
},
},
methods: {
startRemainingTimeInterval() {
if (this.remainingTimeIntervalId) {
clearInterval(this.remainingTimeIntervalId);
}
if (this.isDelayedJob) {
this.updateRemainingTime();
this.remainingTimeIntervalId = setInterval(() => this.updateRemainingTime(), 1000);
}
},
updateRemainingTime() {
const remainingMilliseconds = calculateRemainingMilliseconds(this.job.scheduled_at);
this.remainingTime = formatTime(remainingMilliseconds);
},
},
};
...@@ -227,7 +227,7 @@ export default { ...@@ -227,7 +227,7 @@ export default {
<ul class="dropdown-menu more-actions-dropdown dropdown-open-left"> <ul class="dropdown-menu more-actions-dropdown dropdown-open-left">
<li v-if="canReportAsAbuse"> <li v-if="canReportAsAbuse">
<a :href="reportAbusePath"> <a :href="reportAbusePath">
Report as abuse {{ __('Report abuse to GitLab') }}
</a> </a>
</li> </li>
<li v-if="noteUrl"> <li v-if="noteUrl">
...@@ -236,7 +236,7 @@ export default { ...@@ -236,7 +236,7 @@ export default {
type="button" type="button"
class="btn-default btn-transparent js-btn-copy-note-link" class="btn-default btn-transparent js-btn-copy-note-link"
> >
Copy link {{ __('Copy link') }}
</button> </button>
</li> </li>
<li v-if="canEdit"> <li v-if="canEdit">
...@@ -245,7 +245,7 @@ export default { ...@@ -245,7 +245,7 @@ export default {
type="button" type="button"
@click.prevent="onDelete"> @click.prevent="onDelete">
<span class="text-danger"> <span class="text-danger">
Delete comment {{ __('Delete comment') }}
</span> </span>
</button> </button>
</li> </li>
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
import ActionComponent from './action_component.vue'; import ActionComponent from './action_component.vue';
import JobNameComponent from './job_name_component.vue'; import JobNameComponent from './job_name_component.vue';
import tooltip from '../../../vue_shared/directives/tooltip'; import tooltip from '../../../vue_shared/directives/tooltip';
import { sprintf } from '~/locale';
import delayedJobMixin from '~/jobs/mixins/delayed_job_mixin';
/** /**
* Renders the badge for the pipeline graph and the job's dropdown. * Renders the badge for the pipeline graph and the job's dropdown.
...@@ -36,6 +38,7 @@ export default { ...@@ -36,6 +38,7 @@ export default {
directives: { directives: {
tooltip, tooltip,
}, },
mixins: [delayedJobMixin],
props: { props: {
job: { job: {
type: Object, type: Object,
...@@ -52,6 +55,7 @@ export default { ...@@ -52,6 +55,7 @@ export default {
default: Infinity, default: Infinity,
}, },
}, },
computed: { computed: {
status() { status() {
return this.job && this.job.status ? this.job.status : {}; return this.job && this.job.status ? this.job.status : {};
...@@ -59,17 +63,23 @@ export default { ...@@ -59,17 +63,23 @@ export default {
tooltipText() { tooltipText() {
const textBuilder = []; const textBuilder = [];
const { name: jobName } = this.job;
if (this.job.name) { if (jobName) {
textBuilder.push(this.job.name); textBuilder.push(jobName);
} }
if (this.job.name && this.status.tooltip) { const { tooltip: statusTooltip } = this.status;
if (jobName && statusTooltip) {
textBuilder.push('-'); textBuilder.push('-');
} }
if (this.status.tooltip) { if (statusTooltip) {
textBuilder.push(this.job.status.tooltip); if (this.isDelayedJob) {
textBuilder.push(sprintf(statusTooltip, { remainingTime: this.remainingTime }));
} else {
textBuilder.push(statusTooltip);
}
} }
return textBuilder.join(' '); return textBuilder.join(' ');
...@@ -88,6 +98,7 @@ export default { ...@@ -88,6 +98,7 @@ export default {
return this.job.status && this.job.status.action && this.job.status.action.path; return this.job.status && this.job.status.action && this.job.status.action.path;
}, },
}, },
methods: { methods: {
pipelineActionRequestComplete() { pipelineActionRequestComplete() {
this.$emit('pipelineActionRequestComplete'); this.$emit('pipelineActionRequestComplete');
......
...@@ -102,7 +102,7 @@ module Boards ...@@ -102,7 +102,7 @@ module Boards
def serialize_as_json(resource) def serialize_as_json(resource)
resource.as_json( resource.as_json(
only: [:id, :iid, :project_id, :title, :confidential, :due_date, :relative_position, :weight, :time_estimate], only: [:id, :iid, :project_id, :title, :confidential, :due_date, :relative_position, :weight],
labels: true, labels: true,
issue_endpoints: true, issue_endpoints: true,
include_full_project_path: board.group_board?, include_full_project_path: board.group_board?,
......
# frozen_string_literal: true
class ChaosController < ActionController::Base
before_action :validate_request
def leakmem
memory_mb = (params[:memory_mb]&.to_i || 100)
duration_s = (params[:duration_s]&.to_i || 30).seconds
start = Time.now
retainer = []
# Add `n` 1mb chunks of memory to the retainer array
memory_mb.times { retainer << "x" * 1.megabyte }
duration_taken = (Time.now - start).seconds
Kernel.sleep duration_s - duration_taken if duration_s > duration_taken
render text: "OK", content_type: 'text/plain'
end
def cpuspin
duration_s = (params[:duration_s]&.to_i || 30).seconds
end_time = Time.now + duration_s.seconds
rand while Time.now < end_time
render text: "OK", content_type: 'text/plain'
end
def sleep
duration_s = (params[:duration_s]&.to_i || 30).seconds
Kernel.sleep duration_s
render text: "OK", content_type: 'text/plain'
end
def kill
Process.kill("KILL", Process.pid)
end
private
def validate_request
secret = ENV['GITLAB_CHAOS_SECRET']
# GITLAB_CHAOS_SECRET is required unless you're running in Development mode
if !secret && !Rails.env.development?
render text: "chaos misconfigured: please configure GITLAB_CHAOS_SECRET when using GITLAB_ENABLE_CHAOS_ENDPOINTS outside of a development environment", content_type: 'text/plain', status: 500
end
return unless secret
unless request.headers["HTTP_X_CHAOS_SECRET"] == secret
render text: "To experience chaos, please set X-Chaos-Secret header", content_type: 'text/plain', status: 401
end
end
end
...@@ -86,10 +86,10 @@ module CreatesCommit ...@@ -86,10 +86,10 @@ module CreatesCommit
def new_merge_request_path def new_merge_request_path
project_new_merge_request_path( project_new_merge_request_path(
@project_to_commit_into, @project_to_commit_into,
merge_request_source_branch: @branch_name,
merge_request: { merge_request: {
source_project_id: @project_to_commit_into.id, source_project_id: @project_to_commit_into.id,
target_project_id: @project.id, target_project_id: @project.id,
source_branch: @branch_name,
target_branch: @start_branch target_branch: @start_branch
} }
) )
......
...@@ -54,14 +54,14 @@ class Import::BitbucketServerController < Import::BaseController ...@@ -54,14 +54,14 @@ class Import::BitbucketServerController < Import::BaseController
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def status def status
repos = bitbucket_client.repos @collection = bitbucket_client.repos(page_offset: page_offset, limit: limit_per_page)
@repos, @incompatible_repos = @collection.partition { |repo| repo.valid? }
@repos, @incompatible_repos = repos.partition { |repo| repo.valid? } # Use the import URL to filter beyond what BaseService#find_already_added_projects
@already_added_projects = filter_added_projects('bitbucket_server', @repos.map(&:browse_url))
@already_added_projects = find_already_added_projects('bitbucket_server')
already_added_projects_names = @already_added_projects.pluck(:import_source) already_added_projects_names = @already_added_projects.pluck(:import_source)
@repos.to_a.reject! { |repo| already_added_projects_names.include?(repo.browse_url) } @repos.reject! { |repo| already_added_projects_names.include?(repo.browse_url) }
rescue BitbucketServer::Connection::ConnectionError, BitbucketServer::Client::ServerError => e rescue BitbucketServer::Connection::ConnectionError, BitbucketServer::Client::ServerError => e
flash[:alert] = "Unable to connect to server: #{e}" flash[:alert] = "Unable to connect to server: #{e}"
clear_session_data clear_session_data
...@@ -75,6 +75,12 @@ class Import::BitbucketServerController < Import::BaseController ...@@ -75,6 +75,12 @@ class Import::BitbucketServerController < Import::BaseController
private private
# rubocop: disable CodeReuse/ActiveRecord
def filter_added_projects(import_type, import_sources)
current_user.created_projects.where(import_type: import_type, import_source: import_sources).includes(:import_state)
end
# rubocop: enable CodeReuse/ActiveRecord
def bitbucket_client def bitbucket_client
@bitbucket_client ||= BitbucketServer::Client.new(credentials) @bitbucket_client ||= BitbucketServer::Client.new(credentials)
end end
...@@ -130,4 +136,12 @@ class Import::BitbucketServerController < Import::BaseController ...@@ -130,4 +136,12 @@ class Import::BitbucketServerController < Import::BaseController
password: session[personal_access_token_key] password: session[personal_access_token_key]
} }
end end
def page_offset
[0, params[:page].to_i].max
end
def limit_per_page
BitbucketServer::Paginator::PAGE_LENGTH
end
end end
...@@ -122,7 +122,7 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -122,7 +122,7 @@ class Projects::BlobController < Projects::ApplicationController
@lines.map! do |line| @lines.map! do |line|
# These are marked as context lines but are loaded from blobs. # These are marked as context lines but are loaded from blobs.
# We also have context lines loaded from diffs in other places. # We also have context lines loaded from diffs in other places.
diff_line = Gitlab::Diff::Line.new(line, 'context', nil, nil, nil) diff_line = Gitlab::Diff::Line.new(line, nil, nil, nil, nil)
diff_line.rich_text = line diff_line.rich_text = line
diff_line diff_line
end end
......
...@@ -89,8 +89,6 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap ...@@ -89,8 +89,6 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap
def build_merge_request def build_merge_request
params[:merge_request] ||= ActionController::Parameters.new(source_project: @project) params[:merge_request] ||= ActionController::Parameters.new(source_project: @project)
params[:merge_request][:source_branch] ||= params[:merge_request_source_branch].presence
@merge_request = ::MergeRequests::BuildService.new(project, current_user, merge_request_params.merge(diff_options: diff_options)).execute @merge_request = ::MergeRequests::BuildService.new(project, current_user, merge_request_params.merge(diff_options: diff_options)).execute
end end
......
...@@ -22,6 +22,12 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic ...@@ -22,6 +22,12 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic
def render_diffs def render_diffs
@environment = @merge_request.environments_for(current_user).last @environment = @merge_request.environments_for(current_user).last
notes_grouped_by_path = @notes.group_by { |note| note.position.file_path }
@diffs.diff_files.each do |diff_file|
notes = notes_grouped_by_path.fetch(diff_file.file_path, [])
notes.each { |note| diff_file.unfold_diff_lines(note.position) }
end
@diffs.write_cache @diffs.write_cache
......
...@@ -13,8 +13,8 @@ module CompareHelper ...@@ -13,8 +13,8 @@ module CompareHelper
def create_mr_path(from = params[:from], to = params[:to], project = @project) def create_mr_path(from = params[:from], to = params[:to], project = @project)
project_new_merge_request_path( project_new_merge_request_path(
project, project,
merge_request_source_branch: to,
merge_request: { merge_request: {
source_branch: to,
target_branch: from target_branch: from
} }
) )
......
...@@ -91,7 +91,14 @@ module EventsHelper ...@@ -91,7 +91,14 @@ module EventsHelper
words << "##{event.target_iid}" if event.target_iid words << "##{event.target_iid}" if event.target_iid
words << "in" words << "in"
elsif event.target elsif event.target
words << "##{event.target_iid}:" prefix =
if event.merge_request?
MergeRequest.reference_prefix
else
Issue.reference_prefix
end
words << "#{prefix}#{event.target_iid}:" if event.target_iid
words << event.target.title if event.target.respond_to?(:title) words << event.target.title if event.target.respond_to?(:title)
words << "at" words << "at"
end end
......
...@@ -13,10 +13,10 @@ module MergeRequestsHelper ...@@ -13,10 +13,10 @@ module MergeRequestsHelper
def new_mr_from_push_event(event, target_project) def new_mr_from_push_event(event, target_project)
{ {
merge_request_source_branch: event.branch_name,
merge_request: { merge_request: {
source_project_id: event.project.id, source_project_id: event.project.id,
target_project_id: target_project.id, target_project_id: target_project.id,
source_branch: event.branch_name,
target_branch: target_project.repository.root_ref target_branch: target_project.repository.root_ref
} }
} }
...@@ -53,10 +53,10 @@ module MergeRequestsHelper ...@@ -53,10 +53,10 @@ module MergeRequestsHelper
def mr_change_branches_path(merge_request) def mr_change_branches_path(merge_request)
project_new_merge_request_path( project_new_merge_request_path(
@project, @project,
merge_request_source_branch: merge_request.source_branch,
merge_request: { merge_request: {
source_project_id: merge_request.source_project_id, source_project_id: merge_request.source_project_id,
target_project_id: merge_request.target_project_id, target_project_id: merge_request.target_project_id,
source_branch: merge_request.source_branch,
target_branch: merge_request.target_branch target_branch: merge_request.target_branch
}, },
change_branches: true change_branches: true
......
...@@ -214,6 +214,7 @@ module Ci ...@@ -214,6 +214,7 @@ module Ci
build.deployment&.succeed build.deployment&.succeed
build.run_after_commit do build.run_after_commit do
BuildSuccessWorker.perform_async(id)
PagesWorker.perform_async(:deploy, id) if build.pages_generator? PagesWorker.perform_async(:deploy, id) if build.pages_generator?
end end
end end
...@@ -223,9 +224,7 @@ module Ci ...@@ -223,9 +224,7 @@ module Ci
build.deployment&.drop build.deployment&.drop
next if build.retries_max.zero? if build.retry_failure?
if build.retries_count < build.retries_max
begin begin
Ci::Build.retry(build, build.user) Ci::Build.retry(build, build.user)
rescue Gitlab::Access::AccessDeniedError => ex rescue Gitlab::Access::AccessDeniedError => ex
...@@ -323,7 +322,17 @@ module Ci ...@@ -323,7 +322,17 @@ module Ci
end end
def retries_max def retries_max
self.options.to_h.fetch(:retry, 0).to_i normalized_retry.fetch(:max, 0)
end
def retry_when
normalized_retry.fetch(:when, ['always'])
end
def retry_failure?
return false if retries_max.zero? || retries_count >= retries_max
retry_when.include?('always') || retry_when.include?(failure_reason.to_s)
end end
def latest? def latest?
...@@ -888,6 +897,16 @@ module Ci ...@@ -888,6 +897,16 @@ module Ci
options&.dig(:environment, :url) || persisted_environment&.external_url options&.dig(:environment, :url) || persisted_environment&.external_url
end end
# The format of the retry option changed in GitLab 11.5: Before it was
# integer only, after it is a hash. New builds are created with the new
# format, but builds created before GitLab 11.5 and saved in database still
# have the old integer only format. This method returns the retry option
# normalized as a hash in 11.5+ format.
def normalized_retry
value = options&.dig(:retry)
value.is_a?(Integer) ? { max: value } : value.to_h
end
def build_attributes_from_config def build_attributes_from_config
return {} unless pipeline.config_processor return {} unless pipeline.config_processor
......
# frozen_string_literal: true
module Clusters
module Applications
class Knative < ActiveRecord::Base
VERSION = '0.1.3'.freeze
REPOSITORY = 'https://storage.googleapis.com/triggermesh-charts'.freeze
# This is required for helm version <= 2.10.x in order to support
# Setting up CRDs
ISTIO_CRDS = 'https://storage.googleapis.com/triggermesh-charts/istio-crds.yaml'.freeze
self.table_name = 'clusters_applications_knative'
include ::Clusters::Concerns::ApplicationCore
include ::Clusters::Concerns::ApplicationStatus
include ::Clusters::Concerns::ApplicationVersion
include ::Clusters::Concerns::ApplicationData
default_value_for :version, VERSION
validates :hostname, presence: true, hostname: true
def chart
'knative/knative'
end
def values
{ "domain" => hostname }.to_yaml
end
def install_command
Gitlab::Kubernetes::Helm::InstallCommand.new(
name: name,
version: VERSION,
rbac: cluster.platform_kubernetes_rbac?,
chart: chart,
files: files,
repository: REPOSITORY,
preinstall: install_script
)
end
private
def install_script
["/usr/bin/kubectl apply -f #{ISTIO_CRDS} >/dev/null"]
end
end
end
end
...@@ -14,7 +14,8 @@ module Clusters ...@@ -14,7 +14,8 @@ module Clusters
Applications::Ingress.application_name => Applications::Ingress, Applications::Ingress.application_name => Applications::Ingress,
Applications::Prometheus.application_name => Applications::Prometheus, Applications::Prometheus.application_name => Applications::Prometheus,
Applications::Runner.application_name => Applications::Runner, Applications::Runner.application_name => Applications::Runner,
Applications::Jupyter.application_name => Applications::Jupyter Applications::Jupyter.application_name => Applications::Jupyter,
Applications::Knative.application_name => Applications::Knative
}.freeze }.freeze
DEFAULT_ENVIRONMENT = '*'.freeze DEFAULT_ENVIRONMENT = '*'.freeze
...@@ -37,6 +38,7 @@ module Clusters ...@@ -37,6 +38,7 @@ module Clusters
has_one :application_prometheus, class_name: 'Clusters::Applications::Prometheus' has_one :application_prometheus, class_name: 'Clusters::Applications::Prometheus'
has_one :application_runner, class_name: 'Clusters::Applications::Runner' has_one :application_runner, class_name: 'Clusters::Applications::Runner'
has_one :application_jupyter, class_name: 'Clusters::Applications::Jupyter' has_one :application_jupyter, class_name: 'Clusters::Applications::Jupyter'
has_one :application_knative, class_name: 'Clusters::Applications::Knative'
has_many :kubernetes_namespaces has_many :kubernetes_namespaces
has_one :kubernetes_namespace, -> { order(id: :desc) }, class_name: 'Clusters::KubernetesNamespace' has_one :kubernetes_namespace, -> { order(id: :desc) }, class_name: 'Clusters::KubernetesNamespace'
...@@ -102,7 +104,8 @@ module Clusters ...@@ -102,7 +104,8 @@ module Clusters
application_ingress || build_application_ingress, application_ingress || build_application_ingress,
application_prometheus || build_application_prometheus, application_prometheus || build_application_prometheus,
application_runner || build_application_runner, application_runner || build_application_runner,
application_jupyter || build_application_jupyter application_jupyter || build_application_jupyter,
application_knative || build_application_knative
] ]
end end
......
...@@ -28,6 +28,7 @@ module Clusters ...@@ -28,6 +28,7 @@ module Clusters
algorithm: 'aes-256-cbc' algorithm: 'aes-256-cbc'
before_validation :enforce_namespace_to_lower_case before_validation :enforce_namespace_to_lower_case
before_validation :enforce_ca_whitespace_trimming
validates :namespace, validates :namespace,
allow_blank: true, allow_blank: true,
...@@ -203,6 +204,11 @@ module Clusters ...@@ -203,6 +204,11 @@ module Clusters
self.namespace = self.namespace&.downcase self.namespace = self.namespace&.downcase
end end
def enforce_ca_whitespace_trimming
self.ca_pem = self.ca_pem&.strip
self.token = self.token&.strip
end
def prevent_modification def prevent_modification
return unless managed? return unless managed?
......
...@@ -39,7 +39,15 @@ module EachBatch ...@@ -39,7 +39,15 @@ module EachBatch
# #
# of - The number of rows to retrieve per batch. # of - The number of rows to retrieve per batch.
# column - The column to use for ordering the batches. # column - The column to use for ordering the batches.
def each_batch(of: 1000, column: primary_key) # order_hint - An optional column to append to the `ORDER BY id`
# clause to help the query planner. PostgreSQL might perform badly
# with a LIMIT 1 because the planner is guessing that scanning the
# index in ID order will come across the desired row in less time
# it will take the planner than using another index. The
# order_hint does not affect the search results. For example,
# `ORDER BY id ASC, updated_at ASC` means the same thing as `ORDER
# BY id ASC`.
def each_batch(of: 1000, column: primary_key, order_hint: nil)
unless column unless column
raise ArgumentError, raise ArgumentError,
'the column: argument must be set to a column name to use for ordering rows' 'the column: argument must be set to a column name to use for ordering rows'
...@@ -48,7 +56,9 @@ module EachBatch ...@@ -48,7 +56,9 @@ module EachBatch
start = except(:select) start = except(:select)
.select(column) .select(column)
.reorder(column => :asc) .reorder(column => :asc)
.take
start = start.order(order_hint) if order_hint
start = start.take
return unless start return unless start
...@@ -60,6 +70,9 @@ module EachBatch ...@@ -60,6 +70,9 @@ module EachBatch
.select(column) .select(column)
.where(arel_table[column].gteq(start_id)) .where(arel_table[column].gteq(start_id))
.reorder(column => :asc) .reorder(column => :asc)
stop = stop.order(order_hint) if order_hint
stop = stop
.offset(of) .offset(of)
.limit(1) .limit(1)
.take .take
......
...@@ -10,7 +10,9 @@ class Deployment < ActiveRecord::Base ...@@ -10,7 +10,9 @@ class Deployment < ActiveRecord::Base
belongs_to :user belongs_to :user
belongs_to :deployable, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations belongs_to :deployable, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
has_internal_id :iid, scope: :project, init: ->(s) { s&.project&.deployments&.maximum(:iid) } has_internal_id :iid, scope: :project, init: ->(s) do
Deployment.where(project: s.project).maximum(:iid) if s&.project
end
validates :sha, presence: true validates :sha, presence: true
validates :ref, presence: true validates :ref, presence: true
......
...@@ -66,6 +66,10 @@ class DiffNote < Note ...@@ -66,6 +66,10 @@ class DiffNote < Note
self.original_position.diff_refs == diff_refs self.original_position.diff_refs == diff_refs
end end
def discussion_first_note?
self == discussion.first_note
end
private private
def enqueue_diff_file_creation_job def enqueue_diff_file_creation_job
...@@ -78,26 +82,33 @@ class DiffNote < Note ...@@ -78,26 +82,33 @@ class DiffNote < Note
end end
def should_create_diff_file? def should_create_diff_file?
on_text? && note_diff_file.nil? && self == discussion.first_note on_text? && note_diff_file.nil? && discussion_first_note?
end end
def fetch_diff_file def fetch_diff_file
if note_diff_file file =
diff = Gitlab::Git::Diff.new(note_diff_file.to_hash) if note_diff_file
Gitlab::Diff::File.new(diff, diff = Gitlab::Git::Diff.new(note_diff_file.to_hash)
repository: project.repository, Gitlab::Diff::File.new(diff,
diff_refs: original_position.diff_refs) repository: project.repository,
elsif created_at_diff?(noteable.diff_refs) diff_refs: original_position.diff_refs)
# We're able to use the already persisted diffs (Postgres) if we're elsif created_at_diff?(noteable.diff_refs)
# presenting a "current version" of the MR discussion diff. # We're able to use the already persisted diffs (Postgres) if we're
# So no need to make an extra Gitaly diff request for it. # presenting a "current version" of the MR discussion diff.
# As an extra benefit, the returned `diff_file` already # So no need to make an extra Gitaly diff request for it.
# has `highlighted_diff_lines` data set from Redis on # As an extra benefit, the returned `diff_file` already
# `Diff::FileCollection::MergeRequestDiff`. # has `highlighted_diff_lines` data set from Redis on
noteable.diffs(original_position.diff_options).diff_files.first # `Diff::FileCollection::MergeRequestDiff`.
else noteable.diffs(original_position.diff_options).diff_files.first
original_position.diff_file(self.project.repository) else
end original_position.diff_file(self.project.repository)
end
# Since persisted diff files already have its content "unfolded"
# there's no need to make it pass through the unfolding process.
file&.unfold_diff_lines(position) unless note_diff_file
file
end end
def supported? def supported?
......
...@@ -52,6 +52,7 @@ class Environment < ActiveRecord::Base ...@@ -52,6 +52,7 @@ class Environment < ActiveRecord::Base
scope :in_review_folder, -> { where(environment_type: "review") } scope :in_review_folder, -> { where(environment_type: "review") }
scope :for_name, -> (name) { where(name: name) } scope :for_name, -> (name) { where(name: name) }
scope :for_project, -> (project) { where(project_id: project) } scope :for_project, -> (project) { where(project_id: project) }
scope :with_deployment, -> (sha) { where('EXISTS (?)', Deployment.select(1).where('deployments.environment_id = environments.id').where(sha: sha)) }
state_machine :state, initial: :available do state_machine :state, initial: :available do
event :start do event :start do
......
...@@ -8,17 +8,16 @@ class EnvironmentStatus ...@@ -8,17 +8,16 @@ class EnvironmentStatus
delegate :id, to: :environment delegate :id, to: :environment
delegate :name, to: :environment delegate :name, to: :environment
delegate :project, to: :environment delegate :project, to: :environment
delegate :status, to: :deployment, allow_nil: true
delegate :deployed_at, to: :deployment, allow_nil: true delegate :deployed_at, to: :deployment, allow_nil: true
def self.for_merge_request(mr, user) def self.for_merge_request(mr, user)
build_environments_status(mr, user, mr.head_pipeline) build_environments_status(mr, user, mr.diff_head_sha)
end end
def self.after_merge_request(mr, user) def self.after_merge_request(mr, user)
return [] unless mr.merged? return [] unless mr.merged?
build_environments_status(mr, user, mr.merge_pipeline) build_environments_status(mr, user, mr.merge_commit_sha)
end end
def initialize(environment, merge_request, sha) def initialize(environment, merge_request, sha)
...@@ -29,7 +28,7 @@ class EnvironmentStatus ...@@ -29,7 +28,7 @@ class EnvironmentStatus
def deployment def deployment
strong_memoize(:deployment) do strong_memoize(:deployment) do
environment.first_deployment_for(sha) Deployment.where(environment: environment).find_by_sha(sha)
end end
end end
...@@ -44,6 +43,22 @@ class EnvironmentStatus ...@@ -44,6 +43,22 @@ class EnvironmentStatus
.merge_request_diff_files.where(deleted_file: false) .merge_request_diff_files.where(deleted_file: false)
end end
##
# Since frontend has not supported all statuses yet, BE has to
# proxy some status to a supported status.
def status
return unless deployment
case deployment.status
when 'created'
'running'
when 'canceled'
'failed'
else
deployment.status
end
end
private private
PAGE_EXTENSIONS = /\A\.(s?html?|php|asp|cgi|pl)\z/i.freeze PAGE_EXTENSIONS = /\A\.(s?html?|php|asp|cgi|pl)\z/i.freeze
...@@ -61,21 +76,14 @@ class EnvironmentStatus ...@@ -61,21 +76,14 @@ class EnvironmentStatus
} }
end end
def self.build_environments_status(mr, user, pipeline) def self.build_environments_status(mr, user, sha)
return [] unless pipeline.present? Environment.where(project_id: [mr.source_project_id, mr.target_project_id])
.available
.with_deployment(sha).map do |environment|
next unless Ability.allowed?(user, :read_environment, environment)
find_environments(user, pipeline).map do |environment| EnvironmentStatus.new(environment, mr, sha)
EnvironmentStatus.new(environment, mr, pipeline.sha) end.compact
end
end end
private_class_method :build_environments_status private_class_method :build_environments_status
def self.find_environments(user, pipeline)
env_ids = Deployment.where(deployable: pipeline.builds).select(:environment_id)
Environment.available.where(id: env_ids).select do |environment|
Ability.allowed?(user, :read_environment, environment)
end
end
private_class_method :find_environments
end end
...@@ -1019,6 +1019,18 @@ class Repository ...@@ -1019,6 +1019,18 @@ class Repository
message: merge_request.title) message: merge_request.title)
end end
def update_submodule(user, submodule, commit_sha, message:, branch:)
with_cache_hooks do
raw.update_submodule(
user: user,
submodule: submodule,
commit_sha: commit_sha,
branch: branch,
message: message
)
end
end
def blob_data_at(sha, path) def blob_data_at(sha, path)
blob = blob_at(sha, path) blob = blob_at(sha, path)
return unless blob return unless blob
......
...@@ -195,7 +195,7 @@ class WikiPage ...@@ -195,7 +195,7 @@ class WikiPage
update_attributes(attrs) update_attributes(attrs)
save(page_details: title) do save(page_details: title) do
wiki.create_page(title, content, format, message) wiki.create_page(title, content, format, attrs[:message])
end end
end end
......
# frozen_string_literal: true
class IssueBoardEntity < Grape::Entity
include RequestAwareEntity
expose :id
expose :iid
expose :title
expose :confidential
expose :due_date
expose :project_id
expose :relative_position
expose :weight, if: -> (*) { respond_to?(:weight) }
expose :time_estimate
expose :project do |issue|
API::Entities::Project.represent issue.project, only: [:id, :path]
end
expose :milestone, expose_nil: false do |issue|
API::Entities::Project.represent issue.milestone, only: [:id, :title]
end
expose :assignees do |issue|
API::Entities::UserBasic.represent issue.assignees, only: [:id, :name, :username, :avatar_url]
end
expose :labels do |issue|
LabelEntity.represent issue.labels, project: issue.project, only: [:id, :title, :description, :color, :priority, :text_color]
end
expose :reference_path, if: -> (issue) { issue.project } do |issue, options|
options[:include_full_project_path] ? issue.to_reference(full: true) : issue.to_reference
end
expose :real_path, if: -> (issue) { issue.project } do |issue|
project_issue_path(issue.project, issue)
end
expose :issue_sidebar_endpoint, if: -> (issue) { issue.project } do |issue|
project_issue_path(issue.project, issue, format: :json, serializer: 'sidebar')
end
expose :toggle_subscription_endpoint, if: -> (issue) { issue.project } do |issue|
toggle_subscription_project_issue_path(issue.project, issue)
end
expose :assignable_labels_endpoint, if: -> (issue) { issue.project } do |issue|
project_labels_path(issue.project, format: :json, include_ancestor_groups: true)
end
end
...@@ -14,7 +14,8 @@ module Clusters ...@@ -14,7 +14,8 @@ module Clusters
else else
check_timeout check_timeout
end end
rescue Kubeclient::HttpError rescue Kubeclient::HttpError => e
Rails.logger.error "Kubernetes error: #{e.class.name} #{e.message}"
app.make_errored!("Kubernetes error") unless app.errored? app.make_errored!("Kubernetes error") unless app.errored?
end end
...@@ -51,7 +52,8 @@ module Clusters ...@@ -51,7 +52,8 @@ module Clusters
def remove_installation_pod def remove_installation_pod
helm_api.delete_pod!(install_command.pod_name) helm_api.delete_pod!(install_command.pod_name)
rescue rescue => e
Rails.logger.error "Kubernetes error: #{e.class.name} #{e.message}"
# no-op # no-op
end end
......
...@@ -45,7 +45,8 @@ module Clusters ...@@ -45,7 +45,8 @@ module Clusters
"ingress" => -> (cluster) { cluster.application_ingress || cluster.build_application_ingress }, "ingress" => -> (cluster) { cluster.application_ingress || cluster.build_application_ingress },
"prometheus" => -> (cluster) { cluster.application_prometheus || cluster.build_application_prometheus }, "prometheus" => -> (cluster) { cluster.application_prometheus || cluster.build_application_prometheus },
"runner" => -> (cluster) { cluster.application_runner || cluster.build_application_runner }, "runner" => -> (cluster) { cluster.application_runner || cluster.build_application_runner },
"jupyter" => -> (cluster) { cluster.application_jupyter || cluster.build_application_jupyter } "jupyter" => -> (cluster) { cluster.application_jupyter || cluster.build_application_jupyter },
"knative" => -> (cluster) { cluster.application_knative || cluster.build_application_knative }
} }
end end
......
...@@ -12,9 +12,11 @@ module Clusters ...@@ -12,9 +12,11 @@ module Clusters
ClusterWaitForAppInstallationWorker.perform_in( ClusterWaitForAppInstallationWorker.perform_in(
ClusterWaitForAppInstallationWorker::INTERVAL, app.name, app.id) ClusterWaitForAppInstallationWorker::INTERVAL, app.name, app.id)
rescue Kubeclient::HttpError rescue Kubeclient::HttpError => e
Rails.logger.error "Kubernetes error: #{e.class.name} #{e.message}"
app.make_errored!("Kubernetes error.") app.make_errored!("Kubernetes error.")
rescue StandardError rescue StandardError => e
Rails.logger.error "Can't start installation process: #{e.class.name} #{e.message}"
app.make_errored!("Can't start installation process.") app.make_errored!("Can't start installation process.")
end end
end end
......
# frozen_string_literal: true
module Commits
class CommitPatchService < CreateService
# Requires:
# - project: `Project` to be committed into
# - user: `User` that will be the committer
# - params:
# - branch_name: `String` the branch that will be committed into
# - start_branch: `String` the branch that will will started from
# - patches: `Gitlab::Git::Patches::Collection` that contains the patches
def initialize(*args)
super
@patches = Gitlab::Git::Patches::Collection.new(Array(params[:patches]))
end
private
def new_branch?
!repository.branch_exists?(@branch_name)
end
def create_commit!
if @start_branch && new_branch?
prepare_branch!
end
Gitlab::Git::Patches::CommitPatches
.new(current_user, project.repository, @branch_name, @patches)
.commit
end
def prepare_branch!
branch_result = CreateBranchService.new(project, current_user)
.execute(@branch_name, @start_branch)
if branch_result[:status] != :success
raise ChangeError, branch_result[:message]
end
end
# Overridden from the Commits::CreateService, to skip some validations we
# don't need:
# - validate_on_branch!
# Not needed, the patches are applied on top of HEAD if the branch did not
# exist
# - validate_branch_existence!
# Not needed because we continue applying patches on the branch if it
# already existed, and create it if it did not exist.
def validate!
validate_patches!
validate_new_branch_name! if new_branch?
validate_permissions!
end
def validate_patches!
raise_error("Patches are too big") unless @patches.valid_size?
end
end
end
...@@ -19,7 +19,12 @@ module Commits ...@@ -19,7 +19,12 @@ module Commits
new_commit = create_commit! new_commit = create_commit!
success(result: new_commit) success(result: new_commit)
rescue ValidationError, ChangeError, Gitlab::Git::Index::IndexError, Gitlab::Git::CommitError, Gitlab::Git::PreReceiveError => ex rescue ValidationError,
ChangeError,
Gitlab::Git::Index::IndexError,
Gitlab::Git::CommitError,
Gitlab::Git::PreReceiveError,
Gitlab::Git::CommandError => ex
error(ex.message) error(ex.message)
end end
......
...@@ -128,12 +128,12 @@ class IssuableBaseService < BaseService ...@@ -128,12 +128,12 @@ class IssuableBaseService < BaseService
merge_quick_actions_into_params!(issuable) merge_quick_actions_into_params!(issuable)
end end
def merge_quick_actions_into_params!(issuable) def merge_quick_actions_into_params!(issuable, only: nil)
original_description = params.fetch(:description, issuable.description) original_description = params.fetch(:description, issuable.description)
description, command_params = description, command_params =
QuickActions::InterpretService.new(project, current_user) QuickActions::InterpretService.new(project, current_user)
.execute(original_description, issuable) .execute(original_description, issuable, only: only)
# Avoid a description already set on an issuable to be overwritten by a nil # Avoid a description already set on an issuable to be overwritten by a nil
params[:description] = description if description params[:description] = description if description
......
...@@ -6,8 +6,12 @@ module MergeRequests ...@@ -6,8 +6,12 @@ module MergeRequests
def execute def execute
@params_issue_iid = params.delete(:issue_iid) @params_issue_iid = params.delete(:issue_iid)
self.merge_request = MergeRequest.new
# TODO: this should handle all quick actions that don't have side effects
# https://gitlab.com/gitlab-org/gitlab-ce/issues/53658
merge_quick_actions_into_params!(merge_request, only: [:target_branch])
merge_request.assign_attributes(params)
self.merge_request = MergeRequest.new(params)
merge_request.author = current_user merge_request.author = current_user
merge_request.compare_commits = [] merge_request.compare_commits = []
merge_request.source_project = find_source_project merge_request.source_project = find_source_project
......
...@@ -50,8 +50,8 @@ module MergeRequests ...@@ -50,8 +50,8 @@ module MergeRequests
end end
def url_for_new_merge_request(branch_name) def url_for_new_merge_request(branch_name)
url = Gitlab::Routing.url_helpers.project_new_merge_request_url(project, branch_name) merge_request_params = { source_branch: branch_name }
url = Gitlab::Routing.url_helpers.project_new_merge_request_url(project, merge_request: merge_request_params)
{ branch_name: branch_name, url: url, new_merge_request: true } { branch_name: branch_name, url: url, new_merge_request: true }
end end
......
...@@ -29,10 +29,6 @@ module MergeRequests ...@@ -29,10 +29,6 @@ module MergeRequests
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def clear_cache(new_diff) def clear_cache(new_diff)
# Executing the iteration we cache highlighted diffs for each diff file of
# MergeRequestDiff.
cacheable_collection(new_diff).write_cache
# Remove cache for all diffs on this MR. Do not use the association on the # Remove cache for all diffs on this MR. Do not use the association on the
# model, as that will interfere with other actions happening when # model, as that will interfere with other actions happening when
# reloading the diff. # reloading the diff.
......
# frozen_string_literal: true
module Notes
class BaseService < ::BaseService
def clear_noteable_diffs_cache(note)
noteable = note.noteable
if note.is_a?(DiffNote) &&
note.discussion_first_note? &&
note.position.unfolded_diff?(project.repository)
noteable.diffs.clear_cache
end
end
end
end
# frozen_string_literal: true # frozen_string_literal: true
module Notes module Notes
class CreateService < ::BaseService class CreateService < ::Notes::BaseService
def execute def execute
merge_request_diff_head_sha = params.delete(:merge_request_diff_head_sha) merge_request_diff_head_sha = params.delete(:merge_request_diff_head_sha)
...@@ -35,6 +35,7 @@ module Notes ...@@ -35,6 +35,7 @@ module Notes
if !only_commands && note.save if !only_commands && note.save
todo_service.new_note(note, current_user) todo_service.new_note(note, current_user)
clear_noteable_diffs_cache(note)
end end
if command_params.present? if command_params.present?
......
# frozen_string_literal: true # frozen_string_literal: true
module Notes module Notes
class DestroyService < BaseService class DestroyService < ::Notes::BaseService
def execute(note) def execute(note)
TodoService.new.destroy_target(note) do |note| TodoService.new.destroy_target(note) do |note|
note.destroy note.destroy
end end
clear_noteable_diffs_cache(note)
end end
end end
end end
...@@ -22,13 +22,13 @@ module QuickActions ...@@ -22,13 +22,13 @@ module QuickActions
# Takes a text and interprets the commands that are extracted from it. # Takes a text and interprets the commands that are extracted from it.
# Returns the content without commands, and hash of changes to be applied to a record. # Returns the content without commands, and hash of changes to be applied to a record.
def execute(content, issuable) def execute(content, issuable, only: nil)
return [content, {}] unless current_user.can?(:use_quick_actions) return [content, {}] unless current_user.can?(:use_quick_actions)
@issuable = issuable @issuable = issuable
@updates = {} @updates = {}
content, commands = extractor.extract_commands(content) content, commands = extractor.extract_commands(content, only: only)
extract_updates(commands) extract_updates(commands)
[content, @updates] [content, @updates]
......
# frozen_string_literal: true
module Submodules
class UpdateService < Commits::CreateService
include Gitlab::Utils::StrongMemoize
def initialize(*args)
super
@start_branch = @branch_name
@commit_sha = params[:commit_sha].presence
@submodule = params[:submodule].presence
@commit_message = params[:commit_message].presence || "Update submodule #{@submodule} with oid #{@commit_sha}"
end
def validate!
super
raise ValidationError, 'The repository is empty' if repository.empty?
end
def execute
super
rescue StandardError => e
error(e.message)
end
def create_commit!
repository.update_submodule(current_user,
@submodule,
@commit_sha,
message: @commit_message,
branch: @branch_name)
rescue ArgumentError, TypeError
raise ValidationError, 'Invalid parameters'
end
end
end
- page_title "Report abuse" - page_title _("Report abuse to GitLab")
%h3.page-title Report abuse %h3.page-title
%p Please use this form to report users who create spam issues, comments or behave inappropriately. = _('Report abuse to GitLab')
%p
= _('Please use this form to report users to GitLab who create spam issues, comments or behave inappropriately.')
%p
= _("A member of GitLab's abuse team will review your report as soon as possible.")
%hr %hr
= form_for @abuse_report, html: { class: 'js-quick-submit js-requires-input'} do |f| = form_for @abuse_report, html: { class: 'js-quick-submit js-requires-input'} do |f|
= form_errors(@abuse_report) = form_errors(@abuse_report)
...@@ -16,7 +20,7 @@ ...@@ -16,7 +20,7 @@
.col-sm-10 .col-sm-10
= f.text_area :message, class: "form-control", rows: 2, required: true, value: sanitize(@ref_url) = f.text_area :message, class: "form-control", rows: 2, required: true, value: sanitize(@ref_url)
.form-text.text-muted .form-text.text-muted
Explain the problem with this user. If appropriate, provide a link to the relevant issue or comment. = _('Explain the problem. If appropriate, provide a link to the relevant issue or comment.')
.form-actions .form-actions
= f.submit "Send report", class: "btn btn-success" = f.submit "Send report", class: "btn btn-success"
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
install_prometheus_path: clusterable.install_applications_cluster_path(@cluster, :prometheus), install_prometheus_path: clusterable.install_applications_cluster_path(@cluster, :prometheus),
install_runner_path: clusterable.install_applications_cluster_path(@cluster, :runner), install_runner_path: clusterable.install_applications_cluster_path(@cluster, :runner),
install_jupyter_path: clusterable.install_applications_cluster_path(@cluster, :jupyter), install_jupyter_path: clusterable.install_applications_cluster_path(@cluster, :jupyter),
install_knative_path: clusterable.install_applications_cluster_path(@cluster, :knative),
toggle_status: @cluster.enabled? ? 'true': 'false', toggle_status: @cluster.enabled? ? 'true': 'false',
cluster_status: @cluster.status_name, cluster_status: @cluster.status_name,
cluster_status_reason: @cluster.status_reason, cluster_status_reason: @cluster.status_reason,
......
...@@ -84,4 +84,6 @@ ...@@ -84,4 +84,6 @@
= link_to 'import flow', status_import_bitbucket_server_path = link_to 'import flow', status_import_bitbucket_server_path
again. again.
= paginate_without_count(@collection)
.js-importer-status{ data: { jobs_import_path: "#{jobs_import_bitbucket_server_path}", import_path: "#{import_bitbucket_server_path}" } } .js-importer-status{ data: { jobs_import_path: "#{jobs_import_bitbucket_server_path}", import_path: "#{import_bitbucket_server_path}" } }
...@@ -71,7 +71,7 @@ ...@@ -71,7 +71,7 @@
= link_to admin_impersonation_path, class: 'nav-link impersonation-btn', method: :delete, title: _('Stop impersonation'), aria: { label: _('Stop impersonation') }, data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do = link_to admin_impersonation_path, class: 'nav-link impersonation-btn', method: :delete, title: _('Stop impersonation'), aria: { label: _('Stop impersonation') }, data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do
= icon('user-secret') = icon('user-secret')
- if header_link?(:sign_in) - if header_link?(:sign_in)
%li.nav-item %li.nav-item.m-auto
%div %div
- sign_in_text = allow_signup? ? _('Sign in / Register') : _('Sign in') - sign_in_text = allow_signup? ? _('Sign in / Register') : _('Sign in')
= link_to sign_in_text, new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in' = link_to sign_in_text, new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in'
......
...@@ -11,8 +11,9 @@ ...@@ -11,8 +11,9 @@
- unless is_current_user - unless is_current_user
%li %li
= link_to new_abuse_report_path(user_id: note.author.id, ref_url: noteable_note_url(note)) do = link_to new_abuse_report_path(user_id: note.author.id, ref_url: noteable_note_url(note)) do
Report as abuse = _('Report abuse to GitLab')
- if note_editable - if note_editable
%li %li
= link_to note_url(note), method: :delete, data: { confirm: 'Are you sure you want to delete this comment?' }, remote: true, class: 'js-note-delete' do = link_to note_url(note), method: :delete, data: { confirm: 'Are you sure you want to delete this comment?' }, remote: true, class: 'js-note-delete' do
%span.text-danger Delete comment %span.text-danger
= _('Delete comment')
...@@ -91,7 +91,7 @@ ...@@ -91,7 +91,7 @@
%code \(\d+.\d+\%\) covered %code \(\d+.\d+\%\) covered
%li %li
pytest-cov (Python) - pytest-cov (Python) -
%code \d+\%\s*$ %code ^TOTAL\s+\d+\s+\d+\s+(\d+\%)$
%li %li
phpunit --coverage-text --colors=never (PHP) - phpunit --coverage-text --colors=never (PHP) -
%code ^\s*Lines:\s*\d+.\d+\% %code ^\s*Lines:\s*\d+.\d+\%
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
%p= _("You can also star a label to make it a priority label.") %p= _("You can also star a label to make it a priority label.")
.text-center .text-center
- if can?(current_user, :admin_label, @project) - if can?(current_user, :admin_label, @project)
= link_to _('New label'), new_project_label_path(@project), class: 'btn btn-success', title: _('New label'), id: 'new_label_link' = link_to _('New label'), new_project_label_path(@project), class: 'btn btn-success qa-label-create-new', title: _('New label'), id: 'new_label_link'
= link_to _('Generate a default set of labels'), generate_project_labels_path(@project), method: :post, class: 'btn btn-success btn-inverted', title: _('Generate a default set of labels'), id: 'generate_labels_link' = link_to _('Generate a default set of labels'), generate_project_labels_path(@project), method: :post, class: 'btn btn-success btn-inverted', title: _('Generate a default set of labels'), id: 'generate_labels_link'
- if can?(current_user, :admin_label, @group) - if can?(current_user, :admin_label, @group)
= link_to _('New label'), new_group_label_path(@group), class: 'btn btn-success', title: _('New label'), id: 'new_label_link' = link_to _('New label'), new_group_label_path(@group), class: 'btn btn-success', title: _('New label'), id: 'new_label_link'
...@@ -40,6 +40,8 @@ class EmailReceiverWorker ...@@ -40,6 +40,8 @@ class EmailReceiverWorker
"You are not allowed to perform this action. If you believe this is in error, contact a staff member." "You are not allowed to perform this action. If you believe this is in error, contact a staff member."
when Gitlab::Email::NoteableNotFoundError when Gitlab::Email::NoteableNotFoundError
"The thread you are replying to no longer exists, perhaps it was deleted? If you believe this is in error, contact a staff member." "The thread you are replying to no longer exists, perhaps it was deleted? If you believe this is in error, contact a staff member."
when Gitlab::Email::InvalidAttachment
error.message
when Gitlab::Email::InvalidRecordError when Gitlab::Email::InvalidRecordError
can_retry = true can_retry = true
error.message error.message
......
---
title: Issue board card design
merge_request: 21229
author:
type: changed
---
title: Add endpoints for simulating certain failure modes in the application
merge_request: 22746
author:
type: other
---
title: Fix environment status in merge request widget
merge_request: 22799
author:
type: changed
---
title: Make new merge request URL more friendly when pushing code
merge_request: 22526
author: "@blackst0ne"
type: changed
---
title: Allow adding patches when creating a merge request via email
merge_request: 22723
author: Serdar Dogruyol
type: added
---
title: Show expand all diffs button when a single diff file is collapsed
merge_request:
author:
type: fixed
---
title: Expose {closed,merged}_{at,by} in merge requests API index
merge_request:
author:
type: changed
---
title: Add endpoint to update a git submodule reference
merge_request: 20949
author:
type: added
---
title: Fix bug with wiki page create message
merge_request: 22849
author:
type: fixed
---
title: Bump Gitaly to 0.129.0
merge_request: 22868
author:
type: added
---
title: Align sign in button
merge_request: 22888
author: George Tsiolis
type: fixed
---
title: Use merge request prefix symbol in event feed title
merge_request: 22449
author: George Tsiolis
type: changed
---
title: Introduce Knative support
author: Chris Baumbauer
merge_request: 43959
type: added
---
title: Allow to configure when to retry failed CI jobs
merge_request: 21758
author: Markus Doits
type: added
---
title: Allow commenting on any diff line in Merge Requests
merge_request: 22398
author:
type: added
---
title: 'Rails5: fix mysql milliseconds issue in deployment model specs'
merge_request: 22850
author: Jasper Maes
type: other
---
title: Remove asset_sync gem from Gemfile and related code from codebase
merge_request: 22610
author:
type: other
---
title: Fix statement timeouts in RemoveRestrictedTodos migration
merge_request: 22795
author:
type: other
---
title: Paginate Bitbucket Server importer projects
merge_request: 22825
author:
type: changed
---
title: Add dynamic timer to delayed jobs
merge_request: 22382
author:
type: changed
AssetSync.configure do |config|
# Disable the asset_sync gem by default. If it is enabled, but not configured,
# asset_sync will cause the build to fail.
config.enabled = if ENV.has_key?('ASSET_SYNC_ENABLED')
ENV['ASSET_SYNC_ENABLED'] == 'true'
else
false
end
# Pulled from https://github.com/AssetSync/asset_sync/blob/v2.2.0/lib/asset_sync/engine.rb#L15-L40
# This allows us to disable asset_sync by default and configure through environment variables
# Updates to asset_sync gem should be checked
config.fog_provider = ENV['FOG_PROVIDER'] if ENV.has_key?('FOG_PROVIDER')
config.fog_directory = ENV['FOG_DIRECTORY'] if ENV.has_key?('FOG_DIRECTORY')
config.fog_region = ENV['FOG_REGION'] if ENV.has_key?('FOG_REGION')
config.aws_access_key_id = ENV['ASSETS_AWS_ACCESS_KEY_ID'] if ENV.has_key?('ASSETS_AWS_ACCESS_KEY_ID')
config.aws_secret_access_key = ENV['ASSETS_AWS_SECRET_ACCESS_KEY'] if ENV.has_key?('ASSETS_AWS_SECRET_ACCESS_KEY')
config.aws_reduced_redundancy = ENV['AWS_REDUCED_REDUNDANCY'] == true if ENV.has_key?('AWS_REDUCED_REDUNDANCY')
config.rackspace_username = ENV['RACKSPACE_USERNAME'] if ENV.has_key?('RACKSPACE_USERNAME')
config.rackspace_api_key = ENV['RACKSPACE_API_KEY'] if ENV.has_key?('RACKSPACE_API_KEY')
config.google_storage_access_key_id = ENV['GOOGLE_STORAGE_ACCESS_KEY_ID'] if ENV.has_key?('GOOGLE_STORAGE_ACCESS_KEY_ID')
config.google_storage_secret_access_key = ENV['GOOGLE_STORAGE_SECRET_ACCESS_KEY'] if ENV.has_key?('GOOGLE_STORAGE_SECRET_ACCESS_KEY')
config.existing_remote_files = ENV['ASSET_SYNC_EXISTING_REMOTE_FILES'] || "keep"
config.gzip_compression = (ENV['ASSET_SYNC_GZIP_COMPRESSION'] == 'true') if ENV.has_key?('ASSET_SYNC_GZIP_COMPRESSION')
config.manifest = (ENV['ASSET_SYNC_MANIFEST'] == 'true') if ENV.has_key?('ASSET_SYNC_MANIFEST')
end
return unless Shard.connected? if Shard.connected? && !Gitlab::Database.read_only?
return if Gitlab::Database.read_only? Shard.populate!
end
Shard.populate!
...@@ -89,6 +89,13 @@ Rails.application.routes.draw do ...@@ -89,6 +89,13 @@ Rails.application.routes.draw do
draw :operations draw :operations
draw :instance_statistics draw :instance_statistics
if ENV['GITLAB_ENABLE_CHAOS_ENDPOINTS']
get '/chaos/leakmem' => 'chaos#leakmem'
get '/chaos/cpuspin' => 'chaos#cpuspin'
get '/chaos/sleep' => 'chaos#sleep'
get '/chaos/kill' => 'chaos#kill'
end
end end
concern :clusterable do concern :clusterable do
......
...@@ -182,7 +182,9 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -182,7 +182,9 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
scope path: 'merge_requests', controller: 'merge_requests/creations' do scope path: 'merge_requests', controller: 'merge_requests/creations' do
post '', action: :create, as: nil post '', action: :create, as: nil
scope path: 'new/(:merge_request_source_branch)', as: :new_merge_request do scope path: 'new', as: :new_merge_request do
get '', action: :new
scope constraints: { format: nil }, action: :new do scope constraints: { format: nil }, action: :new do
get :diffs, defaults: { tab: 'diffs' } get :diffs, defaults: { tab: 'diffs' }
get :pipelines, defaults: { tab: 'pipelines' } get :pipelines, defaults: { tab: 'pipelines' }
...@@ -196,7 +198,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -196,7 +198,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
get :diff_for_path get :diff_for_path
get :branch_from get :branch_from
get :branch_to get :branch_to
get '', action: :new
end end
end end
......
...@@ -23,3 +23,10 @@ has_pick_into_stable_label = gitlab.mr_labels.find { |label| label.start_with?(' ...@@ -23,3 +23,10 @@ has_pick_into_stable_label = gitlab.mr_labels.find { |label| label.start_with?('
if gitlab.branch_for_base != "master" && !has_pick_into_stable_label if gitlab.branch_for_base != "master" && !has_pick_into_stable_label
warn "Most of the time, merge requests should target `master`. Otherwise, please set the relevant `Pick into X.Y` label." warn "Most of the time, merge requests should target `master`. Otherwise, please set the relevant `Pick into X.Y` label."
end end
if gitlab.mr_json['title'].length > 72
warn 'The title of this merge request is longer than 72 characters and ' \
'would violate our commit message rules when using the Squash on Merge ' \
'feature. Please consider adjusting the title, or rebase the ' \
"commits manually and don't use Squash on Merge."
end
# frozen_string_literal: true
class AddKnativeApplication < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
create_table "clusters_applications_knative" do |t|
t.references :cluster, null: false, unique: true, foreign_key: { on_delete: :cascade }
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
t.integer "status", null: false
t.string "version", null: false
t.string "hostname"
t.text "status_reason"
end
end
end
# frozen_string_literal: true
class AddIndexToDeployments < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_index :deployments, [:project_id, :status, :created_at]
end
def down
remove_concurrent_index :deployments, [:project_id, :status, :created_at]
end
end
# frozen_string_literal: true
# rescheduling of the revised RemoveRestrictedTodosWithCte background migration
class RemoveRestrictedTodosAgain < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
MIGRATION = 'RemoveRestrictedTodos'.freeze
BATCH_SIZE = 1000
DELAY_INTERVAL = 5.minutes.to_i
class Project < ActiveRecord::Base
include EachBatch
self.table_name = 'projects'
end
def up
Project.where('EXISTS (SELECT 1 FROM todos WHERE todos.project_id = projects.id)')
.each_batch(of: BATCH_SIZE) do |batch, index|
range = batch.pluck('MIN(id)', 'MAX(id)').first
BackgroundMigrationWorker.perform_in(index * DELAY_INTERVAL, MIGRATION, range)
end
end
def down
# nothing to do
end
end
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20181105201455) do ActiveRecord::Schema.define(version: 20181107054254) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
...@@ -823,6 +823,16 @@ ActiveRecord::Schema.define(version: 20181105201455) do ...@@ -823,6 +823,16 @@ ActiveRecord::Schema.define(version: 20181105201455) do
t.text "status_reason" t.text "status_reason"
end end
create_table "clusters_applications_knative", force: :cascade do |t|
t.integer "cluster_id", null: false
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
t.integer "status", null: false
t.string "version", null: false
t.string "hostname"
t.text "status_reason"
end
create_table "clusters_applications_prometheus", force: :cascade do |t| create_table "clusters_applications_prometheus", force: :cascade do |t|
t.integer "cluster_id", null: false t.integer "cluster_id", null: false
t.integer "status", null: false t.integer "status", null: false
...@@ -956,6 +966,7 @@ ActiveRecord::Schema.define(version: 20181105201455) do ...@@ -956,6 +966,7 @@ ActiveRecord::Schema.define(version: 20181105201455) do
add_index "deployments", ["environment_id", "status"], name: "index_deployments_on_environment_id_and_status", using: :btree add_index "deployments", ["environment_id", "status"], name: "index_deployments_on_environment_id_and_status", using: :btree
add_index "deployments", ["id"], name: "partial_index_deployments_for_legacy_successful_deployments", where: "((finished_at IS NULL) AND (status = 2))", using: :btree add_index "deployments", ["id"], name: "partial_index_deployments_for_legacy_successful_deployments", where: "((finished_at IS NULL) AND (status = 2))", using: :btree
add_index "deployments", ["project_id", "iid"], name: "index_deployments_on_project_id_and_iid", unique: true, using: :btree add_index "deployments", ["project_id", "iid"], name: "index_deployments_on_project_id_and_iid", unique: true, using: :btree
add_index "deployments", ["project_id", "status", "created_at"], name: "index_deployments_on_project_id_and_status_and_created_at", using: :btree
add_index "deployments", ["project_id", "status"], name: "index_deployments_on_project_id_and_status", using: :btree add_index "deployments", ["project_id", "status"], name: "index_deployments_on_project_id_and_status", using: :btree
create_table "draft_notes", id: :bigserial, force: :cascade do |t| create_table "draft_notes", id: :bigserial, force: :cascade do |t|
...@@ -3293,7 +3304,8 @@ ActiveRecord::Schema.define(version: 20181105201455) do ...@@ -3293,7 +3304,8 @@ ActiveRecord::Schema.define(version: 20181105201455) do
add_foreign_key "clusters_applications_ingress", "clusters", on_delete: :cascade add_foreign_key "clusters_applications_ingress", "clusters", on_delete: :cascade
add_foreign_key "clusters_applications_jupyter", "clusters", on_delete: :cascade add_foreign_key "clusters_applications_jupyter", "clusters", on_delete: :cascade
add_foreign_key "clusters_applications_jupyter", "oauth_applications", on_delete: :nullify add_foreign_key "clusters_applications_jupyter", "oauth_applications", on_delete: :nullify
add_foreign_key "clusters_applications_prometheus", "clusters", on_delete: :cascade add_foreign_key "clusters_applications_knative", "clusters", on_delete: :cascade
add_foreign_key "clusters_applications_prometheus", "clusters", name: "fk_557e773639", on_delete: :cascade
add_foreign_key "clusters_applications_runners", "ci_runners", column: "runner_id", name: "fk_02de2ded36", on_delete: :nullify add_foreign_key "clusters_applications_runners", "ci_runners", column: "runner_id", name: "fk_02de2ded36", on_delete: :nullify
add_foreign_key "clusters_applications_runners", "clusters", on_delete: :cascade add_foreign_key "clusters_applications_runners", "clusters", on_delete: :cascade
add_foreign_key "clusters_kubernetes_namespaces", "cluster_projects", on_delete: :nullify add_foreign_key "clusters_kubernetes_namespaces", "cluster_projects", on_delete: :nullify
......
...@@ -68,6 +68,7 @@ following locations: ...@@ -68,6 +68,7 @@ following locations:
- [Protected Tags](protected_tags.md) - [Protected Tags](protected_tags.md)
- [Repositories](repositories.md) - [Repositories](repositories.md)
- [Repository Files](repository_files.md) - [Repository Files](repository_files.md)
- [Repository Submodules](repository_submodules.md)
- [Runners](runners.md) - [Runners](runners.md)
- [Search](search.md) - [Search](search.md)
- [Services](services.md) - [Services](services.md)
...@@ -241,7 +242,7 @@ provided you are authenticated as an administrator with an OAuth or Personal Acc ...@@ -241,7 +242,7 @@ provided you are authenticated as an administrator with an OAuth or Personal Acc
You need to pass the `sudo` parameter either via query string or a header with an ID/username of You need to pass the `sudo` parameter either via query string or a header with an ID/username of
the user you want to perform the operation as. If passed as a header, the the user you want to perform the operation as. If passed as a header, the
header name must be `Sudo`. header name must be `Sudo`.
NOTE: **Note:** NOTE: **Note:**
Usernames are case insensitive. Usernames are case insensitive.
......
...@@ -57,7 +57,18 @@ Parameters: ...@@ -57,7 +57,18 @@ Parameters:
"project_id": 3, "project_id": 3,
"title": "test1", "title": "test1",
"description": "fixed login page css paddings", "description": "fixed login page css paddings",
"state": "opened", "state": "merged",
"merged_by": {
"id": 87854,
"name": "Douwe Maan",
"username": "DouweM",
"state": "active",
"avatar_url": "https://gitlab.example.com/uploads/-/system/user/avatar/87854/avatar.png",
"web_url": "https://gitlab.com/DouweM"
},
"merged_at": "2018-09-07T11:16:17.520Z",
"closed_by": null,
"closed_at": null,
"created_at": "2017-04-29T08:46:00Z", "created_at": "2017-04-29T08:46:00Z",
"updated_at": "2017-04-29T08:46:00Z", "updated_at": "2017-04-29T08:46:00Z",
"target_branch": "master", "target_branch": "master",
...@@ -181,7 +192,18 @@ Parameters: ...@@ -181,7 +192,18 @@ Parameters:
"project_id": 3, "project_id": 3,
"title": "test1", "title": "test1",
"description": "fixed login page css paddings", "description": "fixed login page css paddings",
"state": "opened", "state": "merged",
"merged_by": {
"id": 87854,
"name": "Douwe Maan",
"username": "DouweM",
"state": "active",
"avatar_url": "https://gitlab.example.com/uploads/-/system/user/avatar/87854/avatar.png",
"web_url": "https://gitlab.com/DouweM"
},
"merged_at": "2018-09-07T11:16:17.520Z",
"closed_by": null,
"closed_at": null,
"created_at": "2017-04-29T08:46:00Z", "created_at": "2017-04-29T08:46:00Z",
"updated_at": "2017-04-29T08:46:00Z", "updated_at": "2017-04-29T08:46:00Z",
"target_branch": "master", "target_branch": "master",
...@@ -295,7 +317,18 @@ Parameters: ...@@ -295,7 +317,18 @@ Parameters:
"project_id": 3, "project_id": 3,
"title": "test1", "title": "test1",
"description": "fixed login page css paddings", "description": "fixed login page css paddings",
"state": "opened", "state": "merged",
"merged_by": {
"id": 87854,
"name": "Douwe Maan",
"username": "DouweM",
"state": "active",
"avatar_url": "https://gitlab.example.com/uploads/-/system/user/avatar/87854/avatar.png",
"web_url": "https://gitlab.com/DouweM"
},
"merged_at": "2018-09-07T11:16:17.520Z",
"closed_by": null,
"closed_at": null,
"created_at": "2017-04-29T08:46:00Z", "created_at": "2017-04-29T08:46:00Z",
"updated_at": "2017-04-29T08:46:00Z", "updated_at": "2017-04-29T08:46:00Z",
"target_branch": "master", "target_branch": "master",
...@@ -386,7 +419,7 @@ Parameters: ...@@ -386,7 +419,7 @@ Parameters:
"project_id": 3, "project_id": 3,
"title": "test1", "title": "test1",
"description": "fixed login page css paddings", "description": "fixed login page css paddings",
"state": "opened", "state": "merged",
"created_at": "2017-04-29T08:46:00Z", "created_at": "2017-04-29T08:46:00Z",
"updated_at": "2017-04-29T08:46:00Z", "updated_at": "2017-04-29T08:46:00Z",
"target_branch": "master", "target_branch": "master",
...@@ -708,7 +741,7 @@ order for it to take effect: ...@@ -708,7 +741,7 @@ order for it to take effect:
"project_id": 3, "project_id": 3,
"title": "test1", "title": "test1",
"description": "fixed login page css paddings", "description": "fixed login page css paddings",
"state": "opened", "state": "merged",
"created_at": "2017-04-29T08:46:00Z", "created_at": "2017-04-29T08:46:00Z",
"updated_at": "2017-04-29T08:46:00Z", "updated_at": "2017-04-29T08:46:00Z",
"target_branch": "master", "target_branch": "master",
...@@ -836,7 +869,7 @@ Must include at least one non-required attribute from above. ...@@ -836,7 +869,7 @@ Must include at least one non-required attribute from above.
"project_id": 3, "project_id": 3,
"title": "test1", "title": "test1",
"description": "fixed login page css paddings", "description": "fixed login page css paddings",
"state": "opened", "state": "merged",
"created_at": "2017-04-29T08:46:00Z", "created_at": "2017-04-29T08:46:00Z",
"updated_at": "2017-04-29T08:46:00Z", "updated_at": "2017-04-29T08:46:00Z",
"target_branch": "master", "target_branch": "master",
...@@ -980,7 +1013,7 @@ Parameters: ...@@ -980,7 +1013,7 @@ Parameters:
"project_id": 3, "project_id": 3,
"title": "test1", "title": "test1",
"description": "fixed login page css paddings", "description": "fixed login page css paddings",
"state": "opened", "state": "merged",
"created_at": "2017-04-29T08:46:00Z", "created_at": "2017-04-29T08:46:00Z",
"updated_at": "2017-04-29T08:46:00Z", "updated_at": "2017-04-29T08:46:00Z",
"target_branch": "master", "target_branch": "master",
...@@ -1096,7 +1129,7 @@ Parameters: ...@@ -1096,7 +1129,7 @@ Parameters:
"project_id": 3, "project_id": 3,
"title": "test1", "title": "test1",
"description": "fixed login page css paddings", "description": "fixed login page css paddings",
"state": "opened", "state": "merged",
"created_at": "2017-04-29T08:46:00Z", "created_at": "2017-04-29T08:46:00Z",
"updated_at": "2017-04-29T08:46:00Z", "updated_at": "2017-04-29T08:46:00Z",
"target_branch": "master", "target_branch": "master",
...@@ -1296,7 +1329,7 @@ Example response: ...@@ -1296,7 +1329,7 @@ Example response:
"project_id": 3, "project_id": 3,
"title": "test1", "title": "test1",
"description": "fixed login page css paddings", "description": "fixed login page css paddings",
"state": "opened", "state": "merged",
"created_at": "2017-04-29T08:46:00Z", "created_at": "2017-04-29T08:46:00Z",
"updated_at": "2017-04-29T08:46:00Z", "updated_at": "2017-04-29T08:46:00Z",
"target_branch": "master", "target_branch": "master",
...@@ -1418,7 +1451,7 @@ Example response: ...@@ -1418,7 +1451,7 @@ Example response:
"project_id": 3, "project_id": 3,
"title": "test1", "title": "test1",
"description": "fixed login page css paddings", "description": "fixed login page css paddings",
"state": "opened", "state": "merged",
"created_at": "2017-04-29T08:46:00Z", "created_at": "2017-04-29T08:46:00Z",
"updated_at": "2017-04-29T08:46:00Z", "updated_at": "2017-04-29T08:46:00Z",
"target_branch": "master", "target_branch": "master",
...@@ -1559,7 +1592,7 @@ Example response: ...@@ -1559,7 +1592,7 @@ Example response:
"project_id": 3, "project_id": 3,
"title": "Et voluptas laudantium minus nihil recusandae ut accusamus earum aut non.", "title": "Et voluptas laudantium minus nihil recusandae ut accusamus earum aut non.",
"description": "Veniam sunt nihil modi earum cumque illum delectus. Nihil ad quis distinctio quia. Autem eligendi at quibusdam repellendus.", "description": "Veniam sunt nihil modi earum cumque illum delectus. Nihil ad quis distinctio quia. Autem eligendi at quibusdam repellendus.",
"state": "opened", "state": "merged",
"created_at": "2016-06-17T07:48:04.330Z", "created_at": "2016-06-17T07:48:04.330Z",
"updated_at": "2016-07-01T11:14:15.537Z", "updated_at": "2016-07-01T11:14:15.537Z",
"target_branch": "allow_regex_for_project_skip_ref", "target_branch": "allow_regex_for_project_skip_ref",
......
# Repository submodules API
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/41213) in GitLab 11.5
## Update existing submodule reference in repository
In some workflows, especially automated ones, it can be useful to update a
submodule's reference to keep up to date other projects that use it.
This endpoint allows you to update a [Git submodule](https://git-scm.com/book/en/v2/Git-Tools-Submodules) reference in a
specific branch.
```
PUT /projects/:id/repository/submodules/:submodule
```
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `submodule` | string | yes | URL encoded full path to the submodule. For example, `lib%2Fclass%2Erb` |
| `branch` | string | yes | Name of the branch to commit into |
| `commit_sha` | string | yes | Full commit SHA to update the submodule to |
| `commit_message` | string | no | Commit message. If no message is provided, a default one will be set |
```sh
curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/5/repositories/submodules/lib%2Fmodules%2Fexample"
--data "branch=master&commit_sha=3ddec28ea23acc5caa5d8331a6ecb2a65fc03e88&commit_message=Update submodule reference"
```
Example response:
```json
{
"id": "ed899a2f4b50b4370feeea94676502b42383c746",
"short_id": "ed899a2f4b5",
"title": "Updated submodule example_submodule with oid 3ddec28ea23acc5caa5d8331a6ecb2a65fc03e88",
"author_name": "Dmitriy Zaporozhets",
"author_email": "dzaporozhets@sphereconsultinginc.com",
"committer_name": "Dmitriy Zaporozhets",
"committer_email": "dzaporozhets@sphereconsultinginc.com",
"created_at": "2018-09-20T09:26:24.000-07:00",
"message": "Updated submodule example_submodule with oid 3ddec28ea23acc5caa5d8331a6ecb2a65fc03e88",
"parent_ids": [
"ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba"
],
"committed_date": "2018-09-20T09:26:24.000-07:00",
"authored_date": "2018-09-20T09:26:24.000-07:00",
"status": null
}
```
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment