summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js5
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_token_keys.js17
-rw-r--r--app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue10
-rw-r--r--app/assets/javascripts/gfm_auto_complete.js8
-rw-r--r--app/assets/javascripts/groups/components/group_item.vue2
-rw-r--r--app/assets/javascripts/issuable_suggestions/index.js4
-rw-r--r--app/assets/javascripts/lib/graphql.js14
-rw-r--r--app/assets/javascripts/lib/utils/common_utils.js15
-rw-r--r--app/assets/javascripts/lib/utils/poll.js20
-rw-r--r--app/assets/javascripts/pages/groups/issues/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/issues/index/index.js2
-rw-r--r--app/assets/javascripts/pipelines/mixins/pipelines.js3
-rw-r--r--app/assets/javascripts/projects/project_new.js20
-rw-r--r--app/assets/javascripts/releases/store/actions.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/project_avatar/default.vue10
-rw-r--r--app/assets/stylesheets/framework/avatar.scss24
-rw-r--r--app/assets/stylesheets/framework/variables.scss1
-rw-r--r--app/controllers/admin/users_controller.rb2
-rw-r--r--app/controllers/clusters/clusters_controller.rb2
-rw-r--r--app/controllers/concerns/issuable_collections.rb1
-rw-r--r--app/controllers/concerns/lfs_request.rb2
-rw-r--r--app/controllers/concerns/notes_actions.rb57
-rw-r--r--app/controllers/projects/pages_controller.rb3
-rw-r--r--app/controllers/snippets/notes_controller.rb4
-rw-r--r--app/finders/group_descendants_finder.rb2
-rw-r--r--app/finders/issues_finder.rb11
-rw-r--r--app/helpers/count_helper.rb2
-rw-r--r--app/models/ci/build.rb13
-rw-r--r--app/models/ci/build_trace_chunk.rb2
-rw-r--r--app/models/clusters/applications/runner.rb2
-rw-r--r--app/models/concerns/fast_destroy_all.rb2
-rw-r--r--app/models/concerns/iid_routes.rb2
-rw-r--r--app/models/issue.rb1
-rw-r--r--app/models/network/graph.rb9
-rw-r--r--app/models/notification_recipient.rb2
-rw-r--r--app/models/project.rb15
-rw-r--r--app/models/user.rb2
-rw-r--r--app/policies/project_policy.rb2
-rw-r--r--app/presenters/ci/build_runner_presenter.rb42
-rw-r--r--app/services/ci/create_pipeline_service.rb4
-rw-r--r--app/services/ci/pipeline_trigger_service.rb4
-rw-r--r--app/services/git_push_service.rb2
-rw-r--r--app/services/git_tag_push_service.rb2
-rw-r--r--app/services/groups/create_service.rb2
-rw-r--r--app/services/groups/update_service.rb2
-rw-r--r--app/services/projects/fork_service.rb12
-rw-r--r--app/services/system_note_service.rb2
-rw-r--r--app/views/admin/groups/_group.html.haml2
-rw-r--r--app/views/admin/groups/show.html.haml2
-rw-r--r--app/views/admin/projects/_projects.html.haml2
-rw-r--r--app/views/admin/runners/_runner.html.haml2
-rw-r--r--app/views/groups/_home_panel.html.haml2
-rw-r--r--app/views/groups/settings/_general.html.haml2
-rw-r--r--app/views/layouts/nav/sidebar/_group.html.haml2
-rw-r--r--app/views/layouts/nav/sidebar/_project.html.haml2
-rw-r--r--app/views/projects/_home_panel.html.haml2
-rw-r--r--app/views/projects/edit.html.haml2
-rw-r--r--app/views/projects/forks/_fork_button.html.haml4
-rw-r--r--app/views/projects/pages/_destroy.haml2
-rw-r--r--app/views/shared/groups/_group.html.haml2
-rw-r--r--app/views/shared/issuable/_search_bar.html.haml8
-rw-r--r--app/views/shared/projects/_project.html.haml5
-rw-r--r--app/views/users/_groups.html.haml2
-rw-r--r--changelogs/unreleased/40795-set-project-name-on-fork-api.yml5
-rw-r--r--changelogs/unreleased/52734-styling-of-user-project-and-group-avatars.yml5
-rw-r--r--changelogs/unreleased/52778-don-t-display-pipeline-status-if-pipelines-are-disabled.yml5
-rw-r--r--changelogs/unreleased/53325-admin-runners-page-fails-with-an-sql-statement-timeout.yml5
-rw-r--r--changelogs/unreleased/54924-refactor-notes-actions-params.yml5
-rw-r--r--changelogs/unreleased/57785-create-project-template-for-netlify.yml5
-rw-r--r--changelogs/unreleased/58098-auto-devops-postgres-version-variable.yml5
-rw-r--r--changelogs/unreleased/allow-maintainers-to-remove-pages.yml5
-rw-r--r--changelogs/unreleased/api-issuable-bulk-update.yml5
-rw-r--r--changelogs/unreleased/expose-merge-ref-to-runner.yml5
-rw-r--r--changelogs/unreleased/filter-confidential-issues.yml5
-rw-r--r--changelogs/unreleased/fix-badges-logs.yml5
-rw-r--r--changelogs/unreleased/sh-fix-cpp-templates-404.yml5
-rw-r--r--changelogs/unreleased/sh-fix-double-xhr-pipelines.yml5
-rw-r--r--changelogs/unreleased/sh-fix-issue-58103.yml5
-rw-r--r--changelogs/unreleased/update-gitlab-runner-helm-chart-to-0-2-0.yml5
-rw-r--r--doc/administration/index.md1
-rw-r--r--doc/administration/monitoring/prometheus/gitlab_metrics.md2
-rw-r--r--doc/api/issues.md38
-rw-r--r--doc/api/merge_requests.md31
-rw-r--r--doc/api/projects.md2
-rw-r--r--doc/api/runners.md2
-rw-r--r--doc/ci/examples/end_to_end_testing_webdriverio/index.md2
-rw-r--r--doc/development/architecture.md4
-rw-r--r--doc/development/code_review.md5
-rw-r--r--doc/development/contributing/index.md2
-rw-r--r--doc/development/documentation/site_architecture/index.md2
-rw-r--r--doc/development/fe_guide/graphql.md30
-rw-r--r--doc/development/import_export.md2
-rw-r--r--doc/development/ordering_table_columns.md2
-rw-r--r--doc/install/kubernetes/preparation/tiller.md16
-rw-r--r--doc/install/requirements.md2
-rw-r--r--doc/integration/README.md3
-rw-r--r--doc/security/img/ssh_keys_restricted_key_icon.pngbin0 -> 4887 bytes
-rw-r--r--doc/security/ssh_keys_restrictions.md8
-rw-r--r--doc/topics/autodevops/index.md1
-rw-r--r--doc/user/admin_area/index.md29
-rw-r--r--doc/user/permissions.md3
-rw-r--r--doc/user/project/clusters/index.md98
-rw-r--r--doc/user/project/clusters/runbooks/index.md2
-rw-r--r--doc/user/project/clusters/serverless/img/app-domain.pngbin209263 -> 0 bytes
-rw-r--r--doc/user/project/clusters/serverless/img/dns-entry.pngbin19583 -> 66116 bytes
-rw-r--r--doc/user/project/clusters/serverless/img/install-knative.pngbin13003 -> 86225 bytes
-rw-r--r--doc/user/project/clusters/serverless/img/serverless-page.pngbin62369 -> 191568 bytes
-rw-r--r--doc/user/project/clusters/serverless/index.md45
-rw-r--r--doc/user/project/pipelines/settings.md4
-rw-r--r--lib/api/api.rb1
-rw-r--r--lib/api/entities.rb10
-rw-r--r--lib/api/helpers/pagination.rb75
-rw-r--r--lib/api/helpers/runner.rb2
-rw-r--r--lib/api/issuable_bulk_update.rb51
-rw-r--r--lib/api/issues.rb1
-rw-r--r--lib/api/project_templates.rb5
-rw-r--r--lib/api/projects.rb2
-rw-r--r--lib/banzai/filter/relative_link_filter.rb5
-rw-r--r--lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml2
-rw-r--r--lib/gitlab/gitaly_client.rb2
-rw-r--r--lib/gitlab/project_template.rb7
-rw-r--r--lib/gitlab/shell.rb5
-rw-r--r--lib/gitlab/tracing.rb9
-rw-r--r--locale/gitlab.pot15
-rw-r--r--spec/controllers/admin/users_controller_spec.rb14
-rw-r--r--spec/controllers/concerns/issuable_collections_spec.rb4
-rw-r--r--spec/controllers/projects/notes_controller_spec.rb20
-rw-r--r--spec/controllers/projects/pages_controller_spec.rb12
-rw-r--r--spec/features/issues/filtered_search/dropdown_hint_spec.rb11
-rw-r--r--spec/features/issues/filtered_search/search_bar_spec.rb2
-rw-r--r--spec/features/issues/gfm_autocomplete_spec.rb2
-rw-r--r--spec/features/projects/blobs/edit_spec.rb2
-rw-r--r--spec/features/projects/pages_spec.rb103
-rw-r--r--spec/features/projects/wiki/markdown_preview_spec.rb2
-rw-r--r--spec/finders/issues_finder_spec.rb28
-rw-r--r--spec/javascripts/lib/utils/common_utils_spec.js7
-rw-r--r--spec/javascripts/lib/utils/poll_spec.js32
-rw-r--r--spec/lib/api/helpers/pagination_spec.rb155
-rw-r--r--spec/lib/gitlab/project_template_spec.rb7
-rw-r--r--spec/lib/gitlab/serializer/pagination_spec.rb10
-rw-r--r--spec/lib/gitlab/tracing_spec.rb15
-rw-r--r--spec/models/ci/build_spec.rb1
-rw-r--r--spec/models/clusters/applications/runner_spec.rb4
-rw-r--r--spec/models/issue_spec.rb9
-rw-r--r--spec/models/project_spec.rb25
-rw-r--r--spec/models/user_spec.rb14
-rw-r--r--spec/presenters/ci/build_runner_presenter_spec.rb68
-rw-r--r--spec/requests/api/issuable_bulk_update_spec.rb154
-rw-r--r--spec/requests/api/issues_spec.rb36
-rw-r--r--spec/requests/api/project_templates_spec.rb28
-rw-r--r--spec/requests/api/projects_spec.rb48
-rw-r--r--spec/requests/api/runner_spec.rb85
-rw-r--r--spec/serializers/environment_serializer_spec.rb9
-rw-r--r--spec/serializers/pipeline_serializer_spec.rb14
-rw-r--r--spec/support/helpers/test_env.rb3
-rw-r--r--spec/views/projects/commits/_commit.html.haml_spec.rb59
-rw-r--r--vendor/project_templates/nfgitbook.tar.gzbin0 -> 139877 bytes
-rw-r--r--vendor/project_templates/nfhexo.tar.gzbin0 -> 663568 bytes
-rw-r--r--vendor/project_templates/nfhugo.tar.gzbin0 -> 1292761 bytes
-rw-r--r--vendor/project_templates/nfjekyll.tar.gzbin0 -> 154419 bytes
-rw-r--r--vendor/project_templates/nfplainhtml.tar.gzbin0 -> 132509 bytes
161 files changed, 1541 insertions, 462 deletions
diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
index 57ec6603d80..4d05f46ed17 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
@@ -96,6 +96,11 @@ export default class FilteredSearchDropdownManager {
gl: DropdownNonUser,
element: this.container.querySelector('#js-dropdown-wip'),
},
+ confidential: {
+ reference: null,
+ gl: DropdownNonUser,
+ element: this.container.querySelector('#js-dropdown-confidential'),
+ },
status: {
reference: null,
gl: NullDropdown,
diff --git a/app/assets/javascripts/filtered_search/filtered_search_token_keys.js b/app/assets/javascripts/filtered_search/filtered_search_token_keys.js
index b70da240833..48534bdf815 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_token_keys.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_token_keys.js
@@ -72,6 +72,23 @@ export default class FilteredSearchTokenKeys {
);
}
+ addExtraTokensForIssues() {
+ const confidentialToken = {
+ key: 'confidential',
+ type: 'string',
+ param: '',
+ symbol: '',
+ icon: 'eye-slash',
+ tag: 'Yes or No',
+ lowercaseValueOnSubmit: true,
+ uppercaseTokenName: false,
+ capitalizeTokenValue: true,
+ };
+
+ this.tokenKeys.push(confidentialToken);
+ this.tokenKeysWithAlternative.push(confidentialToken);
+ }
+
addExtraTokensForMergeRequests() {
const wipToken = {
key: 'wip',
diff --git a/app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue b/app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue
index 2cbc7c7077b..42d14b65b3a 100644
--- a/app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue
+++ b/app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue
@@ -82,8 +82,14 @@ export default {
<li class="frequent-items-list-item-container">
<a :href="webUrl" class="clearfix">
<div class="frequent-items-item-avatar-container">
- <img v-if="hasAvatar" :src="avatarUrl" class="avatar s32" />
- <identicon v-else :entity-id="itemId" :entity-name="itemName" size-class="s32" />
+ <img v-if="hasAvatar" :src="avatarUrl" class="avatar rect-avatar s32" />
+ <identicon
+ v-else
+ :entity-id="itemId"
+ :entity-name="itemName"
+ size-class="s32"
+ class="rect-avatar"
+ />
</div>
<div class="frequent-items-item-metadata-container">
<div :title="itemName" class="frequent-items-item-title" v-html="highlightedItemName"></div>
diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js
index 570d3b712e0..c81e754df4c 100644
--- a/app/assets/javascripts/gfm_auto_complete.js
+++ b/app/assets/javascripts/gfm_auto_complete.js
@@ -195,11 +195,15 @@ class GfmAutoComplete {
title += ` (${m.count})`;
}
+ const GROUP_TYPE = 'Group';
+
const autoCompleteAvatar = m.avatar_url || m.username.charAt(0).toUpperCase();
+
+ const rectAvatarClass = m.type === GROUP_TYPE ? 'rect-avatar' : '';
const imgAvatar = `<img src="${m.avatar_url}" alt="${
m.username
- }" class="avatar avatar-inline center s26"/>`;
- const txtAvatar = `<div class="avatar center avatar-inline s26">${autoCompleteAvatar}</div>`;
+ }" class="avatar ${rectAvatarClass} avatar-inline center s26"/>`;
+ const txtAvatar = `<div class="avatar ${rectAvatarClass} center avatar-inline s26">${autoCompleteAvatar}</div>`;
return {
username: m.username,
diff --git a/app/assets/javascripts/groups/components/group_item.vue b/app/assets/javascripts/groups/components/group_item.vue
index 688bd37cc56..d5130cd331d 100644
--- a/app/assets/javascripts/groups/components/group_item.vue
+++ b/app/assets/javascripts/groups/components/group_item.vue
@@ -88,7 +88,7 @@ export default {
</div>
<div
:class="{ 'content-loading': group.isChildrenLoading }"
- class="avatar-container s24 d-none d-sm-flex"
+ class="avatar-container rect-avatar s24 d-none d-sm-flex"
>
<a :href="group.relativePath" class="no-expand">
<img v-if="hasAvatar" :src="group.avatarUrl" class="avatar s24" />
diff --git a/app/assets/javascripts/issuable_suggestions/index.js b/app/assets/javascripts/issuable_suggestions/index.js
index 2c80cf1797a..40916c9d27f 100644
--- a/app/assets/javascripts/issuable_suggestions/index.js
+++ b/app/assets/javascripts/issuable_suggestions/index.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import VueApollo from 'vue-apollo';
-import defaultClient from '~/lib/graphql';
+import createDefaultClient from '~/lib/graphql';
import App from './components/app.vue';
Vue.use(VueApollo);
@@ -10,7 +10,7 @@ export default function() {
const issueTitle = document.getElementById('issue_title');
const { projectPath } = el.dataset;
const apolloProvider = new VueApollo({
- defaultClient,
+ defaultClient: createDefaultClient(),
});
return new Vue({
diff --git a/app/assets/javascripts/lib/graphql.js b/app/assets/javascripts/lib/graphql.js
index 20a0f142d9e..64e4e899f44 100644
--- a/app/assets/javascripts/lib/graphql.js
+++ b/app/assets/javascripts/lib/graphql.js
@@ -1,9 +1,11 @@
import ApolloClient from 'apollo-boost';
import csrf from '~/lib/utils/csrf';
-export default new ApolloClient({
- uri: `${gon.relative_url_root}/api/graphql`,
- headers: {
- [csrf.headerKey]: csrf.token,
- },
-});
+export default (clientState = {}) =>
+ new ApolloClient({
+ uri: `${gon.relative_url_root}/api/graphql`,
+ headers: {
+ [csrf.headerKey]: csrf.token,
+ },
+ clientState,
+ });
diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js
index 29fe460017e..a73cdb73690 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js
+++ b/app/assets/javascripts/lib/utils/common_utils.js
@@ -456,21 +456,6 @@ export const historyPushState = newUrl => {
export const parseBoolean = value => (value && value.toString()) === 'true';
/**
- * Converts permission provided as strings to booleans.
- *
- * @param {String} string
- * @returns {Boolean}
- */
-export const convertPermissionToBoolean = permission => {
- if (process.env.NODE_ENV !== 'production') {
- // eslint-disable-next-line no-console
- console.warn('convertPermissionToBoolean is deprecated! Please use parseBoolean instead.');
- }
-
- return parseBoolean(permission);
-};
-
-/**
* @callback backOffCallback
* @param {Function} next
* @param {Function} stop
diff --git a/app/assets/javascripts/lib/utils/poll.js b/app/assets/javascripts/lib/utils/poll.js
index 198711cf427..a900ff34bf5 100644
--- a/app/assets/javascripts/lib/utils/poll.js
+++ b/app/assets/javascripts/lib/utils/poll.js
@@ -63,6 +63,10 @@ export default class Poll {
const headers = normalizeHeaders(response.headers);
const pollInterval = parseInt(headers[this.intervalHeader], 10);
if (pollInterval > 0 && successCodes.indexOf(response.status) !== -1 && this.canPoll) {
+ if (this.timeoutID) {
+ clearTimeout(this.timeoutID);
+ }
+
this.timeoutID = setTimeout(() => {
this.makeRequest();
}, pollInterval);
@@ -101,15 +105,25 @@ export default class Poll {
}
/**
- * Restarts polling after it has been stoped
+ * Enables polling after it has been stopped
*/
- restart(options) {
- // update data
+ enable(options) {
if (options && options.data) {
this.options.data = options.data;
}
this.canPoll = true;
+
+ if (options && options.response) {
+ this.checkConditions(options.response);
+ }
+ }
+
+ /**
+ * Restarts polling after it has been stopped and makes a request
+ */
+ restart(options) {
+ this.enable(options);
this.makeRequest();
}
}
diff --git a/app/assets/javascripts/pages/groups/issues/index.js b/app/assets/javascripts/pages/groups/issues/index.js
index 736c6a62610..21ec3f9f9ba 100644
--- a/app/assets/javascripts/pages/groups/issues/index.js
+++ b/app/assets/javascripts/pages/groups/issues/index.js
@@ -4,6 +4,8 @@ import { FILTERED_SEARCH } from '~/pages/constants';
import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
document.addEventListener('DOMContentLoaded', () => {
+ IssuableFilteredSearchTokenKeys.addExtraTokensForIssues();
+
initFilteredSearch({
page: FILTERED_SEARCH.ISSUES,
isGroupDecendent: true,
diff --git a/app/assets/javascripts/pages/projects/issues/index/index.js b/app/assets/javascripts/pages/projects/issues/index/index.js
index a56c0bb6be8..bb91e38cb64 100644
--- a/app/assets/javascripts/pages/projects/issues/index/index.js
+++ b/app/assets/javascripts/pages/projects/issues/index/index.js
@@ -9,6 +9,8 @@ import { FILTERED_SEARCH } from '~/pages/constants';
import { ISSUABLE_INDEX } from '~/pages/projects/constants';
document.addEventListener('DOMContentLoaded', () => {
+ IssuableFilteredSearchTokenKeys.addExtraTokensForIssues();
+
initFilteredSearch({
page: FILTERED_SEARCH.ISSUES,
filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys,
diff --git a/app/assets/javascripts/pipelines/mixins/pipelines.js b/app/assets/javascripts/pipelines/mixins/pipelines.js
index 32bfa47e5f2..74ca3071364 100644
--- a/app/assets/javascripts/pipelines/mixins/pipelines.js
+++ b/app/assets/javascripts/pipelines/mixins/pipelines.js
@@ -94,8 +94,7 @@ export default {
this.isLoading = false;
this.successCallback(response);
- // restart polling
- this.poll.restart({ data: this.requestData });
+ this.poll.enable({ data: this.requestData, response });
})
.catch(() => {
this.isLoading = false;
diff --git a/app/assets/javascripts/projects/project_new.js b/app/assets/javascripts/projects/project_new.js
index d65e73a3f9c..f021698a7ea 100644
--- a/app/assets/javascripts/projects/project_new.js
+++ b/app/assets/javascripts/projects/project_new.js
@@ -145,6 +145,26 @@ const bindEvents = () => {
text: 'Pages/Hexo',
icon: '.template-option .icon-hexo',
},
+ nfhugo: {
+ text: 'Netlify/Hugo',
+ icon: '.template-option .icon-netlify',
+ },
+ nfjekyll: {
+ text: 'Netlify/Jekyll',
+ icon: '.template-option .icon-netlify',
+ },
+ nfplainhtml: {
+ text: 'Netlify/Plain HTML',
+ icon: '.template-option .icon-netlify',
+ },
+ nfgitbook: {
+ text: 'Netlify/GitBook',
+ icon: '.template-option .icon-netlify',
+ },
+ nfhexo: {
+ text: 'Netlify/Hexo',
+ icon: '.template-option .icon-netlify',
+ },
};
const selectedTemplate = templates[value];
diff --git a/app/assets/javascripts/releases/store/actions.js b/app/assets/javascripts/releases/store/actions.js
index baa2251403e..b5c4d54ac33 100644
--- a/app/assets/javascripts/releases/store/actions.js
+++ b/app/assets/javascripts/releases/store/actions.js
@@ -11,7 +11,7 @@ export const requestReleases = ({ commit }) => commit(types.REQUEST_RELEASES);
/**
* Fetches the main endpoint.
* Will dispatch requestNamespace action before starting the request.
- * Will dispatch receiveNamespaceSuccess if the request is successfull
+ * Will dispatch receiveNamespaceSuccess if the request is successful
* Will dispatch receiveNamesapceError if the request returns an error
*
* @param {String} projectId
diff --git a/app/assets/javascripts/vue_shared/components/project_avatar/default.vue b/app/assets/javascripts/vue_shared/components/project_avatar/default.vue
index b399c232937..881b5059d2a 100644
--- a/app/assets/javascripts/vue_shared/components/project_avatar/default.vue
+++ b/app/assets/javascripts/vue_shared/components/project_avatar/default.vue
@@ -26,7 +26,7 @@ export default {
</script>
<template>
- <span :class="sizeClass" class="avatar-container project-avatar">
+ <span :class="sizeClass" class="avatar-container rect-avatar project-avatar">
<project-avatar-image
v-if="project.avatar_url"
:link-href="project.path"
@@ -34,6 +34,12 @@ export default {
:img-alt="project.name"
:img-size="size"
/>
- <identicon v-else :entity-id="project.id" :entity-name="project.name" :size-class="sizeClass" />
+ <identicon
+ v-else
+ :entity-id="project.id"
+ :entity-name="project.name"
+ :size-class="sizeClass"
+ class="rect-avatar"
+ />
</span>
</template>
diff --git a/app/assets/stylesheets/framework/avatar.scss b/app/assets/stylesheets/framework/avatar.scss
index e132aa4c216..bfd3d776bd4 100644
--- a/app/assets/stylesheets/framework/avatar.scss
+++ b/app/assets/stylesheets/framework/avatar.scss
@@ -124,6 +124,30 @@
&.s64 { min-width: 64px; min-height: 64px; }
}
+.rect-avatar {
+ border-radius: $border-radius-small;
+ &.s16 { border-radius: $border-radius-small; }
+ &.s18 { border-radius: $border-radius-small; }
+ &.s19 { border-radius: $border-radius-small; }
+ &.s20 { border-radius: $border-radius-small; }
+ &.s24 { border-radius: $border-radius-default; }
+ &.s26 { border-radius: $border-radius-default; }
+ &.s32 { border-radius: $border-radius-default; }
+ &.s36 { border-radius: $border-radius-default; }
+ &.s40 { border-radius: $border-radius-default; }
+ &.s46 { border-radius: $border-radius-default; }
+ &.s48 { border-radius: $border-radius-large; }
+ &.s60 { border-radius: $border-radius-large; }
+ &.s64 { border-radius: $border-radius-large; }
+ &.s70 { border-radius: $border-radius-large; }
+ &.s90 { border-radius: $border-radius-large; }
+ &.s96 { border-radius: $border-radius-large; }
+ &.s100 { border-radius: $border-radius-large; }
+ &.s110 { border-radius: $border-radius-large; }
+ &.s140 { border-radius: $border-radius-large; }
+ &.s160 { border-radius: $border-radius-large; }
+}
+
.avatar-counter {
background-color: $gray-darkest;
color: $white-light;
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 27c54cb0b75..25b272ab3a9 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -265,6 +265,7 @@ $container-text-max-width: 540px;
$gl-avatar-size: 40px;
$border-radius-default: 4px;
$border-radius-small: 2px;
+$border-radius-large: 8px;
$default-icon-size: 18px;
$layout-link-gray: #7e7c7c;
$btn-side-margin: 10px;
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index 0eae007715a..bfa7c7d0109 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -7,7 +7,7 @@ class Admin::UsersController < Admin::ApplicationController
before_action :check_impersonation_availability, only: :impersonate
def index
- @users = User.order_name_asc.filter(params[:filter])
+ @users = User.filter_items(params[:filter]).order_name_asc
@users = @users.search_with_secondary_emails(params[:search_query]) if params[:search_query].present?
@users = @users.sort_by_attribute(@sort = params[:sort])
@users = @users.page(params[:page])
diff --git a/app/controllers/clusters/clusters_controller.rb b/app/controllers/clusters/clusters_controller.rb
index 3bd91b71d92..68a2a83f0de 100644
--- a/app/controllers/clusters/clusters_controller.rb
+++ b/app/controllers/clusters/clusters_controller.rb
@@ -24,7 +24,7 @@ class Clusters::ClustersController < Clusters::BaseController
# Note: We are paginating through an array here but this should OK as:
#
# In CE, we can have a maximum group nesting depth of 21, so including
- # project cluster, we can have max 22 clusters for a group hierachy.
+ # project cluster, we can have max 22 clusters for a group hierarchy.
# In EE (Premium) we can have any number, as multiple clusters are
# supported, but the number of clusters are fairly low currently.
#
diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb
index 07d0bf16d93..c529aabf797 100644
--- a/app/controllers/concerns/issuable_collections.rb
+++ b/app/controllers/concerns/issuable_collections.rb
@@ -91,6 +91,7 @@ module IssuableCollections
options = {
scope: params[:scope],
state: params[:state],
+ confidential: Gitlab::Utils.to_boolean(params[:confidential]),
sort: set_sort_order
}
diff --git a/app/controllers/concerns/lfs_request.rb b/app/controllers/concerns/lfs_request.rb
index 5572c3cee2d..57e444319e0 100644
--- a/app/controllers/concerns/lfs_request.rb
+++ b/app/controllers/concerns/lfs_request.rb
@@ -123,7 +123,7 @@ module LfsRequest
(authentication_abilities || []).include?(capability)
end
- # Overriden in EE
+ # Overridden in EE
def limit_exceeded?
false
end
diff --git a/app/controllers/concerns/notes_actions.rb b/app/controllers/concerns/notes_actions.rb
index 0319948a12f..b4fee93713b 100644
--- a/app/controllers/concerns/notes_actions.rb
+++ b/app/controllers/concerns/notes_actions.rb
@@ -6,7 +6,6 @@ module NotesActions
extend ActiveSupport::Concern
included do
- prepend_before_action :normalize_create_params, only: [:create]
before_action :set_polling_interval_header, only: [:index]
before_action :require_noteable!, only: [:index, :create]
before_action :authorize_admin_note!, only: [:update, :destroy]
@@ -44,12 +43,7 @@ module NotesActions
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def create
- create_params = note_params.merge(
- merge_request_diff_head_sha: params[:merge_request_diff_head_sha],
- in_reply_to_discussion_id: params[:in_reply_to_discussion_id]
- )
-
- @note = Notes::CreateService.new(note_project, current_user, create_params).execute
+ @note = Notes::CreateService.new(note_project, current_user, create_note_params).execute
respond_to do |format|
format.json do
@@ -78,7 +72,7 @@ module NotesActions
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def update
- @note = Notes::UpdateService.new(project, current_user, note_params).execute(note)
+ @note = Notes::UpdateService.new(project, current_user, update_note_params).execute(note)
prepare_notes_for_rendering([@note])
respond_to do |format|
@@ -196,24 +190,36 @@ module NotesActions
return access_denied! unless can?(current_user, :admin_note, note)
end
- def note_params
+ def create_note_params
params.require(:note).permit(
- :project_id,
- :noteable_type,
- :noteable_id,
- :commit_id,
- :noteable,
:type,
-
:note,
- :attachment,
+ :line_code, # LegacyDiffNote
+ :position # DiffNote
+ ).tap do |create_params|
+ create_params.merge!(
+ params.permit(:merge_request_diff_head_sha, :in_reply_to_discussion_id)
+ )
- # LegacyDiffNote
- :line_code,
+ # These params are also sent by the client but we need to set these based on
+ # target_type and target_id because we're checking permissions based on that
+ create_params[:noteable_type] = params[:target_type].classify
+
+ case params[:target_type]
+ when 'commit'
+ create_params[:commit_id] = params[:target_id]
+ when 'merge_request'
+ create_params[:noteable_id] = params[:target_id]
+ # Notes on MergeRequest can have an extra `commit_id` context
+ create_params[:commit_id] = params.dig(:note, :commit_id)
+ else
+ create_params[:noteable_id] = params[:target_id]
+ end
+ end
+ end
- # DiffNote
- :position
- )
+ def update_note_params
+ params.require(:note).permit(:note)
end
def set_polling_interval_header
@@ -248,15 +254,6 @@ module NotesActions
DiscussionSerializer.new(project: project, noteable: noteable, current_user: current_user, note_entity: ProjectNoteEntity)
end
- # Avoids checking permissions in the wrong object - this ensures that the object we checked permissions for
- # is the object we're actually creating a note in.
- def normalize_create_params
- params[:note].try do |note|
- note[:noteable_id] = params[:target_id]
- note[:noteable_type] = params[:target_type].classify
- end
- end
-
def note_project
strong_memoize(:note_project) do
next nil unless project
diff --git a/app/controllers/projects/pages_controller.rb b/app/controllers/projects/pages_controller.rb
index d0e35bee986..73e629ab7c3 100644
--- a/app/controllers/projects/pages_controller.rb
+++ b/app/controllers/projects/pages_controller.rb
@@ -5,7 +5,8 @@ class Projects::PagesController < Projects::ApplicationController
before_action :require_pages_enabled!
before_action :authorize_read_pages!, only: [:show]
- before_action :authorize_update_pages!, except: [:show]
+ before_action :authorize_update_pages!, except: [:show, :destroy]
+ before_action :authorize_remove_pages!, only: [:destroy]
# rubocop: disable CodeReuse/ActiveRecord
def show
diff --git a/app/controllers/snippets/notes_controller.rb b/app/controllers/snippets/notes_controller.rb
index 091bcb1253d..eee14b0faf4 100644
--- a/app/controllers/snippets/notes_controller.rb
+++ b/app/controllers/snippets/notes_controller.rb
@@ -26,10 +26,6 @@ class Snippets::NotesController < ApplicationController
# rubocop: enable CodeReuse/ActiveRecord
alias_method :noteable, :snippet
- def note_params
- super.merge(noteable_id: params[:snippet_id])
- end
-
def finder_params
params.merge(last_fetched_at: last_fetched_at, target_id: snippet.id, target_type: 'personal_snippet')
end
diff --git a/app/finders/group_descendants_finder.rb b/app/finders/group_descendants_finder.rb
index 96a36db7ec8..ec340f38450 100644
--- a/app/finders/group_descendants_finder.rb
+++ b/app/finders/group_descendants_finder.rb
@@ -134,7 +134,7 @@ class GroupDescendantsFinder
def subgroups
return Group.none unless Group.supports_nested_objects?
- # When filtering subgroups, we want to find all matches withing the tree of
+ # When filtering subgroups, we want to find all matches within the tree of
# descendants to show to the user
groups = if params[:filter]
subgroups_matching_filter
diff --git a/app/finders/issues_finder.rb b/app/finders/issues_finder.rb
index a0504ca0879..cb44575d6f1 100644
--- a/app/finders/issues_finder.rb
+++ b/app/finders/issues_finder.rb
@@ -69,7 +69,16 @@ class IssuesFinder < IssuableFinder
end
def filter_items(items)
- by_due_date(super)
+ issues = super
+ issues = by_due_date(issues)
+ issues = by_confidential(issues)
+ issues
+ end
+
+ def by_confidential(items)
+ return items if params[:confidential].nil?
+
+ params[:confidential] ? items.confidential_only : items.public_only
end
def by_due_date(items)
diff --git a/app/helpers/count_helper.rb b/app/helpers/count_helper.rb
index 13839474e1f..62bb2e4da23 100644
--- a/app/helpers/count_helper.rb
+++ b/app/helpers/count_helper.rb
@@ -13,7 +13,7 @@ module CountHelper
# memberships, and deducting 1 for each root of the fork network.
# This might be inacurate as the root of the fork network might have been deleted.
#
- # This makes querying this information a lot more effecient and it should be
+ # This makes querying this information a lot more efficient and it should be
# accurate enough for the instance wide statistics
def approximate_fork_count_with_delimiters(count_data)
fork_network_count = count_data[ForkNetwork]
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 6b2b7e77180..c902e49ee6d 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -46,6 +46,7 @@ module Ci
delegate :terminal_specification, to: :runner_session, allow_nil: true
delegate :gitlab_deploy_token, to: :project
delegate :trigger_short_token, to: :trigger_request, allow_nil: true
+ delegate :merge_request?, to: :pipeline
##
# Since Gitlab 11.5, deployments records started being created right after
@@ -441,11 +442,13 @@ module Ci
# All variables, including persisted environment variables.
#
def variables
- Gitlab::Ci::Variables::Collection.new
- .concat(persisted_variables)
- .concat(scoped_variables)
- .concat(persisted_environment_variables)
- .to_runner_variables
+ strong_memoize(:variables) do
+ Gitlab::Ci::Variables::Collection.new
+ .concat(persisted_variables)
+ .concat(scoped_variables)
+ .concat(persisted_environment_variables)
+ .to_runner_variables
+ end
end
##
diff --git a/app/models/ci/build_trace_chunk.rb b/app/models/ci/build_trace_chunk.rb
index da08214963f..33e61cd2111 100644
--- a/app/models/ci/build_trace_chunk.rb
+++ b/app/models/ci/build_trace_chunk.rb
@@ -18,7 +18,7 @@ module Ci
FailedToPersistDataError = Class.new(StandardError)
# Note: The ordering of this enum is related to the precedence of persist store.
- # The bottom item takes the higest precedence, and the top item takes the lowest precedence.
+ # The bottom item takes the highest precedence, and the top item takes the lowest precedence.
enum data_store: {
redis: 1,
database: 2,
diff --git a/app/models/clusters/applications/runner.rb b/app/models/clusters/applications/runner.rb
index f17da0bb7b1..941551dadaa 100644
--- a/app/models/clusters/applications/runner.rb
+++ b/app/models/clusters/applications/runner.rb
@@ -3,7 +3,7 @@
module Clusters
module Applications
class Runner < ActiveRecord::Base
- VERSION = '0.1.45'.freeze
+ VERSION = '0.2.0'.freeze
self.table_name = 'clusters_applications_runners'
diff --git a/app/models/concerns/fast_destroy_all.rb b/app/models/concerns/fast_destroy_all.rb
index 1e3afd641ed..f862031bce0 100644
--- a/app/models/concerns/fast_destroy_all.rb
+++ b/app/models/concerns/fast_destroy_all.rb
@@ -11,7 +11,7 @@
# it is difficult to accomplish it.
#
# This module defines a format to use `delete_all` and delete associated external data.
-# Here is an exmaple
+# Here is an example
#
# Situation
# - `Project` has many `Ci::BuildTraceChunk` through `Ci::Build`
diff --git a/app/models/concerns/iid_routes.rb b/app/models/concerns/iid_routes.rb
index b7f99e845ca..3eeb29b6595 100644
--- a/app/models/concerns/iid_routes.rb
+++ b/app/models/concerns/iid_routes.rb
@@ -4,7 +4,7 @@ module IidRoutes
##
# This automagically enforces all related routes to use `iid` instead of `id`
# If you want to use `iid` for some routes and `id` for other routes, this module should not to be included,
- # instead you should define `iid` or `id` explictly at each route generators. e.g. pipeline_path(project.id, pipeline.iid)
+ # instead you should define `iid` or `id` explicitly at each route generators. e.g. pipeline_path(project.id, pipeline.iid)
def to_param
iid.to_s
end
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 182c5d3d4b0..0b46e949052 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -66,6 +66,7 @@ class Issue < ActiveRecord::Base
scope :preload_associations, -> { preload(:labels, project: :namespace) }
scope :public_only, -> { where(confidential: false) }
+ scope :confidential_only, -> { where(confidential: true) }
after_save :expire_etag_cache
after_save :ensure_metrics, unless: :imported?
diff --git a/app/models/network/graph.rb b/app/models/network/graph.rb
index 6da3bb7bfb7..ecbeb24ee0a 100644
--- a/app/models/network/graph.rb
+++ b/app/models/network/graph.rb
@@ -40,9 +40,12 @@ module Network
# Get commits from repository
#
def collect_commits
- find_commits(count_to_display_commit_in_center).map do |commit|
- # Decorate with app/model/network/commit.rb
- Network::Commit.new(commit)
+ # https://gitlab.com/gitlab-org/gitlab-ce/issues/58013
+ Gitlab::GitalyClient.allow_n_plus_1_calls do
+ find_commits(count_to_display_commit_in_center).map do |commit|
+ # Decorate with app/model/network/commit.rb
+ Network::Commit.new(commit)
+ end
end
end
diff --git a/app/models/notification_recipient.rb b/app/models/notification_recipient.rb
index 9f16eefe074..481c1d963c6 100644
--- a/app/models/notification_recipient.rb
+++ b/app/models/notification_recipient.rb
@@ -153,7 +153,7 @@ class NotificationRecipient
user.global_notification_setting
end
- # Returns the notificaton_setting of the lowest group in hierarchy with non global level
+ # Returns the notification_setting of the lowest group in hierarchy with non global level
def closest_non_global_group_notification_settting
return unless @group
return if indexed_group_notification_settings.empty?
diff --git a/app/models/project.rb b/app/models/project.rb
index c72d3a3b725..83f8d004a46 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -248,10 +248,10 @@ class Project < ActiveRecord::Base
has_many :container_repositories, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :commit_statuses
- # The relation :all_pipelines is intented to be used when we want to get the
+ # The relation :all_pipelines is intended to be used when we want to get the
# whole list of pipelines associated to the project
has_many :all_pipelines, class_name: 'Ci::Pipeline', inverse_of: :project
- # The relation :ci_pipelines is intented to be used when we want to get only
+ # The relation :ci_pipelines is intended to be used when we want to get only
# those pipeline which are directly related to CI. There are
# other pipelines, like webide ones, that we won't retrieve
# if we use this relation.
@@ -305,6 +305,7 @@ class Project < ActiveRecord::Base
delegate :group_runners_enabled, :group_runners_enabled=, :group_runners_enabled?, to: :ci_cd_settings
delegate :group_clusters_enabled?, to: :group, allow_nil: true
delegate :root_ancestor, to: :namespace, allow_nil: true
+ delegate :last_pipeline, to: :commit, allow_nil: true
# Validations
validates :creator, presence: true, on: :create
@@ -595,6 +596,14 @@ class Project < ActiveRecord::Base
end
end
+ def ci_pipelines
+ if builds_enabled?
+ super
+ else
+ super.external
+ end
+ end
+
# returns all ancestor-groups upto but excluding the given namespace
# when no namespace is given, all ancestors upto the top are returned
def ancestors_upto(top = nil, hierarchy_order: nil)
@@ -1206,7 +1215,7 @@ class Project < ActiveRecord::Base
"#{web_url}.git"
end
- # Is overriden in EE
+ # Is overridden in EE
def lfs_http_url_to_repo(_)
http_url_to_repo
end
diff --git a/app/models/user.rb b/app/models/user.rb
index fd32d838e53..ee51c35d576 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -388,7 +388,7 @@ class User < ApplicationRecord
find_by(id: user_id)
end
- def filter(filter_name)
+ def filter_items(filter_name)
case filter_name
when 'admins'
admins
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index cadbc5ae009..95dd8b2795e 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -152,7 +152,6 @@ class ProjectPolicy < BasePolicy
enable :remove_fork_project
enable :destroy_merge_request
enable :destroy_issue
- enable :remove_pages
enable :set_issue_iid
enable :set_issue_created_at
@@ -271,6 +270,7 @@ class ProjectPolicy < BasePolicy
enable :admin_pages
enable :read_pages
enable :update_pages
+ enable :remove_pages
enable :read_cluster
enable :add_cluster
enable :create_cluster
diff --git a/app/presenters/ci/build_runner_presenter.rb b/app/presenters/ci/build_runner_presenter.rb
index 300f85e1e9d..d60281c8a0b 100644
--- a/app/presenters/ci/build_runner_presenter.rb
+++ b/app/presenters/ci/build_runner_presenter.rb
@@ -2,6 +2,11 @@
module Ci
class BuildRunnerPresenter < SimpleDelegator
+ include Gitlab::Utils::StrongMemoize
+
+ RUNNER_REMOTE_TAG_PREFIX = 'refs/tags/'.freeze
+ RUNNER_REMOTE_BRANCH_PREFIX = 'refs/remotes/origin/'.freeze
+
def artifacts
return unless options[:artifacts]
@@ -11,6 +16,35 @@ module Ci
list.flatten.compact
end
+ def ref_type
+ if tag
+ 'tag'
+ else
+ 'branch'
+ end
+ end
+
+ def git_depth
+ strong_memoize(:git_depth) do
+ git_depth = variables&.find { |variable| variable[:key] == 'GIT_DEPTH' }&.dig(:value)
+ git_depth.to_i
+ end
+ end
+
+ def refspecs
+ specs = []
+
+ if git_depth > 0
+ specs << refspec_for_branch(ref) if branch? || merge_request?
+ specs << refspec_for_tag(ref) if tag?
+ else
+ specs << refspec_for_branch
+ specs << refspec_for_tag
+ end
+
+ specs
+ end
+
private
def create_archive(artifacts)
@@ -41,5 +75,13 @@ module Ci
}
end
end
+
+ def refspec_for_branch(ref = '*')
+ "+#{Gitlab::Git::BRANCH_REF_PREFIX}#{ref}:#{RUNNER_REMOTE_BRANCH_PREFIX}#{ref}"
+ end
+
+ def refspec_for_tag(ref = '*')
+ "+#{Gitlab::Git::TAG_REF_PREFIX}#{ref}:#{RUNNER_REMOTE_TAG_PREFIX}#{ref}"
+ end
end
end
diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb
index c4f69175de3..35a0efcd0a1 100644
--- a/app/services/ci/create_pipeline_service.rb
+++ b/app/services/ci/create_pipeline_service.rb
@@ -112,10 +112,10 @@ module Ci
def extra_options(options = {})
# In Ruby 2.4, even when options is empty, f(**options) doesn't work when f
# doesn't have any parameters. We reproduce the Ruby 2.5 behavior by
- # checking explicitely that no arguments are given.
+ # checking explicitly that no arguments are given.
raise ArgumentError if options.any?
- {} # overriden in EE
+ {} # overridden in EE
end
end
end
diff --git a/app/services/ci/pipeline_trigger_service.rb b/app/services/ci/pipeline_trigger_service.rb
index 4ba3f5fb8ba..2dbb7c3917d 100644
--- a/app/services/ci/pipeline_trigger_service.rb
+++ b/app/services/ci/pipeline_trigger_service.rb
@@ -38,11 +38,11 @@ module Ci
end
def create_pipeline_from_job(job)
- # overriden in EE
+ # overridden in EE
end
def job_from_token
- # overriden in EE
+ # overridden in EE
end
def variables
diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb
index 092fd64574d..f387c749a21 100644
--- a/app/services/git_push_service.rb
+++ b/app/services/git_push_service.rb
@@ -235,6 +235,6 @@ class GitPushService < BaseService
private
def pipeline_options
- {} # to be overriden in EE
+ {} # to be overridden in EE
end
end
diff --git a/app/services/git_tag_push_service.rb b/app/services/git_tag_push_service.rb
index 6fef5b3ed1d..e39b3603c6c 100644
--- a/app/services/git_tag_push_service.rb
+++ b/app/services/git_tag_push_service.rb
@@ -61,6 +61,6 @@ class GitTagPushService < BaseService
end
def pipeline_options
- {} # to be overriden in EE
+ {} # to be overridden in EE
end
end
diff --git a/app/services/groups/create_service.rb b/app/services/groups/create_service.rb
index 55a3b9fa7b1..99ead467f74 100644
--- a/app/services/groups/create_service.rb
+++ b/app/services/groups/create_service.rb
@@ -33,7 +33,7 @@ module Groups
private
def after_build_hook(group, params)
- # overriden in EE
+ # overridden in EE
end
def create_chat_team?
diff --git a/app/services/groups/update_service.rb b/app/services/groups/update_service.rb
index 9ff1da270e2..787445180f0 100644
--- a/app/services/groups/update_service.rb
+++ b/app/services/groups/update_service.rb
@@ -31,7 +31,7 @@ module Groups
private
def before_assignment_hook(group, params)
- # overriden in EE
+ # overridden in EE
end
def after_update
diff --git a/app/services/projects/fork_service.rb b/app/services/projects/fork_service.rb
index 91091c4393d..fc234bafc57 100644
--- a/app/services/projects/fork_service.rb
+++ b/app/services/projects/fork_service.rb
@@ -38,8 +38,8 @@ module Projects
new_params = {
visibility_level: allowed_visibility_level,
description: @project.description,
- name: @project.name,
- path: @project.path,
+ name: target_name,
+ path: target_path,
shared_runners_enabled: @project.shared_runners_enabled,
namespace_id: target_namespace.id,
fork_network: fork_network,
@@ -94,6 +94,14 @@ module Projects
Projects::ForksCountService.new(@project).refresh_cache
end
+ def target_path
+ @target_path ||= @params[:path] || @project.path
+ end
+
+ def target_name
+ @target_name ||= @params[:name] || @project.name
+ end
+
def target_namespace
@target_namespace ||= @params[:namespace] || current_user.namespace
end
diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb
index ec6c306227b..ea8ac7e4656 100644
--- a/app/services/system_note_service.rb
+++ b/app/services/system_note_service.rb
@@ -360,7 +360,7 @@ module SystemNoteService
# author - User performing the change
# branch_type - 'source' or 'target'
# old_branch - old branch name
- # new_branch - new branch nmae
+ # new_branch - new branch name
#
# Example Note text:
#
diff --git a/app/views/admin/groups/_group.html.haml b/app/views/admin/groups/_group.html.haml
index 0a688b90f3a..395c469255e 100644
--- a/app/views/admin/groups/_group.html.haml
+++ b/app/views/admin/groups/_group.html.haml
@@ -22,7 +22,7 @@
%span.visibility-icon.has-tooltip{ data: { container: 'body', placement: 'left' }, title: visibility_icon_description(group) }
= visibility_level_icon(group.visibility_level, fw: false)
- .avatar-container.s40
+ .avatar-container.rect-avatar.s40
= group_icon(group, class: "avatar s40 d-none d-sm-block")
.title
= link_to [:admin, group], class: 'group-name' do
diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml
index 93da87538bc..00d255846f9 100644
--- a/app/views/admin/groups/show.html.haml
+++ b/app/views/admin/groups/show.html.haml
@@ -15,7 +15,7 @@
= _('Group info:')
%ul.content-list
%li
- .avatar-container.s60
+ .avatar-container.rect-avatar.s60
= group_icon(@group, class: "avatar s60")
%li
%span.light= _('Name:')
diff --git a/app/views/admin/projects/_projects.html.haml b/app/views/admin/projects/_projects.html.haml
index 50296a2afe7..5bc695aa7b5 100644
--- a/app/views/admin/projects/_projects.html.haml
+++ b/app/views/admin/projects/_projects.html.haml
@@ -19,7 +19,7 @@
.title
= link_to(admin_namespace_project_path(project.namespace, project)) do
.dash-project-avatar
- .avatar-container.s40
+ .avatar-container.rect-avatar.s40
= project_icon(project, alt: '', class: 'avatar project-avatar s40', width: 40, height: 40)
%span.project-full-name
%span.namespace-name
diff --git a/app/views/admin/runners/_runner.html.haml b/app/views/admin/runners/_runner.html.haml
index ec57eb1ed08..4641986cb56 100644
--- a/app/views/admin/runners/_runner.html.haml
+++ b/app/views/admin/runners/_runner.html.haml
@@ -44,7 +44,7 @@
.table-section.section-5
.table-mobile-header{ role: 'rowheader' }= _('Jobs')
.table-mobile-content
- = runner.builds.count(:all)
+ = limited_counter_with_delimiter(runner.builds)
.table-section.section-10.section-wrap
.table-mobile-header{ role: 'rowheader' }= _('Tags')
diff --git a/app/views/groups/_home_panel.html.haml b/app/views/groups/_home_panel.html.haml
index 3a8d95f44d1..39c0c113793 100644
--- a/app/views/groups/_home_panel.html.haml
+++ b/app/views/groups/_home_panel.html.haml
@@ -3,7 +3,7 @@
.group-home-panel
.row.mb-3
.home-panel-title-row.col-md-12.col-lg-6.d-flex
- .avatar-container.home-panel-avatar.append-right-default.float-none
+ .avatar-container.rect-avatar.s64.home-panel-avatar.append-right-default.float-none
= group_icon(@group, class: 'avatar avatar-tile s64', width: 64, height: 64)
.d-flex.flex-column.flex-wrap.align-items-baseline
.d-inline-flex.align-items-baseline
diff --git a/app/views/groups/settings/_general.html.haml b/app/views/groups/settings/_general.html.haml
index 0424ece037d..9ed71d19d32 100644
--- a/app/views/groups/settings/_general.html.haml
+++ b/app/views/groups/settings/_general.html.haml
@@ -20,7 +20,7 @@
= render_if_exists 'shared/repository_size_limit_setting', form: f, type: :group
.form-group.prepend-top-default.append-bottom-20
- .avatar-container.s90
+ .avatar-container.rect-avatar.s90
= group_icon(@group, alt: '', class: 'avatar group-avatar s90')
= f.label :avatar, _('Group avatar'), class: 'label-bold d-block'
= render 'shared/choose_group_avatar_button', f: f
diff --git a/app/views/layouts/nav/sidebar/_group.html.haml b/app/views/layouts/nav/sidebar/_group.html.haml
index 3fbaaafe89e..21ea9f3b2f3 100644
--- a/app/views/layouts/nav/sidebar/_group.html.haml
+++ b/app/views/layouts/nav/sidebar/_group.html.haml
@@ -6,7 +6,7 @@
.nav-sidebar-inner-scroll
.context-header
= link_to group_path(@group), title: @group.name do
- .avatar-container.s40.group-avatar
+ .avatar-container.rect-avatar.s40.group-avatar
= group_icon(@group, class: "avatar s40 avatar-tile")
.sidebar-context-title
= @group.name
diff --git a/app/views/layouts/nav/sidebar/_project.html.haml b/app/views/layouts/nav/sidebar/_project.html.haml
index dd7833647b7..7b492efeb09 100644
--- a/app/views/layouts/nav/sidebar/_project.html.haml
+++ b/app/views/layouts/nav/sidebar/_project.html.haml
@@ -3,7 +3,7 @@
- can_edit = can?(current_user, :admin_project, @project)
.context-header
= link_to project_path(@project), title: @project.name do
- .avatar-container.s40.project-avatar
+ .avatar-container.rect-avatar.s40.project-avatar
= project_icon(@project, alt: @project.name, class: 'avatar s40 avatar-tile', width: 40, height: 40)
.sidebar-context-title
= @project.name
diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml
index 0be41b5888c..bba303c906c 100644
--- a/app/views/projects/_home_panel.html.haml
+++ b/app/views/projects/_home_panel.html.haml
@@ -3,7 +3,7 @@
.project-home-panel{ class: ("empty-project" if empty_repo) }
.row.append-bottom-8
.home-panel-title-row.col-md-12.col-lg-6.d-flex
- .avatar-container.home-panel-avatar.append-right-default.float-none
+ .avatar-container.rect-avatar.s64.home-panel-avatar.append-right-default.float-none
= project_icon(@project, alt: @project.name, class: 'avatar avatar-tile s64', width: 64, height: 64)
.d-flex.flex-column.flex-wrap.align-items-baseline
.d-inline-flex.align-items-baseline
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index a58f736b5b4..1a489bfa275 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -46,7 +46,7 @@
%h5.prepend-top-0= _("Project avatar")
.form-group
- if @project.avatar?
- .avatar-container.s160.append-bottom-15
+ .avatar-container.rect-avatar.s160.append-bottom-15
= project_icon(@project, alt: '', class: 'avatar project-avatar s160', width: 160, height: 160)
- if @project.avatar_in_git
%p.light
diff --git a/app/views/projects/forks/_fork_button.html.haml b/app/views/projects/forks/_fork_button.html.haml
index a69146513d8..3f0798a898d 100644
--- a/app/views/projects/forks/_fork_button.html.haml
+++ b/app/views/projects/forks/_fork_button.html.haml
@@ -5,7 +5,7 @@
.bordered-box.fork-thumbnail.text-center.prepend-left-default.append-right-default.prepend-top-default.append-bottom-default.forked
= link_to project_path(forked_project) do
- if /no_((\w*)_)*avatar/.match(avatar)
- = group_icon(namespace, class: "avatar s100 identicon")
+ = group_icon(namespace, class: "avatar rect-avatar s100 identicon")
- else
.avatar-container.s100
= image_tag(avatar, class: "avatar s100")
@@ -18,7 +18,7 @@
class: ("disabled has-tooltip" unless can_create_project),
title: (_('You have reached your project limit') unless can_create_project) do
- if /no_((\w*)_)*avatar/.match(avatar)
- = group_icon(namespace, class: "avatar s100 identicon")
+ = group_icon(namespace, class: "avatar rect-avatar s100 identicon")
- else
.avatar-container.s100
= image_tag(avatar, class: "avatar s100")
diff --git a/app/views/projects/pages/_destroy.haml b/app/views/projects/pages/_destroy.haml
index ae8c801b705..138e2864bad 100644
--- a/app/views/projects/pages/_destroy.haml
+++ b/app/views/projects/pages/_destroy.haml
@@ -9,4 +9,4 @@
.form-actions
= link_to 'Remove pages', project_pages_path(@project), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove"
- else
- .nothing-here-block Only the project owner can remove pages
+ .nothing-here-block Only project maintainers can remove pages
diff --git a/app/views/shared/groups/_group.html.haml b/app/views/shared/groups/_group.html.haml
index a1b901aaffa..609b8dce21a 100644
--- a/app/views/shared/groups/_group.html.haml
+++ b/app/views/shared/groups/_group.html.haml
@@ -14,7 +14,7 @@
%span.visibility-icon.has-tooltip{ data: { container: 'body', placement: 'left' }, title: visibility_icon_description(group) }
= visibility_level_icon(group.visibility_level, fw: false)
- .avatar-container.s40
+ .avatar-container.rect-avatar.s40
= link_to group do
= group_icon(group, class: "avatar s40 d-none d-sm-block")
.title
diff --git a/app/views/shared/issuable/_search_bar.html.haml b/app/views/shared/issuable/_search_bar.html.haml
index 588659c7e9c..bdba47ed14d 100644
--- a/app/views/shared/issuable/_search_bar.html.haml
+++ b/app/views/shared/issuable/_search_bar.html.haml
@@ -128,6 +128,14 @@
%li.filter-dropdown-item{ data: { value: 'no', capitalize: true } }
%button.btn.btn-link{ type: 'button' }
= _('No')
+ #js-dropdown-confidential.filtered-search-input-dropdown-menu.dropdown-menu
+ %ul.filter-dropdown{ data: { dropdown: true } }
+ %li.filter-dropdown-item{ data: { value: 'yes', capitalize: true } }
+ %button.btn.btn-link{ type: 'button' }
+ = _('Yes')
+ %li.filter-dropdown-item{ data: { value: 'no', capitalize: true } }
+ %button.btn.btn-link{ type: 'button' }
+ = _('No')
= render_if_exists 'shared/issuable/filter_weight', type: type
diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml
index df17ae95e2a..f1a87faa7ac 100644
--- a/app/views/shared/projects/_project.html.haml
+++ b/app/views/shared/projects/_project.html.haml
@@ -13,14 +13,15 @@
- cache_key = project_list_cache_key(project)
- updated_tooltip = time_ago_with_tooltip(project.last_activity_date)
- css_controls_class = compact_mode ? "" : "flex-lg-row justify-content-lg-between"
+- avatar_container_class = project.creator && use_creator_avatar ? '' : 'rect-avatar'
%li.project-row.d-flex{ class: css_class }
= cache(cache_key) do
- if avatar
- .avatar-container.s48.flex-grow-0.flex-shrink-0
+ .avatar-container.s48.flex-grow-0.flex-shrink-0{ class: avatar_container_class }
= link_to project_path(project), class: dom_class(project) do
- if project.creator && use_creator_avatar
- = image_tag avatar_icon_for_user(project.creator, 48), class: "avatar s65", alt:''
+ = image_tag avatar_icon_for_user(project.creator, 48), class: "avatar s48", alt:''
- else
= project_icon(project, alt: '', class: 'avatar project-avatar s48', width: 48, height: 48)
.project-details.d-sm-flex.flex-sm-fill.align-items-center
diff --git a/app/views/users/_groups.html.haml b/app/views/users/_groups.html.haml
index 55799e10a46..6d7a52c7688 100644
--- a/app/views/users/_groups.html.haml
+++ b/app/views/users/_groups.html.haml
@@ -1,5 +1,5 @@
.clearfix
- groups.each do |group|
= link_to group, class: 'profile-groups-avatars inline', title: group.name do
- .avatar-container.s40
+ .avatar-container.rect-avatar.s40
= group_icon(group, class: 'avatar group-avatar s40')
diff --git a/changelogs/unreleased/40795-set-project-name-on-fork-api.yml b/changelogs/unreleased/40795-set-project-name-on-fork-api.yml
new file mode 100644
index 00000000000..742184bbe1e
--- /dev/null
+++ b/changelogs/unreleased/40795-set-project-name-on-fork-api.yml
@@ -0,0 +1,5 @@
+---
+title: Add ability to set path and name for project on fork using API
+merge_request: 25363
+author:
+type: added
diff --git a/changelogs/unreleased/52734-styling-of-user-project-and-group-avatars.yml b/changelogs/unreleased/52734-styling-of-user-project-and-group-avatars.yml
new file mode 100644
index 00000000000..9329e81eb83
--- /dev/null
+++ b/changelogs/unreleased/52734-styling-of-user-project-and-group-avatars.yml
@@ -0,0 +1,5 @@
+---
+title: Add rectangular project and group avatars
+merge_request: 25098
+author:
+type: other
diff --git a/changelogs/unreleased/52778-don-t-display-pipeline-status-if-pipelines-are-disabled.yml b/changelogs/unreleased/52778-don-t-display-pipeline-status-if-pipelines-are-disabled.yml
new file mode 100644
index 00000000000..7fa01e2835a
--- /dev/null
+++ b/changelogs/unreleased/52778-don-t-display-pipeline-status-if-pipelines-are-disabled.yml
@@ -0,0 +1,5 @@
+---
+title: Hide pipeline status when pipelines are disabled on project.
+merge_request: 25204
+author:
+type: fixed
diff --git a/changelogs/unreleased/53325-admin-runners-page-fails-with-an-sql-statement-timeout.yml b/changelogs/unreleased/53325-admin-runners-page-fails-with-an-sql-statement-timeout.yml
new file mode 100644
index 00000000000..e0ed38fc2fa
--- /dev/null
+++ b/changelogs/unreleased/53325-admin-runners-page-fails-with-an-sql-statement-timeout.yml
@@ -0,0 +1,5 @@
+---
+title: Use limited counter for runner build count in admin page.
+merge_request: 25220
+author:
+type: fixed
diff --git a/changelogs/unreleased/54924-refactor-notes-actions-params.yml b/changelogs/unreleased/54924-refactor-notes-actions-params.yml
new file mode 100644
index 00000000000..b6083820401
--- /dev/null
+++ b/changelogs/unreleased/54924-refactor-notes-actions-params.yml
@@ -0,0 +1,5 @@
+---
+title: Fix commenting on commits having SHA1 starting with a large number
+merge_request: 25278
+author:
+type: fixed
diff --git a/changelogs/unreleased/57785-create-project-template-for-netlify.yml b/changelogs/unreleased/57785-create-project-template-for-netlify.yml
new file mode 100644
index 00000000000..78e9e3dece5
--- /dev/null
+++ b/changelogs/unreleased/57785-create-project-template-for-netlify.yml
@@ -0,0 +1,5 @@
+---
+title: Resolve Create Project Template for Netlify
+merge_request: 25453
+author:
+type: changed
diff --git a/changelogs/unreleased/58098-auto-devops-postgres-version-variable.yml b/changelogs/unreleased/58098-auto-devops-postgres-version-variable.yml
new file mode 100644
index 00000000000..a7a87f60c28
--- /dev/null
+++ b/changelogs/unreleased/58098-auto-devops-postgres-version-variable.yml
@@ -0,0 +1,5 @@
+---
+title: Allow configuring POSTGRES_VERSION in Auto DevOps
+merge_request: 25500
+author:
+type: added
diff --git a/changelogs/unreleased/allow-maintainers-to-remove-pages.yml b/changelogs/unreleased/allow-maintainers-to-remove-pages.yml
new file mode 100644
index 00000000000..6e344dbe0e9
--- /dev/null
+++ b/changelogs/unreleased/allow-maintainers-to-remove-pages.yml
@@ -0,0 +1,5 @@
+---
+title: Allow maintainers to remove pages
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/api-issuable-bulk-update.yml b/changelogs/unreleased/api-issuable-bulk-update.yml
new file mode 100644
index 00000000000..3d4e96f9d1b
--- /dev/null
+++ b/changelogs/unreleased/api-issuable-bulk-update.yml
@@ -0,0 +1,5 @@
+---
+title: 'API: Bulk update for issues and MRs'
+merge_request: 25201
+author: Robert Schilling
+type: added
diff --git a/changelogs/unreleased/expose-merge-ref-to-runner.yml b/changelogs/unreleased/expose-merge-ref-to-runner.yml
new file mode 100644
index 00000000000..945f4f6e05a
--- /dev/null
+++ b/changelogs/unreleased/expose-merge-ref-to-runner.yml
@@ -0,0 +1,5 @@
+---
+title: Expose refspecs and depth to runner
+merge_request: 25233
+author:
+type: added
diff --git a/changelogs/unreleased/filter-confidential-issues.yml b/changelogs/unreleased/filter-confidential-issues.yml
new file mode 100644
index 00000000000..83f19a57aab
--- /dev/null
+++ b/changelogs/unreleased/filter-confidential-issues.yml
@@ -0,0 +1,5 @@
+---
+title: Ability to filter confidential issues
+merge_request: 24960
+author: Robert Schilling
+type: added
diff --git a/changelogs/unreleased/fix-badges-logs.yml b/changelogs/unreleased/fix-badges-logs.yml
new file mode 100644
index 00000000000..6236e7b046d
--- /dev/null
+++ b/changelogs/unreleased/fix-badges-logs.yml
@@ -0,0 +1,5 @@
+---
+title: Doc - fix the url of pipeline status badge
+merge_request: 25404
+author: Aviad Levy
+type: fixed
diff --git a/changelogs/unreleased/sh-fix-cpp-templates-404.yml b/changelogs/unreleased/sh-fix-cpp-templates-404.yml
new file mode 100644
index 00000000000..ac958d84099
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-cpp-templates-404.yml
@@ -0,0 +1,5 @@
+---
+title: Fix 404s when C++ .gitignore template selected
+merge_request: 25416
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-fix-double-xhr-pipelines.yml b/changelogs/unreleased/sh-fix-double-xhr-pipelines.yml
new file mode 100644
index 00000000000..e6c762f1d47
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-double-xhr-pipelines.yml
@@ -0,0 +1,5 @@
+---
+title: Remove duplicate XHR request when requesting new pipeline page
+merge_request: 25506
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-fix-issue-58103.yml b/changelogs/unreleased/sh-fix-issue-58103.yml
new file mode 100644
index 00000000000..1599af23fed
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-issue-58103.yml
@@ -0,0 +1,5 @@
+---
+title: Properly handle multiple X-Forwarded-For addresses in runner IP
+merge_request: 25511
+author:
+type: fixed
diff --git a/changelogs/unreleased/update-gitlab-runner-helm-chart-to-0-2-0.yml b/changelogs/unreleased/update-gitlab-runner-helm-chart-to-0-2-0.yml
new file mode 100644
index 00000000000..3bf55630c4d
--- /dev/null
+++ b/changelogs/unreleased/update-gitlab-runner-helm-chart-to-0-2-0.yml
@@ -0,0 +1,5 @@
+---
+title: Update GitLab Runner Helm Chart to 0.2.0
+merge_request: 25493
+author:
+type: other
diff --git a/doc/administration/index.md b/doc/administration/index.md
index a27c11c4f64..6e08d4633cd 100644
--- a/doc/administration/index.md
+++ b/doc/administration/index.md
@@ -53,6 +53,7 @@ Learn how to install, configure, update, and maintain your GitLab instance.
- [Diff limits](../user/admin_area/diff_limits.md): Configure the diff rendering size limits of branch comparison pages.
- [Merge request diffs storage](merge_request_diffs.md): Configure merge requests diffs external storage.
- [Broadcast Messages](../user/admin_area/broadcast_messages.md): Send messages to GitLab users through the UI.
+- [Admin Area](../user/admin_area/index.md): for self-managed instance-wide configuration and maintenance.
#### Customizing GitLab's appearance
diff --git a/doc/administration/monitoring/prometheus/gitlab_metrics.md b/doc/administration/monitoring/prometheus/gitlab_metrics.md
index 08cd23682d1..3bfcc9a289e 100644
--- a/doc/administration/monitoring/prometheus/gitlab_metrics.md
+++ b/doc/administration/monitoring/prometheus/gitlab_metrics.md
@@ -17,7 +17,7 @@ GitLab monitors its own internal service metrics, and makes them available at th
`/-/metrics` endpoint. Unlike other [Prometheus] exporters, in order to access
it, the client IP needs to be [included in a whitelist][whitelist].
-For Omnibus and Chart installations, these metrics are automatically enabled and collected as of [GitLab 9.4](https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests/1702). For source installations or earlier verisons, these metrics will need to be enabled manually and collected by a Prometheus server.
+For Omnibus and Chart installations, these metrics are automatically enabled and collected as of [GitLab 9.4](https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests/1702). For source installations or earlier versions, these metrics will need to be enabled manually and collected by a Prometheus server.
## Unicorn Metrics available
diff --git a/doc/api/issues.md b/doc/api/issues.md
index 0571f280d2a..d8b2ff07e30 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -32,6 +32,7 @@ GET /issues?author_id=5
GET /issues?assignee_id=5
GET /issues?my_reaction_emoji=star
GET /issues?search=foo&in=title
+GET /issues?confidential=true
```
| Attribute | Type | Required | Description |
@@ -52,6 +53,7 @@ GET /issues?search=foo&in=title
| `created_before` | datetime | no | Return issues created on or before the given time |
| `updated_after` | datetime | no | Return issues updated on or after the given time |
| `updated_before` | datetime | no | Return issues updated on or before the given time |
+| `confidential ` | Boolean | no | Filter confidential or public issues. |
```bash
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/issues
@@ -148,6 +150,7 @@ GET /groups/:id/issues?search=issue+title+or+description
GET /groups/:id/issues?author_id=5
GET /groups/:id/issues?assignee_id=5
GET /groups/:id/issues?my_reaction_emoji=star
+GET /groups/:id/issues?confidential=true
```
| Attribute | Type | Required | Description |
@@ -168,6 +171,7 @@ GET /groups/:id/issues?my_reaction_emoji=star
| `created_before` | datetime | no | Return issues created on or before the given time |
| `updated_after` | datetime | no | Return issues updated on or after the given time |
| `updated_before` | datetime | no | Return issues updated on or before the given time |
+| `confidential ` | Boolean | no | Filter confidential or public issues. |
```bash
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/4/issues
@@ -264,6 +268,7 @@ GET /projects/:id/issues?search=issue+title+or+description
GET /projects/:id/issues?author_id=5
GET /projects/:id/issues?assignee_id=5
GET /projects/:id/issues?my_reaction_emoji=star
+GET /projects/:id/issues?confidential=true
```
| Attribute | Type | Required | Description |
@@ -284,6 +289,8 @@ GET /projects/:id/issues?my_reaction_emoji=star
| `created_before` | datetime | no | Return issues created on or before the given time |
| `updated_after` | datetime | no | Return issues updated on or after the given time |
| `updated_before` | datetime | no | Return issues updated on or before the given time |
+| `confidential ` | Boolean | no | Filter confidential or public issues. |
+
```bash
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/4/issues
@@ -639,6 +646,37 @@ Example response:
**Note**: The `closed_by` attribute was [introduced in GitLab 10.6][ce-17042]. This value will only be present for issues which were closed after GitLab 10.6 and when the user account that closed the issue still exists.
+## Bulk update issues
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/21368) in GitLab 11.9.
+
+Update multiple issues using a single API call. Returns the number of successfully updated issues.
+
+```
+PUT /projects/:id/issues/bulk_update
+```
+
+| Attribute | Type | Required | Description **** |
+|----------------|---------|----------|------------------------------------------------------------------------------------------------------------|
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
+| `issuable_ids` | Array[integer] | yes | The IDs of issues to be updated. |
+| `assignee_ids` | Array[integer] | no | The ID of the user(s) to assign the issue to. Set to `0` or provide an empty value to unassign all assignees. |
+| `milestone_id` | integer | no | The global ID of a milestone to assign the issue to. Set to `0` or provide an empty value to unassign a milestone.|
+| `add_label_ids` | Array[integer] | no | Comma-separated label IDs to be added. |
+| `remove_label_ids` | Array[integer] | no | Comma-separated label IDs to be added. |
+| `state_event` | string | no | The state event of an issue. Set `close` to close the issue and `reopen` to reopen it. |
+| `subscription_event` | string | no | The subscription_event event of an issue. Set `subscribe` to subscribe to the issue and `unsubscribe` to unsubscribe from it. |
+
+```bash
+curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/4/issues/bulk_update?issuable_ids[]=1&issuable_ids[]=2&state_event=close
+```
+
+Example response:
+
+```json
+{ "message": "2 issues updated" }
+```
+
## Delete an issue
Only for admins and project owners. Soft deletes the issue in question.
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index e176cdffc5f..ba47e507b79 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -956,6 +956,37 @@ Must include at least one non-required attribute from above.
}
```
+## Bulk update merge requests
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/21368) in GitLab 11.9.
+
+Update multiple merge requests using a single API call. Returns the number of successfully updated merge requests.
+
+```
+PUT /projects/:id/merge_requests/bulk_update
+```
+
+| Attribute | Type | Required | Description **** |
+|----------------|---------|----------|------------------------------------------------------------------------------------------------------------|
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
+| `issuable_ids` | Array[integer] | yes | The IDs of merge requests to be updated. |
+| `assignee_ids` | Array[integer] | no | The ID of the user(s) to assign the issue to. Set to `0` or provide an empty value to unassign all assignees. |
+| `milestone_id` | integer | no | The global ID of a milestone to assign the issue to. Set to `0` or provide an empty value to unassign a milestone.|
+| `add_label_ids` | Array[integer] | no | Comma-separated label IDs to be added. |
+| `remove_label_ids` | Array[integer] | no | Comma-separated label IDs to be added. |
+| `state_event` | string | no | The state event of an issue. Set `close` to close the issue and `reopen` to reopen it. |
+| `subscription_event` | string | no | The subscription_event event of an issue. Set `subscribe` to subscribe to the issue and `unsubscribe` to unsubscribe from it. |
+
+```bash
+curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/4/merge_requests/bulk_update?issuable_ids[]=1&issuable_ids[]=2&state_event=close
+```
+
+Example response:
+
+```json
+{ "message": "2 merge_requests updated" }
+```
+
## Delete a merge request
Only for admins and project owners. Soft deletes the merge request in question.
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 3c0c956ddc2..0a950352ecf 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -771,6 +771,8 @@ POST /projects/:id/fork
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
| `namespace` | integer/string | yes | The ID or path of the namespace that the project will be forked to |
+| `path` | string | no | The path that will be assigned to the resultant project after forking |
+| `name` | string | no | The name that will be assigned to the resultant project after forking |
## List Forks of a project
diff --git a/doc/api/runners.md b/doc/api/runners.md
index 4aa0e4543e5..35c18649fec 100644
--- a/doc/api/runners.md
+++ b/doc/api/runners.md
@@ -478,7 +478,7 @@ Example response:
## Delete a registered Runner
-Deletes a registed Runner.
+Deletes a registered Runner.
```
DELETE /runners
diff --git a/doc/ci/examples/end_to_end_testing_webdriverio/index.md b/doc/ci/examples/end_to_end_testing_webdriverio/index.md
index 9f3b8d9ad14..d7afe11f41f 100644
--- a/doc/ci/examples/end_to_end_testing_webdriverio/index.md
+++ b/doc/ci/examples/end_to_end_testing_webdriverio/index.md
@@ -89,7 +89,7 @@ page and to interact with them - for example, to click on the link back to the h
The simple test shown above
can already give us a lot of confidence if it passes: we know our deployment has succeeded, that the
elements are visible on the page and that actual browsers can interact with it, and that routing
-works as expected. And all that in just 10 lines with gratituous whitespace! Add to that succeeding
+works as expected. And all that in just 10 lines with gratuitous whitespace! Add to that succeeding
unit tests and a successfully completed pipeline, and you can be fairly confident that the
dependency upgrade did not break anything without even having to look at your website.
diff --git a/doc/development/architecture.md b/doc/development/architecture.md
index 63574b28edc..e22552fd3a3 100644
--- a/doc/development/architecture.md
+++ b/doc/development/architecture.md
@@ -58,7 +58,7 @@ GitLab can be considered to have two layers from a process perspective:
- [Omnibus configuration options](https://gitlab.com/gitlab-org/gitaly/tree/master/doc/configuration)
- Layer: Core Service (Data)
-Gitaly is a service designed by GitLab to remove our need for NFS for Git storage in distributed deployments of GitLab. (Think GitLab.com or High Availablity Deployments) As of 11.3.0, This service handles all Git level access in GitLab. You can read more about the project [in the project's readme](https://gitlab.com/gitlab-org/gitaly).
+Gitaly is a service designed by GitLab to remove our need for NFS for Git storage in distributed deployments of GitLab (Think GitLab.com or High Availability Deployments). As of 11.3.0, this service handles all Git level access in GitLab. You can read more about the project [in the project's readme](https://gitlab.com/gitlab-org/gitaly).
### gitlab-monitor
@@ -174,7 +174,7 @@ When making a request to an HTTP Endpoint (Think `/users/sign_in`) the request w
- nginx - Acts as our first line reverse proxy
- gitlab-workhorse - This determines if it needs to go to the Rails application or somewhere else to reduce load on unicorn.
- unicorn - Since this is a web request, and it needs to access the application it will go to Unicorn.
-- Postgres/Gitaly/Redis - Depending on the type of request, it may hit these services to store or retreive data.
+- Postgres/Gitaly/Redis - Depending on the type of request, it may hit these services to store or retrieve data.
### GitLab Git Request Cycle
diff --git a/doc/development/code_review.md b/doc/development/code_review.md
index 1b591c7c322..f115045dbb7 100644
--- a/doc/development/code_review.md
+++ b/doc/development/code_review.md
@@ -203,7 +203,10 @@ first time.
- Extract unrelated changes and refactorings into future merge requests/issues.
- Seek to understand the reviewer's perspective.
- Try to respond to every comment.
-- Let the reviewer select the "Resolve discussion" buttons.
+- The merge request author resolves only the discussions they have fully
+ addressed. If there's an open reply, an open discussion, a suggestion,
+ a question, or anything else, the discussion should be left to be resolved
+ by the reviewer.
- Push commits based on earlier rounds of feedback as isolated commits to the
branch. Do not squash until the branch is ready to merge. Reviewers should be
able to read individual updates based on their earlier feedback.
diff --git a/doc/development/contributing/index.md b/doc/development/contributing/index.md
index 7ac846e4c4d..f7a0fdbeb40 100644
--- a/doc/development/contributing/index.md
+++ b/doc/development/contributing/index.md
@@ -4,7 +4,7 @@ Thank you for your interest in contributing to GitLab. This guide details how
to contribute to GitLab in a way that is easy for everyone.
We want to create a welcoming environment for everyone who is interested in contributing.
-Please visit our [Code of Conduct page](https://about.gitlab.com/contributing/code-of-conduct) to learn more about our committment to an open and welcoming environment.
+Please visit our [Code of Conduct page](https://about.gitlab.com/contributing/code-of-conduct) to learn more about our commitment to an open and welcoming environment.
For a first-time step-by-step guide to the contribution process, please see
["Contributing to GitLab"](https://about.gitlab.com/contributing/).
diff --git a/doc/development/documentation/site_architecture/index.md b/doc/development/documentation/site_architecture/index.md
index 0ce5825fd61..ee3a9caf9a0 100644
--- a/doc/development/documentation/site_architecture/index.md
+++ b/doc/development/documentation/site_architecture/index.md
@@ -44,7 +44,7 @@ read through the [global navigation](global_nav.md) doc.
The docs site is deployed to production with GitLab Pages, and previewed in
merge requests with Review Apps.
-The deployment aspects will be soon transfered from the [original document](https://gitlab.com/gitlab-com/gitlab-docs/blob/master/README.md)
+The deployment aspects will be soon transferred from the [original document](https://gitlab.com/gitlab-com/gitlab-docs/blob/master/README.md)
to this page.
<!--
diff --git a/doc/development/fe_guide/graphql.md b/doc/development/fe_guide/graphql.md
index f55f01720fd..3290f29530a 100644
--- a/doc/development/fe_guide/graphql.md
+++ b/doc/development/fe_guide/graphql.md
@@ -9,7 +9,7 @@ read more about [Feature Flags][feature-flags].
## Apollo Client
To save duplicated clients getting created in different apps, we have a
-[default client][defualt-client] that should be used. This setups the
+[default client][default-client] that should be used. This setups the
Apollo client with the correct URL and also sets the CSRF headers.
## GraphQL Queries
@@ -27,11 +27,11 @@ the Vue application is mounted.
```javascript
import Vue from 'vue';
import VueApollo from 'vue-apollo';
-import defaultClient from '~/lib/graphql';
+import createDefaultClient from '~/lib/graphql';
Vue.use(VueApollo);
const apolloProvider = new VueApollo({
- defaultClient,
+ defaultClient: createDefaultClient(),
});
new Vue({
@@ -43,6 +43,29 @@ new Vue({
Read more about [Vue Apollo][vue-apollo] in the [Vue Apollo documentation][vue-apollo-docs].
+### Local state with `apollo-link-state`
+
+It is possible to use our Apollo setup with [apollo-link-state][apollo-link-state] by passing
+in the client state object when creating the default client.
+
+```javascript
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import createDefaultClient from '~/lib/graphql';
+Vue.use(VueApollo);
+
+const apolloProvider = new VueApollo({
+ defaultClient: createDefaultClient({
+ defaults: {
+ testing: true,
+ },
+ resolvers: {
+ ...
+ },
+ }),
+});
+```
+
### Testing
With [Vue test utils][vue-test-utils] it is easy to quickly test components that
@@ -81,3 +104,4 @@ Read more about the [Apollo] client in the [Apollo documentation][apollo-client-
[default-client]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/app/assets/javascripts/lib/graphql.js
[apollo-client-docs]: https://www.apollographql.com/docs/tutorial/client.html
[vue-test-utils]: https://vue-test-utils.vuejs.org/
+[apollo-link-state]: https://www.apollographql.com/docs/link/links/state.html
diff --git a/doc/development/import_export.md b/doc/development/import_export.md
index e2108605d54..f7f48b03651 100644
--- a/doc/development/import_export.md
+++ b/doc/development/import_export.md
@@ -222,7 +222,7 @@ bundle exec rake gitlab:import_export:bump_version
### Renaming columns or models
-This is a relatively common occurence that will require a version bump.
+This is a relatively common occurrence that will require a version bump.
There is also the _RC problem_ - GitLab.com runs an RC, prior to any customers,
meaning that we want to bump the version up in the next version (or patch release).
diff --git a/doc/development/ordering_table_columns.md b/doc/development/ordering_table_columns.md
index 3e49a65f5ab..cbfd05e731d 100644
--- a/doc/development/ordering_table_columns.md
+++ b/doc/development/ordering_table_columns.md
@@ -19,7 +19,7 @@ The space between rows is also subject to alignment padding. The `user_id`
column takes only 4 bytes, and on 64-bit platform, 4 zeroes will be added for
alignment padding, to allow storing the next row beginning with the "clear" word.
-As a result, the actual size of each column would be (ommiting variable length
+As a result, the actual size of each column would be (omitting variable length
data and 24-byte tuple header): 8 bytes, variable, 8 bytes. This means that
each row will require at least 16 bytes for the two 4-byte integers. If a table
has a few rows this is not an issue. However, once you start storing millions of
diff --git a/doc/install/kubernetes/preparation/tiller.md b/doc/install/kubernetes/preparation/tiller.md
index 107df074b3b..00b0737402b 100644
--- a/doc/install/kubernetes/preparation/tiller.md
+++ b/doc/install/kubernetes/preparation/tiller.md
@@ -15,7 +15,7 @@ cannot be normally deployed.
Tiller is deployed into the cluster and interacts with the Kubernetes API to deploy your applications. If role based access control (RBAC) is enabled, Tiller will need to be [granted permissions](#preparing-for-helm-with-rbac) to allow it to talk to the Kubernetes API.
-If RBAC is not enabled, skip to [initalizing Helm](#initialize-helm).
+If RBAC is not enabled, skip to [initializing Helm](#initialize-helm).
If you are not sure whether RBAC is enabled in your cluster, or to learn more, read through our [RBAC documentation](rbac.md).
@@ -54,19 +54,25 @@ Some clusters require authentication to use `kubectl` to create the Tiller roles
#### Upload the RBAC config as an admin user (GKE)
-For GKE, you need to grab the admin credentials:
+For GKE, you need to obtain the admin credentials. This command will output the admin password:
```
gcloud container clusters describe <cluster-name> --zone <zone> --project <project-id> --format='value(masterAuth.password)'
```
-This command will output the admin password. We need the password to authenticate with `kubectl` and create the role.
+Use the admin password to set the admin credentials. Replace the password value below with the output value from the above step:
```
-kubectl --username=admin --password=xxxxxxxxxxxxxx create -f rbac-config.yaml
+kubectl config set-credentials admin --username=admin --password=xxxxxxxxxxxxxx
```
-#### Upload the RBAC config (Other clusters)
+Once credentials have been set, create the role:
+
+```
+kubectl --user=admin create -f rbac-config.yaml
+```
+
+#### Upload the RBAC config (Non-GKE clusters)
For other clusters like Amazon EKS, you can directly upload the RBAC configuration.
diff --git a/doc/install/requirements.md b/doc/install/requirements.md
index 1b7e0d1d0ab..c1f2297f3be 100644
--- a/doc/install/requirements.md
+++ b/doc/install/requirements.md
@@ -33,7 +33,7 @@ Please consider using a virtual machine to run GitLab.
## Ruby versions
-GitLab requires Ruby (MRI) 2.3. Support for Ruby versions below 2.3 (2.1, 2.2) will stop with GitLab 8.13.
+GitLab requires Ruby (MRI) 2.5. Support for Ruby versions below 2.5 (2.3, 2.4) will stop with GitLab 11.6.
You will have to use the standard MRI implementation of Ruby.
We love [JRuby](https://www.jruby.org/) and [Rubinius](https://rubinius.com) but GitLab
diff --git a/doc/integration/README.md b/doc/integration/README.md
index f7a624def53..f5bc0693b84 100644
--- a/doc/integration/README.md
+++ b/doc/integration/README.md
@@ -11,8 +11,7 @@ See the documentation below for details on how to configure these services.
- [Akismet](akismet.md) Configure Akismet to stop spam
- [Auth0 OmniAuth](auth0.md) Enable the Auth0 OmniAuth provider
-- [Bitbucket](bitbucket.md) Import projects from Bitbucket.org and login to your GitLab instance with your
- Bitbucket.org account
+- [Bitbucket](bitbucket.md) Import projects from Bitbucket.org and login to your GitLab instance with your Bitbucket.org account
- [CAS](cas.md) Configure GitLab to sign in using CAS
- [External issue tracker](external-issue-tracker.md) Redmine, JIRA, etc.
- [Gmail actions buttons](gmail_action_buttons_for_gitlab.md) Adds GitLab actions to messages
diff --git a/doc/security/img/ssh_keys_restricted_key_icon.png b/doc/security/img/ssh_keys_restricted_key_icon.png
new file mode 100644
index 00000000000..ad3749e8233
--- /dev/null
+++ b/doc/security/img/ssh_keys_restricted_key_icon.png
Binary files differ
diff --git a/doc/security/ssh_keys_restrictions.md b/doc/security/ssh_keys_restrictions.md
index 213fa5bfef5..6b6a8a06cc9 100644
--- a/doc/security/ssh_keys_restrictions.md
+++ b/doc/security/ssh_keys_restrictions.md
@@ -17,3 +17,11 @@ In the Admin area under **Settings** (`/admin/application_settings`), look for
the "Visibility and Access Controls" area:
![SSH keys restriction admin settings](img/ssh_keys_restrictions_settings.png)
+
+If a restriction is imposed on any key type, users will be unable to upload new SSH keys that don't meet the requirement. Any existing keys that don't meet it will be disabled but not removed and users will be unable to pull or push code using them.
+
+An icon will be visible to the user of a restricted key in the SSH keys section of their profile:
+
+![Restricted SSH key icon](img/ssh_keys_restricted_key_icon.png)
+
+Hovering over this icon will tell you why the key is restricted.
diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md
index 1ab0372fb67..7a18354bf66 100644
--- a/doc/topics/autodevops/index.md
+++ b/doc/topics/autodevops/index.md
@@ -697,6 +697,7 @@ also be customized, and you can easily use a [custom buildpack](#custom-buildpac
| `POSTGRES_USER` | The PostgreSQL user; defaults to `user`. Set it to use a custom username. |
| `POSTGRES_PASSWORD` | The PostgreSQL password; defaults to `testing-password`. Set it to use a custom password. |
| `POSTGRES_DB` | The PostgreSQL database name; defaults to the value of [`$CI_ENVIRONMENT_SLUG`](../../ci/variables/README.md#predefined-environment-variables). Set it to use a custom database name. |
+| `POSTGRES_VERSION` | The PostgreSQL version; defaults to `9.6.2` |
| `BUILDPACK_URL` | The buildpack's full URL. It can point to either Git repositories or a tarball URL. For Git repositories, it is possible to point to a specific `ref`, for example `https://github.com/heroku/heroku-buildpack-ruby.git#v142` |
| `SAST_CONFIDENCE_LEVEL` | The minimum confidence level of security issues you want to be reported; `1` for Low, `2` for Medium, `3` for High; defaults to `3`.|
| `DEP_SCAN_DISABLE_REMOTE_CHECKS` | Whether remote Dependency Scanning checks are disabled; defaults to `"false"`. Set to `"true"` to disable checks that send data to GitLab central servers. [Read more about remote checks](https://gitlab.com/gitlab-org/security-products/dependency-scanning#remote-checks).|
diff --git a/doc/user/admin_area/index.md b/doc/user/admin_area/index.md
new file mode 100644
index 00000000000..00cea22e4e1
--- /dev/null
+++ b/doc/user/admin_area/index.md
@@ -0,0 +1,29 @@
+# GitLab Admin Area **[CORE ONLY]**
+
+The Admin Area provides a web UI for administering some features of GitLab self-managed instances.
+
+To access the Admin Area, either:
+
+- Click the Admin Area icon (the spanner or wrench icon).
+- Visit `/admin` on your self-managed instance.
+
+NOTE: **Note:**
+Only admin users can access the Admin Area.
+
+## Admin Area sections
+
+The Admin Area is made up of the following sections:
+
+| Section | Description |
+|:------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------|
+| Overview | View your GitLab Dashboard, and maintain projects, users, groups, jobs, runners, and Gitaly servers. |
+| Monitoring | View GitLab system information, and information on background jobs, logs, [health checks](monitoring/health_check.md), request profiles, and audit logs. |
+| Messages | Send and manage [broadcast messages](broadcast_messages.md) for your users. |
+| System Hooks | Configure [system hooks](../../system_hooks/system_hooks.md) for many events. |
+| Applications | Create system [OAuth applications](../../integration/oauth_provider.md) for integrations with other services. |
+| Abuse Reports | Manage [abuse reports](abuse_reports.md) submitted by your users. |
+| Deploy Keys | Create instance-wide [SSH deploy keys](../../ssh/README.md#deploy-keys). |
+| Service Templates | Create [service templates](../project/integrations/services_templates.md) for projects. |
+| Labels | Create and maintain [labels](labels.md) for your GitLab instance. |
+| Appearance | Customize [GitLab's appearance](../../customization/index.md). |
+| Settings | Modify the [settings](settings/index.md) for your GitLab instance. |
diff --git a/doc/user/permissions.md b/doc/user/permissions.md
index 74a966f3a17..dff77acd71b 100644
--- a/doc/user/permissions.md
+++ b/doc/user/permissions.md
@@ -97,7 +97,7 @@ The following table depicts the various user permission levels in a project.
| Manage variables | | | | ✓ | ✓ |
| Manage GitLab Pages | | | | ✓ | ✓ |
| Manage GitLab Pages domains and certificates | | | | ✓ | ✓ |
-| Remove GitLab Pages | | | | | ✓ |
+| Remove GitLab Pages | | | | ✓ | ✓ |
| View GitLab Pages protected by [access control](project/pages/introduction.md#gitlab-pages-access-control-core-only) | ✓ | ✓ | ✓ | ✓ | ✓ |
| Manage clusters | | | | ✓ | ✓ |
| Manage license policy **[ULTIMATE]** | | | | ✓ | ✓ |
@@ -107,7 +107,6 @@ The following table depicts the various user permission levels in a project.
| Transfer project to another namespace | | | | | ✓ |
| Remove project | | | | | ✓ |
| Delete issues | | | | | ✓ |
-| Remove pages | | | | | ✓ |
| Force push to protected branches [^4] | | | | | |
| Remove protected branches [^4] | | | | | |
| View project Audit Events | | | | ✓ | ✓ |
diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md
index 42aa6840609..e601d7b2ccc 100644
--- a/doc/user/project/clusters/index.md
+++ b/doc/user/project/clusters/index.md
@@ -86,15 +86,20 @@ To add an existing Kubernetes cluster to your project:
1. Click **Add Kubernetes cluster**.
1. Click **Add an existing Kubernetes cluster** and fill in the details:
- **Kubernetes cluster name** (required) - The name you wish to give the cluster.
- - **Environment scope** (required)- The
+ - **Environment scope** (required) - The
[associated environment](#setting-the-environment-scope) to this cluster.
- **API URL** (required) -
It's the URL that GitLab uses to access the Kubernetes API. Kubernetes
exposes several APIs, we want the "base" URL that is common to all of them,
e.g., `https://kubernetes.example.com` rather than `https://kubernetes.example.com/api/v1`.
- - **CA certificate** (optional) -
- If the API is using a self-signed TLS certificate, you'll also need to include
- the `ca.crt` contents here.
+ - **CA certificate** (required) - A valid Kubernetes certificate is needed to authenticate to the EKS cluster. We will use the certificate created by default.
+ - List the secrets with `kubectl get secrets`, and one should named similar to
+ `default-token-xxxxx`. Copy that token name for use below.
+ - Get the certificate by running this command:
+
+ ```sh
+ kubectl get secret <secret name> -o jsonpath="{['data']['ca\.crt']}" | base64 --decode
+ ```
- **Token** -
GitLab authenticates against Kubernetes using service tokens, which are
scoped to a particular `namespace`.
@@ -102,36 +107,81 @@ To add an existing Kubernetes cluster to your project:
[`cluster-admin`](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#user-facing-roles)
privileges.** To create this service account:
- 1. Create a `gitlab` service account in the `default` namespace:
+ 1. Create a file called `gitlab-admin-service-account.yaml` with contents:
- ```bash
- kubectl create -f - <<EOF
- apiVersion: v1
- kind: ServiceAccount
- metadata:
- name: gitlab
- namespace: default
- EOF
+ ```yaml
+ apiVersion: v1
+ kind: ServiceAccount
+ metadata:
+ name: gitlab-admin
+ namespace: kube-system
```
- 1. Create a cluster role binding to give the `gitlab` service account
- `cluster-admin` privileges:
+
+ 2. Apply the service account to your cluster:
```bash
- kubectl create -f - <<EOF
+ kubectl apply -f gitlab-admin-service-account.yaml
+ ```
+
+ Output:
+
+ ```bash
+ serviceaccount "gitlab-admin" created
+ ```
+
+ 3. Create a file called `gitlab-admin-cluster-role-binding.yaml` with contents:
+
+ ```yaml
+ apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
- apiVersion: rbac.authorization.k8s.io/v1
metadata:
- name: gitlab-cluster-admin
- subjects:
- - kind: ServiceAccount
- name: gitlab
- namespace: default
+ name: gitlab-admin
roleRef:
+ apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
- apiGroup: rbac.authorization.k8s.io
- EOF
+ subjects:
+ - kind: ServiceAccount
+ name: gitlab-admin
+ namespace: kube-system
+ ```
+
+ 4. Apply the cluster role binding to your cluster:
+
+ ```bash
+ kubectl apply -f gitlab-admin-cluster-role-binding.yaml
+ ```
+
+ Output:
+
+ ```bash
+ clusterrolebinding "gitlab-admin" created
```
+
+ 5. Retrieve the token for the `gitlab-admin` service account:
+
+ ```bash
+ kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep gitlab-admin | awk '{print $1}')
+ ```
+
+ Copy the `<authentication_token>` value from the output:
+
+ ```yaml
+ Name: gitlab-admin-token-b5zv4
+ Namespace: kube-system
+ Labels: <none>
+ Annotations: kubernetes.io/service-account.name=gitlab-admin
+ kubernetes.io/service-account.uid=bcfe66ac-39be-11e8-97e8-026dce96b6e8
+
+ Type: kubernetes.io/service-account-token
+
+ Data
+ ====
+ ca.crt: 1025 bytes
+ namespace: 11 bytes
+ token: <authentication_token>
+ ```
+
NOTE: **Note:**
For GKE clusters, you will need the
`container.clusterRoleBindings.create` permission to create a cluster
diff --git a/doc/user/project/clusters/runbooks/index.md b/doc/user/project/clusters/runbooks/index.md
index e1b8dc07b50..512a4cb32f4 100644
--- a/doc/user/project/clusters/runbooks/index.md
+++ b/doc/user/project/clusters/runbooks/index.md
@@ -23,7 +23,7 @@ runbooks. A sample runbook is provided, showcasing common operations.
**<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
Watch this [video](https://www.youtube.com/watch?v=Q_OqHIIUPjE)
-for an overview of how this is acomplished in GitLab!**
+for an overview of how this is accomplished in GitLab!**
## Requirements
diff --git a/doc/user/project/clusters/serverless/img/app-domain.png b/doc/user/project/clusters/serverless/img/app-domain.png
deleted file mode 100644
index d113dfadd2e..00000000000
--- a/doc/user/project/clusters/serverless/img/app-domain.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/clusters/serverless/img/dns-entry.png b/doc/user/project/clusters/serverless/img/dns-entry.png
index 678743f256e..351e40b77d5 100644
--- a/doc/user/project/clusters/serverless/img/dns-entry.png
+++ b/doc/user/project/clusters/serverless/img/dns-entry.png
Binary files differ
diff --git a/doc/user/project/clusters/serverless/img/install-knative.png b/doc/user/project/clusters/serverless/img/install-knative.png
index 93b1cbe602f..ecc2f8fb481 100644
--- a/doc/user/project/clusters/serverless/img/install-knative.png
+++ b/doc/user/project/clusters/serverless/img/install-knative.png
Binary files differ
diff --git a/doc/user/project/clusters/serverless/img/serverless-page.png b/doc/user/project/clusters/serverless/img/serverless-page.png
index 814b8532205..a872fda7740 100644
--- a/doc/user/project/clusters/serverless/img/serverless-page.png
+++ b/doc/user/project/clusters/serverless/img/serverless-page.png
Binary files differ
diff --git a/doc/user/project/clusters/serverless/index.md b/doc/user/project/clusters/serverless/index.md
index fc3a4a757d0..f7e72efa2ba 100644
--- a/doc/user/project/clusters/serverless/index.md
+++ b/doc/user/project/clusters/serverless/index.md
@@ -25,8 +25,12 @@ To run Knative on Gitlab, you will need:
1. **Kubernetes Cluster:** An RBAC-enabled Kubernetes cluster is required to deploy Knative.
The simplest way to get started is to add a cluster using [GitLab's GKE integration](../index.md#adding-and-creating-a-new-gke-cluster-via-gitlab).
+ The set of minimum recommended cluster specifications to run Knative is 3 nodes, 6 vCPUs, and 22.50 GB memory.
1. **Helm Tiller:** Helm is a package manager for Kubernetes and is required to install
Knative.
+1. **GitLab Runner:** A runner is required to run the CI jobs that will deploy serverless
+ applications or functions onto your cluster. You can install the GitLab Runner
+ onto the existing Kubernetes cluster. See [Installing Applications](../index.md#installing-applications) for more information.
1. **Domain Name:** Knative will provide its own load balancer using Istio. It will provide an
external IP address for all the applications served by Knative. You will be prompted to enter a
wildcard domain where your applications will be served. Configure your DNS server to use the
@@ -45,9 +49,9 @@ To run Knative on Gitlab, you will need:
NOTE: **Note:**
The minimum recommended cluster size to run Knative is 3-nodes, 6 vCPUs, and 22.50 GB memory. **RBAC must be enabled.**
-1. [Add a Kubernetes cluster](../index.md) and install Helm.
-1. Once Helm has been successfully installed, on the Knative app section, enter the domain to be used with
- your application and click "Install".
+1. [Add a Kubernetes cluster](../index.md) and [install Helm](../index.md#installing-applications).
+1. Once Helm has been successfully installed, scroll down to the Knative app section. Enter the domain to be used with
+ your application/functions (e.g. `example.com`) and click **Install**.
![install-knative](img/install-knative.png)
@@ -66,12 +70,16 @@ The minimum recommended cluster size to run Knative is 3-nodes, 6 vCPUs, and 22.
1. The ingress is now available at this address and will route incoming requests to the proper service based on the DNS
name in the request. To support this, a wildcard DNS A record should be created for the desired domain name. For example,
- if your Knative base domain is `knative.info` then you need to create an A record with domain `*.knative.info`
+ if your Knative base domain is `example.com` then you need to create an A record with domain `*.example.com`
pointing the ip address of the ingress.
![dns entry](img/dns-entry.png)
-## Deploying Functions
+NOTE: **Note:**
+You can deploy either [functions](#deploying-functions) or [serverless applications](#deploying-serverless-applications)
+on a given project but not both. The current implementation makes use of a `serverless.yml` file to signal a FaaS project.
+
+## Deploying functions
> Introduced in GitLab 11.6.
@@ -84,7 +92,7 @@ Currently the following [runtimes](https://gitlab.com/triggermesh/runtimes) are
- node.js
- kaniko
-You can find all the files referenced in this doc in the [functions example project](https://gitlab.com/knative-examples/functions).
+You can find and import all the files referenced in this doc in the **[functions example project](https://gitlab.com/knative-examples/functions)**.
Follow these steps to deploy a function using the Node.js runtime to your Knative instance:
@@ -188,16 +196,27 @@ The function details can be retrieved directly from Knative on the cluster:
kubectl -n "$KUBE_NAMESPACE" get services.serving.knative.dev
```
-The sample function can now be triggered from any HTTP client using a simple `POST` call.
+The sample function can now be triggered from any HTTP client using a simple `POST` call:
+
+ 1. Using curl
-![function exection](img/function-execution.png)
+ ```bash
+ curl \
+ --header "Content-Type: application/json" \
+ --request POST \
+ --data '{"GitLab":"FaaS"}' \
+ http://functions-echo.functions-1.functions.example.net/
+ ```
+ 2. Using a web-based tool (ie. postman, restlet, etc)
+
+ ![function execution](img/function-execution.png)
## Deploying Serverless applications
> Introduced in GitLab 11.5.
NOTE: **Note:**
-You can reference the sample [Knative Ruby App](https://gitlab.com/knative-examples/knative-ruby-app) to get started.
+You can reference and import the sample [Knative Ruby App](https://gitlab.com/knative-examples/knative-ruby-app) to get started.
Add the following `.gitlab-ci.yml` to the root of your repository
(you may skip this step if using the sample [Knative Ruby App](https://gitlab.com/knative-examples/knative-ruby-app) mentioned above):
@@ -236,11 +255,7 @@ With all the pieces in place, the next time a CI pipeline runs, the Knative appl
### Obtain the URL for the Knative deployment
-Go to the **Operations > Serverless** page to find the URL for your deployment in the **Domain** column.
-
-![app domain](img/app-domain.png)
-
-Alternatively, use the CI/CD deployment job output to obtain the deployment URL. Once all the stages of the pipeline finish, click the **deploy** stage.
+Go to the **CI/CD > Pipelines** and click on the pipeline that deployed your app. Once all the stages of the pipeline finish, click the **deploy** stage.
![deploy stage](img/deploy-stage.png)
@@ -262,7 +277,7 @@ registry.staging.gitlab.com/danielgruesso/knative
$ tm -n "$KUBE_NAMESPACE" --config "$KUBECONFIG" deploy service "$CI_PROJECT_NAME" --from-image "$CI_REGISTRY_IMAGE" --wait
Deployment started. Run "tm -n knative-4342902 describe service knative" to see the details
Waiting for ready state.......
-Service domain: knative.knative-4342902.knative.info
+Service domain: knative.knative-4342902.example.com
Job succeeded
```
diff --git a/doc/user/project/pipelines/settings.md b/doc/user/project/pipelines/settings.md
index cce330aecc7..4a989afad4d 100644
--- a/doc/user/project/pipelines/settings.md
+++ b/doc/user/project/pipelines/settings.md
@@ -23,7 +23,7 @@ in `.gitlab-ci.yml`.
## Timeout
Timeout defines the maximum amount of time in minutes that a job is able run.
-This is configureable under your project's **Settings > CI/CD > General pipelines settings**.
+This is configurable under your project's **Settings > CI/CD > General pipelines settings**.
The default value is 60 minutes. Decrease the time limit if you want to impose
a hard limit on your jobs' running time or increase it otherwise. In any case,
if the job surpasses the threshold, it is marked as failed.
@@ -134,7 +134,7 @@ Depending on the status of your job, a badge can have the following values:
You can access a pipeline status badge image using the following link:
```text
-https://example.gitlab.com/<namespace>/<project>/badges/<branch>/build.svg
+https://example.gitlab.com/<namespace>/<project>/badges/<branch>/pipeline.svg
```
### Test coverage report badge
diff --git a/lib/api/api.rb b/lib/api/api.rb
index 4dd1b459554..3bcf5150b43 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -115,6 +115,7 @@ module API
mount ::API::GroupVariables
mount ::API::ImportGithub
mount ::API::Internal
+ mount ::API::IssuableBulkUpdate
mount ::API::Issues
mount ::API::JobArtifacts
mount ::API::Jobs
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 9199f898ea0..7c035990fb0 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -1385,13 +1385,9 @@ module API
class GitInfo < Grape::Entity
expose :repo_url, :ref, :sha, :before_sha
- expose :ref_type do |model|
- if model.tag
- 'tag'
- else
- 'branch'
- end
- end
+ expose :ref_type
+ expose :refspecs
+ expose :git_depth, as: :depth
end
class RunnerInfo < Grape::Entity
diff --git a/lib/api/helpers/pagination.rb b/lib/api/helpers/pagination.rb
index de59c915d66..d00e61678b5 100644
--- a/lib/api/helpers/pagination.rb
+++ b/lib/api/helpers/pagination.rb
@@ -13,6 +13,33 @@ module API
strategy.new(self).paginate(relation)
end
+ class Base
+ private
+
+ def per_page
+ @per_page ||= params[:per_page]
+ end
+
+ def base_request_uri
+ @base_request_uri ||= URI.parse(request.url).tap do |uri|
+ uri.host = Gitlab.config.gitlab.host
+ uri.port = nil
+ end
+ end
+
+ def build_page_url(query_params:)
+ base_request_uri.tap do |uri|
+ uri.query = query_params
+ end.to_s
+ end
+
+ def page_href(next_page_params = {})
+ query_params = params.merge(**next_page_params, per_page: per_page).to_query
+
+ build_page_url(query_params: query_params)
+ end
+ end
+
class KeysetPaginationInfo
attr_reader :relation, :request_context
@@ -85,7 +112,7 @@ module API
end
end
- class KeysetPaginationStrategy
+ class KeysetPaginationStrategy < Base
attr_reader :request_context
delegate :params, :header, :request, to: :request_context
@@ -141,10 +168,6 @@ module API
]
end
- def per_page
- params[:per_page]
- end
-
def add_default_pagination_headers
header 'X-Per-Page', per_page.to_s
end
@@ -154,22 +177,12 @@ module API
header 'Link', link_for('next', next_page_params)
end
- def page_href(next_page_params)
- request_url = request.url.split('?').first
- request_params = params.dup
- request_params[:per_page] = per_page
-
- request_params.merge!(next_page_params) if next_page_params
-
- "#{request_url}?#{request_params.to_query}"
- end
-
def link_for(rel, next_page_params)
%(<#{page_href(next_page_params)}>; rel="#{rel}")
end
end
- class DefaultPaginationStrategy
+ class DefaultPaginationStrategy < Base
attr_reader :request_context
delegate :params, :header, :request, to: :request_context
@@ -198,15 +211,13 @@ module API
end
end
- # rubocop: disable CodeReuse/ActiveRecord
def add_default_order(relation)
if relation.is_a?(ActiveRecord::Relation) && relation.order_values.empty?
- relation = relation.order(:id)
+ relation = relation.order(:id) # rubocop: disable CodeReuse/ActiveRecord
end
relation
end
- # rubocop: enable CodeReuse/ActiveRecord
def add_pagination_headers(paginated_data)
header 'X-Per-Page', paginated_data.limit_value.to_s
@@ -222,27 +233,13 @@ module API
end
def pagination_links(paginated_data)
- request_url = request.url.split('?').first
- request_params = params.clone
- request_params[:per_page] = paginated_data.limit_value
-
- links = []
-
- request_params[:page] = paginated_data.prev_page
- links << %(<#{request_url}?#{request_params.to_query}>; rel="prev") if request_params[:page]
-
- request_params[:page] = paginated_data.next_page
- links << %(<#{request_url}?#{request_params.to_query}>; rel="next") if request_params[:page]
-
- request_params[:page] = 1
- links << %(<#{request_url}?#{request_params.to_query}>; rel="first")
-
- unless data_without_counts?(paginated_data)
- request_params[:page] = total_pages(paginated_data)
- links << %(<#{request_url}?#{request_params.to_query}>; rel="last")
- end
+ [].tap do |links|
+ links << %(<#{page_href(page: paginated_data.prev_page)}>; rel="prev") if paginated_data.prev_page
+ links << %(<#{page_href(page: paginated_data.next_page)}>; rel="next") if paginated_data.next_page
+ links << %(<#{page_href(page: 1)}>; rel="first")
- links.join(', ')
+ links << %(<#{page_href(page: total_pages(paginated_data))}>; rel="last") unless data_without_counts?(paginated_data)
+ end.join(', ')
end
def total_pages(paginated_data)
diff --git a/lib/api/helpers/runner.rb b/lib/api/helpers/runner.rb
index 16df8e830e1..ff73a49d5e8 100644
--- a/lib/api/helpers/runner.rb
+++ b/lib/api/helpers/runner.rb
@@ -26,7 +26,7 @@ module API
end
def get_runner_ip
- { ip_address: request.env["HTTP_X_FORWARDED_FOR"] || request.ip }
+ { ip_address: env["action_dispatch.remote_ip"].to_s || request.ip }
end
def current_runner
diff --git a/lib/api/issuable_bulk_update.rb b/lib/api/issuable_bulk_update.rb
new file mode 100644
index 00000000000..5ac6c252d96
--- /dev/null
+++ b/lib/api/issuable_bulk_update.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+module API
+ class IssuableBulkUpdate < Grape::API
+ params do
+ requires :id, type: String, desc: 'The ID of a project'
+ end
+ resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
+ %w(issue merge_request).each do |issuable|
+ desc "Updates a list of #{issuable.pluralize}" do
+ detail 'This feature was introduced in 11.9'
+ end
+ params do
+ requires :issuable_ids, type: Array[Integer], desc: "Array of #{issuable.pluralize} IDs to be updated"
+ optional :state_event, type: String, values: %w(reopen close), desc: 'Reopens or closes a resource'
+ optional :milestone_id, type: Integer, desc: 'The milestone ID number'
+ optional :add_label_ids, type: Array[Integer], desc: 'IDs of labels to be added'
+ optional :remove_label_ids, type: Array[Integer], desc: 'IDs of labels to be removed'
+ optional :subscription_event, type: String, values: %w(subscribe unsubscribe),
+ desc: 'Subscribes or unsubscribes from a resource'
+
+ if issuable == 'issue'
+ optional :assignee_ids, type: Array[Integer], desc: 'List of assignee IDs'
+ at_least_one_of :state_event, :milestone_id, :add_label_ids, :remove_label_ids,
+ :subscription_event, :assignee_ids
+ else
+ optional :assignee_id, type: Integer, desc: 'ID of the assignee'
+ at_least_one_of :state_event, :milestone_id, :add_label_ids, :remove_label_ids,
+ :subscription_event, :assignee_id
+ end
+ end
+ put ":id/#{issuable.pluralize}/bulk_update" do
+ authorize! :"admin_#{issuable}", user_project
+
+ update_params = declared_params(include_missing: false)
+
+ result = Issuable::BulkUpdateService.new(user_project, current_user, update_params)
+ .execute(issuable)
+
+ if result[:success]
+ status 200
+ quantity = result[:count]
+ { message: "#{quantity} #{issuable.pluralize(quantity)} updated" }
+ else
+ render_api_error!('Bulk update failed', 400)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index 94ed9ac6fb1..f43f4d961d6 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -54,6 +54,7 @@ module API
optional :scope, type: String, values: %w[created-by-me assigned-to-me created_by_me assigned_to_me all],
desc: 'Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`'
optional :my_reaction_emoji, type: String, desc: 'Return issues reacted by the authenticated user by the given emoji'
+ optional :confidential, type: Boolean, desc: 'Filter confidential or public issues'
use :pagination
use :issues_params_ee
diff --git a/lib/api/project_templates.rb b/lib/api/project_templates.rb
index d05ddad7466..119902a189c 100644
--- a/lib/api/project_templates.rb
+++ b/lib/api/project_templates.rb
@@ -36,7 +36,10 @@ module API
optional :project, type: String, desc: 'The project name to use when expanding placeholders in the template. Only affects licenses'
optional :fullname, type: String, desc: 'The full name of the copyright holder to use when expanding placeholders in the template. Only affects licenses'
end
- get ':id/templates/:type/:name', requirements: { name: /[\w\.-]+/ } do
+ # The regex is needed to ensure a period (e.g. agpl-3.0)
+ # isn't confused with a format type. We also need to allow encoded
+ # values (e.g. C%2B%2B for C++), so allow % and + as well.
+ get ':id/templates/:type/:name', requirements: { name: /[\w%.+-]+/ } do
template = TemplateFinder
.build(params[:type], user_project, name: params[:name])
.execute
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 6a93ef9f3ad..bd5678f513f 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -258,6 +258,8 @@ module API
end
params do
optional :namespace, type: String, desc: 'The ID or name of the namespace that the project will be forked into'
+ optional :path, type: String, desc: 'The path that will be assigned to the fork'
+ optional :name, type: String, desc: 'The name that will be assigned to the fork'
end
post ':id/fork' do
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42284')
diff --git a/lib/banzai/filter/relative_link_filter.rb b/lib/banzai/filter/relative_link_filter.rb
index 93e6d6470f1..2745905c5ff 100644
--- a/lib/banzai/filter/relative_link_filter.rb
+++ b/lib/banzai/filter/relative_link_filter.rb
@@ -150,7 +150,10 @@ module Banzai
end
def uri_type(path)
- @uri_types[path] ||= current_commit.uri_type(path)
+ # https://gitlab.com/gitlab-org/gitlab-ce/issues/58011
+ Gitlab::GitalyClient.allow_n_plus_1_calls do
+ @uri_types[path] ||= current_commit.uri_type(path)
+ end
end
def current_commit
diff --git a/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml b/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
index 7c1182d4293..37fc1fccfb9 100644
--- a/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
@@ -48,6 +48,7 @@ variables:
POSTGRES_PASSWORD: testing-password
POSTGRES_ENABLED: "true"
POSTGRES_DB: $CI_ENVIRONMENT_SLUG
+ POSTGRES_VERSION: 9.6.2
KUBERNETES_VERSION: 1.11.7
HELM_VERSION: 2.12.3
@@ -700,6 +701,7 @@ rollout 100%:
--set postgresql.postgresUser="$POSTGRES_USER" \
--set postgresql.postgresPassword="$POSTGRES_PASSWORD" \
--set postgresql.postgresDatabase="$POSTGRES_DB" \
+ --set postgresql.imageTag="$POSTGRES_VERSION" \
--set application.initializeCommand="$DB_INITIALIZE" \
--namespace="$KUBE_NAMESPACE" \
"$name" \
diff --git a/lib/gitlab/gitaly_client.rb b/lib/gitlab/gitaly_client.rb
index 0ab53f8f706..5aeedb0f50d 100644
--- a/lib/gitlab/gitaly_client.rb
+++ b/lib/gitlab/gitaly_client.rb
@@ -28,7 +28,7 @@ module Gitlab
PEM_REGEX = /\-+BEGIN CERTIFICATE\-+.+?\-+END CERTIFICATE\-+/m
SERVER_VERSION_FILE = 'GITALY_SERVER_VERSION'
- MAXIMUM_GITALY_CALLS = 35
+ MAXIMUM_GITALY_CALLS = 30
CLIENT_NAME = (Sidekiq.server? ? 'gitlab-sidekiq' : 'gitlab-web').freeze
MUTEX = Mutex.new
diff --git a/lib/gitlab/project_template.rb b/lib/gitlab/project_template.rb
index ef656e5b2ce..45045cb8c7d 100644
--- a/lib/gitlab/project_template.rb
+++ b/lib/gitlab/project_template.rb
@@ -32,7 +32,12 @@ module Gitlab
ProjectTemplate.new('jekyll', 'Pages/Jekyll', _('Everything you need to create a GitLab Pages site using Jekyll.'), 'https://gitlab.com/pages/jekyll'),
ProjectTemplate.new('plainhtml', 'Pages/Plain HTML', _('Everything you need to create a GitLab Pages site using plain HTML.'), 'https://gitlab.com/pages/plain-html'),
ProjectTemplate.new('gitbook', 'Pages/GitBook', _('Everything you need to create a GitLab Pages site using GitBook.'), 'https://gitlab.com/pages/gitbook'),
- ProjectTemplate.new('hexo', 'Pages/Hexo', _('Everything you need to create a GitLab Pages site using Hexo.'), 'https://gitlab.com/pages/hexo')
+ ProjectTemplate.new('hexo', 'Pages/Hexo', _('Everything you need to create a GitLab Pages site using Hexo.'), 'https://gitlab.com/pages/hexo'),
+ ProjectTemplate.new('nfhugo', 'Netlify/Hugo', _('A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfhugo', 'illustrations/logos/netlify.svg'),
+ ProjectTemplate.new('nfjekyll', 'Netlify/Jekyll', _('A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfjekyll', 'illustrations/logos/netlify.svg'),
+ ProjectTemplate.new('nfplainhtml', 'Netlify/Plain HTML', _('A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfplain-html', 'illustrations/logos/netlify.svg'),
+ ProjectTemplate.new('nfgitbook', 'Netlify/GitBook', _('A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfgitbook', 'illustrations/logos/netlify.svg'),
+ ProjectTemplate.new('nfhexo', 'Netlify/Hexo', _('A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfhexo', 'illustrations/logos/netlify.svg')
].freeze
class << self
diff --git a/lib/gitlab/shell.rb b/lib/gitlab/shell.rb
index 1153e69d3de..c7d8dfcd495 100644
--- a/lib/gitlab/shell.rb
+++ b/lib/gitlab/shell.rb
@@ -280,7 +280,10 @@ module Gitlab
# add_namespace("default", "gitlab")
#
def add_namespace(storage, name)
- Gitlab::GitalyClient::NamespaceService.new(storage).add(name)
+ # https://gitlab.com/gitlab-org/gitlab-ce/issues/58012
+ Gitlab::GitalyClient.allow_n_plus_1_calls do
+ Gitlab::GitalyClient::NamespaceService.new(storage).add(name)
+ end
rescue GRPC::InvalidArgument => e
raise ArgumentError, e.message
end
diff --git a/lib/gitlab/tracing.rb b/lib/gitlab/tracing.rb
index 0d9b0be1c8e..29517591c51 100644
--- a/lib/gitlab/tracing.rb
+++ b/lib/gitlab/tracing.rb
@@ -27,10 +27,11 @@ module Gitlab
def self.tracing_url
return unless tracing_url_enabled?
- tracing_url_template % {
- correlation_id: Gitlab::CorrelationId.current_id.to_s,
- service: Gitlab.process_name
- }
+ # Avoid using `format` since it can throw TypeErrors
+ # which we want to avoid on unsanitised env var input
+ tracing_url_template.to_s
+ .gsub(/\{\{\s*correlation_id\s*\}\}/, Gitlab::CorrelationId.current_id.to_s)
+ .gsub(/\{\{\s*service\s*\}\}/, Gitlab.process_name)
end
end
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 5a8ab9aa996..f26103ef6ae 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -285,6 +285,18 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
@@ -300,6 +312,9 @@ msgstr ""
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
msgstr ""
diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb
index c934db9e237..cb24a6ef142 100644
--- a/spec/controllers/admin/users_controller_spec.rb
+++ b/spec/controllers/admin/users_controller_spec.rb
@@ -8,6 +8,20 @@ describe Admin::UsersController do
sign_in(admin)
end
+ describe 'GET #index' do
+ it 'retrieves all users' do
+ get :index
+
+ expect(assigns(:users)).to match_array([user, admin])
+ end
+
+ it 'filters by admins' do
+ get :index, params: { filter: 'admins' }
+
+ expect(assigns(:users)).to eq([admin])
+ end
+ end
+
describe 'GET :id' do
it 'finds a user case-insensitively' do
user = create(:user, username: 'CaseSensitive')
diff --git a/spec/controllers/concerns/issuable_collections_spec.rb b/spec/controllers/concerns/issuable_collections_spec.rb
index 307c5d60c57..8580900215c 100644
--- a/spec/controllers/concerns/issuable_collections_spec.rb
+++ b/spec/controllers/concerns/issuable_collections_spec.rb
@@ -112,7 +112,8 @@ describe IssuableCollections do
assignee_username: 'user1',
author_id: '2',
author_username: 'user2',
- authorized_only: 'true',
+ authorized_only: 'yes',
+ confidential: true,
due_date: '2017-01-01',
group_id: '3',
iids: '4',
@@ -140,6 +141,7 @@ describe IssuableCollections do
'assignee_username' => 'user1',
'author_id' => '2',
'author_username' => 'user2',
+ 'confidential' => true,
'label_name' => 'foo',
'milestone_title' => 'bar',
'my_reaction_emoji' => 'thumbsup',
diff --git a/spec/controllers/projects/notes_controller_spec.rb b/spec/controllers/projects/notes_controller_spec.rb
index 81892575889..0b0f5117784 100644
--- a/spec/controllers/projects/notes_controller_spec.rb
+++ b/spec/controllers/projects/notes_controller_spec.rb
@@ -252,8 +252,8 @@ describe Projects::NotesController do
note: 'some note',
noteable_id: merge_request.id.to_s,
noteable_type: 'MergeRequest',
- merge_request_diff_head_sha: 'sha',
- in_reply_to_discussion_id: nil
+ commit_id: nil,
+ merge_request_diff_head_sha: 'sha'
}).permit!
expect(Notes::CreateService).to receive(:new).with(project, user, service_params).and_return(double(execute: true))
@@ -266,6 +266,22 @@ describe Projects::NotesController do
end
end
+ context 'when creating a comment on a commit with SHA1 starting with a large number' do
+ let(:commit) { create(:commit, project: project, id: '842616594688d2351480dfebd67b3d8d15571e6d') }
+
+ it 'creates a note successfully' do
+ expect do
+ post :create, params: {
+ note: { note: 'some note', commit_id: commit.id },
+ namespace_id: project.namespace,
+ project_id: project,
+ target_type: 'commit',
+ target_id: commit.id
+ }
+ end.to change { Note.count }.by(1)
+ end
+ end
+
context 'when creating a commit comment from an MR fork' do
let(:project) { create(:project, :repository) }
diff --git a/spec/controllers/projects/pages_controller_spec.rb b/spec/controllers/projects/pages_controller_spec.rb
index 4b742a5d427..d6eece47804 100644
--- a/spec/controllers/projects/pages_controller_spec.rb
+++ b/spec/controllers/projects/pages_controller_spec.rb
@@ -42,6 +42,18 @@ describe Projects::PagesController do
expect(response).to have_gitlab_http_status(302)
end
+
+ context 'when user is developer' do
+ before do
+ project.add_developer(user)
+ end
+
+ it 'returns 404 status' do
+ delete :destroy, params: request_params
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
end
context 'pages disabled' do
diff --git a/spec/features/issues/filtered_search/dropdown_hint_spec.rb b/spec/features/issues/filtered_search/dropdown_hint_spec.rb
index 0e296ab2109..096756f19cc 100644
--- a/spec/features/issues/filtered_search/dropdown_hint_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_hint_spec.rb
@@ -66,7 +66,7 @@ describe 'Dropdown hint', :js do
it 'filters with text' do
filtered_search.set('a')
- expect(find(js_dropdown_hint)).to have_selector('.filter-dropdown .filter-dropdown-item', count: 4)
+ expect(find(js_dropdown_hint)).to have_selector('.filter-dropdown .filter-dropdown-item', count: 5)
end
end
@@ -119,6 +119,15 @@ describe 'Dropdown hint', :js do
expect_tokens([{ name: 'my-reaction' }])
expect_filtered_search_input_empty
end
+
+ it 'opens the yes-no dropdown when you click on confidential' do
+ click_hint('confidential')
+
+ expect(page).to have_css(js_dropdown_hint, visible: false)
+ expect(page).to have_css('#js-dropdown-confidential', visible: true)
+ expect_tokens([{ name: 'confidential' }])
+ expect_filtered_search_input_empty
+ end
end
describe 'selecting from dropdown with some input' do
diff --git a/spec/features/issues/filtered_search/search_bar_spec.rb b/spec/features/issues/filtered_search/search_bar_spec.rb
index 8abab3f35d6..c4468922883 100644
--- a/spec/features/issues/filtered_search/search_bar_spec.rb
+++ b/spec/features/issues/filtered_search/search_bar_spec.rb
@@ -100,7 +100,7 @@ describe 'Search bar', :js do
find('.filtered-search-box .clear-search').click
filtered_search.click
- expect(find('#js-dropdown-hint')).to have_selector('.filter-dropdown .filter-dropdown-item', count: 5)
+ expect(find('#js-dropdown-hint')).to have_selector('.filter-dropdown .filter-dropdown-item', count: 6)
expect(get_left_style(find('#js-dropdown-hint')['style'])).to eq(hint_offset)
end
end
diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb
index c22ad0d20ef..986f3823275 100644
--- a/spec/features/issues/gfm_autocomplete_spec.rb
+++ b/spec/features/issues/gfm_autocomplete_spec.rb
@@ -278,7 +278,7 @@ describe 'GFM autocomplete', :js do
end
end
- # This context has jsut one example in each contexts in order to improve spec performance.
+ # This context has just one example in each contexts in order to improve spec performance.
context 'labels', :quarantine do
let!(:backend) { create(:label, project: project, title: 'backend') }
let!(:bug) { create(:label, project: project, title: 'bug') }
diff --git a/spec/features/projects/blobs/edit_spec.rb b/spec/features/projects/blobs/edit_spec.rb
index 6e6c299ee2e..1522a3361a1 100644
--- a/spec/features/projects/blobs/edit_spec.rb
+++ b/spec/features/projects/blobs/edit_spec.rb
@@ -77,7 +77,7 @@ describe 'Editing file blob', :js do
click_link 'Preview'
wait_for_requests
- # the above generates two seperate lists (not embedded) in CommonMark
+ # the above generates two separate lists (not embedded) in CommonMark
expect(page).to have_content("sublist")
expect(page).not_to have_xpath("//ol//li//ul")
end
diff --git a/spec/features/projects/pages_spec.rb b/spec/features/projects/pages_spec.rb
index 435fb229b69..72faeba92ee 100644
--- a/spec/features/projects/pages_spec.rb
+++ b/spec/features/projects/pages_spec.rb
@@ -13,16 +13,6 @@ describe 'Pages' do
sign_in(user)
end
- shared_examples 'no pages deployed' do
- it 'does not see anything to destroy' do
- visit project_pages_path(project)
-
- expect(page).to have_content('Configure pages')
- expect(page).not_to have_link('Remove pages')
- expect(page).not_to have_text('Only the project owner can remove pages')
- end
- end
-
context 'when user is the owner' do
before do
project.namespace.update(owner: user)
@@ -181,7 +171,12 @@ describe 'Pages' do
end
end
- it_behaves_like 'no pages deployed'
+ it 'does not see anything to destroy' do
+ visit project_pages_path(project)
+
+ expect(page).to have_content('Configure pages')
+ expect(page).not_to have_link('Remove pages')
+ end
describe 'project settings page' do
it 'renders "Pages" tab' do
@@ -208,22 +203,6 @@ describe 'Pages' do
end
end
- context 'when the user is not the owner' do
- context 'when pages deployed' do
- before do
- allow_any_instance_of(Project).to receive(:pages_deployed?) { true }
- end
-
- it 'sees "Only the project owner can remove pages" text' do
- visit project_pages_path(project)
-
- expect(page).to have_text('Only the project owner can remove pages')
- end
- end
-
- it_behaves_like 'no pages deployed'
- end
-
describe 'HTTPS settings', :js, :https_pages_enabled do
before do
project.namespace.update(owner: user)
@@ -289,51 +268,45 @@ describe 'Pages' do
end
describe 'Remove page' do
- context 'when user is the owner' do
- let(:project) { create :project, :repository }
-
- before do
- project.namespace.update(owner: user)
+ let(:project) { create :project, :repository }
+
+ context 'when pages are deployed' do
+ let(:pipeline) do
+ commit_sha = project.commit('HEAD').sha
+
+ project.ci_pipelines.create(
+ ref: 'HEAD',
+ sha: commit_sha,
+ source: :push,
+ protected: false
+ )
end
- context 'when pages are deployed' do
- let(:pipeline) do
- commit_sha = project.commit('HEAD').sha
-
- project.ci_pipelines.create(
- ref: 'HEAD',
- sha: commit_sha,
- source: :push,
- protected: false
- )
- end
-
- let(:ci_build) do
- create(
- :ci_build,
- project: project,
- pipeline: pipeline,
- ref: 'HEAD',
- legacy_artifacts_file: fixture_file_upload(File.join('spec/fixtures/pages.zip')),
- legacy_artifacts_metadata: fixture_file_upload(File.join('spec/fixtures/pages.zip.meta'))
- )
- end
+ let(:ci_build) do
+ create(
+ :ci_build,
+ project: project,
+ pipeline: pipeline,
+ ref: 'HEAD',
+ legacy_artifacts_file: fixture_file_upload(File.join('spec/fixtures/pages.zip')),
+ legacy_artifacts_metadata: fixture_file_upload(File.join('spec/fixtures/pages.zip.meta'))
+ )
+ end
- before do
- result = Projects::UpdatePagesService.new(project, ci_build).execute
- expect(result[:status]).to eq(:success)
- expect(project).to be_pages_deployed
- end
+ before do
+ result = Projects::UpdatePagesService.new(project, ci_build).execute
+ expect(result[:status]).to eq(:success)
+ expect(project).to be_pages_deployed
+ end
- it 'removes the pages' do
- visit project_pages_path(project)
+ it 'removes the pages' do
+ visit project_pages_path(project)
- expect(page).to have_link('Remove pages')
+ expect(page).to have_link('Remove pages')
- click_link 'Remove pages'
+ click_link 'Remove pages'
- expect(project.pages_deployed?).to be_falsey
- end
+ expect(project.pages_deployed?).to be_falsey
end
end
end
diff --git a/spec/features/projects/wiki/markdown_preview_spec.rb b/spec/features/projects/wiki/markdown_preview_spec.rb
index 49244c53a91..49058d1372a 100644
--- a/spec/features/projects/wiki/markdown_preview_spec.rb
+++ b/spec/features/projects/wiki/markdown_preview_spec.rb
@@ -170,7 +170,7 @@ describe 'Projects > Wiki > User previews markdown changes', :js do
fill_in :wiki_content, with: "1. one\n - sublist\n"
click_on "Preview"
- # the above generates two seperate lists (not embedded) in CommonMark
+ # the above generates two separate lists (not embedded) in CommonMark
expect(page).to have_content("sublist")
expect(page).not_to have_xpath("//ol//li//ul")
end
diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb
index fe8000e419b..47e2548c3d6 100644
--- a/spec/finders/issues_finder_spec.rb
+++ b/spec/finders/issues_finder_spec.rb
@@ -490,6 +490,32 @@ describe IssuesFinder do
end
end
+ context 'filtering by confidential' do
+ set(:confidential_issue) { create(:issue, project: project1, confidential: true) }
+
+ context 'no filtering' do
+ it 'returns all issues' do
+ expect(issues).to contain_exactly(issue1, issue2, issue3, issue4, confidential_issue)
+ end
+ end
+
+ context 'user filters confidential issues' do
+ let(:params) { { confidential: true } }
+
+ it 'returns only confdential issues' do
+ expect(issues).to contain_exactly(confidential_issue)
+ end
+ end
+
+ context 'user filters only public issues' do
+ let(:params) { { confidential: false } }
+
+ it 'returns only confdential issues' do
+ expect(issues).to contain_exactly(issue1, issue2, issue3, issue4)
+ end
+ end
+ end
+
context 'when the user is unauthorized' do
let(:search_user) { nil }
@@ -556,7 +582,7 @@ describe IssuesFinder do
it 'returns the number of rows for the default state' do
finder = described_class.new(user)
- expect(finder.row_count).to eq(4)
+ expect(finder.row_count).to eq(5)
end
it 'returns the number of rows for a given state' do
diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js
index c02e37950f8..0bb43c94f6a 100644
--- a/spec/javascripts/lib/utils/common_utils_spec.js
+++ b/spec/javascripts/lib/utils/common_utils_spec.js
@@ -409,13 +409,6 @@ describe('common_utils', () => {
});
});
- describe('convertPermissionToBoolean', () => {
- it('should convert a boolean in a string to a boolean', () => {
- expect(commonUtils.convertPermissionToBoolean('true')).toEqual(true);
- expect(commonUtils.convertPermissionToBoolean('false')).toEqual(false);
- });
- });
-
describe('backOff', () => {
beforeEach(() => {
// shortcut our timeouts otherwise these tests will take a long time to finish
diff --git a/spec/javascripts/lib/utils/poll_spec.js b/spec/javascripts/lib/utils/poll_spec.js
index d0da659c3d7..138041a349f 100644
--- a/spec/javascripts/lib/utils/poll_spec.js
+++ b/spec/javascripts/lib/utils/poll_spec.js
@@ -153,6 +153,36 @@ describe('Poll', () => {
});
});
+ describe('enable', () => {
+ it('should enable polling upon a response', done => {
+ jasmine.clock().install();
+
+ const Polling = new Poll({
+ resource: service,
+ method: 'fetch',
+ data: { page: 1 },
+ successCallback: () => {},
+ });
+
+ Polling.enable({
+ data: { page: 4 },
+ response: { status: 200, headers: { 'poll-interval': 1 } },
+ });
+
+ jasmine.clock().tick(1);
+ jasmine.clock().uninstall();
+
+ waitForAllCallsToFinish(service, 1, () => {
+ Polling.stop();
+
+ expect(service.fetch.calls.count()).toEqual(1);
+ expect(service.fetch).toHaveBeenCalledWith({ page: 4 });
+ expect(Polling.options.data).toEqual({ page: 4 });
+ done();
+ });
+ });
+ });
+
describe('restart', () => {
it('should restart polling when its called', done => {
mockServiceCall(service, { status: 200, headers: { 'poll-interval': 1 } });
@@ -171,6 +201,7 @@ describe('Poll', () => {
});
spyOn(Polling, 'stop').and.callThrough();
+ spyOn(Polling, 'enable').and.callThrough();
spyOn(Polling, 'restart').and.callThrough();
Polling.makeRequest();
@@ -181,6 +212,7 @@ describe('Poll', () => {
expect(service.fetch.calls.count()).toEqual(2);
expect(service.fetch).toHaveBeenCalledWith({ page: 4 });
expect(Polling.stop).toHaveBeenCalled();
+ expect(Polling.enable).toHaveBeenCalled();
expect(Polling.restart).toHaveBeenCalled();
expect(Polling.options.data).toEqual({ page: 4 });
done();
diff --git a/spec/lib/api/helpers/pagination_spec.rb b/spec/lib/api/helpers/pagination_spec.rb
index 2890aa4ae38..6e215ea1561 100644
--- a/spec/lib/api/helpers/pagination_spec.rb
+++ b/spec/lib/api/helpers/pagination_spec.rb
@@ -2,6 +2,8 @@ require 'spec_helper'
describe API::Helpers::Pagination do
let(:resource) { Project.all }
+ let(:incoming_api_projects_url) { "#{Gitlab.config.gitlab.url}:8080/api/v4/projects" }
+ let(:canonical_api_projects_url) { "#{Gitlab.config.gitlab.url}/api/v4/projects" }
subject do
Class.new.include(described_class).new
@@ -9,13 +11,19 @@ describe API::Helpers::Pagination do
describe '#paginate (keyset pagination)' do
let(:value) { spy('return value') }
+ let(:base_query) do
+ {
+ pagination: 'keyset',
+ foo: 'bar',
+ bar: 'baz'
+ }
+ end
+ let(:query) { base_query }
before do
- allow(value).to receive(:to_query).and_return(value)
-
allow(subject).to receive(:header).and_return(value)
- allow(subject).to receive(:params).and_return(value)
- allow(subject).to receive(:request).and_return(value)
+ allow(subject).to receive(:params).and_return(query)
+ allow(subject).to receive(:request).and_return(double(url: "#{incoming_api_projects_url}?#{query.to_query}"))
end
context 'when resource can be paginated' do
@@ -28,10 +36,7 @@ describe API::Helpers::Pagination do
end
describe 'first page' do
- before do
- allow(subject).to receive(:params)
- .and_return({ pagination: 'keyset', per_page: 2 })
- end
+ let(:query) { base_query.merge(per_page: 2) }
it 'returns appropriate amount of resources' do
expect(subject.paginate(resource).count).to eq 2
@@ -43,7 +48,7 @@ describe API::Helpers::Pagination do
it 'adds appropriate headers' do
expect_header('X-Per-Page', '2')
- expect_header('X-Next-Page', "#{value}?ks_prev_id=#{projects[1].id}&pagination=keyset&per_page=2")
+ expect_header('X-Next-Page', "#{canonical_api_projects_url}?#{query.merge(ks_prev_id: projects[1].id).to_query}")
expect_header('Link', anything) do |_key, val|
expect(val).to include('rel="next"')
@@ -54,10 +59,7 @@ describe API::Helpers::Pagination do
end
describe 'second page' do
- before do
- allow(subject).to receive(:params)
- .and_return({ pagination: 'keyset', per_page: 2, ks_prev_id: projects[1].id })
- end
+ let(:query) { base_query.merge(per_page: 2, ks_prev_id: projects[1].id) }
it 'returns appropriate amount of resources' do
expect(subject.paginate(resource).count).to eq 1
@@ -69,7 +71,7 @@ describe API::Helpers::Pagination do
it 'adds appropriate headers' do
expect_header('X-Per-Page', '2')
- expect_header('X-Next-Page', "#{value}?ks_prev_id=#{projects[2].id}&pagination=keyset&per_page=2")
+ expect_header('X-Next-Page', "#{canonical_api_projects_url}?#{query.merge(ks_prev_id: projects[2].id).to_query}")
expect_header('Link', anything) do |_key, val|
expect(val).to include('rel="next"')
@@ -80,10 +82,7 @@ describe API::Helpers::Pagination do
end
describe 'third page' do
- before do
- allow(subject).to receive(:params)
- .and_return({ pagination: 'keyset', per_page: 2, ks_prev_id: projects[2].id })
- end
+ let(:query) { base_query.merge(per_page: 2, ks_prev_id: projects[2].id) }
it 'returns appropriate amount of resources' do
expect(subject.paginate(resource).count).to eq 0
@@ -91,6 +90,7 @@ describe API::Helpers::Pagination do
it 'adds appropriate headers' do
expect_header('X-Per-Page', '2')
+ expect_no_header('X-Next-Page')
expect(subject).not_to receive(:header).with('Link')
subject.paginate(resource)
@@ -99,10 +99,7 @@ describe API::Helpers::Pagination do
context 'if order' do
context 'is not present' do
- before do
- allow(subject).to receive(:params)
- .and_return({ pagination: 'keyset', per_page: 2 })
- end
+ let(:query) { base_query.merge(per_page: 2) }
it 'is not present it adds default order(:id) desc' do
resource.order_values = []
@@ -144,9 +141,7 @@ describe API::Helpers::Pagination do
# (key is the id)
end
- it 'it also orders by primary key' do
- allow(subject).to receive(:params)
- .and_return({ pagination: 'keyset', per_page: 2 })
+ it 'also orders by primary key' do
paginated_relation = subject.paginate(resource)
expect(paginated_relation.order_values).to be_present
@@ -157,46 +152,45 @@ describe API::Helpers::Pagination do
expect(paginated_relation.order_values.second.expr.name).to eq :id
end
- it 'it returns the right records (first page)' do
- allow(subject).to receive(:params)
- .and_return({ pagination: 'keyset', per_page: 2 })
+ it 'returns the right records (first page)' do
result = subject.paginate(resource)
expect(result.first).to eq(projects[1])
expect(result.second).to eq(projects[3])
end
- it 'it returns the right records (second page)' do
- allow(subject).to receive(:params)
- .and_return({ pagination: 'keyset', ks_prev_id: projects[3].id, ks_prev_name: projects[3].name, per_page: 2 })
- result = subject.paginate(resource)
+ describe 'second page' do
+ let(:query) { base_query.merge(ks_prev_id: projects[3].id, ks_prev_name: projects[3].name, per_page: 2) }
- expect(result.first).to eq(projects[2])
- expect(result.second).to eq(projects[6])
- end
+ it 'returns the right records (second page)' do
+ result = subject.paginate(resource)
- it 'it returns the right records (third page), note increased per_page' do
- allow(subject).to receive(:params)
- .and_return({ pagination: 'keyset', ks_prev_id: projects[6].id, ks_prev_name: projects[6].name, per_page: 5 })
- result = subject.paginate(resource)
+ expect(result.first).to eq(projects[2])
+ expect(result.second).to eq(projects[6])
+ end
- expect(result.size).to eq(3)
- expect(result.first).to eq(projects[0])
- expect(result.second).to eq(projects[4])
- expect(result.last).to eq(projects[5])
+ it 'returns the right link to the next page' do
+ expect_header('X-Per-Page', '2')
+ expect_header('X-Next-Page', "#{canonical_api_projects_url}?#{query.merge(ks_prev_id: projects[6].id, ks_prev_name: projects[6].name).to_query}")
+ expect_header('Link', anything) do |_key, val|
+ expect(val).to include('rel="next"')
+ end
+
+ subject.paginate(resource)
+ end
end
- it 'it returns the right link to the next page' do
- allow(subject).to receive(:params)
- .and_return({ pagination: 'keyset', ks_prev_id: projects[3].id, ks_prev_name: projects[3].name, per_page: 2 })
+ describe 'third page' do
+ let(:query) { base_query.merge(ks_prev_id: projects[6].id, ks_prev_name: projects[6].name, per_page: 5) }
- expect_header('X-Per-Page', '2')
- expect_header('X-Next-Page', "#{value}?ks_prev_id=#{projects[6].id}&ks_prev_name=#{projects[6].name}&pagination=keyset&per_page=2")
- expect_header('Link', anything) do |_key, val|
- expect(val).to include('rel="next"')
- end
+ it 'returns the right records (third page), note increased per_page' do
+ result = subject.paginate(resource)
- subject.paginate(resource)
+ expect(result.size).to eq(3)
+ expect(result.first).to eq(projects[0])
+ expect(result.second).to eq(projects[4])
+ expect(result.last).to eq(projects[5])
+ end
end
end
end
@@ -205,25 +199,13 @@ describe API::Helpers::Pagination do
describe '#paginate (default offset-based pagination)' do
let(:value) { spy('return value') }
+ let(:base_query) { { foo: 'bar', bar: 'baz' } }
+ let(:query) { base_query }
before do
- allow(value).to receive(:to_query).and_return(value)
-
allow(subject).to receive(:header).and_return(value)
- allow(subject).to receive(:params).and_return(value)
- allow(subject).to receive(:request).and_return(value)
- end
-
- describe 'required instance methods' do
- let(:return_spy) { spy }
-
- it 'requires some instance methods' do
- expect_message(:header)
- expect_message(:params)
- expect_message(:request)
-
- subject.paginate(resource)
- end
+ allow(subject).to receive(:params).and_return(query)
+ allow(subject).to receive(:request).and_return(double(url: "#{incoming_api_projects_url}?#{query.to_query}"))
end
context 'when resource can be paginated' do
@@ -232,11 +214,6 @@ describe API::Helpers::Pagination do
end
describe 'first page' do
- before do
- allow(subject).to receive(:params)
- .and_return({ page: 1, per_page: 2 })
- end
-
shared_examples 'response with pagination headers' do
it 'adds appropriate headers' do
expect_header('X-Total', '3')
@@ -247,9 +224,9 @@ describe API::Helpers::Pagination do
expect_header('X-Prev-Page', '')
expect_header('Link', anything) do |_key, val|
- expect(val).to include('rel="first"')
- expect(val).to include('rel="last"')
- expect(val).to include('rel="next"')
+ expect(val).to include(%Q(<#{canonical_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="first"))
+ expect(val).to include(%Q(<#{canonical_api_projects_url}?#{query.merge(page: 2).to_query}>; rel="last"))
+ expect(val).to include(%Q(<#{canonical_api_projects_url}?#{query.merge(page: 2).to_query}>; rel="next"))
expect(val).not_to include('rel="prev"')
end
@@ -267,6 +244,8 @@ describe API::Helpers::Pagination do
end
end
+ let(:query) { base_query.merge(page: 1, per_page: 2) }
+
context 'when the api_kaminari_count_with_limit feature flag is unset' do
it_behaves_like 'paginated response'
it_behaves_like 'response with pagination headers'
@@ -311,9 +290,9 @@ describe API::Helpers::Pagination do
expect_header('X-Prev-Page', '')
expect_header('Link', anything) do |_key, val|
- expect(val).to include('rel="first"')
+ expect(val).to include(%Q(<#{canonical_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="first"))
+ expect(val).to include(%Q(<#{canonical_api_projects_url}?#{query.merge(page: 2).to_query}>; rel="next"))
expect(val).not_to include('rel="last"')
- expect(val).to include('rel="next"')
expect(val).not_to include('rel="prev"')
end
@@ -324,10 +303,7 @@ describe API::Helpers::Pagination do
end
describe 'second page' do
- before do
- allow(subject).to receive(:params)
- .and_return({ page: 2, per_page: 2 })
- end
+ let(:query) { base_query.merge(page: 2, per_page: 2) }
it 'returns appropriate amount of resources' do
expect(subject.paginate(resource).count).to eq 1
@@ -342,9 +318,9 @@ describe API::Helpers::Pagination do
expect_header('X-Prev-Page', '1')
expect_header('Link', anything) do |_key, val|
- expect(val).to include('rel="first"')
- expect(val).to include('rel="last"')
- expect(val).to include('rel="prev"')
+ expect(val).to include(%Q(<#{canonical_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="first"))
+ expect(val).to include(%Q(<#{canonical_api_projects_url}?#{query.merge(page: 2).to_query}>; rel="last"))
+ expect(val).to include(%Q(<#{canonical_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="prev"))
expect(val).not_to include('rel="next"')
end
@@ -376,10 +352,7 @@ describe API::Helpers::Pagination do
context 'when resource empty' do
describe 'first page' do
- before do
- allow(subject).to receive(:params)
- .and_return({ page: 1, per_page: 2 })
- end
+ let(:query) { base_query.merge(page: 1, per_page: 2) }
it 'returns appropriate amount of resources' do
expect(subject.paginate(resource).count).to eq 0
@@ -394,8 +367,8 @@ describe API::Helpers::Pagination do
expect_header('X-Prev-Page', '')
expect_header('Link', anything) do |_key, val|
- expect(val).to include('rel="first"')
- expect(val).to include('rel="last"')
+ expect(val).to include(%Q(<#{canonical_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="first"))
+ expect(val).to include(%Q(<#{canonical_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="last"))
expect(val).not_to include('rel="prev"')
expect(val).not_to include('rel="next"')
expect(val).not_to include('page=0')
diff --git a/spec/lib/gitlab/project_template_spec.rb b/spec/lib/gitlab/project_template_spec.rb
index 1cc2bde50e9..2cd6f35b93b 100644
--- a/spec/lib/gitlab/project_template_spec.rb
+++ b/spec/lib/gitlab/project_template_spec.rb
@@ -11,7 +11,12 @@ describe Gitlab::ProjectTemplate do
described_class.new('jekyll', 'Pages/Jekyll', 'Everything you need to get started using a Jekyll Pages site.', 'https://gitlab.com/pages/jekyll'),
described_class.new('plainhtml', 'Pages/Plain HTML', 'Everything you need to get started using a plain HTML Pages site.', 'https://gitlab.com/pages/plain-html'),
described_class.new('gitbook', 'Pages/GitBook', 'Everything you need to get started using a GitBook Pages site.', 'https://gitlab.com/pages/gitbook'),
- described_class.new('hexo', 'Pages/Hexo', 'Everything you need to get started using a plan Hexo Pages site.', 'https://gitlab.com/pages/hexo')
+ described_class.new('hexo', 'Pages/Hexo', 'Everything you need to get started using a Hexo Pages site.', 'https://gitlab.com/pages/hexo'),
+ described_class.new('nfhugo', 'Netlify/Hugo', _('A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfhugo'),
+ described_class.new('nfjekyll', 'Netlify/Jekyll', _('A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfjekyll'),
+ described_class.new('nfplainhtml', 'Netlify/Plain HTML', _('A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfplain-html'),
+ described_class.new('nfgitbook', 'Netlify/GitBook', _('A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfgitbook'),
+ described_class.new('nfhexo', 'Netlify/Hexo', _('A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfhexo')
]
expect(described_class.all).to be_an(Array)
diff --git a/spec/lib/gitlab/serializer/pagination_spec.rb b/spec/lib/gitlab/serializer/pagination_spec.rb
index 1bc6536439e..c54be78f050 100644
--- a/spec/lib/gitlab/serializer/pagination_spec.rb
+++ b/spec/lib/gitlab/serializer/pagination_spec.rb
@@ -1,16 +1,12 @@
require 'spec_helper'
describe Gitlab::Serializer::Pagination do
- let(:request) { spy('request') }
+ let(:request) { double(url: "#{Gitlab.config.gitlab.url}:8080/api/v4/projects?#{query.to_query}", query_parameters: query) }
let(:response) { spy('response') }
let(:headers) { spy('headers') }
before do
- allow(request).to receive(:query_parameters)
- .and_return(params)
-
- allow(response).to receive(:headers)
- .and_return(headers)
+ allow(response).to receive(:headers).and_return(headers)
end
let(:pagination) { described_class.new(request, response) }
@@ -19,7 +15,7 @@ describe Gitlab::Serializer::Pagination do
subject { pagination.paginate(resource) }
let(:resource) { User.all }
- let(:params) { { page: 1, per_page: 2 } }
+ let(:query) { { page: 1, per_page: 2 } }
context 'when a multiple resources are present in relation' do
before do
diff --git a/spec/lib/gitlab/tracing_spec.rb b/spec/lib/gitlab/tracing_spec.rb
index 2cddc895026..566b5050e47 100644
--- a/spec/lib/gitlab/tracing_spec.rb
+++ b/spec/lib/gitlab/tracing_spec.rb
@@ -44,12 +44,15 @@ describe Gitlab::Tracing do
describe '.tracing_url' do
where(:tracing_url_enabled?, :tracing_url_template, :correlation_id, :process_name, :tracing_url) do
- false | "https://localhost" | "123" | "web" | nil
- true | "https://localhost" | "123" | "web" | "https://localhost"
- true | "https://localhost?service=%{service}" | "123" | "web" | "https://localhost?service=web"
- true | "https://localhost?c=%{correlation_id}" | "123" | "web" | "https://localhost?c=123"
- true | "https://localhost?c=%{correlation_id}&s=%{service}" | "123" | "web" | "https://localhost?c=123&s=web"
- true | "https://localhost?c=%{correlation_id}" | nil | "web" | "https://localhost?c="
+ false | "https://localhost" | "123" | "web" | nil
+ true | "https://localhost" | "123" | "web" | "https://localhost"
+ true | "https://localhost?service={{ service }}" | "123" | "web" | "https://localhost?service=web"
+ true | "https://localhost?c={{ correlation_id }}" | "123" | "web" | "https://localhost?c=123"
+ true | "https://localhost?c={{ correlation_id }}&s={{ service }}" | "123" | "web" | "https://localhost?c=123&s=web"
+ true | "https://localhost?c={{ correlation_id }}" | nil | "web" | "https://localhost?c="
+ true | "https://localhost?c={{ correlation_id }}&s=%22{{ service }}%22" | "123" | "web" | "https://localhost?c=123&s=%22web%22"
+ true | "https://localhost?c={{correlation_id}}&s={{service}}" | "123" | "web" | "https://localhost?c=123&s=web"
+ true | "https://localhost?c={{correlation_id }}&s={{ service}}" | "123" | "web" | "https://localhost?c=123&s=web"
end
with_them do
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 47865e4d08f..17540443688 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -23,6 +23,7 @@ describe Ci::Build do
it { is_expected.to validate_presence_of(:ref) }
it { is_expected.to respond_to(:has_trace?) }
it { is_expected.to respond_to(:trace) }
+ it { is_expected.to delegate_method(:merge_request?).to(:pipeline) }
it { is_expected.to be_a(ArtifactMigratable) }
diff --git a/spec/models/clusters/applications/runner_spec.rb b/spec/models/clusters/applications/runner_spec.rb
index 5e5b25cbf8a..6972fc03415 100644
--- a/spec/models/clusters/applications/runner_spec.rb
+++ b/spec/models/clusters/applications/runner_spec.rb
@@ -22,7 +22,7 @@ describe Clusters::Applications::Runner do
it 'should be initialized with 4 arguments' do
expect(subject.name).to eq('runner')
expect(subject.chart).to eq('runner/gitlab-runner')
- expect(subject.version).to eq('0.1.45')
+ expect(subject.version).to eq('0.2.0')
expect(subject).to be_rbac
expect(subject.repository).to eq('https://charts.gitlab.io')
expect(subject.files).to eq(gitlab_runner.files)
@@ -40,7 +40,7 @@ describe Clusters::Applications::Runner do
let(:gitlab_runner) { create(:clusters_applications_runner, :errored, runner: ci_runner, version: '0.1.13') }
it 'should be initialized with the locked version' do
- expect(subject.version).to eq('0.1.45')
+ expect(subject.version).to eq('0.2.0')
end
end
end
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index 5d18e085a6f..6101df2e099 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -765,6 +765,15 @@ describe Issue do
end
end
+ describe '.confidential_only' do
+ it 'only returns confidential_only issues' do
+ create(:issue)
+ confidential_issue = create(:issue, confidential: true)
+
+ expect(described_class.confidential_only).to eq([confidential_issue])
+ end
+ end
+
it_behaves_like 'throttled touch' do
subject { create(:issue, updated_at: 1.hour.ago) }
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 1f9088c2e6b..bcbe687f4a2 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -428,6 +428,30 @@ describe Project do
end
end
+ describe '#ci_pipelines' do
+ let(:project) { create(:project) }
+
+ before do
+ create(:ci_pipeline, project: project, ref: 'master', source: :web)
+ create(:ci_pipeline, project: project, ref: 'master', source: :external)
+ end
+
+ it 'has ci pipelines' do
+ expect(project.ci_pipelines.size).to eq(2)
+ end
+
+ context 'when builds are disabled' do
+ before do
+ project.project_feature.update_attribute(:builds_access_level, ProjectFeature::DISABLED)
+ end
+
+ it 'should return .external pipelines' do
+ expect(project.ci_pipelines).to all(have_attributes(source: 'external'))
+ expect(project.ci_pipelines.size).to eq(1)
+ end
+ end
+ end
+
describe 'project token' do
it 'sets an random token if none provided' do
project = FactoryBot.create(:project, runners_token: '')
@@ -458,6 +482,7 @@ describe Project do
it { is_expected.to delegate_method(:name).to(:owner).with_prefix(true).with_arguments(allow_nil: true) }
it { is_expected.to delegate_method(:group_clusters_enabled?).to(:group).with_arguments(allow_nil: true) }
it { is_expected.to delegate_method(:root_ancestor).to(:namespace).with_arguments(allow_nil: true) }
+ it { is_expected.to delegate_method(:last_pipeline).to(:commit).with_arguments(allow_nil: true) }
end
describe '#to_reference_with_postfix' do
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 1edd8e69b8f..85b157a9435 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -976,43 +976,43 @@ describe User do
end
end
- describe '.filter' do
+ describe '.filter_items' do
let(:user) { double }
it 'filters by active users by default' do
expect(described_class).to receive(:active).and_return([user])
- expect(described_class.filter(nil)).to include user
+ expect(described_class.filter_items(nil)).to include user
end
it 'filters by admins' do
expect(described_class).to receive(:admins).and_return([user])
- expect(described_class.filter('admins')).to include user
+ expect(described_class.filter_items('admins')).to include user
end
it 'filters by blocked' do
expect(described_class).to receive(:blocked).and_return([user])
- expect(described_class.filter('blocked')).to include user
+ expect(described_class.filter_items('blocked')).to include user
end
it 'filters by two_factor_disabled' do
expect(described_class).to receive(:without_two_factor).and_return([user])
- expect(described_class.filter('two_factor_disabled')).to include user
+ expect(described_class.filter_items('two_factor_disabled')).to include user
end
it 'filters by two_factor_enabled' do
expect(described_class).to receive(:with_two_factor).and_return([user])
- expect(described_class.filter('two_factor_enabled')).to include user
+ expect(described_class.filter_items('two_factor_enabled')).to include user
end
it 'filters by wop' do
expect(described_class).to receive(:without_projects).and_return([user])
- expect(described_class.filter('wop')).to include user
+ expect(described_class.filter_items('wop')).to include user
end
end
diff --git a/spec/presenters/ci/build_runner_presenter_spec.rb b/spec/presenters/ci/build_runner_presenter_spec.rb
index 170e0ac5717..f50bcf54b46 100644
--- a/spec/presenters/ci/build_runner_presenter_spec.rb
+++ b/spec/presenters/ci/build_runner_presenter_spec.rb
@@ -98,4 +98,72 @@ describe Ci::BuildRunnerPresenter do
end
end
end
+
+ describe '#ref_type' do
+ subject { presenter.ref_type }
+
+ let(:build) { create(:ci_build, tag: tag) }
+ let(:tag) { true }
+
+ it 'returns the correct ref type' do
+ is_expected.to eq('tag')
+ end
+
+ context 'when tag is false' do
+ let(:tag) { false }
+
+ it 'returns the correct ref type' do
+ is_expected.to eq('branch')
+ end
+ end
+ end
+
+ describe '#git_depth' do
+ subject { presenter.git_depth }
+
+ let(:build) { create(:ci_build) }
+
+ it 'returns the correct git depth' do
+ is_expected.to eq(0)
+ end
+
+ context 'when GIT_DEPTH variable is specified' do
+ before do
+ create(:ci_pipeline_variable, key: 'GIT_DEPTH', value: 1, pipeline: build.pipeline)
+ end
+
+ it 'returns the correct git depth' do
+ is_expected.to eq(1)
+ end
+ end
+ end
+
+ describe '#refspecs' do
+ subject { presenter.refspecs }
+
+ let(:build) { create(:ci_build) }
+
+ it 'returns the correct refspecs' do
+ is_expected.to contain_exactly('+refs/tags/*:refs/tags/*',
+ '+refs/heads/*:refs/remotes/origin/*')
+ end
+
+ context 'when GIT_DEPTH variable is specified' do
+ before do
+ create(:ci_pipeline_variable, key: 'GIT_DEPTH', value: 1, pipeline: build.pipeline)
+ end
+
+ it 'returns the correct refspecs' do
+ is_expected.to contain_exactly("+refs/heads/#{build.ref}:refs/remotes/origin/#{build.ref}")
+ end
+
+ context 'when ref is tag' do
+ let(:build) { create(:ci_build, :tag) }
+
+ it 'returns the correct refspecs' do
+ is_expected.to contain_exactly("+refs/tags/#{build.ref}:refs/tags/#{build.ref}")
+ end
+ end
+ end
+ end
end
diff --git a/spec/requests/api/issuable_bulk_update_spec.rb b/spec/requests/api/issuable_bulk_update_spec.rb
new file mode 100644
index 00000000000..6463f3f5d35
--- /dev/null
+++ b/spec/requests/api/issuable_bulk_update_spec.rb
@@ -0,0 +1,154 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe API::IssuableBulkUpdate do
+ set(:project) { create(:project) }
+ set(:user) { project.creator }
+
+ shared_examples "PUT /projects/:id/:issuable/bulk_update" do |issuable|
+ def bulk_update(issuable, issuables, params, update_user = user)
+ put api("/projects/#{project.id}/#{issuable.pluralize}/bulk_update", update_user),
+ params: { issuable_ids: Array(issuables).map(&:id) }.merge(params)
+ end
+
+ context 'with not enough permissions' do
+ it 'returns 403 for guest users' do
+ guest = create(:user)
+ project.add_guest(guest)
+
+ bulk_update(issuable, issuables, { state_event: 'close' }, guest)
+
+ expect(response).to have_gitlab_http_status(403)
+ end
+ end
+
+ context 'when modifying the state' do
+ it "closes #{issuable}" do
+ bulk_update(issuable, issuables, { state_event: 'close' })
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['message']).to eq("#{issuables.count} #{issuable.pluralize(issuables.count)} updated")
+ expect(project.public_send(issuable.pluralize).opened).to be_empty
+ expect(project.public_send(issuable.pluralize).closed).not_to be_empty
+ end
+
+ it "opens #{issuable}" do
+ closed_issuables = create_list("closed_#{issuable}".to_sym, 2)
+
+ bulk_update(issuable, closed_issuables, { state_event: 'reopen' })
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(project.public_send(issuable.pluralize).closed).to be_empty
+ end
+ end
+
+ context 'when modifying the milestone' do
+ let(:milestone) { create(:milestone, project: project) }
+
+ it "adds a milestone #{issuable}" do
+ bulk_update(issuable, issuables, { milestone_id: milestone.id })
+
+ expect(response).to have_gitlab_http_status(200)
+ issuables.each do |issuable|
+ expect(issuable.reload.milestone).to eq(milestone)
+ end
+ end
+
+ it 'removes a milestone' do
+ issuables.first.milestone = milestone
+ milestone_issuable = issuables.first
+
+ bulk_update(issuable, [milestone_issuable], { milestone_id: 0 })
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(milestone_issuable.reload.milestone).to eq(nil)
+ end
+ end
+
+ context 'when modifying the subscription state' do
+ it "subscribes to #{issuable}" do
+ bulk_update(issuable, issuables, { subscription_event: 'subscribe' })
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(issuables).to all(be_subscribed(user, project))
+ end
+
+ it 'unsubscribes from issues' do
+ issuables.each do |issuable|
+ issuable.subscriptions.create(user: user, project: project, subscribed: true)
+ end
+
+ bulk_update(issuable, issuables, { subscription_event: 'unsubscribe' })
+
+ expect(response).to have_gitlab_http_status(200)
+ issuables.each do |issuable|
+ expect(issuable).not_to be_subscribed(user, project)
+ end
+ end
+ end
+
+ context 'when modifying the assignee' do
+ it 'adds assignee to issues' do
+ params = issuable == 'issue' ? { assignee_ids: [user.id] } : { assignee_id: user.id }
+
+ bulk_update(issuable, issuables, params)
+
+ expect(response).to have_gitlab_http_status(200)
+ issuables.each do |issuable|
+ expect(issuable.reload.assignees).to eq([user])
+ end
+ end
+
+ it 'removes assignee' do
+ assigned_issuable = issuables.first
+
+ if issuable == 'issue'
+ params = { assignee_ids: 0 }
+ assigned_issuable.assignees << user
+ else
+ params = { assignee_id: 0 }
+ assigned_issuable.update_attribute(:assignee, user)
+ end
+
+ bulk_update(issuable, [assigned_issuable], params)
+ expect(assigned_issuable.reload.assignees).to eq([])
+ end
+ end
+
+ context 'when modifying labels' do
+ let(:bug) { create(:label, project: project) }
+ let(:regression) { create(:label, project: project) }
+ let(:feature) { create(:label, project: project) }
+
+ it 'adds new labels' do
+ bulk_update(issuable, issuables, { add_label_ids: [bug.id, regression.id, feature.id] })
+
+ issuables.each do |issusable|
+ expect(issusable.reload.label_ids).to contain_exactly(bug.id, regression.id, feature.id)
+ end
+ end
+
+ it 'removes labels' do
+ labled_issuable = issuables.first
+ labled_issuable.labels << bug
+ labled_issuable.labels << regression
+ labled_issuable.labels << feature
+
+ bulk_update(issuable, [labled_issuable], { remove_label_ids: [bug.id, regression.id] })
+
+ expect(labled_issuable.reload.label_ids).to contain_exactly(feature.id)
+ end
+ end
+ end
+
+ it_behaves_like 'PUT /projects/:id/:issuable/bulk_update', 'issue' do
+ let(:issuables) { create_list(:issue, 2, project: project) }
+ end
+
+ it_behaves_like 'PUT /projects/:id/:issuable/bulk_update', 'merge_request' do
+ let(:merge_request_1) { create(:merge_request, source_project: project) }
+ let(:merge_request_2) { create(:merge_request, :simple, source_project: project) }
+ let(:issuables) { [merge_request_1, merge_request_2] }
+ end
+end
diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb
index d10ee6cc320..1a4be2bd30f 100644
--- a/spec/requests/api/issues_spec.rb
+++ b/spec/requests/api/issues_spec.rb
@@ -183,6 +183,18 @@ describe API::Issues do
expect_paginated_array_response([issue.id, confidential_issue.id, closed_issue.id])
end
+ it 'returns only confidential issues' do
+ get api('/issues', user), params: { confidential: true, scope: 'all' }
+
+ expect_paginated_array_response(confidential_issue.id)
+ end
+
+ it 'returns only public issues' do
+ get api('/issues', user), params: { confidential: false }
+
+ expect_paginated_array_response([issue.id, closed_issue.id])
+ end
+
it 'returns issues reacted by the authenticated user' do
issue2 = create(:issue, project: project, author: user, assignees: [user])
create(:award_emoji, awardable: issue2, user: user2, name: 'star')
@@ -557,6 +569,18 @@ describe API::Issues do
expect_paginated_array_response([group_confidential_issue.id, group_issue.id])
end
+ it 'returns only confidential issues' do
+ get api(base_url, user), params: { confidential: true }
+
+ expect_paginated_array_response(group_confidential_issue.id)
+ end
+
+ it 'returns only public issues' do
+ get api(base_url, user), params: { confidential: false }
+
+ expect_paginated_array_response([group_closed_issue.id, group_issue.id])
+ end
+
it 'returns an array of labeled group issues' do
get api(base_url, user), params: { labels: group_label.title }
@@ -782,6 +806,18 @@ describe API::Issues do
expect_paginated_array_response([issue.id, confidential_issue.id, closed_issue.id])
end
+ it 'returns only confidential issues' do
+ get api("#{base_url}/issues", author), params: { confidential: true }
+
+ expect_paginated_array_response(confidential_issue.id)
+ end
+
+ it 'returns only public issues' do
+ get api("#{base_url}/issues", author), params: { confidential: false }
+
+ expect_paginated_array_response([issue.id, closed_issue.id])
+ end
+
it 'returns project confidential issues for assignee' do
get api("#{base_url}/issues", assignee)
diff --git a/spec/requests/api/project_templates_spec.rb b/spec/requests/api/project_templates_spec.rb
index ab5d4de7ff7..80e5033dab4 100644
--- a/spec/requests/api/project_templates_spec.rb
+++ b/spec/requests/api/project_templates_spec.rb
@@ -92,6 +92,22 @@ describe API::ProjectTemplates do
expect(json_response['name']).to eq('Actionscript')
end
+ it 'returns C++ gitignore' do
+ get api("/projects/#{public_project.id}/templates/gitignores/C++")
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to match_response_schema('public_api/v4/template')
+ expect(json_response['name']).to eq('C++')
+ end
+
+ it 'returns C++ gitignore for URL-encoded names' do
+ get api("/projects/#{public_project.id}/templates/gitignores/C%2B%2B")
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to match_response_schema('public_api/v4/template')
+ expect(json_response['name']).to eq('C++')
+ end
+
it 'returns a specific gitlab_ci_yml' do
get api("/projects/#{public_project.id}/templates/gitlab_ci_ymls/Android")
@@ -125,6 +141,18 @@ describe API::ProjectTemplates do
expect(response).to have_gitlab_http_status(200)
expect(response).to match_response_schema('public_api/v4/license')
end
+
+ shared_examples 'path traversal attempt' do |template_type|
+ it 'rejects invalid filenames' do
+ get api("/projects/#{public_project.id}/templates/#{template_type}/%2e%2e%2fPython%2ea")
+
+ expect(response).to have_gitlab_http_status(500)
+ end
+ end
+
+ TemplateFinder::VENDORED_TEMPLATES.each do |template_type, _|
+ it_behaves_like 'path traversal attempt', template_type
+ end
end
describe 'GET /projects/:id/templates/licenses/:key' do
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index cfa7a1a31a3..494808e399f 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -1995,6 +1995,11 @@ describe API::Projects do
let(:project) do
create(:project, :repository, creator: user, namespace: user.namespace)
end
+
+ let(:project2) do
+ create(:project, :repository, creator: user, namespace: user.namespace)
+ end
+
let(:group) { create(:group) }
let(:group2) do
group = create(:group, name: 'group2_name')
@@ -2010,6 +2015,7 @@ describe API::Projects do
before do
project.add_reporter(user2)
+ project2.add_reporter(user2)
end
context 'when authenticated' do
@@ -2124,6 +2130,48 @@ describe API::Projects do
expect(response).to have_gitlab_http_status(201)
expect(json_response['namespace']['name']).to eq(group.name)
end
+
+ it 'accepts a path for the target project' do
+ post api("/projects/#{project.id}/fork", user2), params: { path: 'foobar' }
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['name']).to eq(project.name)
+ expect(json_response['path']).to eq('foobar')
+ expect(json_response['owner']['id']).to eq(user2.id)
+ expect(json_response['namespace']['id']).to eq(user2.namespace.id)
+ expect(json_response['forked_from_project']['id']).to eq(project.id)
+ expect(json_response['import_status']).to eq('scheduled')
+ expect(json_response).to include("import_error")
+ end
+
+ it 'fails to fork if path is already taken' do
+ post api("/projects/#{project.id}/fork", user2), params: { path: 'foobar' }
+ post api("/projects/#{project2.id}/fork", user2), params: { path: 'foobar' }
+
+ expect(response).to have_gitlab_http_status(409)
+ expect(json_response['message']['path']).to eq(['has already been taken'])
+ end
+
+ it 'accepts a name for the target project' do
+ post api("/projects/#{project.id}/fork", user2), params: { name: 'My Random Project' }
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['name']).to eq('My Random Project')
+ expect(json_response['path']).to eq(project.path)
+ expect(json_response['owner']['id']).to eq(user2.id)
+ expect(json_response['namespace']['id']).to eq(user2.namespace.id)
+ expect(json_response['forked_from_project']['id']).to eq(project.id)
+ expect(json_response['import_status']).to eq('scheduled')
+ expect(json_response).to include("import_error")
+ end
+
+ it 'fails to fork if name is already taken' do
+ post api("/projects/#{project.id}/fork", user2), params: { name: 'My Random Project' }
+ post api("/projects/#{project2.id}/fork", user2), params: { name: 'My Random Project' }
+
+ expect(response).to have_gitlab_http_status(409)
+ expect(json_response['message']['name']).to eq(['has already been taken'])
+ end
end
context 'when unauthenticated' do
diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb
index d7ddd97e8c8..e6c235ca26e 100644
--- a/spec/requests/api/runner_spec.rb
+++ b/spec/requests/api/runner_spec.rb
@@ -417,7 +417,9 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
'ref' => job.ref,
'sha' => job.sha,
'before_sha' => job.before_sha,
- 'ref_type' => 'branch' }
+ 'ref_type' => 'branch',
+ 'refspecs' => %w[+refs/heads/*:refs/remotes/origin/* +refs/tags/*:refs/tags/*],
+ 'depth' => 0 }
end
let(:expected_steps) do
@@ -489,6 +491,29 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
expect(response).to have_gitlab_http_status(201)
expect(json_response['git_info']['ref_type']).to eq('tag')
end
+
+ context 'when GIT_DEPTH is specified' do
+ before do
+ create(:ci_pipeline_variable, key: 'GIT_DEPTH', value: 1, pipeline: pipeline)
+ end
+
+ it 'specifies refspecs' do
+ request_job
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['git_info']['refspecs']).to include("+refs/tags/#{job.ref}:refs/tags/#{job.ref}")
+ end
+ end
+
+ context 'when GIT_DEPTH is not specified' do
+ it 'specifies refspecs' do
+ request_job
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['git_info']['refspecs'])
+ .to contain_exactly('+refs/tags/*:refs/tags/*', '+refs/heads/*:refs/remotes/origin/*')
+ end
+ end
end
context 'when job is made for branch' do
@@ -498,6 +523,55 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
expect(response).to have_gitlab_http_status(201)
expect(json_response['git_info']['ref_type']).to eq('branch')
end
+
+ context 'when GIT_DEPTH is specified' do
+ before do
+ create(:ci_pipeline_variable, key: 'GIT_DEPTH', value: 1, pipeline: pipeline)
+ end
+
+ it 'specifies refspecs' do
+ request_job
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['git_info']['refspecs']).to include("+refs/heads/#{job.ref}:refs/remotes/origin/#{job.ref}")
+ end
+ end
+
+ context 'when GIT_DEPTH is not specified' do
+ it 'specifies refspecs' do
+ request_job
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['git_info']['refspecs'])
+ .to contain_exactly('+refs/tags/*:refs/tags/*', '+refs/heads/*:refs/remotes/origin/*')
+ end
+ end
+ end
+
+ context 'when job is made for merge request' do
+ let(:pipeline) { create(:ci_pipeline_without_jobs, source: :merge_request, project: project, ref: 'feature', merge_request: merge_request) }
+ let!(:job) { create(:ci_build, pipeline: pipeline, name: 'spinach', ref: 'feature', stage: 'test', stage_idx: 0) }
+ let(:merge_request) { create(:merge_request) }
+
+ it 'sets branch as ref_type' do
+ request_job
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['git_info']['ref_type']).to eq('branch')
+ end
+
+ context 'when GIT_DEPTH is specified' do
+ before do
+ create(:ci_pipeline_variable, key: 'GIT_DEPTH', value: 1, pipeline: pipeline)
+ end
+
+ it 'returns the overwritten git depth for merge request refspecs' do
+ request_job
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['git_info']['depth']).to eq(1)
+ end
+ end
end
it 'updates runner info' do
@@ -526,6 +600,15 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
expect(runner.reload.ip_address).to eq('123.222.123.222')
end
+ it "handles multiple X-Forwarded-For addresses" do
+ post api('/jobs/request'),
+ params: { token: runner.token },
+ headers: { 'User-Agent' => user_agent, 'X-Forwarded-For' => '123.222.123.222, 127.0.0.1' }
+
+ expect(response).to have_gitlab_http_status 201
+ expect(runner.reload.ip_address).to eq('123.222.123.222')
+ end
+
context 'when concurrently updating a job' do
before do
expect_any_instance_of(Ci::Build).to receive(:run!)
diff --git a/spec/serializers/environment_serializer_spec.rb b/spec/serializers/environment_serializer_spec.rb
index 3541bd5f12e..375a28a8c72 100644
--- a/spec/serializers/environment_serializer_spec.rb
+++ b/spec/serializers/environment_serializer_spec.rb
@@ -124,10 +124,10 @@ describe EnvironmentSerializer do
end
context 'when used with pagination' do
- let(:request) { spy('request') }
+ let(:request) { double(url: "#{Gitlab.config.gitlab.url}:8080/api/v4/projects?#{query.to_query}", query_parameters: query) }
let(:response) { spy('response') }
let(:resource) { Environment.all }
- let(:pagination) { { page: 1, per_page: 2 } }
+ let(:query) { { page: 1, per_page: 2 } }
let(:serializer) do
described_class
@@ -135,11 +135,6 @@ describe EnvironmentSerializer do
.with_pagination(request, response)
end
- before do
- allow(request).to receive(:query_parameters)
- .and_return(pagination)
- end
-
subject { serializer.represent(resource) }
it 'creates a paginated serializer' do
diff --git a/spec/serializers/pipeline_serializer_spec.rb b/spec/serializers/pipeline_serializer_spec.rb
index 79aa32b29bb..2bdcb2a45f6 100644
--- a/spec/serializers/pipeline_serializer_spec.rb
+++ b/spec/serializers/pipeline_serializer_spec.rb
@@ -38,15 +38,9 @@ describe PipelineSerializer do
end
context 'when used with pagination' do
- let(:request) { spy('request') }
+ let(:request) { double(url: "#{Gitlab.config.gitlab.url}:8080/api/v4/projects?#{query.to_query}", query_parameters: query) }
let(:response) { spy('response') }
- let(:pagination) { {} }
-
- before do
- allow(request)
- .to receive(:query_parameters)
- .and_return(pagination)
- end
+ let(:query) { {} }
let(:serializer) do
described_class.new(current_user: user)
@@ -60,7 +54,7 @@ describe PipelineSerializer do
context 'when resource is not paginatable' do
context 'when a single pipeline object is being serialized' do
let(:resource) { create(:ci_empty_pipeline) }
- let(:pagination) { { page: 1, per_page: 1 } }
+ let(:query) { { page: 1, per_page: 1 } }
it 'raises error' do
expect { subject }.to raise_error(
@@ -71,7 +65,7 @@ describe PipelineSerializer do
context 'when resource is paginatable relation' do
let(:resource) { Ci::Pipeline.all }
- let(:pagination) { { page: 1, per_page: 2 } }
+ let(:query) { { page: 1, per_page: 2 } }
context 'when a single pipeline object is present in relation' do
before do
diff --git a/spec/support/helpers/test_env.rb b/spec/support/helpers/test_env.rb
index f485eb7b0eb..06bcf4f8013 100644
--- a/spec/support/helpers/test_env.rb
+++ b/spec/support/helpers/test_env.rb
@@ -63,7 +63,8 @@ module TestEnv
'after-create-delete-modify-move' => 'ba3faa7',
'with-codeowners' => '219560e',
'submodule_inside_folder' => 'b491b92',
- 'png-lfs' => 'fe42f41'
+ 'png-lfs' => 'fe42f41',
+ 'sha-starting-with-large-number' => '8426165'
}.freeze
# gitlab-test-fork is a fork of gitlab-fork, but we don't necessarily
diff --git a/spec/views/projects/commits/_commit.html.haml_spec.rb b/spec/views/projects/commits/_commit.html.haml_spec.rb
index 00547e433c4..6bf1b5fd2d0 100644
--- a/spec/views/projects/commits/_commit.html.haml_spec.rb
+++ b/spec/views/projects/commits/_commit.html.haml_spec.rb
@@ -1,15 +1,15 @@
require 'spec_helper'
describe 'projects/commits/_commit.html.haml' do
+ let(:project) { create(:project, :repository) }
+ let(:commit) { project.repository.commit(ref) }
+
before do
allow(view).to receive(:current_application_settings).and_return(Gitlab::CurrentSettings.current_application_settings)
end
- context 'with a singed commit' do
- let(:project) { create(:project, :repository) }
- let(:repository) { project.repository }
+ context 'with a signed commit' do
let(:ref) { GpgHelpers::SIGNED_COMMIT_SHA }
- let(:commit) { repository.commit(ref) }
it 'does not display a loading spinner for GPG status' do
render partial: 'projects/commits/commit', locals: {
@@ -23,4 +23,55 @@ describe 'projects/commits/_commit.html.haml' do
end
end
end
+
+ context 'with ci status' do
+ let(:ref) { 'master' }
+ let(:user) { create(:user) }
+
+ before do
+ allow(view).to receive(:current_user).and_return(user)
+
+ project.add_developer(user)
+
+ create(
+ :ci_empty_pipeline,
+ ref: 'master',
+ sha: commit.id,
+ status: 'success',
+ project: project
+ )
+ end
+
+ context 'when pipelines are disabled' do
+ before do
+ allow(project).to receive(:builds_enabled?).and_return(false)
+ end
+
+ it 'does not display a ci status icon' do
+ render partial: 'projects/commits/commit', locals: {
+ project: project,
+ ref: ref,
+ commit: commit
+ }
+
+ expect(rendered).not_to have_css('.ci-status-link')
+ end
+ end
+
+ context 'when pipelines are enabled' do
+ before do
+ allow(project).to receive(:builds_enabled?).and_return(true)
+ end
+
+ it 'does display a ci status icon when pipelines are enabled' do
+ render partial: 'projects/commits/commit', locals: {
+ project: project,
+ ref: ref,
+ commit: commit
+ }
+
+ expect(rendered).to have_css('.ci-status-link')
+ end
+ end
+ end
end
diff --git a/vendor/project_templates/nfgitbook.tar.gz b/vendor/project_templates/nfgitbook.tar.gz
new file mode 100644
index 00000000000..e10873e1a9e
--- /dev/null
+++ b/vendor/project_templates/nfgitbook.tar.gz
Binary files differ
diff --git a/vendor/project_templates/nfhexo.tar.gz b/vendor/project_templates/nfhexo.tar.gz
new file mode 100644
index 00000000000..25632f241cc
--- /dev/null
+++ b/vendor/project_templates/nfhexo.tar.gz
Binary files differ
diff --git a/vendor/project_templates/nfhugo.tar.gz b/vendor/project_templates/nfhugo.tar.gz
new file mode 100644
index 00000000000..14e6289b841
--- /dev/null
+++ b/vendor/project_templates/nfhugo.tar.gz
Binary files differ
diff --git a/vendor/project_templates/nfjekyll.tar.gz b/vendor/project_templates/nfjekyll.tar.gz
new file mode 100644
index 00000000000..3b93f8661b5
--- /dev/null
+++ b/vendor/project_templates/nfjekyll.tar.gz
Binary files differ
diff --git a/vendor/project_templates/nfplainhtml.tar.gz b/vendor/project_templates/nfplainhtml.tar.gz
new file mode 100644
index 00000000000..cdf5ea9fe12
--- /dev/null
+++ b/vendor/project_templates/nfplainhtml.tar.gz
Binary files differ