Commit e5170caa authored by Stan Hu's avatar Stan Hu

Merge branch 'master' into tc-hotfix-geo-cursor-gaps

parents 81264452 63e4129d
......@@ -572,13 +572,12 @@ setup-test-env:
- vendor/gitaly-ruby
danger-review:
image: registry.gitlab.com/gitlab-org/gitaly/dangercontainer:latest
image: registry.gitlab.com/gitlab-org/gitlab-build-images:danger
stage: test
allow_failure: true
before_script:
- source scripts/utils.sh
- retry gem install danger --no-ri --no-rdoc
cache: {}
dependencies: []
before_script: []
only:
variables:
- $DANGER_GITLAB_API_TOKEN
......
Please view this file on the master branch, on stable branches it's out of date.
## 11.1.3 (2018-07-27)
### Fixed (1 change)
- Resolve Environments dropdown is showing on the cluster health page. !6528
## 11.1.2 (2018-07-26)
### Security (1 change)
- Don't expose project names in EE counters.
## 11.1.1 (2018-07-23)
### Fixed (2 changes)
......@@ -75,6 +89,13 @@ Please view this file on the master branch, on stable branches it's out of date.
- Geo - Make Geo repository verification flag opt-out by default. !6369
## 11.0.5 (2018-07-26)
### Security (1 change)
- Don't expose project names in EE counters.
## 11.0.4 (2018-07-17)
- No changes.
......
......@@ -2,6 +2,42 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
## 11.1.3 (2018-07-27)
### Fixed (8 changes, 1 of them is from the community)
- Rework some projects table indexes around repository_storage field. !20377
- Fix navigation to First and Next discussion on MR Changes tab. !20434
- Fix showing outdated discussions on Changes tab. !20445
- Fix autosave and ESC confirmation issues for MR discussions. !20569
- Fix rendering of the context lines in MR diffs page. !20642
- Don't overflow project/group dropdown results. !20704 (gfyoung)
- Fixed IDE not opening JSON files. !20798
- Disable Gitaly timeouts when creating or restoring backups. !20810
### Performance (1 change)
- Reduces the client side memory footprint on merge requests. !20744
## 11.1.2 (2018-07-26)
### Security (4 changes)
- Adding CSRF protection to Hooks test action.
- Don't expose project names in GitHub counters.
- Don't expose project names in various counters.
- Fixed XSS in branch name in Web IDE.
### Fixed (1 change)
- Escapes milestone and label's names on flash notice when promoting them.
### Performance (1 change)
- Fix slow Markdown rendering. !20820
## 11.1.1 (2018-07-23)
### Fixed (2 changes)
......@@ -253,6 +289,20 @@ entry.
- Use monospaced font for MR diff commit link ref on GFM.
## 11.0.5 (2018-07-26)
### Security (4 changes)
- Don't expose project names in various counters.
- Don't expose project names in GitHub counters.
- Adding CSRF protection to Hooks test action.
- Fixed XSS in branch name in Web IDE.
### Fixed (1 change)
- Escapes milestone and label's names on flash notice when promoting them.
## 11.0.4 (2018-07-17)
### Security (1 change)
......
......@@ -402,7 +402,8 @@ GEM
googleauth (>= 0.5.1, < 0.7)
gssapi (1.2.0)
ffi (>= 1.0.1)
haml (4.0.7)
haml (5.0.4)
temple (>= 0.8.0)
tilt
haml_lint (0.26.0)
haml (>= 4.0, < 5.1)
......
......@@ -93,7 +93,7 @@ export default {
<icon
:size="16"
class="prepend-left-8 append-right-8"
name="doc_image"
name="doc-image"
aria-hidden="true"
/>
</div>
......
......@@ -3,6 +3,7 @@ import tooltip from '~/vue_shared/directives/tooltip';
import Icon from '~/vue_shared/components/icon.vue';
import { pluralize } from '~/lib/utils/text_utility';
import { __, sprintf } from '~/locale';
import { getCommitIconMap } from '../utils';
export default {
components: {
......@@ -34,16 +35,14 @@ export default {
},
computed: {
changedIcon() {
const suffix = this.file.staged && !this.showStagedIcon ? '-solid' : '';
return this.file.tempFile && !this.forceModifiedIcon
? `file-addition${suffix}`
: `file-modified${suffix}`;
},
stagedIcon() {
return `${this.changedIcon}-solid`;
const suffix = !this.file.changed && this.file.staged && !this.showStagedIcon ? '-solid' : '';
if (this.forceModifiedIcon) return `file-modified${suffix}`;
return `${getCommitIconMap(this.file).icon}${suffix}`;
},
changedIconClass() {
return `multi-${this.changedIcon} float-left`;
return `ide-${this.changedIcon} float-left`;
},
tooltipTitle() {
if (!this.showTooltip) return undefined;
......@@ -66,6 +65,9 @@ export default {
return undefined;
},
showIcon() {
return this.file.changed || this.file.tempFile || this.file.staged || this.file.deleted;
},
},
};
</script>
......@@ -79,7 +81,7 @@ export default {
class="ide-file-changed-icon"
>
<icon
v-if="file.changed || file.tempFile || file.staged"
v-if="showIcon"
:name="changedIcon"
:size="12"
:css-classes="changedIconClass"
......
......@@ -5,6 +5,7 @@ import Icon from '~/vue_shared/components/icon.vue';
import StageButton from './stage_button.vue';
import UnstageButton from './unstage_button.vue';
import { viewerTypes } from '../../constants';
import { getCommitIconMap } from '../../utils';
export default {
components: {
......@@ -42,11 +43,12 @@ export default {
},
computed: {
iconName() {
const prefix = this.stagedList ? '-solid' : '';
return this.file.tempFile ? `file-addition${prefix}` : `file-modified${prefix}`;
const suffix = this.stagedList ? '-solid' : '';
return `${getCommitIconMap(this.file).icon}${suffix}`;
},
iconClass() {
return `multi-file-${this.file.tempFile ? 'addition' : 'modified'} append-right-8`;
return `${getCommitIconMap(this.file).class} append-right-8`;
},
fullKey() {
return `${this.keyPrefix}-${this.file.key}`;
......@@ -67,6 +69,8 @@ export default {
'stageChange',
]),
openFileInEditor() {
if (this.file.type === 'tree') return null;
return this.openPendingTab({
file: this.file,
keyPrefix: this.keyPrefix,
......
......@@ -56,7 +56,7 @@ export default {
>
<icon
:size="12"
name="more"
name="ellipsis_h"
/>
</button>
<div class="dropdown-menu dropdown-menu-right">
......
......@@ -10,7 +10,7 @@ export default {
EditorModeDropdown,
},
computed: {
...mapGetters(['currentMergeRequest']),
...mapGetters(['currentMergeRequest', 'activeFile']),
...mapState(['viewer', 'currentMergeRequestId']),
showLatestChangesText() {
return !this.currentMergeRequestId || this.viewer === viewerTypes.diff;
......@@ -23,12 +23,20 @@ export default {
},
},
mounted() {
if (this.activeFile && this.activeFile.pending && !this.activeFile.deleted) {
this.$router.push(`/project${this.activeFile.url}`, () => {
this.updateViewer('editor');
});
} else if (this.activeFile && this.activeFile.deleted) {
this.resetOpenFiles();
}
this.$nextTick(() => {
this.updateViewer(this.currentMergeRequestId ? viewerTypes.mr : viewerTypes.diff);
});
},
methods: {
...mapActions(['updateViewer']),
...mapActions(['updateViewer', 'resetOpenFiles']),
},
};
</script>
......@@ -36,7 +44,6 @@ export default {
<template>
<ide-tree-list
:viewer-type="viewer"
:disable-action-dropdown="true"
header-class="ide-review-header"
>
<template
......
......@@ -17,14 +17,18 @@ export default {
...mapGetters(['currentProject', 'currentTree', 'activeFile']),
},
mounted() {
if (this.activeFile && this.activeFile.pending) {
if (!this.activeFile) return;
if (this.activeFile.pending && !this.activeFile.deleted) {
this.$router.push(`/project${this.activeFile.url}`, () => {
this.updateViewer('editor');
});
} else if (this.activeFile.deleted) {
this.resetOpenFiles();
}
},
methods: {
...mapActions(['updateViewer', 'openNewEntryModal', 'createTempEntry']),
...mapActions(['updateViewer', 'openNewEntryModal', 'createTempEntry', 'resetOpenFiles']),
},
};
</script>
......
......@@ -22,11 +22,6 @@ export default {
required: false,
default: null,
},
disableActionDropdown: {
type: Boolean,
required: false,
default: false,
},
},
computed: {
...mapState(['currentBranchId']),
......@@ -69,7 +64,6 @@ export default {
:key="file.key"
:file="file"
:level="0"
:disable-action-dropdown="disableActionDropdown"
/>
</template>
</div>
......
......@@ -13,7 +13,7 @@ export default {
ItemButton,
},
props: {
branch: {
type: {
type: String,
required: true,
},
......@@ -45,7 +45,7 @@ export default {
},
},
methods: {
...mapActions(['createTempEntry', 'openNewEntryModal']),
...mapActions(['createTempEntry', 'openNewEntryModal', 'deleteEntry']),
createNewItem(type) {
this.openNewEntryModal({ type, path: this.path });
this.dropdownOpen = false;
......@@ -82,28 +82,40 @@ export default {
ref="dropdownMenu"
class="dropdown-menu dropdown-menu-right"
>
<template v-if="type === 'tree'">
<li>
<item-button
:label="__('New file')"
class="d-flex"
icon="doc-new"
icon-classes="mr-2"
@click="createNewItem('blob')"
/>
</li>
<li>
<upload
:path="path"
@create="createTempEntry"
/>
</li>
<li>
<item-button
:label="__('New directory')"
class="d-flex"
icon="folder-new"
icon-classes="mr-2"
@click="createNewItem('tree')"
/>
</li>
<li class="divider"></li>
</template>
<li>
<item-button
:label="__('New file')"
:label="__('Delete')"
class="d-flex"
icon="doc-new"
icon="remove"
icon-classes="mr-2"
@click="createNewItem('blob')"
/>
</li>
<li>
<upload
:path="path"
@create="createTempEntry"
/>
</li>
<li>
<item-button
:label="__('New directory')"
class="d-flex"
icon="folder-new"
icon-classes="mr-2"
@click="createNewItem('tree')"
@click="deleteEntry(path)"
/>
</li>
</ul>
......
......@@ -44,7 +44,7 @@ export default {
},
},
mounted() {
if (this.lastOpenedFile) {
if (this.lastOpenedFile && this.lastOpenedFile.type !== 'tree') {
this.openPendingTab({
file: this.lastOpenedFile,
keyPrefix: this.lastOpenedFile.changed ? stageKeys.unstaged : stageKeys.staged,
......
......@@ -87,7 +87,9 @@ export default {
this.editor.updateDimensions();
},
viewer() {
this.createEditorInstance();
if (!this.file.pending) {
this.createEditorInstance();
}
},
panelResizing() {
if (!this.panelResizing) {
......@@ -109,6 +111,7 @@ export default {
},
methods: {
...mapActions([
'getFileData',
'getRawFileData',
'changeFileContent',
'setFileLanguage',
......@@ -123,10 +126,16 @@ export default {
this.editor.clearEditor();
this.getRawFileData({
this.getFileData({
path: this.file.path,
baseSha: this.currentMergeRequest ? this.currentMergeRequest.baseCommitSha : '',
makeFileActive: false,
})
.then(() =>
this.getRawFileData({
path: this.file.path,
baseSha: this.currentMergeRequest ? this.currentMergeRequest.baseCommitSha : '',
}),
)
.then(() => {
this.createEditorInstance();
})
......@@ -246,6 +255,8 @@ export default {
ref="editor"
:class="{
'is-readonly': isCommitModeActive,
'is-deleted': file.deleted,
'is-added': file.tempFile
}"
class="multi-file-editor-holder"
>
......
......@@ -34,11 +34,6 @@ export default {
type: Number,
required: true,
},
disableActionDropdown: {
type: Boolean,
required: false,
default: false,
},
},
data() {
return {
......@@ -212,8 +207,7 @@ export default {
/>
</span>
<new-dropdown
v-if="isTree && !disableActionDropdown"
:project-id="file.projectId"
:type="file.type"
:branch="file.branchId"
:path="file.path"
:mouse-over="mouseOver"
......
......@@ -37,7 +37,7 @@ export default {
return this.fileHasChanged ? !this.tabMouseOver : false;
},
fileHasChanged() {
return this.tab.changed || this.tab.tempFile || this.tab.staged;
return this.tab.changed || this.tab.tempFile || this.tab.staged || this.tab.deleted;
},
},
......@@ -71,7 +71,8 @@ export default {
<template>
<li
:class="{
active: tab.active
active: tab.active,
disabled: tab.pending
}"
@click="clickFile(tab)"
@mouseover="mouseOverTab"
......@@ -105,7 +106,6 @@ export default {
<changed-file-icon
v-else
:file="tab"
:force-modified-icon="true"
/>
</button>
</li>
......
......@@ -38,3 +38,18 @@ export const stageKeys = {
unstaged: 'unstaged',
staged: 'staged',
};
export const commitItemIconMap = {
addition: {
icon: 'file-addition',
class: 'ide-file-addition',
},
modified: {
icon: 'file-modified',
class: 'ide-file-modified',
},
deleted: {
icon: 'file-deletion',
class: 'ide-file-deletion',
},
};
......@@ -7,7 +7,7 @@ export default class Model {
this.disposable = new Disposable();
this.file = file;
this.head = head;
this.content = file.content !== '' ? file.content : file.raw;
this.content = file.content !== '' || file.deleted ? file.content : file.raw;
this.disposable.add(
(this.originalModel = monacoEditor.createModel(
......
......@@ -18,7 +18,7 @@ export default {
return axios
.get(file.rawPath, {
params: { format: 'json' },
transformResponse: [f => f],
})
.then(({ data }) => data);
},
......@@ -33,7 +33,7 @@ export default {
return axios
.get(file.rawPath.replace(`/raw/${file.branchId}/${file.path}`, `/raw/${sha}/${file.path}`), {
params: { format: 'json' },
transformResponse: [f => f],
})
.then(({ data }) => data);
},
......
......@@ -185,6 +185,14 @@ export const openNewEntryModal = ({ commit }, { type, path = '' }) => {
$('#ide-new-entry').modal('show');
};
export const deleteEntry = ({ commit, dispatch, state }, path) => {
dispatch('burstUnusedSeal');
dispatch('closeFile', state.entries[path]);
commit(types.DELETE_ENTRY, path);
};
export const resetOpenFiles = ({ commit }) => commit(types.RESET_OPEN_FILES);
export * from './actions/tree';
export * from './actions/file';
export * from './actions/project';
......
......@@ -61,7 +61,11 @@ export const setFileActive = ({ commit, state, getters, dispatch }, path) => {
export const getFileData = ({ state, commit, dispatch }, { path, makeFileActive = true }) => {
const file = state.entries[path];
if (file.raw || file.tempFile) return Promise.resolve();
commit(types.TOGGLE_LOADING, { entry: file });
return service
.getFileData(
`${gon.relative_url_root ? gon.relative_url_root : ''}${file.url.replace('/-/', '/')}`,
......@@ -71,7 +75,7 @@ export const getFileData = ({ state, commit, dispatch }, { path, makeFileActive
setPageTitle(decodeURI(normalizedHeaders['PAGE-TITLE']));
commit(types.SET_FILE_DATA, { data, file });
commit(types.TOGGLE_FILE_OPEN, path);
if (makeFileActive) commit(types.TOGGLE_FILE_OPEN, path);
if (makeFileActive) dispatch('setFileActive', path);
commit(types.TOGGLE_LOADING, { entry: file });
})
......@@ -97,7 +101,7 @@ export const getRawFileData = ({ state, commit, dispatch }, { path, baseSha }) =
service
.getRawFileData(file)
.then(raw => {
commit(types.SET_FILE_RAW_DATA, { file, raw });
if (!file.tempFile) commit(types.SET_FILE_RAW_DATA, { file, raw });
if (file.mrChange && file.mrChange.new_file === false) {
service
.getBaseRawFileData(file, baseSha)
......
......@@ -21,14 +21,12 @@ export const showTreeEntry = ({ commit, dispatch, state }, path) => {
export const handleTreeEntryAction = ({ commit, dispatch }, row) => {
if (row.type === 'tree') {
dispatch('toggleTreeOpen', row.path);
} else if (row.type === 'blob' && (row.opened || row.changed)) {
if (row.changed && !row.opened) {
} else if (row.type === 'blob') {
if (!row.opened) {
commit(types.TOGGLE_FILE_OPEN, row.path);
}
dispatch('setFileActive', row.path);
} else {
dispatch('getFileData', { path: row.path });
}
dispatch('showTreeEntry', row.path);
......
......@@ -174,11 +174,13 @@ export const commitChanges = ({ commit, state, getters, dispatch, rootState, roo
dispatch('updateActivityBarView', activityBarViews.edit, { root: true });
dispatch('updateViewer', 'editor', { root: true });
router.push(
`/project/${rootState.currentProjectId}/blob/${getters.branchName}/-/${
rootGetters.activeFile.path
}`,
);
if (rootGetters.activeFile) {
router.push(
`/project/${rootState.currentProjectId}/blob/${getters.branchName}/-/${
rootGetters.activeFile.path
}`,
);
}
}
})
.then(() => dispatch('updateCommitAction', consts.COMMIT_TO_CURRENT_BRANCH))
......
import { sprintf, n__ } from '../../../../locale';
import { sprintf, n__, __ } from '../../../../locale';
import * as consts from './constants';
const BRANCH_SUFFIX_COUNT = 5;
const createTranslatedTextForFiles = (files, text) => {
if (!files.length) return null;
return sprintf(n__('%{text} %{files}', '%{text} %{files} files', files.length), {
files: files.reduce((acc, val) => acc.concat(val.path), []).join(', '),
text,
});
};
export const discardDraftButtonDisabled = state =>
state.commitMessage === '' || state.submitCommitLoading;
......@@ -29,14 +37,16 @@ export const branchName = (state, getters, rootState) => {
export const preBuiltCommitMessage = (state, _, rootState) => {
if (state.commitMessage) return state.commitMessage;
const files = (rootState.stagedFiles.length
? rootState.stagedFiles
: rootState.changedFiles
).reduce((acc, val) => acc.concat(val.path), []);
const files = rootState.stagedFiles.length ? rootState.stagedFiles : rootState.changedFiles;
const modifiedFiles = files.filter(f => !f.deleted);
const deletedFiles = files.filter(f => f.deleted);
return sprintf(n__('Update %{files}', 'Update %{files} files', files.length), {
files: files.join(', '),
});
return [
createTranslatedTextForFiles(modifiedFiles, __('Update')),
createTranslatedTextForFiles(deletedFiles, __('Deleted')),
]
.filter(t => t)
.join('\n');
};
// prevent babel-plugin-rewire from generating an invalid default during karma tests
......
......@@ -76,3 +76,4 @@ export const RESET_OPEN_FILES = 'RESET_OPEN_FILES';
export const SET_ERROR_MESSAGE = 'SET_ERROR_MESSAGE';
export const OPEN_NEW_ENTRY_MODAL = 'OPEN_NEW_ENTRY_MODAL';
export const DELETE_ENTRY = 'DELETE_ENTRY';
/* eslint-disable no-param-reassign */
import * as types from './mutation_types';
import projectMutations from './mutations/project';
import mergeRequestMutation from './mutations/merge_request';
......@@ -171,6 +172,16 @@ export default {
newEntryModal: { type, path },
});
},
[types.DELETE_ENTRY](state, path) {
const entry = state.entries[path];
const parent = entry.parentPath
? state.entries[entry.parentPath]
: state.trees[`${state.currentProjectId}/${state.currentBranchId}`];
entry.deleted = true;
state.changedFiles = state.changedFiles.concat(entry);
parent.tree = parent.tree.filter(f => f.path !== entry.path);
},
...projectMutations,
...mergeRequestMutation,
...fileMutations,
......
/* eslint-disable no-param-reassign */
import * as types from '../mutation_types';
import { sortTree } from '../utils';
import { diffModes } from '../../constants';
export default {
......@@ -51,9 +52,17 @@ export default {
});
},
[types.SET_FILE_RAW_DATA](state, { file, raw }) {
const openPendingFile = state.openFiles.find(
f => f.path === file.path && f.pending && !f.tempFile,
);
Object.assign(state.entries[file.path], {
raw,
});
if (openPendingFile) {
openPendingFile.raw = raw;
}
},
[types.SET_FILE_BASE_RAW_DATA](state, { file, baseRaw }) {
Object.assign(state.entries[file.path], {
......@@ -109,11 +118,22 @@ export default {
},
[types.DISCARD_FILE_CHANGES](state, path) {
const stagedFile = state.stagedFiles.find(f => f.path === path);
const entry = state.entries[path];
const { deleted } = entry;
Object.assign(state.entries[path], {
content: stagedFile ? stagedFile.content : state.entries[path].raw,
changed: false,
deleted: false,
});
if (deleted) {
const parent = entry.parentPath
? state.entries[entry.parentPath]
: state.trees[`${state.currentProjectId}/${state.currentBranchId}`];
parent.tree = sortTree(parent.tree.concat(entry));
}
},
[types.ADD_FILE_TO_CHANGED](state, path) {
Object.assign(state, {
......
......@@ -46,6 +46,7 @@ export const dataStructure = () => ({
parentPath: null,
lastOpenedAt: 0,
mrChange: null,
deleted: false,
});
export const decorateData = entity => {
......@@ -105,15 +106,37 @@ export const setPageTitle = title => {
document.title = title;
};
export const commitActionForFile = file => {
if (file.deleted) {
return 'delete';
} else if (file.tempFile) {
return 'create';
}
return 'update';
};
export const getCommitFiles = (stagedFiles, deleteTree = false) =>
stagedFiles.reduce((acc, file) => {
if ((file.deleted || deleteTree) && file.type === 'tree') {
return acc.concat(getCommitFiles(file.tree, true));
}
return acc.concat({
...file,
deleted: deleteTree || file.deleted,
});
}, []);
export const createCommitPayload = ({ branch, getters, newBranch, state, rootState }) => ({
branch,
commit_message: state.commitMessage || getters.preBuiltCommitMessage,
actions: rootState.stagedFiles.map(f => ({
action: f.tempFile ? 'create' : 'update',
actions: getCommitFiles(rootState.stagedFiles).map(f => ({
action: commitActionForFile(f),
file_path: f.path,
content: f.content,
encoding: f.base64 ? 'base64' : 'text',
last_commit_id: newBranch ? undefined : f.lastCommitSha,
last_commit_id: newBranch || f.deleted ? undefined : f.lastCommitSha,
})),
start_branch: newBranch ? rootState.currentBranchId : undefined,
});
......
import { commitItemIconMap } from './constants';
// eslint-disable-next-line import/prefer-default-export
export const getCommitIconMap = file => {
if (file.deleted) {
return commitItemIconMap.deleted;
} else if (file.tempFile) {
return commitItemIconMap.addition;
}
return commitItemIconMap.modified;
};
......@@ -98,6 +98,11 @@ export default {
type: String,
required: true,
},
showEnvironmentDropdown: {
type: Boolean,
required: false,
default: true,
},
},
data() {
return {
......@@ -126,9 +131,25 @@ export default {
},
mounted() {
this.resizeThrottled = _.throttle(this.resize, 600);
this.servicePromises = [
this.service
.getGraphsData()
.then(data => this.store.storeMetrics(data))
.catch(() => Flash(s__('Metrics|There was an error while retrieving metrics'))),
this.service
.getDeploymentData()
.then(data => this.store.storeDeploymentData(data))
.catch(() => Flash(s__('Metrics|There was an error getting deployment information.'))),
];
if (!this.hasMetrics) {
this.state = 'gettingStarted';
} else {
if (this.showEnvironmentDropdown) {
this.servicePromises.push(this.service
.getEnvironmentsData()
.then((data) => this.store.storeEnvironmentsData(data))
.catch(() => Flash(s__('Metrics|There was an error getting environments information.'))));
}
this.getGraphsData();
window.addEventListener('resize', this.resizeThrottled, false);
}
......@@ -136,17 +157,7 @@ export default {
methods: {
getGraphsData() {
this.state = 'loading';
Promise.all([
this.service.getGraphsData().then(data => this.store.storeMetrics(data)),
this.service
.getDeploymentData()
.then(data => this.store.storeDeploymentData(data))
.catch(() => Flash(s__('Metrics|There was an error getting deployment information.'))),
this.service
.getEnvironmentsData()
.then(data => this.store.storeEnvironmentsData(data))
.catch(() => Flash(s__('Metrics|There was an error getting environments information.'))),
])
Promise.all(this.servicePromises)
.then(() => {
if (this.store.groups.length < 1) {
this.state = 'noData';
......@@ -180,7 +191,10 @@ export default {
v-if="!showEmptyState"
class="prometheus-graphs prepend-top-10"
>
<div class="environments d-flex align-items-center">
<div
v-if="showEnvironmentDropdown"
class="environments d-flex align-items-center"
>
{{ s__('Metrics|Environment') }}
<div class="dropdown prepend-left-10">
<button
......
......@@ -32,7 +32,7 @@
},
computed: {
className() {
return `drag${this.side}`;
return `drag-${this.side}`;
},
cursorStyle() {
if (this.enabled) {
......@@ -44,8 +44,15 @@
methods: {
resetSize(e) {
e.preventDefault();
this.$emit('resize-start', this.size);
this.size = this.startSize;
this.$emit('update:size', this.size);
// End resizing on next tick so that listeners can react to DOM changes
this.$nextTick(() => {
this.$emit('resize-end', this.size);
});
},
startDrag(e) {
if (this.enabled) {
......@@ -84,7 +91,7 @@
<div
:class="className"
:style="cursorStyle"
class="dragHandle"
class="drag-handle"
@mousedown="startDrag"
@dblclick="resetSize"
></div>
......
......@@ -38,27 +38,12 @@ export default {
required: false,
default: () => [],
},
allIssues: {
type: Array,
required: false,
default: () => [],
},
component: {
type: String,
required: false,
default: '',
},
},
data() {
return {
isFullReportVisible: false,
};
},
methods: {
openFullReport() {
this.isFullReportVisible = true;
},
},
};
</script>
<template>
......@@ -72,14 +57,6 @@ export default {
class="js-mr-code-new-issues"
/>
<issues-block
v-if="isFullReportVisible"
:component="component"
:issues="allIssues"
:status="$options.failed"
class="js-mr-code-all-issues"
/>
<issues-block
v-if="neutralIssues.length"
:component="component"
......@@ -95,14 +72,5 @@ export default {
:status="$options.success"
class="js-mr-code-resolved-issues"
/>
<button
v-if="allIssues.length && !isFullReportVisible"
type="button"
class="btn-link btn-blank prepend-left-10 js-expand-full-list break-link"
@click="openFullReport"
>
{{ s__("ciReport|Show complete code vulnerabilities report") }}
</button>
</div>
</template>
......@@ -59,11 +59,6 @@ export default {
required: false,
default: () => [],
},
allIssues: {
type: Array,
required: false,
default: () => [],
},
infoText: {
type: [String, Boolean],
required: false,
......@@ -142,18 +137,10 @@ export default {
</script>
<template>
<section class="media-section">
<div
class="media"
>
<status-icon
:status="statusIconName"
/>
<div
class="media-body space-children d-flex flex-align-self-center"
>
<span
class="js-code-text code-text"
>
<div class="media">
<status-icon :status="statusIconName" />
<div class="media-body space-children d-flex flex-align-self-center">
<span class="js-code-text code-text">
{{ headerText }}
<popover
......@@ -163,10 +150,12 @@ export default {
/>
</span>
<slot name="actionButtons"></slot>
<button
v-if="isCollapsible"
type="button"
class="js-collapse-btn btn bt-default float-right btn-sm"
class="js-collapse-btn btn float-right btn-sm"
@click="toggleCollapsed"
>
{{ collapseText }}
......@@ -184,7 +173,6 @@ export default {
:unresolved-issues="unresolvedIssues"
:resolved-issues="resolvedIssues"
:neutral-issues="neutralIssues"
:all-issues="allIssues"
:component="component"
/>
</slot>
......
......@@ -567,9 +567,6 @@
border-bottom: 1px solid $white-normal;
.mx-auto {
margin: 8px 0;
text-align: center;
.tanuki-logo,
img {
height: 36px;
......
......@@ -874,3 +874,5 @@ $font-family-monospace: $monospace-font;
$input-line-height: 20px;
$btn-line-height: 20px;
$table-accent-bg: $gray-light;
$card-border-color: $border-color;
$card-cap-bg: $gray-light;
@import 'framework/variables';
@import 'framework/mixins';
.project-refs-form,
.project-refs-target-form {
display: inline-block;
......@@ -74,6 +77,7 @@
.ide-file-icon-holder {
display: flex;
align-items: center;
color: $theme-gray-700;
}
.ide-file-changed-icon {
......@@ -161,12 +165,23 @@
background-color: $white-light;
border-bottom-color: $white-light;
}
&:not(.disabled) {
.multi-file-tab {
cursor: pointer;
}
}
&.disabled {
.multi-file-tab-close {
cursor: default;
}
}
}
}
.multi-file-tab {
@include str-truncated(141px);
cursor: pointer;
svg {
vertical-align: middle;
......@@ -241,6 +256,38 @@
}
}
.is-deleted {
.editor.modified {
.margin-view-overlays,
.lines-content,
.decorationsOverviewRuler {
// !important to override monaco inline styles
display: none !important;
}
}
.diffOverviewRuler.modified {
// !important to override monaco inline styles
display: none !important;
}
}
.is-added {
.editor.original {
.margin-view-overlays,
.lines-content,
.decorationsOverviewRuler {
// !important to override monaco inline styles
display: none !important;
}
}
.diffOverviewRuler.original {
// !important to override monaco inline styles
display: none !important;
}
}
.monaco-diff-editor.vs {
.editor.modified {
box-shadow: none;
......@@ -557,16 +604,21 @@
}
}
.multi-file-addition,
.multi-file-addition-solid {
.ide-file-addition,
.ide-file-addition-solid {
color: $green-500;
}
.multi-file-modified,
.multi-file-modified-solid {
.ide-file-modified,
.ide-file-modified-solid {
color: $orange-500;
}
.ide-file-deletion,
.ide-file-deletion-solid {
color: $red-500;
}
.multi-file-commit-list-collapsed {
display: flex;
flex-direction: column;
......@@ -781,18 +833,21 @@
}
}
.dragHandle {
.drag-handle {
position: absolute;
top: 0;
bottom: 0;
width: 1px;
background-color: $white-dark;
width: 4px;
&:hover {
background-color: $white-normal;
}
&.dragright {
&.drag-right {
right: 0;
}
&.dragleft {
&.drag-left {
left: 0;
}
}
......@@ -1014,6 +1069,10 @@
.ide-new-btn {
margin-left: auto;
}
button {
color: $gl-text-color;
}
}
.ide-sidebar-branch-title {
......
......@@ -199,6 +199,7 @@
.block {
width: 100%;
word-break: break-word;
&:last-child {
border-bottom: 1px solid $border-gray-normal;
......
......@@ -16,6 +16,7 @@
svg {
vertical-align: middle;
top: -1px;
}
}
......
......@@ -856,18 +856,6 @@
}
}
.meter-container {
background: $border-gray-light;
border-radius: 3px;
.meter-fill {
max-width: 100%;
height: 5px;
border-radius: 3px;
background: $gl-primary;
}
}
.compare-display-container {
display: flex;
justify-content: space-between;
......
......@@ -22,8 +22,8 @@
height: 16px;
background-size: cover;
&.gl-snippet-icon-doc_code { background-position: 0 0; }
&.gl-snippet-icon-doc_text { background-position: 0 -16px; }
&.gl-snippet-icon-doc-code { background-position: 0 0; }
&.gl-snippet-icon-doc-text { background-position: 0 -16px; }
&.gl-snippet-icon-download { background-position: 0 -32px; }
}
......
......@@ -4,13 +4,17 @@ class Projects::CommitsController < Projects::ApplicationController
include ExtractsPath
include RendersCommits
before_action :whitelist_query_limiting
before_action :whitelist_query_limiting, except: :commits_root
before_action :require_non_empty_project
before_action :assign_ref_vars
before_action :assign_ref_vars, except: :commits_root
before_action :authorize_download_code!
before_action :set_commits
before_action :set_commits, except: :commits_root
before_action :set_request_format, only: :show
def commits_root
redirect_to project_commits_path(@project, @project.default_branch)
end
def show
@merge_request = MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened
.find_by(source_project: @project, source_branch: @ref, target_branch: @repository.root_ref)
......
......@@ -7,7 +7,7 @@ class Admin::ProjectsFinder
end
def execute
items = Project.without_deleted.with_statistics
items = Project.without_deleted.with_statistics.with_route
items = by_namespace_id(items)
items = by_visibilty_level(items)
items = by_with_push(items)
......@@ -16,7 +16,7 @@ class Admin::ProjectsFinder
items = by_archived(items)
items = by_personal(items)
items = by_name(items)
items = items.includes(namespace: [:owner])
items = items.includes(namespace: [:owner, :route])
sort(items).page(params[:page])
end
......
......@@ -7,5 +7,5 @@ class GitlabSchema < GraphQL::Schema
query(Types::QueryType)
default_max_page_size 100
# mutation(Types::MutationType)
mutation(Types::MutationType)
end
# frozen_string_literal: true
module Mutations
class BaseMutation < GraphQL::Schema::RelayClassicMutation
field :errors, [GraphQL::STRING_TYPE],
null: false,
description: "Reasons why the mutation failed."
def current_user
context[:current_user]
end
end
end
module Mutations
module ResolvesProject
extend ActiveSupport::Concern
def resolve_project(full_path:)
resolver.resolve(full_path: full_path)
end
def resolver
Resolvers::ProjectResolver.new(object: nil, context: context)
end
end
end
module Mutations
module MergeRequests
class Base < BaseMutation
include Gitlab::Graphql::Authorize::AuthorizeResource
include Mutations::ResolvesProject
argument :project_path, GraphQL::ID_TYPE,
required: true,
description: "The project the merge request to mutate is in"
argument :iid, GraphQL::ID_TYPE,
required: true,
description: "The iid of the merge request to mutate"
field :merge_request,
Types::MergeRequestType,
null: true,
description: "The merge request after mutation"
authorize :update_merge_request
private
def find_object(project_path:, iid:)
project = resolve_project(full_path: project_path)
resolver = Resolvers::MergeRequestResolver.new(object: project, context: context)
resolver.resolve(iid: iid)
end
end
end
end
# frozen_string_literal: true
module Mutations
module MergeRequests
class SetWip < Base
graphql_name 'MergeRequestSetWip'
argument :wip,
GraphQL::BOOLEAN_TYPE,
required: true,
description: <<~DESC
Whether or not to set the merge request as a WIP.
DESC
def resolve(project_path:, iid:, wip: nil)
merge_request = authorized_find!(project_path: project_path, iid: iid)
project = merge_request.project
::MergeRequests::UpdateService.new(project, current_user, wip_event: wip_event(merge_request, wip))
.execute(merge_request)
{
merge_request: merge_request,
errors: merge_request.errors.full_messages
}
end
private
def wip_event(merge_request, wip)
wip ? 'wip' : 'unwip'
end
end
end
end
# frozen_string_literal: true
module Types
class MutationType < BaseObject
include Gitlab::Graphql::MountMutation
graphql_name "Mutation"
# TODO: Add Mutations as fields
mount_mutation Mutations::MergeRequests::SetWip
end
end
......@@ -112,7 +112,7 @@ module ButtonHelper
def geo_button(modal_target: nil)
data = { placement: 'bottom', container: 'body', toggle: 'modal', target: modal_target }
content_tag :button,
sprite_icon('geo-nodes', size: 15),
sprite_icon('location-dot', size: 15),
class: 'btn btn-geo has-tooltip',
data: data,
type: :button,
......
......@@ -11,6 +11,7 @@ module EnvironmentsHelper
{
"settings-path" => edit_project_service_path(project, 'prometheus'),
"clusters-path" => project_clusters_path(project),
"current-environment-name": environment.name,
"documentation-path" => help_page_path('administration/monitoring/prometheus/index.md'),
"empty-getting-started-svg-path" => image_path('illustrations/monitoring/getting_started.svg'),
"empty-loading-svg-path" => image_path('illustrations/monitoring/loading.svg'),
......
......@@ -10,7 +10,7 @@ module HooksHelper
trigger_human_name = trigger.to_s.tr('_', ' ').camelize
link_to path, rel: 'nofollow' do
link_to path, rel: 'nofollow', method: :post do
content_tag(:span, trigger_human_name)
end
end
......
......@@ -116,7 +116,7 @@ module SnippetsHelper
raw_project_snippet_url(@snippet.project, @snippet)
end
link_to external_snippet_icon('doc_code'), snippet_raw_url, class: 'btn', target: '_blank', rel: 'noopener noreferrer', title: 'Open raw'
link_to external_snippet_icon('doc-code'), snippet_raw_url, class: 'btn', target: '_blank', rel: 'noopener noreferrer', title: 'Open raw'
end
def embedded_snippet_download_button
......
......@@ -90,34 +90,17 @@ module Routable
end
def full_name
if route && route.name.present?
@full_name ||= route.name # rubocop:disable Gitlab/ModuleWithInstanceVariables
else
update_route if persisted?
build_full_name
end
route&.name || build_full_name
end
# Every time `project.namespace.becomes(Namespace)` is called for polymorphic_path,
# a new instance is instantiated, and we end up duplicating the same query to retrieve
# the route. Caching this per request ensures that even if we have multiple instances,
# we will not have to duplicate work, avoiding N+1 queries in some cases.
def full_path
return uncached_full_path unless RequestStore.active? && persisted?
RequestStore[full_path_key] ||= uncached_full_path
route&.path || build_full_path
end
def full_path_components
full_path.split('/')
end
def expires_full_path_cache
RequestStore.delete(full_path_key) if RequestStore.active?
@full_path = nil # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
def build_full_path
if parent && path
parent.full_path + '/' + path
......@@ -138,16 +121,6 @@ module Routable
self.errors[:path].concat(route_path_errors) if route_path_errors
end
def uncached_full_path
if route && route.path.present?
@full_path ||= route.path # rubocop:disable Gitlab/ModuleWithInstanceVariables
else
update_route if persisted?
build_full_path
end
end
def full_name_changed?
name_changed? || parent_changed?
end
......@@ -156,10 +129,6 @@ module Routable
path_changed? || parent_changed?
end
def full_path_key
@full_path_key ||= "routable/full_path/#{self.class.name}/#{self.id}"
end
def build_full_name
if parent && name
parent.human_name + ' / ' + name
......@@ -168,18 +137,9 @@ module Routable
end
end
def update_route
return if Gitlab::Database.read_only?
prepare_route
route.save
end
def prepare_route
route || build_route(source: self)
route.path = build_full_path
route.name = build_full_name
@full_path = nil # rubocop:disable Gitlab/ModuleWithInstanceVariables
@full_name = nil # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
end
......@@ -11,8 +11,6 @@ module Storage
Namespace.find(parent_id_was) # raise NotFound early if needed
end
expires_full_path_cache
move_repositories
if parent_changed?
......
class DeployToken < ActiveRecord::Base
include Expirable
include TokenAuthenticatable
include PolicyActor
add_authentication_token_field :token
prepend EE::DeployToken
AVAILABLE_SCOPES = %i(read_repository read_registry).freeze
GITLAB_DEPLOY_TOKEN_NAME = 'gitlab-deploy-token'.freeze
......@@ -60,10 +59,6 @@ class DeployToken < ActiveRecord::Base
write_attribute(:expires_at, value.presence || Forever.date)
end
def admin?
false
end
private
def ensure_at_least_one_scope
......
......@@ -305,7 +305,6 @@ class Namespace < ActiveRecord::Base
def write_projects_repository_config
all_projects.find_each do |project|
project.expires_full_path_cache # we need to clear cache to validate renames correctly
project.write_repository_config
end
end
......
......@@ -1256,8 +1256,6 @@ class Project < ActiveRecord::Base
return true if skip_disk_validation
return false unless repository_storage
expires_full_path_cache # we need to clear cache to validate renames correctly
# Check if repository with same path already exists on disk we can
# skip this for the hashed storage because the path does not change
if legacy_storage? && repository_with_same_path_already_exists?
......@@ -1636,7 +1634,6 @@ class Project < ActiveRecord::Base
# When we import a project overwriting the original project, there
# is a move operation. In that case we don't want to send the instructions.
send_move_instructions(full_path_was) unless import_started?
expires_full_path_cache
self.old_path_with_namespace = full_path_was
SystemHooksService.new.execute_hooks_for(self, :rename)
......
......@@ -50,13 +50,13 @@ class RemoteMirror < ActiveRecord::Base
state :failed
after_transition any => :started do |remote_mirror, _|
Gitlab::Metrics.add_event(:remote_mirrors_running, path: remote_mirror.project.full_path)
Gitlab::Metrics.add_event(:remote_mirrors_running)
remote_mirror.update(last_update_started_at: Time.now)
end
after_transition started: :finished do |remote_mirror, _|
Gitlab::Metrics.add_event(:remote_mirrors_finished, path: remote_mirror.project.full_path)
Gitlab::Metrics.add_event(:remote_mirrors_finished)
timestamp = Time.now
remote_mirror.update!(
......@@ -65,7 +65,7 @@ class RemoteMirror < ActiveRecord::Base
end
after_transition started: :failed do |remote_mirror, _|
Gitlab::Metrics.add_event(:remote_mirrors_failed, path: remote_mirror.project.full_path)
Gitlab::Metrics.add_event(:remote_mirrors_failed)
remote_mirror.update(last_update_at: Time.now)
end
......
......@@ -1034,7 +1034,7 @@ class Repository
end
def repository_event(event, tags = {})
Gitlab::Metrics.add_event(event, { path: full_path }.merge(tags))
Gitlab::Metrics.add_event(event, tags)
end
def initialize_raw_repository
......
......@@ -22,14 +22,4 @@ class BasePolicy < DeclarativePolicy::Base
# This is prevented in some cases in `gitlab-ee`
rule { default }.enable :read_cross_project
# EE Extensions
with_scope :user
condition(:auditor, score: 0) { @user&.auditor? }
with_scope :user
condition(:support_bot, score: 0) { @user&.support_bot? }
with_scope :global
condition(:license_block) { License.block_changes? }
end
# frozen_string_literal: true
# Include this module if we want to pass something else than the user to
# check policies. This defines several methods which the policy checker
# would call and check.
module PolicyActor
extend ActiveSupport::Concern
prepend EE::PolicyActor
def blocked?
false
end
def admin?
false
end
def external?
false
end
def internal?
false
end
def access_locked?
false
end
def required_terms_not_accepted?
false
end
def can_create_group
false
end
end
......@@ -11,12 +11,17 @@ class PipelineSerializer < BaseSerializer
:retryable_builds,
:cancelable_statuses,
:trigger_requests,
:project,
{ triggered_by_pipeline: [:project, :user] },
{ triggered_pipelines: [:project, :user] },
:manual_actions,
:artifacts,
{ pending_builds: :project }
{
pending_builds: :project,
project: [:route, { namespace: :route }],
artifacts: {
project: [:route, { namespace: :route }]
}
}
])
end
......
......@@ -79,7 +79,6 @@ module Projects
Gitlab::PagesTransfer.new.move_project(project.path, @old_namespace.full_path, @new_namespace.full_path)
project.old_path_with_namespace = @old_path
project.expires_full_path_cache
write_repository_config(@new_path)
......
......@@ -17,7 +17,7 @@
- if project.archived
%span.badge.badge-warning archived
.title
= link_to [:admin, project.namespace.becomes(Namespace), project] do
= link_to(admin_namespace_project_path(project.namespace, project)) do
.dash-project-avatar
.avatar-container.s40
= project_icon(project, alt: '', class: 'avatar project-avatar s40')
......
- @body_class = 'ide'
- page_title 'IDE'
- content_for :page_specific_javascripts do
= stylesheet_link_tag 'page_bundles/ide'
#ide.ide-loading{ data: {"empty-state-svg-path" => image_path('illustrations/multi_file_editor_empty.svg'),
"no-changes-state-svg-path" => image_path('illustrations/multi-editor_no_changes_empty.svg'),
"committed-state-svg-path" => image_path('illustrations/multi-editor_all_changes_committed_empty.svg'),
......
......@@ -3,6 +3,11 @@
- site_name = "GitLab"
%head{ prefix: "og: http://ogp.me/ns#" }
%meta{ charset: "utf-8" }
- if Feature.enabled?('asset_host_prefetch') && ActionController::Base.asset_host
%link{ rel: 'dns-prefetch', href: ActionController::Base.asset_host }
%link{ rel: 'preconnnect', href: ActionController::Base.asset_host, crossorigin: '' }
%meta{ 'http-equiv' => 'X-UA-Compatible', content: 'IE=edge' }
-# Open Graph - http://ogp.me/
......
......@@ -67,5 +67,5 @@
%button.navbar-toggler.d-block.d-sm-none{ type: 'button' }
%span.sr-only= _("Toggle navigation")
= sprite_icon('more', size: 12, css_class: 'more-icon js-navbar-toggle-right')
= sprite_icon('ellipsis_h', size: 12, css_class: 'more-icon js-navbar-toggle-right')
= sprite_icon('close', size: 12, css_class: 'close-icon js-navbar-toggle-left')
......@@ -78,4 +78,4 @@
- if Gitlab::Geo.secondary? && Gitlab::Geo.primary_node_configured?
%li
= link_to Gitlab::Geo.primary_node.url, title: 'Go to primary node', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= sprite_icon('geo-nodes', size: 18)
= sprite_icon('location-dot', size: 18)
......@@ -181,7 +181,7 @@
= nav_link(controller: :geo_nodes) do
= link_to admin_geo_nodes_path do
.nav-icon-container
= sprite_icon('geo-nodes')
= sprite_icon('location-dot')
%span.nav-item-name
Geo Nodes
%ul.sidebar-sub-level-items.is-fly-out-only
......
......@@ -15,7 +15,7 @@
= nav_link(path: ['groups#show', 'groups#activity', 'groups#subgroups', 'analytics#show'], html_options: { class: 'home' }) do
= link_to group_path(@group) do
.nav-icon-container
= sprite_icon('project')
= sprite_icon('home')
%span.nav-item-name
= _('Overview')
......
......@@ -122,7 +122,7 @@
= nav_link(controller: :gpg_keys) do
= link_to profile_gpg_keys_path do
.nav-icon-container
= sprite_icon('key-2')
= sprite_icon('key-modern')
%span.nav-item-name
= _('GPG Keys')
%ul.sidebar-sub-level-items.is-fly-out-only
......
......@@ -11,7 +11,7 @@
= nav_link(path: sidebar_projects_paths, html_options: { class: 'home' }) do
= link_to project_path(@project), class: 'shortcuts-project' do
.nav-icon-container
= sprite_icon('project')
= sprite_icon('home')
%span.nav-item-name
= _('Project')
......@@ -40,7 +40,7 @@
= nav_link(controller: sidebar_repository_paths) do
= link_to project_tree_path(@project), class: 'shortcuts-tree' do
.nav-icon-container
= sprite_icon('doc_text')
= sprite_icon('doc-text')
%span.nav-item-name
= _('Repository')
......
......@@ -22,7 +22,7 @@
= s_('Branches|protected')
- if @project.mirror_ever_updated_successfully? && @repository.diverged_from_upstream?(branch.name)
%span.badge.badge-danger.has-tooltip{ data: { html: "true", title: branch_diverged_tooltip_message } }
%span.badge.badge-danger.prepend-left-5.has-tooltip{ data: { html: "true", title: branch_diverged_tooltip_message } }
= icon('exclamation-triangle')
= s_('Branches|diverged from upstream')
......
......@@ -2,7 +2,7 @@
.gitlab-embed-snippets
.js-file-title.file-title-flex-parent
.file-header-content
= external_snippet_icon('doc_text')
= external_snippet_icon('doc-text')
%strong.file-title-name
%a.gitlab-embedded-snippets-title{ href: url_for(only_path: false, overwrite_params: nil) }
......
......@@ -22,7 +22,7 @@ module Gitlab
importer_class.new(object, project, client).execute
counter.increment(project: project.full_path)
counter.increment
end
def counter
......
......@@ -23,9 +23,7 @@ class RepositoryForkWorker
def fork_repository(target_project, source_repository_storage_name, source_disk_path)
return unless start_fork(target_project)
Gitlab::Metrics.add_event(:fork_repository,
source_path: source_disk_path,
target_path: target_project.disk_path)
Gitlab::Metrics.add_event(:fork_repository)
result = gitlab_shell.fork_repository(source_repository_storage_name, source_disk_path,
target_project.repository_storage, target_project.disk_path)
......
......@@ -11,9 +11,7 @@ class RepositoryImportWorker
return unless start_import(project)
Gitlab::Metrics.add_event(:import_repository,
import_url: project.import_url,
path: project.full_path)
Gitlab::Metrics.add_event(:import_repository)
service = Projects::ImportService.new(project, project.creator)
result = service.execute
......
---
title: Redirect commits to root if no ref is provided (31576)
merge_request: 20738
author: Kia Mei Somabes
type: added
---
title: Rework some projects table indexes around repository_storage field
merge_request: 20377
author:
type: fixed
---
title: Increase width of Web IDE sidebar resize handles
merge_request: 20818
author:
type: fixed
---
title: Fix new MR card styles
merge_request: 20822
author:
type: fixed
---
title: Fix navigation to First and Next discussion on MR Changes tab
merge_request: 20434
author:
type: fixed
---
title: DNS prefetching if asset_host for CDN hosting is set
merge_request: 20781
author:
type: performance
---
title: Fix rendering of the context lines in MR diffs page
merge_request: 20642
author:
type: fixed
---
title: Fix autosave and ESC confirmation issues for MR discussions
merge_request: 20569
author:
type: fixed
---
title: Fix showing outdated discussions on Changes tab
merge_request: 20445
author:
type: fixed
---
title: Add the first mutations for merge requests to GraphQL
merge_request: 20443
author:
type: added
---
title: Don't expose project names in various counters
merge_request:
author:
type: security
---
title: Removes "show all" on reports and adds an actionButtons slot
merge_request: 20855
author:
type: changed
---
title: Enabled deletion of files in the Web IDE
merge_request:
author:
type: added
---
title: Don't expose project names in GitHub counters
merge_request:
author:
type: security
---
title: Don't overflow project/group dropdown results
merge_request: 20704
author: gfyoung
type: fixed
---
title: Adding CSRF protection to Hooks test action
merge_request:
author:
type: security
---
title: Fix slow Markdown rendering
merge_request: 20820
title: Bump haml gem to 5.0.4
merge_request: 20847
author:
type: performance
---
title: Stop dynamically creating project and namespace routes
merge_request: 20313
author:
type: performance
---
title: Reduces the client side memory footprint on merge requests
merge_request: 20744
author:
type: performance
---
title: Wrap job name on pipeline job sidebar
merge_request: 20804
author: George Tsiolis
type: changed
---
title: Disable Gitaly timeouts when creating or restoring backups
merge_request: 20810
author:
type: fixed
......@@ -45,10 +45,12 @@ module Gitlab
#{config.root}/app/models/members
#{config.root}/app/models/project_services
#{config.root}/app/workers/concerns
#{config.root}/app/policies/concerns
#{config.root}/app/services/concerns
#{config.root}/app/serializers/concerns
#{config.root}/app/finders/concerns
#{config.root}/app/graphql/resolvers/concerns])
#{config.root}/app/graphql/resolvers/concerns
#{config.root}/app/graphql/mutations/concerns])
config.generators.templates.push("#{config.root}/generator_templates")
......@@ -146,6 +148,7 @@ module Gitlab
config.assets.precompile << "notify.css"
config.assets.precompile << "mailers/*.css"
config.assets.precompile << "xterm/xterm.css"
config.assets.precompile << "page_bundles/ide.css"
config.assets.precompile << "performance_bar.css"
config.assets.precompile << "lib/ace.js"
config.assets.precompile << "test.css"
......
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