summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-03-24 18:07:55 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-03-24 18:07:55 +0000
commit603c7d4cac5e28bc1c75e50c23ed2cbe56f1aafc (patch)
tree907f5b8ee1b6f5aad396e95e3327a08400b9e8ea /app
parent120f4aaedc8fe830a3f572491d240d8ee6addefb (diff)
downloadgitlab-ce-603c7d4cac5e28bc1c75e50c23ed2cbe56f1aafc.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/api.js9
-rw-r--r--app/assets/javascripts/code_navigation/components/app.vue3
-rw-r--r--app/assets/javascripts/code_navigation/components/popover.vue13
-rw-r--r--app/assets/javascripts/code_navigation/store/actions.js9
-rw-r--r--app/assets/javascripts/code_navigation/store/mutations.js7
-rw-r--r--app/assets/javascripts/ide/components/branches/search_list.vue4
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/actions.vue4
-rw-r--r--app/assets/javascripts/ide/components/jobs/detail.vue4
-rw-r--r--app/assets/javascripts/ide/components/merge_requests/list.vue4
-rw-r--r--app/assets/javascripts/ide/components/panes/collapsible_sidebar.vue3
-rw-r--r--app/assets/javascripts/ide/components/pipelines/list.vue4
-rw-r--r--app/assets/javascripts/ide/components/preview/clientside.vue6
-rw-r--r--app/assets/javascripts/ide/index.js4
-rw-r--r--app/assets/javascripts/ide/lib/diff/controller.js2
-rw-r--r--app/assets/javascripts/ide/lib/editor.js4
-rw-r--r--app/assets/javascripts/ide/stores/actions.js4
-rw-r--r--app/assets/javascripts/ide/stores/actions/project.js6
-rw-r--r--app/assets/javascripts/ide/stores/actions/tree.js4
-rw-r--r--app/assets/javascripts/logs/components/environment_logs.vue205
-rw-r--r--app/assets/javascripts/logs/components/log_advanced_filters.vue128
-rw-r--r--app/assets/javascripts/logs/components/log_simple_filters.vue73
-rw-r--r--app/assets/javascripts/logs/stores/getters.js8
-rw-r--r--app/assets/stylesheets/pages/builds.scss22
-rw-r--r--app/assets/stylesheets/utilities.scss1
-rw-r--r--app/controllers/admin/services_controller.rb16
-rw-r--r--app/controllers/projects/blob_controller.rb13
-rw-r--r--app/helpers/milestones_helper.rb23
-rw-r--r--app/models/ci/build.rb2
-rw-r--r--app/models/ci/job_artifact.rb10
-rw-r--r--app/models/concerns/milestoneish.rb12
-rw-r--r--app/models/issue.rb2
-rw-r--r--app/models/service.rb33
-rw-r--r--app/uploaders/content_type_whitelist.rb12
-rw-r--r--app/views/projects/blob/_blob.html.haml5
-rw-r--r--app/views/shared/milestones/_issues_tab.html.haml5
-rw-r--r--app/views/shared/milestones/_labels_tab.html.haml6
36 files changed, 412 insertions, 258 deletions
diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js
index 022d79ecf49..14381f63e4b 100644
--- a/app/assets/javascripts/api.js
+++ b/app/assets/javascripts/api.js
@@ -44,7 +44,6 @@ const Api = {
mergeRequestsPipeline: '/api/:version/projects/:id/merge_requests/:merge_request_iid/pipelines',
adminStatisticsPath: '/api/:version/application/statistics',
pipelineSinglePath: '/api/:version/projects/:id/pipelines/:pipeline_id',
- lsifPath: '/api/:version/projects/:id/commits/:commit_id/lsif/info',
environmentsPath: '/api/:version/projects/:id/environments',
group(groupId, callback) {
@@ -474,14 +473,6 @@ const Api = {
return axios.get(url);
},
- lsifData(projectPath, commitId, paths) {
- const url = Api.buildUrl(this.lsifPath)
- .replace(':id', encodeURIComponent(projectPath))
- .replace(':commit_id', commitId);
-
- return axios.get(url, { params: { paths } });
- },
-
environments(id) {
const url = Api.buildUrl(this.environmentsPath).replace(':id', encodeURIComponent(id));
return axios.get(url);
diff --git a/app/assets/javascripts/code_navigation/components/app.vue b/app/assets/javascripts/code_navigation/components/app.vue
index 0e5f1f0485d..0e0160b9832 100644
--- a/app/assets/javascripts/code_navigation/components/app.vue
+++ b/app/assets/javascripts/code_navigation/components/app.vue
@@ -7,7 +7,7 @@ export default {
Popover,
},
computed: {
- ...mapState(['currentDefinition', 'currentDefinitionPosition']),
+ ...mapState(['currentDefinition', 'currentDefinitionPosition', 'definitionPathPrefix']),
},
mounted() {
this.blobViewer = document.querySelector('.blob-viewer');
@@ -39,5 +39,6 @@ export default {
v-if="currentDefinition"
:position="currentDefinitionPosition"
:data="currentDefinition"
+ :definition-path-prefix="definitionPathPrefix"
/>
</template>
diff --git a/app/assets/javascripts/code_navigation/components/popover.vue b/app/assets/javascripts/code_navigation/components/popover.vue
index d5bbe430fcd..f216a4c6e6f 100644
--- a/app/assets/javascripts/code_navigation/components/popover.vue
+++ b/app/assets/javascripts/code_navigation/components/popover.vue
@@ -14,6 +14,10 @@ export default {
type: Object,
required: true,
},
+ definitionPathPrefix: {
+ type: String,
+ required: true,
+ },
},
data() {
return {
@@ -27,6 +31,11 @@ export default {
top: `${this.position.y + this.position.height}px`,
};
},
+ definitionPath() {
+ return (
+ this.data.definition_path && `${this.definitionPathPrefix}/${this.data.definition_path}`
+ );
+ },
},
watch: {
position: {
@@ -67,8 +76,8 @@ export default {
{{ hover.value }}
</p>
</div>
- <div v-if="data.definition_url" class="popover-body">
- <gl-button :href="data.definition_url" target="_blank" class="w-100" variant="default">
+ <div v-if="definitionPath" class="popover-body">
+ <gl-button :href="definitionPath" target="_blank" class="w-100" variant="default">
{{ __('Go to definition') }}
</gl-button>
</div>
diff --git a/app/assets/javascripts/code_navigation/store/actions.js b/app/assets/javascripts/code_navigation/store/actions.js
index 5220b1215b8..9b607023f39 100644
--- a/app/assets/javascripts/code_navigation/store/actions.js
+++ b/app/assets/javascripts/code_navigation/store/actions.js
@@ -1,4 +1,4 @@
-import api from '~/api';
+import axios from '~/lib/utils/axios_utils';
import * as types from './mutation_types';
import { getCurrentHoverElement, setCurrentHoverElement, addInteractionClass } from '../utils';
@@ -12,11 +12,10 @@ export default {
fetchData({ commit, dispatch, state }) {
commit(types.REQUEST_DATA);
- api
- .lsifData(state.projectPath, state.commitId, [state.blobPath])
+ axios
+ .get(state.codeNavUrl)
.then(({ data }) => {
- const dataForPath = data[state.blobPath];
- const normalizedData = dataForPath.reduce((acc, d) => {
+ const normalizedData = data.reduce((acc, d) => {
if (d.hover) {
acc[`${d.start_line}:${d.start_char}`] = d;
addInteractionClass(d);
diff --git a/app/assets/javascripts/code_navigation/store/mutations.js b/app/assets/javascripts/code_navigation/store/mutations.js
index bb833a5adbc..febb7afe2f8 100644
--- a/app/assets/javascripts/code_navigation/store/mutations.js
+++ b/app/assets/javascripts/code_navigation/store/mutations.js
@@ -1,10 +1,9 @@
import * as types from './mutation_types';
export default {
- [types.SET_INITIAL_DATA](state, { projectPath, commitId, blobPath }) {
- state.projectPath = projectPath;
- state.commitId = commitId;
- state.blobPath = blobPath;
+ [types.SET_INITIAL_DATA](state, { codeNavUrl, definitionPathPrefix }) {
+ state.codeNavUrl = codeNavUrl;
+ state.definitionPathPrefix = definitionPathPrefix;
},
[types.REQUEST_DATA](state) {
state.loading = true;
diff --git a/app/assets/javascripts/ide/components/branches/search_list.vue b/app/assets/javascripts/ide/components/branches/search_list.vue
index 31f1dec43ad..76821bcd986 100644
--- a/app/assets/javascripts/ide/components/branches/search_list.vue
+++ b/app/assets/javascripts/ide/components/branches/search_list.vue
@@ -1,6 +1,6 @@
<script>
import { mapActions, mapState } from 'vuex';
-import _ from 'underscore';
+import { debounce } from 'lodash';
import { GlLoadingIcon } from '@gitlab/ui';
import Icon from '~/vue_shared/components/icon.vue';
import Item from './item.vue';
@@ -39,7 +39,7 @@ export default {
loadBranches() {
this.fetchBranches({ search: this.search });
},
- searchBranches: _.debounce(function debounceSearch() {
+ searchBranches: debounce(function debounceSearch() {
this.loadBranches();
}, 250),
focusSearch() {
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/actions.vue b/app/assets/javascripts/ide/components/commit_sidebar/actions.vue
index 2581c3e9928..beff95eb47b 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/actions.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/actions.vue
@@ -1,5 +1,5 @@
<script>
-import _ from 'underscore';
+import { escape as esc } from 'lodash';
import { mapState, mapGetters, createNamespacedHelpers } from 'vuex';
import { sprintf, s__ } from '~/locale';
import consts from '../../stores/modules/commit/constants';
@@ -22,7 +22,7 @@ export default {
commitToCurrentBranchText() {
return sprintf(
s__('IDE|Commit to %{branchName} branch'),
- { branchName: `<strong class="monospace">${_.escape(this.currentBranchId)}</strong>` },
+ { branchName: `<strong class="monospace">${esc(this.currentBranchId)}</strong>` },
false,
);
},
diff --git a/app/assets/javascripts/ide/components/jobs/detail.vue b/app/assets/javascripts/ide/components/jobs/detail.vue
index 7710bfb49ec..504391ffdc7 100644
--- a/app/assets/javascripts/ide/components/jobs/detail.vue
+++ b/app/assets/javascripts/ide/components/jobs/detail.vue
@@ -1,6 +1,6 @@
<script>
import { mapActions, mapState } from 'vuex';
-import _ from 'underscore';
+import { throttle } from 'lodash';
import { __ } from '../../../locale';
import tooltip from '../../../vue_shared/directives/tooltip';
import Icon from '../../../vue_shared/components/icon.vue';
@@ -53,7 +53,7 @@ export default {
this.$refs.buildTrace.scrollTo(0, 0);
}
},
- scrollBuildLog: _.throttle(function buildLogScrollDebounce() {
+ scrollBuildLog: throttle(function buildLogScrollDebounce() {
const { scrollTop } = this.$refs.buildTrace;
const { offsetHeight, scrollHeight } = this.$refs.buildTrace;
diff --git a/app/assets/javascripts/ide/components/merge_requests/list.vue b/app/assets/javascripts/ide/components/merge_requests/list.vue
index 5a8face062b..15c08988977 100644
--- a/app/assets/javascripts/ide/components/merge_requests/list.vue
+++ b/app/assets/javascripts/ide/components/merge_requests/list.vue
@@ -1,6 +1,6 @@
<script>
import { mapActions, mapState } from 'vuex';
-import _ from 'underscore';
+import { debounce } from 'lodash';
import { GlLoadingIcon } from '@gitlab/ui';
import { __ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
@@ -59,7 +59,7 @@ export default {
loadMergeRequests() {
this.fetchMergeRequests({ type: this.type, search: this.search });
},
- searchMergeRequests: _.debounce(function debounceSearch() {
+ searchMergeRequests: debounce(function debounceSearch() {
this.loadMergeRequests();
}, 250),
onSearchFocus() {
diff --git a/app/assets/javascripts/ide/components/panes/collapsible_sidebar.vue b/app/assets/javascripts/ide/components/panes/collapsible_sidebar.vue
index 7f65d089148..8adf0122fb4 100644
--- a/app/assets/javascripts/ide/components/panes/collapsible_sidebar.vue
+++ b/app/assets/javascripts/ide/components/panes/collapsible_sidebar.vue
@@ -1,6 +1,5 @@
<script>
import { mapActions, mapState } from 'vuex';
-import _ from 'underscore';
import tooltip from '~/vue_shared/directives/tooltip';
import Icon from '~/vue_shared/components/icon.vue';
import ResizablePanel from '../resizable_panel.vue';
@@ -55,7 +54,7 @@ export default {
return this.extensionTabs.filter(tab => tab.show);
},
tabViews() {
- return _.flatten(this.tabs.map(tab => tab.views));
+ return this.tabs.map(tab => tab.views).flat();
},
aliveTabViews() {
return this.tabViews.filter(view => this.isAliveView(view.name));
diff --git a/app/assets/javascripts/ide/components/pipelines/list.vue b/app/assets/javascripts/ide/components/pipelines/list.vue
index 3a63fc32639..343b0b6e90c 100644
--- a/app/assets/javascripts/ide/components/pipelines/list.vue
+++ b/app/assets/javascripts/ide/components/pipelines/list.vue
@@ -1,6 +1,6 @@
<script>
import { mapActions, mapGetters, mapState } from 'vuex';
-import _ from 'underscore';
+import { escape as esc } from 'lodash';
import { GlLoadingIcon } from '@gitlab/ui';
import { sprintf, __ } from '../../../locale';
import Icon from '../../../vue_shared/components/icon.vue';
@@ -35,7 +35,7 @@ export default {
return sprintf(
__('You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}.'),
{
- linkStart: `<a href="${_.escape(this.currentProject.web_url)}/-/ci/lint">`,
+ linkStart: `<a href="${esc(this.currentProject.web_url)}/-/ci/lint">`,
linkEnd: '</a>',
},
false,
diff --git a/app/assets/javascripts/ide/components/preview/clientside.vue b/app/assets/javascripts/ide/components/preview/clientside.vue
index aa8d932da6e..86a773499bc 100644
--- a/app/assets/javascripts/ide/components/preview/clientside.vue
+++ b/app/assets/javascripts/ide/components/preview/clientside.vue
@@ -1,6 +1,6 @@
<script>
import { mapActions, mapGetters, mapState } from 'vuex';
-import _ from 'underscore';
+import { isEmpty } from 'lodash';
import { Manager } from 'smooshpack';
import { listen } from 'codesandbox-api';
import { GlLoadingIcon } from '@gitlab/ui';
@@ -78,7 +78,7 @@ export default {
.then(() => this.initPreview());
},
beforeDestroy() {
- if (!_.isEmpty(this.manager)) {
+ if (!isEmpty(this.manager)) {
this.manager.listener();
}
this.manager = {};
@@ -125,7 +125,7 @@ export default {
clearTimeout(this.timeout);
this.timeout = setTimeout(() => {
- if (_.isEmpty(this.manager)) {
+ if (isEmpty(this.manager)) {
this.initPreview();
return;
diff --git a/app/assets/javascripts/ide/index.js b/app/assets/javascripts/ide/index.js
index 9e9d9df8f82..55a0dd848c8 100644
--- a/app/assets/javascripts/ide/index.js
+++ b/app/assets/javascripts/ide/index.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import { mapActions } from 'vuex';
-import _ from 'underscore';
import Translate from '~/vue_shared/translate';
+import { identity } from 'lodash';
import ide from './components/ide.vue';
import store from './stores';
import router from './ide_router';
@@ -31,7 +31,7 @@ Vue.use(Translate);
export function initIde(el, options = {}) {
if (!el) return null;
- const { rootComponent = ide, extendStore = _.identity } = options;
+ const { rootComponent = ide, extendStore = identity } = options;
return new Vue({
el,
diff --git a/app/assets/javascripts/ide/lib/diff/controller.js b/app/assets/javascripts/ide/lib/diff/controller.js
index 046e562ba2b..234a7f903a1 100644
--- a/app/assets/javascripts/ide/lib/diff/controller.js
+++ b/app/assets/javascripts/ide/lib/diff/controller.js
@@ -1,5 +1,5 @@
import { Range } from 'monaco-editor';
-import { throttle } from 'underscore';
+import { throttle } from 'lodash';
import DirtyDiffWorker from './diff_worker';
import Disposable from '../common/disposable';
diff --git a/app/assets/javascripts/ide/lib/editor.js b/app/assets/javascripts/ide/lib/editor.js
index 3d729463cb4..3aff4d30d81 100644
--- a/app/assets/javascripts/ide/lib/editor.js
+++ b/app/assets/javascripts/ide/lib/editor.js
@@ -1,4 +1,4 @@
-import _ from 'underscore';
+import { debounce } from 'lodash';
import { editor as monacoEditor, KeyCode, KeyMod } from 'monaco-editor';
import store from '../stores';
import DecorationsController from './decorations/controller';
@@ -38,7 +38,7 @@ export default class Editor {
setupThemes();
- this.debouncedUpdate = _.debounce(() => {
+ this.debouncedUpdate = debounce(() => {
this.updateDimensions();
}, 200);
}
diff --git a/app/assets/javascripts/ide/stores/actions.js b/app/assets/javascripts/ide/stores/actions.js
index ddc0925efb9..04cf0ad53d5 100644
--- a/app/assets/javascripts/ide/stores/actions.js
+++ b/app/assets/javascripts/ide/stores/actions.js
@@ -1,6 +1,6 @@
import $ from 'jquery';
import Vue from 'vue';
-import _ from 'underscore';
+import { escape as esc } from 'lodash';
import { __, sprintf } from '~/locale';
import { visitUrl } from '~/lib/utils/url_utility';
import flash from '~/flash';
@@ -296,7 +296,7 @@ export const getBranchData = ({ commit, state }, { projectId, branchId, force =
sprintf(
__('Branch not loaded - %{branchId}'),
{
- branchId: `<strong>${_.escape(projectId)}/${_.escape(branchId)}</strong>`,
+ branchId: `<strong>${esc(projectId)}/${esc(branchId)}</strong>`,
},
false,
),
diff --git a/app/assets/javascripts/ide/stores/actions/project.js b/app/assets/javascripts/ide/stores/actions/project.js
index 62084892d13..0b168009847 100644
--- a/app/assets/javascripts/ide/stores/actions/project.js
+++ b/app/assets/javascripts/ide/stores/actions/project.js
@@ -1,4 +1,4 @@
-import _ from 'underscore';
+import { escape as esc } from 'lodash';
import flash from '~/flash';
import { __, sprintf } from '~/locale';
import service from '../../services';
@@ -73,7 +73,7 @@ export const showBranchNotFoundError = ({ dispatch }, branchId) => {
text: sprintf(
__("Branch %{branchName} was not found in this project's repository."),
{
- branchName: `<strong>${_.escape(branchId)}</strong>`,
+ branchName: `<strong>${esc(branchId)}</strong>`,
},
false,
),
@@ -154,7 +154,7 @@ export const openBranch = ({ dispatch, state, getters }, { projectId, branchId,
sprintf(
__('An error occurred while getting files for - %{branchId}'),
{
- branchId: `<strong>${_.escape(projectId)}/${_.escape(branchId)}</strong>`,
+ branchId: `<strong>${esc(projectId)}/${esc(branchId)}</strong>`,
},
false,
),
diff --git a/app/assets/javascripts/ide/stores/actions/tree.js b/app/assets/javascripts/ide/stores/actions/tree.js
index 828e4ed5eb9..7d48f0adc4c 100644
--- a/app/assets/javascripts/ide/stores/actions/tree.js
+++ b/app/assets/javascripts/ide/stores/actions/tree.js
@@ -1,4 +1,4 @@
-import _ from 'underscore';
+import { defer } from 'lodash';
import { __ } from '../../../locale';
import service from '../../services';
import * as types from '../mutation_types';
@@ -71,7 +71,7 @@ export const getFiles = ({ state, commit, dispatch }, payload = {}) =>
// Defer setting the directory data because this triggers some intense rendering.
// The entries is all we need to load the file editor.
- _.defer(() => dispatch('setDirectoryData', { projectId, branchId, treeList }));
+ defer(() => dispatch('setDirectoryData', { projectId, branchId, treeList }));
resolve();
})
diff --git a/app/assets/javascripts/logs/components/environment_logs.vue b/app/assets/javascripts/logs/components/environment_logs.vue
index 92d4be81c75..70b3af8dc75 100644
--- a/app/assets/javascripts/logs/components/environment_logs.vue
+++ b/app/assets/javascripts/logs/components/environment_logs.vue
@@ -7,17 +7,15 @@ import {
GlAlert,
GlDropdown,
GlDropdownHeader,
- GlDropdownDivider,
GlDropdownItem,
- GlFormGroup,
- GlSearchBoxByClick,
GlInfiniteScroll,
} from '@gitlab/ui';
-import { s__ } from '~/locale';
-import DateTimePicker from '~/vue_shared/components/date_time_picker/date_time_picker.vue';
+
+import LogSimpleFilters from './log_simple_filters.vue';
+import LogAdvancedFilters from './log_advanced_filters.vue';
import LogControlButtons from './log_control_buttons.vue';
-import { timeRanges, defaultTimeRange } from '~/vue_shared/constants';
+import { defaultTimeRange } from '~/vue_shared/constants';
import { timeRangeFromUrl } from '~/monitoring/utils';
import { formatDate } from '../utils';
@@ -28,12 +26,10 @@ export default {
GlAlert,
GlDropdown,
GlDropdownHeader,
- GlDropdownDivider,
GlDropdownItem,
- GlFormGroup,
- GlSearchBoxByClick,
GlInfiniteScroll,
- DateTimePicker,
+ LogSimpleFilters,
+ LogAdvancedFilters,
LogControlButtons,
},
filters: {
@@ -63,49 +59,22 @@ export default {
traceHeight: 600,
data() {
return {
- searchQuery: '',
- timeRanges,
isElasticStackCalloutDismissed: false,
scrollDownButtonDisabled: true,
};
},
computed: {
...mapState('environmentLogs', ['environments', 'timeRange', 'logs', 'pods']),
- ...mapGetters('environmentLogs', ['trace']),
-
- timeRangeModel: {
- get() {
- return this.timeRange.selected;
- },
- set(val) {
- this.setTimeRange(val);
- },
- },
+ ...mapGetters('environmentLogs', ['trace', 'showAdvancedFilters']),
showLoader() {
return this.logs.isLoading;
},
- advancedFeaturesEnabled() {
- const environment = this.environments.options.find(
- ({ name }) => name === this.environments.current,
- );
- return environment && environment.enable_advanced_logs_querying;
- },
- disableAdvancedControls() {
- return this.environments.isLoading || !this.advancedFeaturesEnabled;
- },
shouldShowElasticStackCallout() {
- return !this.isElasticStackCalloutDismissed && this.disableAdvancedControls;
- },
-
- podDropdownText() {
- if (this.pods.current) {
- return this.pods.current;
- } else if (this.advancedFeaturesEnabled) {
- // "All pods" is a valid option when advanced querying is available
- return s__('Environments|All pods');
- }
- return s__('Environments|No pod selected');
+ return (
+ !this.isElasticStackCalloutDismissed &&
+ (this.environments.isLoading || !this.showAdvancedFilters)
+ );
},
},
mounted() {
@@ -121,7 +90,6 @@ export default {
...mapActions('environmentLogs', [
'setInitData',
'setSearch',
- 'setTimeRange',
'showPodLogs',
'showEnvironment',
'fetchEnvironments',
@@ -131,9 +99,6 @@ export default {
isCurrentEnvironment(envName) {
return envName === this.environments.current;
},
- isCurrentPod(podName) {
- return podName === this.pods.current;
- },
topReached() {
if (!this.logs.isLoading) {
this.fetchMoreLogsPrepend();
@@ -167,123 +132,49 @@ export default {
</strong>
</a>
</gl-alert>
- <div class="top-bar js-top-bar d-flex">
- <div class="row mx-n1">
- <gl-form-group
- id="environments-dropdown-fg"
- label-size="sm"
- label-for="environments-dropdown"
- class="col-3 px-1"
+ <div class="top-bar d-md-flex border bg-secondary-50 pt-2 pr-1 pb-0 pl-2">
+ <div class="flex-grow-0">
+ <gl-dropdown
+ id="environments-dropdown"
+ :text="environments.current"
+ :disabled="environments.isLoading"
+ class="mb-2 gl-h-32 pr-2 d-flex d-md-block js-environments-dropdown"
>
- <gl-dropdown
- id="environments-dropdown"
- :text="environments.current"
- :disabled="environments.isLoading"
- class="d-flex gl-h-32 js-environments-dropdown"
- toggle-class="dropdown-menu-toggle"
+ <gl-dropdown-header class="text-center">
+ {{ s__('Environments|Select environment') }}
+ </gl-dropdown-header>
+ <gl-dropdown-item
+ v-for="env in environments.options"
+ :key="env.id"
+ @click="showEnvironment(env.name)"
>
- <gl-dropdown-header class="text-center">
- {{ s__('Environments|Select environment') }}
- </gl-dropdown-header>
- <gl-dropdown-item
- v-for="env in environments.options"
- :key="env.id"
- @click="showEnvironment(env.name)"
- >
- <div class="d-flex">
- <gl-icon
- :class="{ invisible: !isCurrentEnvironment(env.name) }"
- name="status_success_borderless"
- />
- <div class="flex-grow-1">{{ env.name }}</div>
- </div>
- </gl-dropdown-item>
- </gl-dropdown>
- </gl-form-group>
-
- <gl-form-group
- id="pods-dropdown-fg"
- label-size="sm"
- label-for="pods-dropdown"
- class="col-3 px-1"
- >
- <gl-dropdown
- id="pods-dropdown"
- :text="podDropdownText"
- :disabled="environments.isLoading"
- class="d-flex gl-h-32 js-pods-dropdown"
- toggle-class="dropdown-menu-toggle"
- >
- <gl-dropdown-header class="text-center">
- {{ s__('Environments|Filter by pod') }}
- </gl-dropdown-header>
-
- <template v-if="advancedFeaturesEnabled">
- <gl-dropdown-item key="all-pods" @click="showPodLogs(null)">
- <div class="d-flex">
- <gl-icon
- :class="{ invisible: !isCurrentPod(null) }"
- name="status_success_borderless"
- />
- <div class="flex-grow-1">{{ s__('Environments|All pods') }}</div>
- </div>
- </gl-dropdown-item>
- <gl-dropdown-divider />
- </template>
-
- <gl-dropdown-item v-if="!pods.options.length" :disabled="true">
- <span class="text-muted">
- {{ s__('Environments|No pods to display') }}
- </span>
- </gl-dropdown-item>
- <gl-dropdown-item
- v-for="podName in pods.options"
- :key="podName"
- class="text-nowrap"
- @click="showPodLogs(podName)"
- >
- <div class="d-flex">
- <gl-icon
- :class="{ invisible: !isCurrentPod(podName) }"
- name="status_success_borderless"
- />
- <div class="flex-grow-1">{{ podName }}</div>
- </div>
- </gl-dropdown-item>
- </gl-dropdown>
- </gl-form-group>
- <gl-form-group id="search-fg" label-size="sm" label-for="search" class="col-3 px-1">
- <gl-search-box-by-click
- v-model.trim="searchQuery"
- :disabled="disableAdvancedControls"
- :placeholder="s__('Environments|Search')"
- class="js-logs-search"
- type="search"
- autofocus
- @submit="setSearch(searchQuery)"
- />
- </gl-form-group>
-
- <gl-form-group
- id="dates-fg"
- label-size="sm"
- label-for="time-window-dropdown"
- class="col-3 px-1"
- >
- <date-time-picker
- ref="dateTimePicker"
- v-model="timeRangeModel"
- class="w-100 gl-h-32"
- right
- :disabled="disableAdvancedControls"
- :options="timeRanges"
- />
- </gl-form-group>
+ <div class="d-flex">
+ <gl-icon
+ :class="{ invisible: !isCurrentEnvironment(env.name) }"
+ name="status_success_borderless"
+ />
+ <div class="flex-grow-1">{{ env.name }}</div>
+ </div>
+ </gl-dropdown-item>
+ </gl-dropdown>
</div>
+ <log-advanced-filters
+ v-if="showAdvancedFilters"
+ ref="log-advanced-filters"
+ class="d-md-flex flex-grow-1"
+ :disabled="environments.isLoading"
+ />
+ <log-simple-filters
+ v-else
+ ref="log-simple-filters"
+ class="d-md-flex flex-grow-1"
+ :disabled="environments.isLoading"
+ />
+
<log-control-buttons
ref="scrollButtons"
- class="controllers"
+ class="flex-grow-0 pr-2 mb-2 controllers"
:scroll-down-button-disabled="scrollDownButtonDisabled"
@refresh="showPodLogs(pods.current)"
@scrollDown="scrollDown"
diff --git a/app/assets/javascripts/logs/components/log_advanced_filters.vue b/app/assets/javascripts/logs/components/log_advanced_filters.vue
new file mode 100644
index 00000000000..dfbd858bf18
--- /dev/null
+++ b/app/assets/javascripts/logs/components/log_advanced_filters.vue
@@ -0,0 +1,128 @@
+<script>
+import { s__ } from '~/locale';
+import DateTimePicker from '~/vue_shared/components/date_time_picker/date_time_picker.vue';
+import { mapActions, mapState } from 'vuex';
+import {
+ GlIcon,
+ GlDropdown,
+ GlDropdownHeader,
+ GlDropdownDivider,
+ GlDropdownItem,
+ GlSearchBoxByClick,
+} from '@gitlab/ui';
+import { timeRanges } from '~/vue_shared/constants';
+
+export default {
+ components: {
+ GlIcon,
+ GlDropdown,
+ GlDropdownHeader,
+ GlDropdownDivider,
+ GlDropdownItem,
+ GlSearchBoxByClick,
+ DateTimePicker,
+ },
+ props: {
+ disabled: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ data() {
+ return {
+ timeRanges,
+ searchQuery: '',
+ };
+ },
+ computed: {
+ ...mapState('environmentLogs', ['timeRange', 'pods']),
+
+ timeRangeModel: {
+ get() {
+ return this.timeRange.selected;
+ },
+ set(val) {
+ this.setTimeRange(val);
+ },
+ },
+
+ podDropdownText() {
+ return this.pods.current || s__('Environments|All pods');
+ },
+ },
+ methods: {
+ ...mapActions('environmentLogs', ['setSearch', 'showPodLogs', 'setTimeRange']),
+ isCurrentPod(podName) {
+ return podName === this.pods.current;
+ },
+ },
+};
+</script>
+<template>
+ <div>
+ <gl-dropdown
+ ref="podsDropdown"
+ :text="podDropdownText"
+ :disabled="disabled"
+ class="mb-2 gl-h-32 pr-2 d-flex d-md-block flex-grow-0 qa-pods-dropdown"
+ >
+ <gl-dropdown-header class="text-center">
+ {{ s__('Environments|Filter by pod') }}
+ </gl-dropdown-header>
+
+ <gl-dropdown-item v-if="!pods.options.length" disabled>
+ <span ref="noPodsMsg" class="text-muted">
+ {{ s__('Environments|No pods to display') }}
+ </span>
+ </gl-dropdown-item>
+
+ <template v-else>
+ <gl-dropdown-item ref="allPodsOption" key="all-pods" @click="showPodLogs(null)">
+ <div class="d-flex">
+ <gl-icon
+ :class="{ invisible: pods.current !== null }"
+ name="status_success_borderless"
+ />
+ <div class="flex-grow-1">{{ s__('Environments|All pods') }}</div>
+ </div>
+ </gl-dropdown-item>
+ <gl-dropdown-divider />
+ <gl-dropdown-item
+ v-for="podName in pods.options"
+ :key="podName"
+ class="text-nowrap"
+ @click="showPodLogs(podName)"
+ >
+ <div class="d-flex">
+ <gl-icon
+ :class="{ invisible: !isCurrentPod(podName) }"
+ name="status_success_borderless"
+ />
+ <div class="flex-grow-1">{{ podName }}</div>
+ </div>
+ </gl-dropdown-item>
+ </template>
+ </gl-dropdown>
+
+ <gl-search-box-by-click
+ ref="searchBox"
+ v-model.trim="searchQuery"
+ :disabled="disabled"
+ :placeholder="s__('Environments|Search')"
+ class="mb-2 pr-2 flex-grow-1"
+ type="search"
+ autofocus
+ @submit="setSearch(searchQuery)"
+ />
+
+ <date-time-picker
+ ref="dateTimePicker"
+ v-model="timeRangeModel"
+ :disabled="disabled"
+ :options="timeRanges"
+ class="mb-2 gl-h-32 pr-2 d-block date-time-picker-wrapper"
+ right
+ />
+ </div>
+</template>
diff --git a/app/assets/javascripts/logs/components/log_simple_filters.vue b/app/assets/javascripts/logs/components/log_simple_filters.vue
new file mode 100644
index 00000000000..21fe1695624
--- /dev/null
+++ b/app/assets/javascripts/logs/components/log_simple_filters.vue
@@ -0,0 +1,73 @@
+<script>
+import { s__ } from '~/locale';
+import { mapActions, mapState } from 'vuex';
+import { GlIcon, GlDropdown, GlDropdownHeader, GlDropdownItem } from '@gitlab/ui';
+
+export default {
+ components: {
+ GlIcon,
+ GlDropdown,
+ GlDropdownHeader,
+ GlDropdownItem,
+ },
+ props: {
+ disabled: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ data() {
+ return {
+ searchQuery: '',
+ };
+ },
+ computed: {
+ ...mapState('environmentLogs', ['pods']),
+
+ podDropdownText() {
+ return this.pods.current || s__('Environments|No pod selected');
+ },
+ },
+ methods: {
+ ...mapActions('environmentLogs', ['showPodLogs']),
+ isCurrentPod(podName) {
+ return podName === this.pods.current;
+ },
+ },
+};
+</script>
+<template>
+ <div>
+ <gl-dropdown
+ ref="podsDropdown"
+ :text="podDropdownText"
+ :disabled="disabled"
+ class="mb-2 gl-h-32 pr-2 d-flex d-md-block flex-grow-0 qa-pods-dropdown"
+ >
+ <gl-dropdown-header class="text-center">
+ {{ s__('Environments|Select pod') }}
+ </gl-dropdown-header>
+
+ <gl-dropdown-item v-if="!pods.options.length" disabled>
+ <span ref="noPodsMsg" class="text-muted">
+ {{ s__('Environments|No pods to display') }}
+ </span>
+ </gl-dropdown-item>
+ <gl-dropdown-item
+ v-for="podName in pods.options"
+ :key="podName"
+ class="text-nowrap"
+ @click="showPodLogs(podName)"
+ >
+ <div class="d-flex">
+ <gl-icon
+ :class="{ invisible: !isCurrentPod(podName) }"
+ name="status_success_borderless"
+ />
+ <div class="flex-grow-1">{{ podName }}</div>
+ </div>
+ </gl-dropdown-item>
+ </gl-dropdown>
+ </div>
+</template>
diff --git a/app/assets/javascripts/logs/stores/getters.js b/app/assets/javascripts/logs/stores/getters.js
index 8770306fdd6..d92969c5389 100644
--- a/app/assets/javascripts/logs/stores/getters.js
+++ b/app/assets/javascripts/logs/stores/getters.js
@@ -5,5 +5,9 @@ const mapTrace = ({ timestamp = null, pod = '', message = '' }) =>
export const trace = state => state.logs.lines.map(mapTrace).join('\n');
-// prevent babel-plugin-rewire from generating an invalid default during karma tests
-export default () => {};
+export const showAdvancedFilters = state => {
+ const environment = state.environments.options.find(
+ ({ name }) => name === state.environments.current,
+ );
+ return Boolean(environment?.enable_advanced_logs_querying);
+};
diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss
index 11687378e20..0ecb38a1ea7 100644
--- a/app/assets/stylesheets/pages/builds.scss
+++ b/app/assets/stylesheets/pages/builds.scss
@@ -379,25 +379,19 @@
}
.top-bar {
- @include build-trace-top-bar($gl-line-height * 3);
- position: relative;
- top: 0;
-
- .dropdown-menu-toggle {
- width: 200px;
+ .date-time-picker-wrapper,
+ .dropdown-toggle {
+ @include media-breakpoint-up(md) {
+ width: 140px;
+ }
- @include media-breakpoint-up(sm) {
- width: 300px;
+ @include media-breakpoint-up(lg) {
+ width: 160px;
}
}
.controllers {
- @include build-controllers(16px, flex-end, true, 2);
- }
-
- .refresh-control {
- @include build-controllers(16px, flex-end, true, 0);
- margin-left: 2px;
+ @include build-controllers(16px, flex-end, false, 2);
}
}
diff --git a/app/assets/stylesheets/utilities.scss b/app/assets/stylesheets/utilities.scss
index 00d738a50be..f161d76c623 100644
--- a/app/assets/stylesheets/utilities.scss
+++ b/app/assets/stylesheets/utilities.scss
@@ -43,6 +43,7 @@
.border-color-blue-300 { border-color: $blue-300; }
.border-color-default { border-color: $border-color; }
.border-bottom-color-default { border-bottom-color: $border-color; }
+.border-radius-default { border-radius: $border-radius-default; }
.box-shadow-default { box-shadow: 0 2px 4px 0 $black-transparent; }
.gl-children-ml-sm-3 > * {
diff --git a/app/controllers/admin/services_controller.rb b/app/controllers/admin/services_controller.rb
index 2f554519632..55817550b4b 100644
--- a/app/controllers/admin/services_controller.rb
+++ b/app/controllers/admin/services_controller.rb
@@ -3,11 +3,10 @@
class Admin::ServicesController < Admin::ApplicationController
include ServiceParams
- before_action :whitelist_query_limiting, only: [:index]
before_action :service, only: [:edit, :update]
def index
- @services = services_templates
+ @services = Service.find_or_create_templates
end
def edit
@@ -31,21 +30,8 @@ class Admin::ServicesController < Admin::ApplicationController
private
# rubocop: disable CodeReuse/ActiveRecord
- def services_templates
- Service.available_services_names.map do |service_name|
- service_template = "#{service_name}_service".camelize.constantize
- service_template.where(template: true).first_or_create
- end
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
- # rubocop: disable CodeReuse/ActiveRecord
def service
@service ||= Service.find_by(id: params[:id], template: true)
end
# rubocop: enable CodeReuse/ActiveRecord
-
- def whitelist_query_limiting
- Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42430')
- end
end
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index 5788fc17a9b..8c8824ae47f 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -208,11 +208,24 @@ class Projects::BlobController < Projects::ApplicationController
.last_for_path(@repository, @ref, @path).sha
end
+ def set_code_navigation_build
+ return if Feature.disabled?(:code_navigation, @project)
+
+ artifact =
+ Ci::JobArtifact
+ .for_sha(@blob.commit_id, @project.id)
+ .for_job_name(Ci::Build::CODE_NAVIGATION_JOB_NAME)
+ .last
+
+ @code_navigation_build = artifact&.job
+ end
+
def show_html
environment_params = @repository.branch_exists?(@ref) ? { ref: @ref } : { commit: @commit }
environment_params[:find_latest] = true
@environment = EnvironmentsFinder.new(@project, current_user, environment_params).execute.last
@last_commit = @repository.last_commit_for_path(@commit.id, @blob.path)
+ set_code_navigation_build
render 'show'
end
diff --git a/app/helpers/milestones_helper.rb b/app/helpers/milestones_helper.rb
index da6a0e38c44..2f5aac892ab 100644
--- a/app/helpers/milestones_helper.rb
+++ b/app/helpers/milestones_helper.rb
@@ -26,7 +26,7 @@ module MilestonesHelper
end
end
- def milestones_label_path(opts = {})
+ def milestones_issues_path(opts = {})
if @project
project_issues_path(@project, opts)
elsif @group
@@ -283,6 +283,27 @@ module MilestonesHelper
can?(current_user, :admin_milestone, @project.group)
end
end
+
+ def display_issues_count_warning?(milestone)
+ milestone_visible_issues_count(milestone) > Milestone::DISPLAY_ISSUES_LIMIT
+ end
+
+ def milestone_issues_count_message(milestone)
+ total_count = milestone_visible_issues_count(milestone)
+ limit = Milestone::DISPLAY_ISSUES_LIMIT
+ link_options = { milestone_title: @milestone.title }
+
+ message = _('Showing %{limit} of %{total_count} issues. ') % { limit: limit, total_count: total_count }
+ message += link_to(_('View all issues'), milestones_issues_path(link_options))
+
+ message.html_safe
+ end
+
+ private
+
+ def milestone_visible_issues_count(milestone)
+ @milestone_visible_issues_count ||= milestone.issues_visible_to_user(current_user).size
+ end
end
MilestonesHelper.prepend_if_ee('EE::MilestonesHelper')
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index b555b78cda6..d0ea7439556 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -33,6 +33,8 @@ module Ci
scheduler_failure: 2
}.freeze
+ CODE_NAVIGATION_JOB_NAME = 'code_navigation'
+
has_one :deployment, as: :deployable, class_name: 'Deployment'
has_one :resource, class_name: 'Ci::Resource', inverse_of: :build
has_many :trace_sections, class_name: 'Ci::BuildTraceSection'
diff --git a/app/models/ci/job_artifact.rb b/app/models/ci/job_artifact.rb
index ae57da9c546..ef0701b3874 100644
--- a/app/models/ci/job_artifact.rb
+++ b/app/models/ci/job_artifact.rb
@@ -31,7 +31,8 @@ module Ci
metrics: 'metrics.txt',
lsif: 'lsif.json',
dotenv: '.env',
- cobertura: 'cobertura-coverage.xml'
+ cobertura: 'cobertura-coverage.xml',
+ terraform: 'tfplan.json'
}.freeze
INTERNAL_TYPES = {
@@ -59,7 +60,8 @@ module Ci
dast: :raw,
license_management: :raw,
license_scanning: :raw,
- performance: :raw
+ performance: :raw,
+ terraform: :raw
}.freeze
TYPE_AND_FORMAT_PAIRS = INTERNAL_TYPES.merge(REPORT_TYPES).freeze
@@ -80,6 +82,7 @@ module Ci
scope :with_files_stored_locally, -> { where(file_store: [nil, ::JobArtifactUploader::Store::LOCAL]) }
scope :with_files_stored_remotely, -> { where(file_store: ::JobArtifactUploader::Store::REMOTE) }
scope :for_sha, ->(sha, project_id) { joins(job: :pipeline).where(ci_pipelines: { sha: sha, project_id: project_id }) }
+ scope :for_job_name, ->(name) { joins(:job).where(ci_builds: { name: name }) }
scope :with_file_types, -> (file_types) do
types = self.file_types.select { |file_type| file_types.include?(file_type) }.values
@@ -129,7 +132,8 @@ module Ci
network_referee: 14, ## runner referees
lsif: 15, # LSIF data for code navigation
dotenv: 16,
- cobertura: 17
+ cobertura: 17,
+ terraform: 18 # Transformed json
}
enum file_format: {
diff --git a/app/models/concerns/milestoneish.rb b/app/models/concerns/milestoneish.rb
index 6dbb9649b9f..fac058e5a46 100644
--- a/app/models/concerns/milestoneish.rb
+++ b/app/models/concerns/milestoneish.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
module Milestoneish
+ DISPLAY_ISSUES_LIMIT = 3000
+
def total_issues_count
@total_issues_count ||= Milestones::IssuesCountService.new(self).count
end
@@ -55,7 +57,15 @@ module Milestoneish
end
def sorted_issues(user)
- issues_visible_to_user(user).preload_associated_models.sort_by_attribute('label_priority')
+ # This method is used on milestone view to filter opened assigned, opened unassigned and closed issues columns.
+ # We want a limit of DISPLAY_ISSUES_LIMIT for total issues present on all columns.
+ limited_ids =
+ issues_visible_to_user(user).sort_by_attribute('label_priority').limit(DISPLAY_ISSUES_LIMIT)
+
+ Issue
+ .where(id: Issue.select(:id).from(limited_ids))
+ .preload_associated_models
+ .sort_by_attribute('label_priority')
end
def sorted_merge_requests(user)
diff --git a/app/models/issue.rb b/app/models/issue.rb
index bdcebb4b942..0f00a78c728 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -70,7 +70,7 @@ class Issue < ApplicationRecord
scope :order_closed_date_desc, -> { reorder(closed_at: :desc) }
scope :order_created_at_desc, -> { reorder(created_at: :desc) }
- scope :preload_associated_models, -> { preload(:labels, project: :namespace) }
+ scope :preload_associated_models, -> { preload(:assignees, :labels, project: :namespace) }
scope :with_api_entity_associations, -> { preload(:timelogs, :assignees, :author, :notes, :labels, project: [:route, { namespace: :route }] ) }
scope :public_only, -> { where(confidential: false) }
diff --git a/app/models/service.rb b/app/models/service.rb
index 5782fab3266..138da0c546e 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -46,6 +46,7 @@ class Service < ApplicationRecord
scope :active, -> { where(active: true) }
scope :without_defaults, -> { where(default: false) }
scope :by_type, -> (type) { where(type: type) }
+ scope :templates, -> { where(template: true, type: available_services_types) }
scope :push_hooks, -> { where(push_events: true, active: true) }
scope :tag_push_hooks, -> { where(tag_push_events: true, active: true) }
@@ -259,14 +260,32 @@ class Service < ApplicationRecord
self.category == :issue_tracker
end
+ # Find all service templates; if some of them do not exist, create them
+ # within a transaction to perform the lowest possible SQL queries.
+ def self.find_or_create_templates
+ create_nonexistent_templates
+ templates
+ end
+
+ private_class_method def self.create_nonexistent_templates
+ nonexistent_services = available_services_types - templates.map(&:type)
+ return if nonexistent_services.empty?
+
+ transaction do
+ nonexistent_services.each do |service_type|
+ service_type.constantize.create(template: true)
+ end
+ end
+ end
+
def self.available_services_names
service_names = %w[
alerts
asana
assembla
bamboo
- buildkite
bugzilla
+ buildkite
campfire
custom_issue_tracker
discord
@@ -278,20 +297,20 @@ class Service < ApplicationRecord
hipchat
irker
jira
- mattermost_slash_commands
mattermost
+ mattermost_slash_commands
+ microsoft_teams
packagist
pipelines_email
pivotaltracker
prometheus
pushover
redmine
- youtrack
- slack_slash_commands
slack
+ slack_slash_commands
teamcity
- microsoft_teams
unify_circuit
+ youtrack
]
if Rails.env.development?
@@ -301,6 +320,10 @@ class Service < ApplicationRecord
service_names.sort_by(&:downcase)
end
+ def self.available_services_types
+ available_services_names.map { |service_name| "#{service_name}_service".camelize }
+ end
+
def self.build_from_template(project_id, template)
service = template.dup
diff --git a/app/uploaders/content_type_whitelist.rb b/app/uploaders/content_type_whitelist.rb
index b3975d7e2e0..3210d57b00c 100644
--- a/app/uploaders/content_type_whitelist.rb
+++ b/app/uploaders/content_type_whitelist.rb
@@ -26,14 +26,14 @@ module ContentTypeWhitelist
# Here we override and extend CarrierWave's method that does not parse the
# magic headers.
def check_content_type_whitelist!(new_file)
- new_file.content_type = mime_magic_content_type(new_file.path)
+ if content_type_whitelist
+ content_type = mime_magic_content_type(new_file.path)
- if content_type_whitelist && !whitelisted_content_type?(new_file.content_type)
- message = I18n.translate(:"errors.messages.content_type_whitelist_error", allowed_types: Array(content_type_whitelist).join(", "))
- raise CarrierWave::IntegrityError, message
+ unless whitelisted_content_type?(content_type)
+ message = I18n.translate(:"errors.messages.content_type_whitelist_error", allowed_types: Array(content_type_whitelist).join(", "))
+ raise CarrierWave::IntegrityError, message
+ end
end
-
- super(new_file)
end
def whitelisted_content_type?(content_type)
diff --git a/app/views/projects/blob/_blob.html.haml b/app/views/projects/blob/_blob.html.haml
index 91d1fc06a41..02a327c5a49 100644
--- a/app/views/projects/blob/_blob.html.haml
+++ b/app/views/projects/blob/_blob.html.haml
@@ -9,8 +9,9 @@
= render "projects/blob/auxiliary_viewer", blob: blob
#blob-content-holder.blob-content-holder
- - if native_code_navigation_enabled?(@project)
- #js-code-navigation{ data: { commit_id: blob.commit_id, blob_path: blob.path, project_path: @project.full_path } }
+ - if @code_navigation_build
+ - code_nav_url = raw_project_job_artifacts_url(@project, @code_navigation_build, path: "lsif/#{blob.path}")
+ #js-code-navigation{ data: { code_nav_url: "#{code_nav_url}.json", definition_path_prefix: project_blob_path(@project, @ref) } }
%article.file-holder
= render 'projects/blob/header', blob: blob
= render 'projects/blob/content', blob: blob
diff --git a/app/views/shared/milestones/_issues_tab.html.haml b/app/views/shared/milestones/_issues_tab.html.haml
index a8db7f8a556..d7e4f2ed5a0 100644
--- a/app/views/shared/milestones/_issues_tab.html.haml
+++ b/app/views/shared/milestones/_issues_tab.html.haml
@@ -1,6 +1,11 @@
- args = { show_project_name: local_assigns.fetch(:show_project_name, false),
show_full_project_name: local_assigns.fetch(:show_full_project_name, false) }
+- if display_issues_count_warning?(@milestone)
+ .flash-container
+ .flash-warning#milestone-issue-count-warning
+ = milestone_issues_count_message(@milestone)
+
.row.prepend-top-default
.col-md-4
= render 'shared/milestones/issuables', args.merge(title: 'Unstarted Issues (open and unassigned)', issuables: issues.opened.unassigned, id: 'unassigned', show_counter: true)
diff --git a/app/views/shared/milestones/_labels_tab.html.haml b/app/views/shared/milestones/_labels_tab.html.haml
index 4c930b90ce7..6d79b0d31b2 100644
--- a/app/views/shared/milestones/_labels_tab.html.haml
+++ b/app/views/shared/milestones/_labels_tab.html.haml
@@ -3,12 +3,12 @@
- options = { milestone_title: @milestone.title, label_name: label.title }
%li.no-border
- = render_label(label, tooltip: false, link: milestones_label_path(options))
+ = render_label(label, tooltip: false, link: milestones_issues_path(options))
%span.prepend-description-left
= markdown_field(label, :description)
.float-right.d-none.d-lg-block.d-xl-block
- = link_to milestones_label_path(options.merge(state: 'opened')), class: 'btn btn-transparent btn-action' do
+ = link_to milestones_issues_path(options.merge(state: 'opened')), class: 'btn btn-transparent btn-action' do
- pluralize milestone_issues_by_label_count(@milestone, label, state: :opened), 'open issue'
- = link_to milestones_label_path(options.merge(state: 'closed')), class: 'btn btn-transparent btn-action' do
+ = link_to milestones_issues_path(options.merge(state: 'closed')), class: 'btn btn-transparent btn-action' do
- pluralize milestone_issues_by_label_count(@milestone, label, state: :closed), 'closed issue'