Commit 58423fa8 authored by Douglas Barbosa Alexandre's avatar Douglas Barbosa Alexandre

Merge remote-tracking branch 'origin/master' into...

Merge remote-tracking branch 'origin/master' into camilstaps/gitlab-ce-new-66023-public-private-fork-counts
parents ed1192c5 814d12b8
...@@ -65,6 +65,7 @@ eslint-report.html ...@@ -65,6 +65,7 @@ eslint-report.html
/vendor/gitaly-ruby /vendor/gitaly-ruby
/builds* /builds*
/.gitlab_workhorse_secret /.gitlab_workhorse_secret
/.gitlab_pages_shared_secret
/webpack-report/ /webpack-report/
/knapsack/ /knapsack/
/rspec_flaky/ /rspec_flaky/
......
...@@ -28,7 +28,8 @@ package-and-qa-manual:master: ...@@ -28,7 +28,8 @@ package-and-qa-manual:master:
extends: .package-and-qa-base extends: .package-and-qa-base
only: only:
refs: refs:
- master - master@gitlab-org/gitlab-ce
- master@gitlab-org/gitlab-ee
when: manual when: manual
needs: ["build-qa-image", "gitlab:assets:compile"] needs: ["build-qa-image", "gitlab:assets:compile"]
......
...@@ -5,7 +5,17 @@ ...@@ -5,7 +5,17 @@
### Intended users ### Intended users
<!-- Who will use this feature? If known, include any of the following: types of users (e.g. Developer), personas, or specific company roles (e.g. Release Manager). It's okay to write "Unknown" and fill this field in later. <!-- Who will use this feature? If known, include any of the following: types of users (e.g. Developer), personas, or specific company roles (e.g. Release Manager). It's okay to write "Unknown" and fill this field in later.
Personas can be found at https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/ -->
* [Parker (Product Manager)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#parker-product-manager)
* [Delaney (Development Team Lead)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#delaney-development-team-lead)
* [Sasha (Software Developer)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#sasha-software-developer)
* [Presley (Product Designer)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#presley-product-designer)
* [Devon (DevOps Engineer)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#devon-devops-engineer)
* [Sidney (Systems Administrator)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#sidney-systems-administrator)
* [Sam (Security Analyst)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#sam-security-analyst)
* [Dana (Data Analyst)](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/#dana-data-analyst)
Personas are described at https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/ -->
### Further details ### Further details
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
## Author's checklist ## Author's checklist
- [ ] Follow the [Documentation Guidelines](https://docs.gitlab.com/ee/development/documentation/) and [Style Guide](https://docs.gitlab.com/ee/development/documentation/styleguide.html). - [ ] Follow the [Documentation Guidelines](https://docs.gitlab.com/ee/development/documentation/) and [Style Guide](https://docs.gitlab.com/ee/development/documentation/styleguide.html).
- [ ] If applicable, update the [permissions table](https://docs.gitlab.com/ee/user/permissions.html).
- [ ] Link docs to and from the higher-level index page, plus other related docs where helpful. - [ ] Link docs to and from the higher-level index page, plus other related docs where helpful.
- [ ] Apply the ~Documentation label. - [ ] Apply the ~Documentation label.
......
...@@ -106,7 +106,7 @@ gem 'fog-aws', '~> 3.5' ...@@ -106,7 +106,7 @@ gem 'fog-aws', '~> 3.5'
# Locked until fog-google resolves https://github.com/fog/fog-google/issues/421. # Locked until fog-google resolves https://github.com/fog/fog-google/issues/421.
# Also see config/initializers/fog_core_patch.rb. # Also see config/initializers/fog_core_patch.rb.
gem 'fog-core', '= 2.1.0' gem 'fog-core', '= 2.1.0'
gem 'fog-google', '~> 1.8' gem 'fog-google', '~> 1.9'
gem 'fog-local', '~> 0.6' gem 'fog-local', '~> 0.6'
gem 'fog-openstack', '~> 1.0' gem 'fog-openstack', '~> 1.0'
gem 'fog-rackspace', '~> 0.1.1' gem 'fog-rackspace', '~> 0.1.1'
...@@ -135,7 +135,7 @@ gem 'wikicloth', '0.8.1' ...@@ -135,7 +135,7 @@ gem 'wikicloth', '0.8.1'
gem 'asciidoctor', '~> 2.0.10' gem 'asciidoctor', '~> 2.0.10'
gem 'asciidoctor-include-ext', '~> 0.3.1', require: false gem 'asciidoctor-include-ext', '~> 0.3.1', require: false
gem 'asciidoctor-plantuml', '0.0.9' gem 'asciidoctor-plantuml', '0.0.9'
gem 'rouge', '~> 3.7' gem 'rouge', '~> 3.10'
gem 'truncato', '~> 0.7.11' gem 'truncato', '~> 0.7.11'
gem 'bootstrap_form', '~> 4.2.0' gem 'bootstrap_form', '~> 4.2.0'
gem 'nokogiri', '~> 1.10.4' gem 'nokogiri', '~> 1.10.4'
...@@ -234,7 +234,7 @@ gem 'asana', '~> 0.8.1' ...@@ -234,7 +234,7 @@ gem 'asana', '~> 0.8.1'
gem 'ruby-fogbugz', '~> 0.2.1' gem 'ruby-fogbugz', '~> 0.2.1'
# Kubernetes integration # Kubernetes integration
gem 'kubeclient', '~> 4.2.2' gem 'kubeclient', '~> 4.4.0'
# Sanitize user input # Sanitize user input
gem 'sanitize', '~> 4.6' gem 'sanitize', '~> 4.6'
......
...@@ -284,7 +284,7 @@ GEM ...@@ -284,7 +284,7 @@ GEM
excon (~> 0.58) excon (~> 0.58)
formatador (~> 0.2) formatador (~> 0.2)
mime-types mime-types
fog-google (1.8.2) fog-google (1.9.1)
fog-core (<= 2.1.0) fog-core (<= 2.1.0)
fog-json (~> 1.2) fog-json (~> 1.2)
fog-xml (~> 0.1.0) fog-xml (~> 0.1.0)
...@@ -505,7 +505,7 @@ GEM ...@@ -505,7 +505,7 @@ GEM
kramdown (2.1.0) kramdown (2.1.0)
kramdown-parser-gfm (1.1.0) kramdown-parser-gfm (1.1.0)
kramdown (~> 2.0) kramdown (~> 2.0)
kubeclient (4.2.2) kubeclient (4.4.0)
http (~> 3.0) http (~> 3.0)
recursive-open-struct (~> 1.0, >= 1.0.4) recursive-open-struct (~> 1.0, >= 1.0.4)
rest-client (~> 2.0) rest-client (~> 2.0)
...@@ -799,7 +799,7 @@ GEM ...@@ -799,7 +799,7 @@ GEM
retriable (3.1.2) retriable (3.1.2)
rinku (2.0.0) rinku (2.0.0)
rotp (2.1.2) rotp (2.1.2)
rouge (3.7.0) rouge (3.10.0)
rqrcode (0.7.0) rqrcode (0.7.0)
chunky_png chunky_png
rqrcode-rails3 (0.1.7) rqrcode-rails3 (0.1.7)
...@@ -1114,7 +1114,7 @@ DEPENDENCIES ...@@ -1114,7 +1114,7 @@ DEPENDENCIES
fog-aliyun (~> 0.3) fog-aliyun (~> 0.3)
fog-aws (~> 3.5) fog-aws (~> 3.5)
fog-core (= 2.1.0) fog-core (= 2.1.0)
fog-google (~> 1.8) fog-google (~> 1.9)
fog-local (~> 0.6) fog-local (~> 0.6)
fog-openstack (~> 1.0) fog-openstack (~> 1.0)
fog-rackspace (~> 0.1.1) fog-rackspace (~> 0.1.1)
...@@ -1164,7 +1164,7 @@ DEPENDENCIES ...@@ -1164,7 +1164,7 @@ DEPENDENCIES
jwt (~> 2.1.0) jwt (~> 2.1.0)
kaminari (~> 1.0) kaminari (~> 1.0)
knapsack (~> 1.17) knapsack (~> 1.17)
kubeclient (~> 4.2.2) kubeclient (~> 4.4.0)
letter_opener_web (~> 1.3.4) letter_opener_web (~> 1.3.4)
license_finder (~> 5.4) license_finder (~> 5.4)
licensee (~> 8.9) licensee (~> 8.9)
...@@ -1229,7 +1229,7 @@ DEPENDENCIES ...@@ -1229,7 +1229,7 @@ DEPENDENCIES
redis-rails (~> 5.0.2) redis-rails (~> 5.0.2)
request_store (~> 1.3) request_store (~> 1.3)
responders (~> 2.0) responders (~> 2.0)
rouge (~> 3.7) rouge (~> 3.10)
rqrcode-rails3 (~> 0.1.7) rqrcode-rails3 (~> 0.1.7)
rspec-parameterized rspec-parameterized
rspec-rails (~> 3.8.0) rspec-rails (~> 3.8.0)
......
<script>
import { mapState, mapGetters, mapActions } from 'vuex';
import { GlLoadingIcon } from '@gitlab/ui';
import statisticsLabels from '../constants';
export default {
components: {
GlLoadingIcon,
},
data() {
return {
statisticsLabels,
};
},
computed: {
...mapState(['isLoading', 'statistics']),
...mapGetters(['getStatistics']),
},
mounted() {
this.fetchStatistics();
},
methods: {
...mapActions(['fetchStatistics']),
},
};
</script>
<template>
<div class="info-well">
<div class="well-segment admin-well admin-well-statistics">
<h4>{{ __('Statistics') }}</h4>
<gl-loading-icon v-if="isLoading" size="md" class="my-3" />
<template v-else>
<p
v-for="statistic in getStatistics(statisticsLabels)"
:key="statistic.key"
class="js-stats"
>
{{ statistic.label }}
<span class="light float-right">{{ statistic.value }}</span>
</p>
</template>
</div>
</div>
</template>
import { s__ } from '~/locale';
const statisticsLabels = {
forks: s__('AdminStatistics|Forks'),
issues: s__('AdminStatistics|Issues'),
mergeRequests: s__('AdminStatistics|Merge Requests'),
notes: s__('AdminStatistics|Notes'),
snippets: s__('AdminStatistics|Snippets'),
sshKeys: s__('AdminStatistics|SSH Keys'),
milestones: s__('AdminStatistics|Milestones'),
activeUsers: s__('AdminStatistics|Active Users'),
};
export default statisticsLabels;
import Vue from 'vue';
import StatisticsPanelApp from './components/app.vue';
import createStore from './store';
export default function(el) {
if (!el) {
return false;
}
const store = createStore();
return new Vue({
el,
store,
components: {
StatisticsPanelApp,
},
render(h) {
return h(StatisticsPanelApp);
},
});
}
import Api from '~/api';
import { s__ } from '~/locale';
import createFlash from '~/flash';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import * as types from './mutation_types';
export const requestStatistics = ({ commit }) => commit(types.REQUEST_STATISTICS);
export const fetchStatistics = ({ dispatch }) => {
dispatch('requestStatistics');
Api.adminStatistics()
.then(({ data }) => {
dispatch('receiveStatisticsSuccess', convertObjectPropsToCamelCase(data, { deep: true }));
})
.catch(error => dispatch('receiveStatisticsError', error));
};
export const receiveStatisticsSuccess = ({ commit }, statistics) =>
commit(types.RECEIVE_STATISTICS_SUCCESS, statistics);
export const receiveStatisticsError = ({ commit }, error) => {
commit(types.RECEIVE_STATISTICS_ERROR, error);
createFlash(s__('AdminDashboard|Error loading the statistics. Please try again'));
};
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
/**
* Merges the statisticsLabels with the state's data
* and returns an array of the following form:
* [{ key: "forks", label: "Forks", value: 50 }]
*/
export const getStatistics = state => labels =>
Object.keys(labels).map(key => {
const result = {
key,
label: labels[key],
value: state.statistics && state.statistics[key] ? state.statistics[key] : null,
};
return result;
});
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
import Vue from 'vue';
import Vuex from 'vuex';
import * as actions from './actions';
import * as getters from './getters';
import mutations from './mutations';
import state from './state';
Vue.use(Vuex);
export default () =>
new Vuex.Store({
actions,
getters,
mutations,
state: state(),
});
export const REQUEST_STATISTICS = 'REQUEST_STATISTICS';
export const RECEIVE_STATISTICS_SUCCESS = 'RECEIVE_STATISTICS_SUCCESS';
export const RECEIVE_STATISTICS_ERROR = 'RECEIVE_STATISTICS_ERROR';
import * as types from './mutation_types';
export default {
[types.REQUEST_STATISTICS](state) {
state.isLoading = true;
},
[types.RECEIVE_STATISTICS_SUCCESS](state, data) {
state.isLoading = false;
state.error = null;
state.statistics = data;
},
[types.RECEIVE_STATISTICS_ERROR](state, error) {
state.isLoading = false;
state.error = error;
},
};
export default () => ({
error: null,
isLoading: false,
statistics: null,
});
...@@ -36,6 +36,7 @@ const Api = { ...@@ -36,6 +36,7 @@ const Api = {
branchSinglePath: '/api/:version/projects/:id/repository/branches/:branch', branchSinglePath: '/api/:version/projects/:id/repository/branches/:branch',
createBranchPath: '/api/:version/projects/:id/repository/branches', createBranchPath: '/api/:version/projects/:id/repository/branches',
releasesPath: '/api/:version/projects/:id/releases', releasesPath: '/api/:version/projects/:id/releases',
adminStatisticsPath: 'api/:version/application/statistics',
group(groupId, callback) { group(groupId, callback) {
const url = Api.buildUrl(Api.groupPath).replace(':id', groupId); const url = Api.buildUrl(Api.groupPath).replace(':id', groupId);
...@@ -376,6 +377,11 @@ const Api = { ...@@ -376,6 +377,11 @@ const Api = {
return axios.get(url); return axios.get(url);
}, },
adminStatistics() {
const url = Api.buildUrl(this.adminStatisticsPath);
return axios.get(url);
},
buildUrl(url) { buildUrl(url) {
return joinPaths(gon.relative_url_root || '', url.replace(':version', gon.api_version)); return joinPaths(gon.relative_url_root || '', url.replace(':version', gon.api_version));
}, },
......
...@@ -46,7 +46,6 @@ export default class Shortcuts { ...@@ -46,7 +46,6 @@ export default class Shortcuts {
$(document).on('click.more_help', '.js-more-help-button', function clickMoreHelp(e) { $(document).on('click.more_help', '.js-more-help-button', function clickMoreHelp(e) {
$(this).remove(); $(this).remove();
$('.hidden-shortcut').show();
e.preventDefault(); e.preventDefault();
}); });
} }
...@@ -104,7 +103,6 @@ export default class Shortcuts { ...@@ -104,7 +103,6 @@ export default class Shortcuts {
return results; return results;
} }
$('.hidden-shortcut').show();
return $('.js-more-help-button').remove(); return $('.js-more-help-button').remove();
}); });
} }
......
...@@ -6,7 +6,7 @@ import { CopyAsGFM } from '../markdown/copy_as_gfm'; ...@@ -6,7 +6,7 @@ import { CopyAsGFM } from '../markdown/copy_as_gfm';
import { getSelectedFragment } from '~/lib/utils/common_utils'; import { getSelectedFragment } from '~/lib/utils/common_utils';
export default class ShortcutsIssuable extends Shortcuts { export default class ShortcutsIssuable extends Shortcuts {
constructor(isMergeRequest) { constructor() {
super(); super();
Mousetrap.bind('a', () => ShortcutsIssuable.openSidebarDropdown('assignee')); Mousetrap.bind('a', () => ShortcutsIssuable.openSidebarDropdown('assignee'));
...@@ -14,12 +14,6 @@ export default class ShortcutsIssuable extends Shortcuts { ...@@ -14,12 +14,6 @@ export default class ShortcutsIssuable extends Shortcuts {
Mousetrap.bind('l', () => ShortcutsIssuable.openSidebarDropdown('labels')); Mousetrap.bind('l', () => ShortcutsIssuable.openSidebarDropdown('labels'));
Mousetrap.bind('r', ShortcutsIssuable.replyWithSelectedText); Mousetrap.bind('r', ShortcutsIssuable.replyWithSelectedText);
Mousetrap.bind('e', ShortcutsIssuable.editIssue); Mousetrap.bind('e', ShortcutsIssuable.editIssue);
if (isMergeRequest) {
this.enabledHelp.push('.hidden-shortcut.merge_requests');
} else {
this.enabledHelp.push('.hidden-shortcut.issues');
}
} }
static replyWithSelectedText() { static replyWithSelectedText() {
......
...@@ -23,7 +23,5 @@ export default class ShortcutsNavigation extends Shortcuts { ...@@ -23,7 +23,5 @@ export default class ShortcutsNavigation extends Shortcuts {
Mousetrap.bind('g e', () => findAndFollowLink('.shortcuts-environments')); Mousetrap.bind('g e', () => findAndFollowLink('.shortcuts-environments'));
Mousetrap.bind('g l', () => findAndFollowLink('.shortcuts-metrics')); Mousetrap.bind('g l', () => findAndFollowLink('.shortcuts-metrics'));
Mousetrap.bind('i', () => findAndFollowLink('.shortcuts-new-issue')); Mousetrap.bind('i', () => findAndFollowLink('.shortcuts-new-issue'));
this.enabledHelp.push('.hidden-shortcut.project');
} }
} }
...@@ -11,7 +11,5 @@ export default class ShortcutsNetwork extends ShortcutsNavigation { ...@@ -11,7 +11,5 @@ export default class ShortcutsNetwork extends ShortcutsNavigation {
Mousetrap.bind(['down', 'j'], graph.scrollDown); Mousetrap.bind(['down', 'j'], graph.scrollDown);
Mousetrap.bind(['shift+up', 'shift+k'], graph.scrollTop); Mousetrap.bind(['shift+up', 'shift+k'], graph.scrollTop);
Mousetrap.bind(['shift+down', 'shift+j'], graph.scrollBottom); Mousetrap.bind(['shift+down', 'shift+j'], graph.scrollBottom);
this.enabledHelp.push('.hidden-shortcut.network');
} }
} }
...@@ -6,8 +6,6 @@ export default class ShortcutsWiki extends ShortcutsNavigation { ...@@ -6,8 +6,6 @@ export default class ShortcutsWiki extends ShortcutsNavigation {
constructor() { constructor() {
super(); super();
Mousetrap.bind('e', ShortcutsWiki.editWiki); Mousetrap.bind('e', ShortcutsWiki.editWiki);
this.enabledHelp.push('.hidden-shortcut.wiki');
} }
static editWiki() { static editWiki() {
......
...@@ -2,9 +2,9 @@ ...@@ -2,9 +2,9 @@
import $ from 'jquery'; import $ from 'jquery';
import { GlButton } from '@gitlab/ui'; import { GlButton } from '@gitlab/ui';
import { getMilestone } from 'ee_else_ce/boards/boards_util'; import { getMilestone } from 'ee_else_ce/boards/boards_util';
import ListIssue from 'ee_else_ce/boards/models/issue';
import eventHub from '../eventhub'; import eventHub from '../eventhub';
import ProjectSelect from './project_select.vue'; import ProjectSelect from './project_select.vue';
import ListIssue from '../models/issue';
import boardsStore from '../stores/boards_store'; import boardsStore from '../stores/boards_store';
export default { export default {
...@@ -54,6 +54,9 @@ export default { ...@@ -54,6 +54,9 @@ export default {
const assignees = this.list.assignee ? [this.list.assignee] : []; const assignees = this.list.assignee ? [this.list.assignee] : [];
const milestone = getMilestone(this.list); const milestone = getMilestone(this.list);
const { weightFeatureAvailable } = boardsStore;
const { weight } = weightFeatureAvailable ? boardsStore.state.currentBoard : {};
const issue = new ListIssue({ const issue = new ListIssue({
title: this.title, title: this.title,
labels, labels,
...@@ -61,6 +64,7 @@ export default { ...@@ -61,6 +64,7 @@ export default {
assignees, assignees,
milestone, milestone,
project_id: this.selectedProject.id, project_id: this.selectedProject.id,
weight,
}); });
eventHub.$emit(`scroll-board-list-${this.list.id}`); eventHub.$emit(`scroll-board-list-${this.list.id}`);
......
...@@ -245,6 +245,7 @@ export default { ...@@ -245,6 +245,7 @@ export default {
<div <div
v-if="!loading" v-if="!loading"
ref="content" ref="content"
data-qa-selector="boards_dropdown_content"
class="dropdown-content flex-fill" class="dropdown-content flex-fill"
@scroll.passive="throttledSetScrollFade" @scroll.passive="throttledSetScrollFade"
> >
......
...@@ -458,7 +458,6 @@ export default { ...@@ -458,7 +458,6 @@ export default {
</div> </div>
</application-row> </application-row>
<application-row <application-row
v-if="isProjectCluster"
id="knative" id="knative"
:logo-url="knativeLogo" :logo-url="knativeLogo"
:title="applications.knative.title" :title="applications.knative.title"
......
import initAdmin from './admin'; import initAdmin from './admin';
import initAdminStatisticsPanel from '../../admin/statistics_panel/index';
document.addEventListener('DOMContentLoaded', initAdmin()); document.addEventListener('DOMContentLoaded', () => {
const statisticsPanelContainer = document.getElementById('js-admin-statistics-container');
initAdmin();
initAdminStatisticsPanel(statisticsPanelContainer);
});
...@@ -20,6 +20,9 @@ export default { ...@@ -20,6 +20,9 @@ export default {
<stage-column-component <stage-column-component
v-for="(stage, index) in graph" v-for="(stage, index) in graph"
:key="stage.name" :key="stage.name"
:class="{
'append-right-48': shouldAddRightMargin(index),
}"
:title="capitalizeStageName(stage.name)" :title="capitalizeStageName(stage.name)"
:groups="stage.groups" :groups="stage.groups"
:stage-connector-class="stageConnectorClass(index, stage)" :stage-connector-class="stageConnectorClass(index, stage)"
......
...@@ -40,5 +40,15 @@ export default { ...@@ -40,5 +40,15 @@ export default {
refreshPipelineGraph() { refreshPipelineGraph() {
this.$emit('refreshPipelineGraph'); this.$emit('refreshPipelineGraph');
}, },
/**
* CSS class is applied:
* - if pipeline graph contains only one stage column component
*
* @param {number} index
* @returns {boolean}
*/
shouldAddRightMargin(index) {
return !(index === this.graph.length - 1);
},
}, },
}; };
<script>
import { GlToggle } from '@gitlab/ui';
import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
export default {
name: 'GlToggleVuex',
components: {
GlToggle,
},
props: {
stateProperty: {
type: String,
required: true,
},
storeModule: {
type: String,
required: false,
default: null,
},
setAction: {
type: String,
required: false,
default() {
return `set${capitalizeFirstCharacter(this.stateProperty)}`;
},
},
},
computed: {
value: {
get() {
const { state } = this.$store;
const { stateProperty, storeModule } = this;
return storeModule ? state[storeModule][stateProperty] : state[stateProperty];
},
set(value) {
const { stateProperty, storeModule, setAction } = this;
const action = storeModule ? `${storeModule}/${setAction}` : setAction;
this.$store.dispatch(action, { key: stateProperty, value });
},
},
},
};
</script>
<template>
<gl-toggle v-model="value">
<slot v-bind="{ value }"></slot>
</gl-toggle>
</template>
...@@ -395,6 +395,7 @@ img.emoji { ...@@ -395,6 +395,7 @@ img.emoji {
.prepend-left-default { margin-left: $gl-padding; } .prepend-left-default { margin-left: $gl-padding; }
.prepend-left-20 { margin-left: 20px; } .prepend-left-20 { margin-left: 20px; }
.prepend-left-32 { margin-left: 32px; } .prepend-left-32 { margin-left: 32px; }
.prepend-left-64 { margin-left: 64px; }
.append-right-4 { margin-right: 4px; } .append-right-4 { margin-right: 4px; }
.append-right-5 { margin-right: 5px; } .append-right-5 { margin-right: 5px; }
.append-right-8 { margin-right: 8px; } .append-right-8 { margin-right: 8px; }
...@@ -402,6 +403,8 @@ img.emoji { ...@@ -402,6 +403,8 @@ img.emoji {
.append-right-15 { margin-right: 15px; } .append-right-15 { margin-right: 15px; }
.append-right-default { margin-right: $gl-padding; } .append-right-default { margin-right: $gl-padding; }
.append-right-20 { margin-right: 20px; } .append-right-20 { margin-right: 20px; }
.append-right-32 { margin-right: 32px; }
.append-right-48 { margin-right: 48px; }
.prepend-right-32 { margin-right: 32px; } .prepend-right-32 { margin-right: 32px; }
.append-bottom-0 { margin-bottom: 0; } .append-bottom-0 { margin-bottom: 0; }
.append-bottom-4 { margin-bottom: $gl-padding-4; } .append-bottom-4 { margin-bottom: $gl-padding-4; }
......
...@@ -2,6 +2,12 @@ ...@@ -2,6 +2,12 @@
max-width: 98%; max-width: 98%;
} }
.modal-1040 {
@include media-breakpoint-up(xl) {
max-width: 1040px;
}
}
.modal-header { .modal-header {
background-color: $modal-body-bg; background-color: $modal-body-bg;
......
...@@ -476,10 +476,6 @@ ...@@ -476,10 +476,6 @@
display: inline-block; display: inline-block;
vertical-align: top; vertical-align: top;
&:not(:last-child) {
margin-right: 44px;
}
&.left-margin { &.left-margin {
&:not(:first-child) { &:not(:first-child) {
margin-left: 44px; margin-left: 44px;
......
...@@ -85,7 +85,10 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController ...@@ -85,7 +85,10 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
params[:application_setting][:import_sources]&.delete("") params[:application_setting][:import_sources]&.delete("")
params[:application_setting][:restricted_visibility_levels]&.delete("") params[:application_setting][:restricted_visibility_levels]&.delete("")
# TODO Remove domain_blacklist_raw in APIv5 (See https://gitlab.com/gitlab-org/gitlab-ce/issues/67204)
params.delete(:domain_blacklist_raw) if params[:domain_blacklist_file] params.delete(:domain_blacklist_raw) if params[:domain_blacklist_file]
params.delete(:domain_blacklist_raw) if params[:domain_blacklist]
params.delete(:domain_whitelist_raw) if params[:domain_whitelist]
params.require(:application_setting).permit( params.require(:application_setting).permit(
visible_application_setting_attributes visible_application_setting_attributes
......
...@@ -3,8 +3,7 @@ ...@@ -3,8 +3,7 @@
class Admin::DashboardController < Admin::ApplicationController class Admin::DashboardController < Admin::ApplicationController
include CountHelper include CountHelper
COUNTED_ITEMS = [Project, User, Group, ForkNetworkMember, ForkNetwork, Issue, COUNTED_ITEMS = [Project, User, Group].freeze
MergeRequest, Note, Snippet, Key, Milestone].freeze
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def index def index
......
...@@ -31,6 +31,10 @@ class Clusters::BaseController < ApplicationController ...@@ -31,6 +31,10 @@ class Clusters::BaseController < ApplicationController
access_denied! unless can?(current_user, :create_cluster, clusterable) access_denied! unless can?(current_user, :create_cluster, clusterable)
end end
def authorize_read_prometheus!
access_denied! unless can?(current_user, :read_prometheus, clusterable)
end
def clusterable def clusterable
raise NotImplementedError raise NotImplementedError
end end
......
...@@ -35,6 +35,12 @@ class Clusters::ClustersController < Clusters::BaseController ...@@ -35,6 +35,12 @@ class Clusters::ClustersController < Clusters::BaseController
end end
def new def new
return unless Feature.enabled?(:create_eks_clusters)
@gke_selected = params[:provider] == 'gke'
@eks_selected = params[:provider] == 'eks'
return redirect_to @authorize_url if @gke_selected && @authorize_url && !@valid_gcp_token
end end
# Overridding ActionController::Metal#status is NOT a good idea # Overridding ActionController::Metal#status is NOT a good idea
...@@ -99,7 +105,7 @@ class Clusters::ClustersController < Clusters::BaseController ...@@ -99,7 +105,7 @@ class Clusters::ClustersController < Clusters::BaseController
validate_gcp_token validate_gcp_token
user_cluster user_cluster
render :new, locals: { active_tab: 'gcp' } render :new, locals: { active_tab: 'create' }
end end
end end
...@@ -116,7 +122,7 @@ class Clusters::ClustersController < Clusters::BaseController ...@@ -116,7 +122,7 @@ class Clusters::ClustersController < Clusters::BaseController
validate_gcp_token validate_gcp_token
gcp_cluster gcp_cluster
render :new, locals: { active_tab: 'user' } render :new, locals: { active_tab: 'add' }
end end
end end
...@@ -189,7 +195,8 @@ class Clusters::ClustersController < Clusters::BaseController ...@@ -189,7 +195,8 @@ class Clusters::ClustersController < Clusters::BaseController
end end
def generate_gcp_authorize_url def generate_gcp_authorize_url
state = generate_session_key_redirect(clusterable.new_path.to_s) params = Feature.enabled?(:create_eks_clusters) ? { provider: :gke } : {}
state = generate_session_key_redirect(clusterable.new_path(params).to_s)
@authorize_url = GoogleApi::CloudPlatform::Client.new( @authorize_url = GoogleApi::CloudPlatform::Client.new(
nil, callback_google_api_auth_url, nil, callback_google_api_auth_url,
......
...@@ -18,10 +18,23 @@ class Projects::ServicesController < Projects::ApplicationController ...@@ -18,10 +18,23 @@ class Projects::ServicesController < Projects::ApplicationController
def update def update
@service.attributes = service_params[:service] @service.attributes = service_params[:service]
if @service.save(context: :manual_change) saved = @service.save(context: :manual_change)
redirect_to(project_settings_integrations_path(@project), notice: success_message)
else respond_to do |format|
render 'edit' format.html do
if saved
redirect_to project_settings_integrations_path(@project),
notice: success_message
else
render 'edit'
end
end
format.json do
status = saved ? :ok : :unprocessable_entity
render json: serialize_as_json, status: status
end
end end
end end
...@@ -67,4 +80,10 @@ class Projects::ServicesController < Projects::ApplicationController ...@@ -67,4 +80,10 @@ class Projects::ServicesController < Projects::ApplicationController
def ensure_service_enabled def ensure_service_enabled
render_404 unless service render_404 unless service
end end
def serialize_as_json
@service
.as_json(only: @service.json_fields)
.merge(errors: @service.errors.as_json)
end
end end
...@@ -20,6 +20,7 @@ class RegistrationsController < Devise::RegistrationsController ...@@ -20,6 +20,7 @@ class RegistrationsController < Devise::RegistrationsController
super do |new_user| super do |new_user|
persist_accepted_terms_if_required(new_user) persist_accepted_terms_if_required(new_user)
yield new_user if block_given?
end end
rescue Gitlab::Access::AccessDeniedError rescue Gitlab::Access::AccessDeniedError
redirect_to(new_user_session_path) redirect_to(new_user_session_path)
......
...@@ -193,15 +193,30 @@ class IssuableFinder ...@@ -193,15 +193,30 @@ class IssuableFinder
projects = projects =
if current_user && params[:authorized_only].presence && !current_user_related? if current_user && params[:authorized_only].presence && !current_user_related?
current_user.authorized_projects(min_access_level) current_user.authorized_projects(min_access_level)
elsif group
find_group_projects
else else
Project.public_or_visible_to_user(current_user, min_access_level) projects_public_or_visible_to_user
end end
@projects = projects.with_feature_available_for_user(klass, current_user).reorder(nil) # rubocop: disable CodeReuse/ActiveRecord @projects = projects.with_feature_available_for_user(klass, current_user).reorder(nil) # rubocop: disable CodeReuse/ActiveRecord
end end
def projects_public_or_visible_to_user
projects =
if group
if params[:projects]
find_group_projects.id_in(params[:projects])
else
find_group_projects
end
elsif params[:projects]
Project.id_in(params[:projects])
else
Project
end
projects.public_or_visible_to_user(current_user, min_access_level)
end
def find_group_projects def find_group_projects
return Project.none unless group return Project.none unless group
...@@ -209,7 +224,7 @@ class IssuableFinder ...@@ -209,7 +224,7 @@ class IssuableFinder
Project.where(namespace_id: group.self_and_descendants) # rubocop: disable CodeReuse/ActiveRecord Project.where(namespace_id: group.self_and_descendants) # rubocop: disable CodeReuse/ActiveRecord
else else
group.projects group.projects
end.public_or_visible_to_user(current_user, min_access_level) end
end end
def search def search
......
...@@ -180,8 +180,12 @@ module ApplicationSettingsHelper ...@@ -180,8 +180,12 @@ module ApplicationSettingsHelper
:default_projects_limit, :default_projects_limit,
:default_snippet_visibility, :default_snippet_visibility,
:disabled_oauth_sign_in_sources, :disabled_oauth_sign_in_sources,
:domain_blacklist,
:domain_blacklist_enabled, :domain_blacklist_enabled,
# TODO Remove domain_blacklist_raw in APIv5 (See https://gitlab.com/gitlab-org/gitlab-ce/issues/67204)
:domain_blacklist_raw, :domain_blacklist_raw,
:domain_whitelist,
# TODO Remove domain_whitelist_raw in APIv5 (See https://gitlab.com/gitlab-org/gitlab-ce/issues/67204)
:domain_whitelist_raw, :domain_whitelist_raw,
:outbound_local_requests_whitelist_raw, :outbound_local_requests_whitelist_raw,
:dsa_key_restriction, :dsa_key_restriction,
......
# frozen_string_literal: true # frozen_string_literal: true
module UserCalloutsHelper module UserCalloutsHelper
GKE_CLUSTER_INTEGRATION = 'gke_cluster_integration'.freeze GKE_CLUSTER_INTEGRATION = 'gke_cluster_integration'
GCP_SIGNUP_OFFER = 'gcp_signup_offer'.freeze GCP_SIGNUP_OFFER = 'gcp_signup_offer'
SUGGEST_POPOVER_DISMISSED = 'suggest_popover_dismissed'.freeze SUGGEST_POPOVER_DISMISSED = 'suggest_popover_dismissed'
def show_gke_cluster_integration_callout?(project) def show_gke_cluster_integration_callout?(project)
can?(current_user, :create_cluster, project) && can?(current_user, :create_cluster, project) &&
......
...@@ -23,6 +23,7 @@ module Ci ...@@ -23,6 +23,7 @@ module Ci
belongs_to :auto_canceled_by, class_name: 'Ci::Pipeline' belongs_to :auto_canceled_by, class_name: 'Ci::Pipeline'
belongs_to :pipeline_schedule, class_name: 'Ci::PipelineSchedule' belongs_to :pipeline_schedule, class_name: 'Ci::PipelineSchedule'
belongs_to :merge_request, class_name: 'MergeRequest' belongs_to :merge_request, class_name: 'MergeRequest'
belongs_to :external_pull_request
has_internal_id :iid, scope: :project, presence: false, init: ->(s) do has_internal_id :iid, scope: :project, presence: false, init: ->(s) do
s&.project&.all_pipelines&.maximum(:iid) || s&.project&.all_pipelines&.count s&.project&.all_pipelines&.maximum(:iid) || s&.project&.all_pipelines&.count
...@@ -64,6 +65,11 @@ module Ci ...@@ -64,6 +65,11 @@ module Ci
validates :merge_request, presence: { if: :merge_request_event? } validates :merge_request, presence: { if: :merge_request_event? }
validates :merge_request, absence: { unless: :merge_request_event? } validates :merge_request, absence: { unless: :merge_request_event? }
validates :tag, inclusion: { in: [false], if: :merge_request_event? } validates :tag, inclusion: { in: [false], if: :merge_request_event? }
validates :external_pull_request, presence: { if: :external_pull_request_event? }
validates :external_pull_request, absence: { unless: :external_pull_request_event? }
validates :tag, inclusion: { in: [false], if: :external_pull_request_event? }
validates :status, presence: { unless: :importing? } validates :status, presence: { unless: :importing? }
validate :valid_commit_sha, unless: :importing? validate :valid_commit_sha, unless: :importing?
validates :source, exclusion: { in: %w(unknown), unless: :importing? }, on: :create validates :source, exclusion: { in: %w(unknown), unless: :importing? }, on: :create
...@@ -683,6 +689,10 @@ module Ci ...@@ -683,6 +689,10 @@ module Ci
variables.append(key: 'CI_MERGE_REQUEST_TARGET_BRANCH_SHA', value: target_sha.to_s) variables.append(key: 'CI_MERGE_REQUEST_TARGET_BRANCH_SHA', value: target_sha.to_s)
variables.concat(merge_request.predefined_variables) variables.concat(merge_request.predefined_variables)
end end
if external_pull_request_event? && external_pull_request
variables.concat(external_pull_request.predefined_variables)
end
end end
end end
......
...@@ -23,7 +23,8 @@ module Ci ...@@ -23,7 +23,8 @@ module Ci
api: 5, api: 5,
external: 6, external: 6,
chat: 8, chat: 8,
merge_request_event: 10 merge_request_event: 10,
external_pull_request_event: 11
} }
end end
......
...@@ -10,7 +10,6 @@ module Clusters ...@@ -10,7 +10,6 @@ module Clusters
self.table_name = 'clusters' self.table_name = 'clusters'
PROJECT_ONLY_APPLICATIONS = { PROJECT_ONLY_APPLICATIONS = {
Applications::Knative.application_name => Applications::Knative
}.freeze }.freeze
APPLICATIONS = { APPLICATIONS = {
Applications::Helm.application_name => Applications::Helm, Applications::Helm.application_name => Applications::Helm,
...@@ -18,7 +17,8 @@ module Clusters ...@@ -18,7 +17,8 @@ module Clusters
Applications::CertManager.application_name => Applications::CertManager, Applications::CertManager.application_name => Applications::CertManager,
Applications::Prometheus.application_name => Applications::Prometheus, Applications::Prometheus.application_name => Applications::Prometheus,
Applications::Runner.application_name => Applications::Runner, Applications::Runner.application_name => Applications::Runner,
Applications::Jupyter.application_name => Applications::Jupyter Applications::Jupyter.application_name => Applications::Jupyter,
Applications::Knative.application_name => Applications::Knative
}.merge(PROJECT_ONLY_APPLICATIONS).freeze }.merge(PROJECT_ONLY_APPLICATIONS).freeze
DEFAULT_ENVIRONMENT = '*' DEFAULT_ENVIRONMENT = '*'
KUBE_INGRESS_BASE_DOMAIN = 'KUBE_INGRESS_BASE_DOMAIN' KUBE_INGRESS_BASE_DOMAIN = 'KUBE_INGRESS_BASE_DOMAIN'
......
# frozen_string_literal: true
# This model stores pull requests coming from external providers, such as
# GitHub, when GitLab project is set as CI/CD only and remote mirror.
#
# When setting up a remote mirror with GitHub we subscribe to push and
# pull_request webhook events. When a pull request is opened on GitHub,
# a webhook is sent out, we create or update the status of the pull
# request locally.
#
# When the mirror is updated and changes are pushed to branches we check
# if there are open pull requests for the source and target branch.
# If so, we create pipelines for external pull requests.
class ExternalPullRequest < ApplicationRecord
include Gitlab::Utils::StrongMemoize
include ShaAttribute
belongs_to :project
sha_attribute :source_sha
sha_attribute :target_sha
validates :source_branch, presence: true
validates :target_branch, presence: true
validates :source_sha, presence: true
validates :target_sha, presence: true
validates :source_repository, presence: true
validates :target_repository, presence: true
validates :status, presence: true
enum status: {
open: 1,
closed: 2
}
# We currently don't support pull requests from fork, so
# we are going to return an error to the webhook
validate :not_from_fork
scope :by_source_branch, ->(branch) { where(source_branch: branch) }
scope :by_source_repository, -> (repository) { where(source_repository: repository) }
def self.create_or_update_from_params(params)
find_params = params.slice(:project_id, :source_branch, :target_branch)
safe_find_or_initialize_and_update(find: find_params, update: params) do |pull_request|
yield(pull_request) if block_given?
end
end
def actual_branch_head?
actual_source_branch_sha == source_sha
end
def from_fork?
source_repository != target_repository
end
def source_ref
Gitlab::Git::BRANCH_REF_PREFIX + source_branch
end
def predefined_variables
Gitlab::Ci::Variables::Collection.new.tap do |variables|
variables.append(key: 'CI_EXTERNAL_PULL_REQUEST_IID', value: pull_request_iid.to_s)
variables.append(key: 'CI_EXTERNAL_PULL_REQUEST_SOURCE_BRANCH_SHA', value: source_sha)
variables.append(key: 'CI_EXTERNAL_PULL_REQUEST_TARGET_BRANCH_SHA', value: target_sha)
variables.append(key: 'CI_EXTERNAL_PULL_REQUEST_SOURCE_BRANCH_NAME', value: source_branch)
variables.append(key: 'CI_EXTERNAL_PULL_REQUEST_TARGET_BRANCH_NAME', value: target_branch)
end
end
private
def actual_source_branch_sha
project.commit(source_ref)&.sha
end
def not_from_fork
if from_fork?
errors.add(:base, 'Pull requests from fork are not supported')
end
end
def self.safe_find_or_initialize_and_update(find:, update:)
safe_ensure_unique(retries: 1) do
model = find_or_initialize_by(find)
if model.update(update)
yield(model) if block_given?
end
model
end
end
end
...@@ -31,7 +31,7 @@ class Issue < ApplicationRecord ...@@ -31,7 +31,7 @@ class Issue < ApplicationRecord
has_internal_id :iid, scope: :project, init: ->(s) { s&.project&.issues&.maximum(:iid) } has_internal_id :iid, scope: :project, init: ->(s) { s&.project&.issues&.maximum(:iid) }
has_many :events, as: :target, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :events, as: :target, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
has_many :merge_requests_closing_issues, has_many :merge_requests_closing_issues,
class_name: 'MergeRequestsClosingIssues', class_name: 'MergeRequestsClosingIssues',
......
...@@ -54,7 +54,7 @@ class MergeRequest < ApplicationRecord ...@@ -54,7 +54,7 @@ class MergeRequest < ApplicationRecord
belongs_to :head_pipeline, foreign_key: "head_pipeline_id", class_name: "Ci::Pipeline" belongs_to :head_pipeline, foreign_key: "head_pipeline_id", class_name: "Ci::Pipeline"
has_many :events, as: :target, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :events, as: :target, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
has_many :merge_requests_closing_issues, has_many :merge_requests_closing_issues,
class_name: 'MergeRequestsClosingIssues', class_name: 'MergeRequestsClosingIssues',
......
...@@ -37,7 +37,7 @@ class Milestone < ApplicationRecord ...@@ -37,7 +37,7 @@ class Milestone < ApplicationRecord
has_many :issues has_many :issues
has_many :labels, -> { distinct.reorder('labels.title') }, through: :issues has_many :labels, -> { distinct.reorder('labels.title') }, through: :issues
has_many :merge_requests has_many :merge_requests
has_many :events, as: :target, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :events, as: :target, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
scope :of_projects, ->(ids) { where(project_id: ids) } scope :of_projects, ->(ids) { where(project_id: ids) }
scope :of_groups, ->(ids) { where(group_id: ids) } scope :of_groups, ->(ids) { where(group_id: ids) }
......
...@@ -78,7 +78,7 @@ class Note < ApplicationRecord ...@@ -78,7 +78,7 @@ class Note < ApplicationRecord
# suggestions.delete_all calls # suggestions.delete_all calls
has_many :suggestions, -> { order(:relative_order) }, has_many :suggestions, -> { order(:relative_order) },
inverse_of: :note, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent inverse_of: :note, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
has_many :events, as: :target, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :events, as: :target, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
has_one :system_note_metadata has_one :system_note_metadata
has_one :note_diff_file, inverse_of: :diff_note, foreign_key: :diff_note_id has_one :note_diff_file, inverse_of: :diff_note, foreign_key: :diff_note_id
......
...@@ -17,7 +17,7 @@ class PagesDomain < ApplicationRecord ...@@ -17,7 +17,7 @@ class PagesDomain < ApplicationRecord
validates :certificate, certificate: true, if: ->(domain) { domain.certificate.present? } validates :certificate, certificate: true, if: ->(domain) { domain.certificate.present? }
validates :key, presence: { message: 'must be present if HTTPS-only is enabled' }, validates :key, presence: { message: 'must be present if HTTPS-only is enabled' },
if: :certificate_should_be_present? if: :certificate_should_be_present?
validates :key, certificate_key: true, if: ->(domain) { domain.key.present? } validates :key, certificate_key: true, named_ecdsa_key: true, if: ->(domain) { domain.key.present? }
validates :verification_code, presence: true, allow_blank: false validates :verification_code, presence: true, allow_blank: false
validate :validate_pages_domain validate :validate_pages_domain
...@@ -247,7 +247,7 @@ class PagesDomain < ApplicationRecord ...@@ -247,7 +247,7 @@ class PagesDomain < ApplicationRecord
def pkey def pkey
return unless key return unless key
@pkey ||= OpenSSL::PKey::RSA.new(key) @pkey ||= OpenSSL::PKey.read(key)
rescue OpenSSL::PKey::PKeyError, OpenSSL::Cipher::CipherError rescue OpenSSL::PKey::PKeyError, OpenSSL::Cipher::CipherError
nil nil
end end
......
...@@ -291,6 +291,8 @@ class Project < ApplicationRecord ...@@ -291,6 +291,8 @@ class Project < ApplicationRecord
has_many :remote_mirrors, inverse_of: :project has_many :remote_mirrors, inverse_of: :project
has_many :cycle_analytics_stages, class_name: 'Analytics::CycleAnalytics::ProjectStage' has_many :cycle_analytics_stages, class_name: 'Analytics::CycleAnalytics::ProjectStage'
has_many :external_pull_requests, inverse_of: :project
accepts_nested_attributes_for :variables, allow_destroy: true accepts_nested_attributes_for :variables, allow_destroy: true
accepts_nested_attributes_for :project_feature, update_only: true accepts_nested_attributes_for :project_feature, update_only: true
accepts_nested_attributes_for :import_data accepts_nested_attributes_for :import_data
......
...@@ -107,6 +107,13 @@ class Service < ApplicationRecord ...@@ -107,6 +107,13 @@ class Service < ApplicationRecord
[] []
end end
# Expose a list of fields in the JSON endpoint.
#
# This list is used in `Service#as_json(only: json_fields)`.
def json_fields
%w(active)
end
def test_data(project, user) def test_data(project, user)
Gitlab::DataBuilder::Push.build_sample(project, user) Gitlab::DataBuilder::Push.build_sample(project, user)
end end
......
...@@ -131,7 +131,7 @@ class User < ApplicationRecord ...@@ -131,7 +131,7 @@ class User < ApplicationRecord
has_many :notes, dependent: :destroy, foreign_key: :author_id # rubocop:disable Cop/ActiveRecordDependent has_many :notes, dependent: :destroy, foreign_key: :author_id # rubocop:disable Cop/ActiveRecordDependent
has_many :issues, dependent: :destroy, foreign_key: :author_id # rubocop:disable Cop/ActiveRecordDependent has_many :issues, dependent: :destroy, foreign_key: :author_id # rubocop:disable Cop/ActiveRecordDependent
has_many :merge_requests, dependent: :destroy, foreign_key: :author_id # rubocop:disable Cop/ActiveRecordDependent has_many :merge_requests, dependent: :destroy, foreign_key: :author_id # rubocop:disable Cop/ActiveRecordDependent
has_many :events, dependent: :destroy, foreign_key: :author_id # rubocop:disable Cop/ActiveRecordDependent has_many :events, dependent: :delete_all, foreign_key: :author_id # rubocop:disable Cop/ActiveRecordDependent
has_many :releases, dependent: :nullify, foreign_key: :author_id # rubocop:disable Cop/ActiveRecordDependent has_many :releases, dependent: :nullify, foreign_key: :author_id # rubocop:disable Cop/ActiveRecordDependent
has_many :subscriptions, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :subscriptions, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :oauth_applications, class_name: 'Doorkeeper::Application', as: :owner, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent has_many :oauth_applications, class_name: 'Doorkeeper::Application', as: :owner, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
......
...@@ -8,6 +8,7 @@ module Clusters ...@@ -8,6 +8,7 @@ module Clusters
enable :create_cluster enable :create_cluster
enable :update_cluster enable :update_cluster
enable :admin_cluster enable :admin_cluster
enable :read_prometheus
end end
end end
end end
...@@ -25,8 +25,8 @@ class ClusterablePresenter < Gitlab::View::Presenter::Delegated ...@@ -25,8 +25,8 @@ class ClusterablePresenter < Gitlab::View::Presenter::Delegated
polymorphic_path([clusterable, :clusters]) polymorphic_path([clusterable, :clusters])
end end
def new_path def new_path(options = {})
new_polymorphic_path([clusterable, :cluster]) new_polymorphic_path([clusterable, :cluster], options)
end end
def create_user_clusters_path def create_user_clusters_path
......
...@@ -18,8 +18,8 @@ class InstanceClusterablePresenter < ClusterablePresenter ...@@ -18,8 +18,8 @@ class InstanceClusterablePresenter < ClusterablePresenter
end end
override :new_path override :new_path
def new_path def new_path(options = {})
new_admin_cluster_path new_admin_cluster_path(options)
end end
override :cluster_status_cluster_path override :cluster_status_cluster_path
......
# frozen_string_literal: true # frozen_string_literal: true
class MergeRequestNoteableEntity < Grape::Entity class MergeRequestNoteableEntity < IssuableEntity
include RequestAwareEntity include RequestAwareEntity
# Currently this attr is exposed to be used in app/assets/javascripts/notes/stores/getters.js # Currently this attr is exposed to be used in app/assets/javascripts/notes/stores/getters.js
......
...@@ -18,7 +18,8 @@ module Ci ...@@ -18,7 +18,8 @@ module Ci
Gitlab::Ci::Pipeline::Chain::Limit::Activity, Gitlab::Ci::Pipeline::Chain::Limit::Activity,
Gitlab::Ci::Pipeline::Chain::Limit::JobActivity].freeze Gitlab::Ci::Pipeline::Chain::Limit::JobActivity].freeze
def execute(source, ignore_skip_ci: false, save_on_errors: true, trigger_request: nil, schedule: nil, merge_request: nil, **options, &block) # rubocop: disable Metrics/ParameterLists
def execute(source, ignore_skip_ci: false, save_on_errors: true, trigger_request: nil, schedule: nil, merge_request: nil, external_pull_request: nil, **options, &block)
@pipeline = Ci::Pipeline.new @pipeline = Ci::Pipeline.new
command = Gitlab::Ci::Pipeline::Chain::Command.new( command = Gitlab::Ci::Pipeline::Chain::Command.new(
...@@ -32,6 +33,7 @@ module Ci ...@@ -32,6 +33,7 @@ module Ci
trigger_request: trigger_request, trigger_request: trigger_request,
schedule: schedule, schedule: schedule,
merge_request: merge_request, merge_request: merge_request,
external_pull_request: external_pull_request,
ignore_skip_ci: ignore_skip_ci, ignore_skip_ci: ignore_skip_ci,
save_incompleted: save_on_errors, save_incompleted: save_on_errors,
seeds_block: block, seeds_block: block,
...@@ -62,6 +64,7 @@ module Ci ...@@ -62,6 +64,7 @@ module Ci
pipeline pipeline
end end
# rubocop: enable Metrics/ParameterLists
def execute!(*args, &block) def execute!(*args, &block)
execute(*args, &block).tap do |pipeline| execute(*args, &block).tap do |pipeline|
......
# frozen_string_literal: true
# This service is responsible for creating a pipeline for a given
# ExternalPullRequest coming from other providers such as GitHub.
module ExternalPullRequests
class CreatePipelineService < BaseService
def execute(pull_request)
return unless pull_request.open? && pull_request.actual_branch_head?
create_pipeline_for(pull_request)
end
private
def create_pipeline_for(pull_request)
Ci::CreatePipelineService.new(project, current_user, create_params(pull_request))
.execute(:external_pull_request_event, external_pull_request: pull_request)
end
def create_params(pull_request)
{
ref: pull_request.source_ref,
source_sha: pull_request.source_sha,
target_sha: pull_request.target_sha
}
end
end
end
...@@ -57,7 +57,9 @@ module Git ...@@ -57,7 +57,9 @@ module Git
Ci::CreatePipelineService Ci::CreatePipelineService
.new(project, current_user, pipeline_params) .new(project, current_user, pipeline_params)
.execute(:push, pipeline_options) .execute!(:push, pipeline_options)
rescue Ci::CreatePipelineService::CreateError => ex
log_pipeline_errors(ex)
end end
def execute_project_hooks def execute_project_hooks
...@@ -125,5 +127,29 @@ module Git ...@@ -125,5 +127,29 @@ module Git
project.mark_stuck_remote_mirrors_as_failed! project.mark_stuck_remote_mirrors_as_failed!
project.update_remote_mirrors project.update_remote_mirrors
end end
def log_pipeline_errors(exception)
data = {
class: self.class.name,
correlation_id: Labkit::Correlation::CorrelationId.current_id.to_s,
project_id: project.id,
project_path: project.full_path,
message: "Error creating pipeline",
errors: exception.to_s,
pipeline_params: pipeline_params
}
logger.warn(data)
end
def logger
if Sidekiq.server?
Sidekiq.logger
else
# This service runs in Sidekiq, so this shouldn't ever be
# called, but this is included just in case.
Gitlab::ProjectServiceLogger
end
end
end end
end end
# frozen_string_literal: true # frozen_string_literal: true
# UrlValidator # CertificateKeyValidator
# #
# Custom validator for private keys. # Custom validator for private keys.
# #
...@@ -20,7 +20,7 @@ class CertificateKeyValidator < ActiveModel::EachValidator ...@@ -20,7 +20,7 @@ class CertificateKeyValidator < ActiveModel::EachValidator
def valid_private_key_pem?(value) def valid_private_key_pem?(value)
return false unless value return false unless value
pkey = OpenSSL::PKey::RSA.new(value) pkey = OpenSSL::PKey.read(value)
pkey.private? pkey.private?
rescue OpenSSL::PKey::PKeyError rescue OpenSSL::PKey::PKeyError
false false
......
# frozen_string_literal: true
# NamedEcdsaKeyValidator
#
# Custom validator for ecdsa private keys.
# Golang currently doesn't support explicit curves for ECDSA certificates
# This validator checks if curve is set by name, not by parameters
#
# class Project < ActiveRecord::Base
# validates :certificate_key, named_ecdsa_key: true
# end
#
class NamedEcdsaKeyValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
if explicit_ec?(value)
record.errors.add(attribute, "ECDSA keys with explicit curves are not supported")
end
end
private
UNNAMED_CURVE = "UNDEF"
def explicit_ec?(value)
return false unless value
pkey = OpenSSL::PKey.read(value)
return false unless pkey.is_a?(OpenSSL::PKey::EC)
pkey.group.curve_name == UNNAMED_CURVE
rescue OpenSSL::PKey::PKeyError
false
end
end
...@@ -35,41 +35,7 @@ ...@@ -35,41 +35,7 @@
= link_to 'New group', new_admin_group_path, class: "btn btn-success" = link_to 'New group', new_admin_group_path, class: "btn btn-success"
.row .row
.col-md-4 .col-md-4
.info-well #js-admin-statistics-container
.well-segment.admin-well.admin-well-statistics
%h4 Statistics
%p
Forks
%span.light.float-right
= approximate_fork_count_with_delimiters(@counts)
%p
Issues
%span.light.float-right
= approximate_count_with_delimiters(@counts, Issue)
%p
Merge Requests
%span.light.float-right
= approximate_count_with_delimiters(@counts, MergeRequest)
%p
Notes
%span.light.float-right
= approximate_count_with_delimiters(@counts, Note)
%p
Snippets
%span.light.float-right
= approximate_count_with_delimiters(@counts, Snippet)
%p
SSH Keys
%span.light.float-right
= approximate_count_with_delimiters(@counts, Key)
%p
Milestones
%span.light.float-right
= approximate_count_with_delimiters(@counts, Milestone)
%p
Active Users
%span.light.float-right
= number_with_delimiter(User.active.count)
.col-md-4 .col-md-4
.info-well .info-well
.well-segment.admin-well.admin-well-features .well-segment.admin-well.admin-well-features
......
- provider = local_assigns.fetch(:provider)
- logo_path = local_assigns.fetch(:logo_path)
- label = local_assigns.fetch(:label)
= link_to clusterable.new_path(provider: provider), class: 'btn gl-button btn-outline flex-fill d-inline-flex flex-column mr-3 justify-content-center align-items-center' do
= image_tag logo_path, alt: label, class: 'gl-w-13 gl-h-13'
%span
= label
- gke_label = s_('ClusterIntegration|Google GKE')
- eks_label = s_('ClusterIntegration|Amazon EKS')
- create_cluster_label = s_('ClusterIntegration|Create cluster on')
.d-flex.flex-column
%h5
= create_cluster_label
.d-flex
= render partial: 'clusters/clusters/cloud_providers/cloud_provider_button',
locals: { provider: 'gke', label: gke_label, logo_path: '' }
= render partial: 'clusters/clusters/cloud_providers/cloud_provider_button',
locals: { provider: 'eks', label: eks_label, logo_path: '' }
.js-create-eks-cluster-form-container
- breadcrumb_title _('Kubernetes') - breadcrumb_title _('Kubernetes')
- page_title _('Kubernetes Cluster') - page_title _('Kubernetes Cluster')
- active_tab = local_assigns.fetch(:active_tab, 'gcp') - create_eks_enabled = Feature.enabled?(:create_eks_clusters)
- active_tab = local_assigns.fetch(:active_tab, 'create')
- link_end = '<a/>'.html_safe
= javascript_include_tag 'https://apis.google.com/js/api.js' = javascript_include_tag 'https://apis.google.com/js/api.js'
= render_gcp_signup_offer = render_gcp_signup_offer
...@@ -11,26 +13,36 @@ ...@@ -11,26 +13,36 @@
.col-md-9.js-toggle-container .col-md-9.js-toggle-container
%ul.nav-links.nav-tabs.gitlab-tabs.nav{ role: 'tablist' } %ul.nav-links.nav-tabs.gitlab-tabs.nav{ role: 'tablist' }
%li.nav-item{ role: 'presentation' } %li.nav-item{ role: 'presentation' }
%a.nav-link{ href: '#create-gcp-cluster-pane', id: 'create-gcp-cluster-tab', class: active_when(active_tab == 'gcp'), data: { toggle: 'tab' }, role: 'tab' } %a.nav-link{ href: '#create-cluster-pane', id: 'create-cluster-tab', class: active_when(active_tab == 'create'), data: { toggle: 'tab' }, role: 'tab' }
%span Create new Cluster on GKE %span Create new Cluster on GKE
%li.nav-item{ role: 'presentation' } %li.nav-item{ role: 'presentation' }
%a.nav-link{ href: '#add-user-cluster-pane', id: 'add-user-cluster-tab', class: active_when(active_tab == 'user'), data: { toggle: 'tab' }, role: 'tab' } %a.nav-link{ href: '#add-cluster-pane', id: 'add-cluster-tab', class: active_when(active_tab == 'add'), data: { toggle: 'tab' }, role: 'tab' }
%span Add existing cluster %span Add existing cluster
.tab-content.gitlab-tab-content .tab-content.gitlab-tab-content
.tab-pane{ id: 'create-gcp-cluster-pane', class: active_when(active_tab == 'gcp'), role: 'tabpanel' } - if create_eks_enabled
= render 'clusters/clusters/gcp/header' .tab-pane{ id: 'create-cluster-pane', class: active_when(active_tab == 'create'), role: 'tabpanel' }
- if @valid_gcp_token - if @gke_selected && @valid_gcp_token
= render 'clusters/clusters/gcp/form' = render 'clusters/clusters/gcp/header'
- elsif @authorize_url = render 'clusters/clusters/gcp/form'
.signin-with-google - elsif @eks_selected
= link_to(image_tag('auth_buttons/signin_with_google.png', width: '191px'), @authorize_url) = render 'clusters/clusters/eks/index'
= _('or') - else
= link_to('create a new Google account', 'https://accounts.google.com/SignUpWithoutGmail?service=cloudconsole&continue=https%3A%2F%2Fconsole.cloud.google.com%2Ffreetrial%3Futm_campaign%3D2018_cpanel%26utm_source%3Dgitlab%26utm_medium%3Dreferral', target: '_blank', rel: 'noopener noreferrer') = render 'clusters/clusters/cloud_providers/cloud_provider_selector'
- else - else
- link = link_to(s_('ClusterIntegration|properly configured'), help_page_path("integration/google"), target: '_blank', rel: 'noopener noreferrer') .tab-pane{ id: 'create-cluster-pane', class: active_when(active_tab == 'create'), role: 'tabpanel' }
= s_('Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service.').html_safe % { link_to_documentation: link } = render 'clusters/clusters/gcp/header'
- if @valid_gcp_token
= render 'clusters/clusters/gcp/form'
- elsif @authorize_url
.signin-with-google
- create_account_link = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: 'https://accounts.google.com/SignUpWithoutGmail?service=cloudconsole&continue=https%3A%2F%2Fconsole.cloud.google.com%2Ffreetrial%3Futm_campaign%3D2018_cpanel%26utm_source%3Dgitlab%26utm_medium%3Dreferral' }
= link_to(image_tag('auth_buttons/signin_with_google.png', width: '191px', alt: _('Sign in with Google')), @authorize_url)
= s_('or %{link_start}create a new Google account%{link_end}').html_safe % { link_start: create_account_link, link_end: link_end }
- else
- documentation_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: help_page_path("integration/google") }
= s_('Google authentication is not %{link_start}property configured%{link_end}. Ask your GitLab administrator if you want to use this service.').html_safe % { link_start: documentation_link_start, link_end: link_end }
.tab-pane{ id: 'add-user-cluster-pane', class: active_when(active_tab == 'user'), role: 'tabpanel' } .tab-pane{ id: 'add-cluster-pane', class: active_when(active_tab == 'add'), role: 'tabpanel' }
= render 'clusters/clusters/user/header' = render 'clusters/clusters/user/header'
= render 'clusters/clusters/user/form' = render 'clusters/clusters/user/form'
This diff is collapsed.
...@@ -48,14 +48,14 @@ ...@@ -48,14 +48,14 @@
- if group_sidebar_link?(:issues) - if group_sidebar_link?(:issues)
= nav_link(path: group_issues_sub_menu_items) do = nav_link(path: group_issues_sub_menu_items) do
= link_to issues_group_path(@group) do = link_to issues_group_path(@group), data: { qa_selector: 'group_issues_item' } do
.nav-icon-container .nav-icon-container
= sprite_icon('issues') = sprite_icon('issues')
%span.nav-item-name %span.nav-item-name
= _('Issues') = _('Issues')
%span.badge.badge-pill.count= number_with_delimiter(issues_count) %span.badge.badge-pill.count= number_with_delimiter(issues_count)
%ul.sidebar-sub-level-items %ul.sidebar-sub-level-items{ data: { qa_selector: 'group_issues_sidebar_submenu'} }
= nav_link(path: ['groups#issues', 'labels#index', 'milestones#index'], html_options: { class: "fly-out-top-item" } ) do = nav_link(path: ['groups#issues', 'labels#index', 'milestones#index'], html_options: { class: "fly-out-top-item" } ) do
= link_to issues_group_path(@group) do = link_to issues_group_path(@group) do
%strong.fly-out-top-item-name %strong.fly-out-top-item-name
...@@ -70,7 +70,7 @@ ...@@ -70,7 +70,7 @@
- if group_sidebar_link?(:boards) - if group_sidebar_link?(:boards)
= nav_link(path: ['boards#index', 'boards#show']) do = nav_link(path: ['boards#index', 'boards#show']) do
= link_to group_boards_path(@group), title: boards_link_text do = link_to group_boards_path(@group), title: boards_link_text, data: { qa_selector: 'group_issue_boards_link' } do
%span %span
= boards_link_text = boards_link_text
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
- else - else
- can_create_fork = current_user.can?(:create_fork) - can_create_fork = current_user.can?(:create_fork)
= link_to new_project_fork_path(@project), = link_to new_project_fork_path(@project),
class: "btn btn-default has-tooltip count-badge-button d-flex align-items-center fork-btn #{'has-tooltip disabled' unless can_create_fork}", class: "btn btn-default btn-xs has-tooltip count-badge-button d-flex align-items-center fork-btn #{'has-tooltip disabled' unless can_create_fork}",
title: (s_('ProjectOverview|You have reached your project limit') unless can_create_fork) do title: (s_('ProjectOverview|You have reached your project limit') unless can_create_fork) do
= sprite_icon('fork', { css_class: 'icon' }) = sprite_icon('fork', { css_class: 'icon' })
%span= s_('ProjectOverview|Fork') %span= s_('ProjectOverview|Fork')
......
...@@ -13,12 +13,13 @@ ...@@ -13,12 +13,13 @@
%pre.dark#merge-info-1 %pre.dark#merge-info-1
- if @merge_request.for_fork? - if @merge_request.for_fork?
:preserve :preserve
git fetch #{h default_url_to_repo(@merge_request.source_project)} #{h @merge_request.source_branch} -# All repo/branch refs have been quoted to allow support for special characters (such as #my-branch)
git checkout -b #{h @merge_request.source_project_path}-#{h @merge_request.source_branch} FETCH_HEAD git fetch "#{h default_url_to_repo(@merge_request.source_project)}" "#{h @merge_request.source_branch}"
git checkout -b "#{h @merge_request.source_project_path}-#{h @merge_request.source_branch}" FETCH_HEAD
- else - else
:preserve :preserve
git fetch origin git fetch origin
git checkout -b #{h @merge_request.source_branch} origin/#{h @merge_request.source_branch} git checkout -b "#{h @merge_request.source_branch}" "origin/#{h @merge_request.source_branch}"
%p %p
%strong Step 2. %strong Step 2.
Review the changes locally Review the changes locally
...@@ -31,20 +32,20 @@ ...@@ -31,20 +32,20 @@
- if @merge_request.for_fork? - if @merge_request.for_fork?
:preserve :preserve
git fetch origin git fetch origin
git checkout origin/#{h @merge_request.target_branch} git checkout "origin/#{h @merge_request.target_branch}"
git merge --no-ff #{h @merge_request.source_project_path}-#{h @merge_request.source_branch} git merge --no-ff "#{h @merge_request.source_project_path}-#{h @merge_request.source_branch}"
- else - else
:preserve :preserve
git fetch origin git fetch origin
git checkout origin/#{h @merge_request.target_branch} git checkout "origin/#{h @merge_request.target_branch}"
git merge --no-ff #{h @merge_request.source_branch} git merge --no-ff "#{h @merge_request.source_branch}"
%p %p
%strong Step 4. %strong Step 4.
Push the result of the merge to GitLab Push the result of the merge to GitLab
= clipboard_button(target: "pre#merge-info-4", title: "Copy commands to clipboard") = clipboard_button(target: "pre#merge-info-4", title: "Copy commands to clipboard")
%pre.dark#merge-info-4 %pre.dark#merge-info-4
:preserve :preserve
git push origin #{h @merge_request.target_branch} git push origin "#{h @merge_request.target_branch}"
- unless @merge_request.can_be_merged_by?(current_user) - unless @merge_request.can_be_merged_by?(current_user)
%p %p
Note that pushing to GitLab requires write access to this repository. Note that pushing to GitLab requires write access to this repository.
......
= label_tag :sort_by, 'Sort by', class: 'col-form-label label-bold pr-2' = label_tag :sort_by, 'Sort by', class: 'col-form-label label-bold px-2'
.dropdown.inline.qa-user-sort-dropdown .dropdown.inline.qa-user-sort-dropdown
= dropdown_toggle(member_sort_options_hash[@sort], { toggle: 'dropdown' }) = dropdown_toggle(member_sort_options_hash[@sort], { toggle: 'dropdown' })
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable %ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable
......
...@@ -17,14 +17,14 @@ ...@@ -17,14 +17,14 @@
.js-notification-toggle-btns .js-notification-toggle-btns
%div{ class: ("btn-group" if notification_setting.custom?) } %div{ class: ("btn-group" if notification_setting.custom?) }
- if notification_setting.custom? - if notification_setting.custom?
%button.dropdown-new.btn.btn-default.has-tooltip.notifications-btn.text-left#notifications-button{ type: "button", title: button_title, class: "#{btn_class}", "aria-label" => aria_label, data: { container: "body", toggle: "modal", target: "#" + notifications_menu_identifier("modal", notification_setting), display: 'static' } } %button.dropdown-new.btn.btn-default.btn-xs.has-tooltip.notifications-btn.text-left#notifications-button{ type: "button", title: button_title, class: "#{btn_class}", "aria-label" => aria_label, data: { container: "body", toggle: "modal", target: "#" + notifications_menu_identifier("modal", notification_setting), display: 'static' } }
= icon("bell", class: "js-notification-loading") = icon("bell", class: "js-notification-loading")
= notification_title(notification_setting.level) = notification_title(notification_setting.level)
%button.btn.dropdown-toggle{ data: { toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting), flip: "false" } } %button.btn.dropdown-toggle{ data: { toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting), flip: "false" } }
= icon('caret-down') = icon('caret-down')
.sr-only Toggle dropdown .sr-only Toggle dropdown
- else - else
%button.dropdown-new.btn.btn-default.has-tooltip.notifications-btn#notifications-button{ type: "button", title: button_title, class: "#{btn_class}", "aria-label" => aria_label, data: { container: "body", toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting), flip: "false" } } %button.dropdown-new.btn.btn-default.btn-xs.has-tooltip.notifications-btn#notifications-button{ type: "button", title: button_title, class: "#{btn_class}", "aria-label" => aria_label, data: { container: "body", toggle: "dropdown", target: notifications_menu_identifier("dropdown", notification_setting), flip: "false" } }
.float-left .float-left
= icon("bell", class: "js-notification-loading") = icon("bell", class: "js-notification-loading")
= notification_title(notification_setting.level) = notification_title(notification_setting.level)
......
...@@ -160,6 +160,7 @@ ...@@ -160,6 +160,7 @@
- repository_import - repository_import
- repository_remove_remote - repository_remove_remote
- system_hook_push - system_hook_push
- update_external_pull_requests
- update_merge_requests - update_merge_requests
- update_project_statistics - update_project_statistics
- upload_checksum - upload_checksum
......
# frozen_string_literal: true
class UpdateExternalPullRequestsWorker
include ApplicationWorker
def perform(project_id, user_id, ref)
project = Project.find_by_id(project_id)
return unless project
user = User.find_by_id(user_id)
return unless user
branch = Gitlab::Git.branch_name(ref)
return unless branch
external_pull_requests = project.external_pull_requests
.by_source_repository(project.import_source)
.by_source_branch(branch)
external_pull_requests.find_each do |pull_request|
ExternalPullRequests::CreatePipelineService.new(project, user)
.execute(pull_request)
end
end
end
---
title: Only show /copy_metadata quick action when usable
merge_request: 31735
author: Lee Tickett
type: fixed
---
title: Bump Kubeclient to 4.4.0
merge_request: 32811
author:
type: other
---
title: 'Admin dashboard: Fetch and render statistics async'
merge_request: 32449
author:
type: other
---
title: Fix watch button styling and notifications buttons consistency
merge_request: 32827
author:
type: fixed
---
title: Update merge train documentation
merge_request: 32218
author:
type: changed
---
title: Preprocess wiki attachments with GitLab-Workhorse
merge_request: 32663
author:
type: performance
---
title: Improve application settings API
merge_request: 31149
author: Mathieu Parent
type: fixed
---
title: Allow ECDSA certificates for pages domains
merge_request: 32393
author:
type: added
---
title: Lower search counters
merge_request: 11777
author:
type: performance
---
title: Upgrade to Gitaly v1.62.0
merge_request: 32608
author:
type: changed
---
title: Fix sharing localStorage with all MRs
merge_request: 32699
author:
type: fixed
---
title: Allow Knative to be installed on group and instance level clusters
merge_request: 32128
author:
type: added
---
title: Passing job rules downstream and E2E specs for job:rules configuration
merge_request: 32609
author:
type: fixed
---
title: Reduce N+1 when doing project export
merge_request: 32423
author:
type: performance
---
title: Clean up keyboard shortcuts help modal, removing and adding as needed
merge_request: 31642
author:
type: other
---
title: Expose update project service endpoint JSON
merge_request: 32759
author:
type: added
---
title: Quote branch names in how to merge instructions
merge_request: 32639
author: Lee Tickett
type: fixed
---
title: Add padding to left of "Sort by" in members dropdown
merge_request: 32602
author:
type: other
---
title: Log errors for failed pipeline creation in PostReceive
merge_request: 32633
author:
type: other
---
title: Update rouge to v3.10.0
merge_request: 32745
author:
type: other
---
title: Prevent empty external authorization classification labels from overriding
the default label
merge_request: 32517
author: Will Chandler
type: fixed
...@@ -321,6 +321,9 @@ production: &base ...@@ -321,6 +321,9 @@ production: &base
# external_https: ["1.1.1.1:443", "[2001::1]:443"] # If defined, enables custom domain and certificate support in GitLab Pages # external_https: ["1.1.1.1:443", "[2001::1]:443"] # If defined, enables custom domain and certificate support in GitLab Pages
admin: admin:
address: unix:/home/git/gitlab/tmp/sockets/private/pages-admin.socket # TCP connections are supported too (e.g. tcp://host:port) address: unix:/home/git/gitlab/tmp/sockets/private/pages-admin.socket # TCP connections are supported too (e.g. tcp://host:port)
# File that contains the shared secret key for verifying access for gitlab-pages.
# Default is '.gitlab_pages_shared_secret' relative to Rails.root (i.e. root of the GitLab app).
# secret_file: /home/git/gitlab/.gitlab_pages_shared_secret
## Mattermost ## Mattermost
## For enabling Add to Mattermost button ## For enabling Add to Mattermost button
......
...@@ -292,6 +292,7 @@ Settings.pages['artifacts_server'] ||= Settings.pages['enabled'] if Settings.pa ...@@ -292,6 +292,7 @@ Settings.pages['artifacts_server'] ||= Settings.pages['enabled'] if Settings.pa
Settings.pages['admin'] ||= Settingslogic.new({}) Settings.pages['admin'] ||= Settingslogic.new({})
Settings.pages.admin['certificate'] ||= '' Settings.pages.admin['certificate'] ||= ''
Settings.pages['secret_file'] ||= Rails.root.join('.gitlab_pages_shared_secret')
# #
# Geo # Geo
......
...@@ -115,3 +115,4 @@ ...@@ -115,3 +115,4 @@
- [export_csv, 1] - [export_csv, 1]
- [incident_management, 2] - [incident_management, 2]
- [jira_connect, 1] - [jira_connect, 1]
- [update_external_pull_requests, 3]
...@@ -3,8 +3,23 @@ ...@@ -3,8 +3,23 @@
class RemoveEpicIssuesDefaultRelativePosition < ActiveRecord::Migration[5.2] class RemoveEpicIssuesDefaultRelativePosition < ActiveRecord::Migration[5.2]
DOWNTIME = false DOWNTIME = false
def change include Gitlab::Database::MigrationHelpers
change_column_null :epic_issues, :relative_position, true
change_column_default :epic_issues, :relative_position, from: 1073741823, to: nil disable_ddl_transaction!
def up
# The column won't exist if someone installed EE, downgraded to CE
# before it was added in EE, then tries to upgrade CE.
if column_exists?(:epic_issues, :relative_position)
change_column_null :epic_issues, :relative_position, true
change_column_default :epic_issues, :relative_position, from: 1073741823, to: nil
else
add_column_with_default(:epic_issues, :relative_position, :integer, default: nil, allow_null: true)
end
end
def down
change_column_default :epic_issues, :relative_position, from: nil, to: 1073741823
change_column_null :epic_issues, :relative_position, false
end end
end end
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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