summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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/notes/components/discussion_resolve_with_issue_button.vue34
-rw-r--r--app/assets/javascripts/notes/components/noteable_discussion.vue19
-rw-r--r--app/assets/javascripts/pipelines/mixins/pipelines.js3
-rw-r--r--app/assets/javascripts/projects/project_new.js24
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/deployment.vue14
-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/projects/graphs_controller.rb9
-rw-r--r--app/controllers/projects/merge_requests/creations_controller.rb6
-rw-r--r--app/controllers/projects/pages_controller.rb3
-rw-r--r--app/graphql/types/issue_type.rb10
-rw-r--r--app/graphql/types/merge_request_type.rb4
-rw-r--r--app/graphql/types/project_type.rb10
-rw-r--r--app/graphql/types/query_type.rb5
-rw-r--r--app/models/ci/build.rb13
-rw-r--r--app/models/clusters/applications/runner.rb2
-rw-r--r--app/models/project.rb9
-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/projects/fork_service.rb12
-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/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/_sort_dropdown.html.haml2
-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/55925-if-there-is-only-one-changed-page-in-review-app-go-directly-there.yml5
-rw-r--r--changelogs/unreleased/57582-dropdown-icon-misalignment-on-issues-list-on-mobile-screen.yml5
-rw-r--r--changelogs/unreleased/57712-project-import-error-user-expected-got-hash.yml5
-rw-r--r--changelogs/unreleased/57785-create-project-template-for-netlify.yml5
-rw-r--r--changelogs/unreleased/57794-project-template-for-net.yml5
-rw-r--r--changelogs/unreleased/58020-fix-merge-api-endpoint-param.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/expose-merge-ref-to-runner.yml5
-rw-r--r--changelogs/unreleased/fix-badges-logs.yml5
-rw-r--r--changelogs/unreleased/refactor-56367-extract-resolve-with-issue-button-component.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--changelogs/unreleased/zj-load-languages-from-database.yml5
-rw-r--r--config/initializers/graphql.rb4
-rw-r--r--doc/administration/high_availability/redis.md2
-rw-r--r--doc/administration/high_availability/redis_source.md4
-rw-r--r--doc/administration/monitoring/prometheus/gitlab_metrics.md2
-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/api_graphql_styleguide.md24
-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.md2
-rw-r--r--doc/install/requirements.md2
-rw-r--r--doc/integration/README.md3
-rw-r--r--doc/topics/autodevops/index.md1
-rw-r--r--doc/university/high-availability/aws/README.md4
-rw-r--r--doc/user/permissions.md3
-rw-r--r--doc/user/project/clusters/index.md2
-rw-r--r--doc/user/project/clusters/runbooks/index.md2
-rw-r--r--doc/user/project/clusters/serverless/index.md2
-rw-r--r--doc/user/project/pipelines/settings.md4
-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/merge_requests.rb4
-rw-r--r--lib/api/projects.rb8
-rw-r--r--lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml2
-rw-r--r--lib/gitlab/graphql/authorize.rb15
-rw-r--r--lib/gitlab/graphql/authorize/authorize_resource.rb17
-rw-r--r--lib/gitlab/graphql/authorize/instrumentation.rb10
-rw-r--r--lib/gitlab/import_export/import_export.yml1
-rw-r--r--lib/gitlab/project_template.rb8
-rw-r--r--locale/gitlab.pot18
-rw-r--r--spec/controllers/admin/users_controller_spec.rb14
-rw-r--r--spec/controllers/projects/graphs_controller_spec.rb16
-rw-r--r--spec/controllers/projects/pages_controller_spec.rb12
-rw-r--r--spec/features/issues/filtered_search/visual_tokens_spec.rb21
-rw-r--r--spec/features/projects/pages_spec.rb103
-rw-r--r--spec/graphql/features/authorization_spec.rb106
-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/javascripts/notes/components/discussion_resolve_with_issue_button_spec.js31
-rw-r--r--spec/javascripts/notes/components/noteable_discussion_spec.js39
-rw-r--r--spec/javascripts/vue_mr_widget/components/deployment_spec.js81
-rw-r--r--spec/lib/api/helpers/pagination_spec.rb155
-rw-r--r--spec/lib/gitlab/graphql/authorize/authorize_resource_spec.rb18
-rw-r--r--spec/lib/gitlab/graphql/authorize_spec.rb20
-rw-r--r--spec/lib/gitlab/import_export/project_tree_saver_spec.rb8
-rw-r--r--spec/lib/gitlab/project_template_spec.rb8
-rw-r--r--spec/lib/gitlab/serializer/pagination_spec.rb10
-rw-r--r--spec/models/ci/build_spec.rb1
-rw-r--r--spec/models/clusters/applications/runner_spec.rb4
-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/merge_requests_spec.rb15
-rw-r--r--spec/requests/api/projects_spec.rb74
-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/matchers/graphql_matchers.rb4
-rw-r--r--spec/support/shared_examples/services/boards/issues_move_service.rb16
-rw-r--r--spec/views/projects/commits/_commit.html.haml_spec.rb59
-rw-r--r--vendor/project_templates/dotnetcore.tar.gzbin0 -> 44710 bytes
-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
136 files changed, 1329 insertions, 482 deletions
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/notes/components/discussion_resolve_with_issue_button.vue b/app/assets/javascripts/notes/components/discussion_resolve_with_issue_button.vue
new file mode 100644
index 00000000000..e413398696a
--- /dev/null
+++ b/app/assets/javascripts/notes/components/discussion_resolve_with_issue_button.vue
@@ -0,0 +1,34 @@
+<script>
+import Icon from '~/vue_shared/components/icon.vue';
+import { GlTooltipDirective, GlButton } from '@gitlab/ui';
+
+export default {
+ name: 'ResolveWithIssueButton',
+ components: {
+ Icon,
+ GlButton,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ props: {
+ url: {
+ type: String,
+ required: true,
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="btn-group" role="group">
+ <gl-button
+ v-gl-tooltip
+ :href="url"
+ :title="s__('MergeRequests|Resolve this discussion in a new issue')"
+ class="new-issue-for-discussion discussion-create-issue-btn"
+ >
+ <icon name="issue-new" />
+ </gl-button>
+ </div>
+</template>
diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue
index 2d6fd8b116f..3894dc8c677 100644
--- a/app/assets/javascripts/notes/components/noteable_discussion.vue
+++ b/app/assets/javascripts/notes/components/noteable_discussion.vue
@@ -25,6 +25,7 @@ import noteable from '../mixins/noteable';
import resolvable from '../mixins/resolvable';
import discussionNavigation from '../mixins/discussion_navigation';
import ReplyPlaceholder from './discussion_reply_placeholder.vue';
+import ResolveWithIssueButton from './discussion_resolve_with_issue_button.vue';
import jumpToNextDiscussionButton from './discussion_jump_to_next_button.vue';
import eventHub from '../event_hub';
@@ -44,6 +45,7 @@ export default {
ReplyPlaceholder,
placeholderNote,
placeholderSystemNote,
+ ResolveWithIssueButton,
systemNote,
TimelineEntryItem,
},
@@ -234,6 +236,9 @@ export default {
url: this.discussion.discussion_path,
};
},
+ resolveWithIssuePath() {
+ return !this.discussionResolved && this.discussion.resolve_with_issue_path;
+ },
},
watch: {
isReplying() {
@@ -487,16 +492,10 @@ Please check your network connection and try again.`;
class="btn-group discussion-actions ml-sm-2"
role="group"
>
- <div v-if="!discussionResolved" class="btn-group" role="group">
- <a
- v-gl-tooltip
- :href="discussion.resolve_with_issue_path"
- :title="s__('MergeRequests|Resolve this discussion in a new issue')"
- class="new-issue-for-discussion btn btn-default discussion-create-issue-btn"
- >
- <icon name="issue-new" />
- </a>
- </div>
+ <resolve-with-issue-button
+ v-if="resolveWithIssuePath"
+ :url="resolveWithIssuePath"
+ />
<jump-to-next-discussion-button
v-if="shouldShowJumpToNextDiscussion"
@onClick="jumpToNextDiscussion"
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..6fb25622a05 100644
--- a/app/assets/javascripts/projects/project_new.js
+++ b/app/assets/javascripts/projects/project_new.js
@@ -125,6 +125,10 @@ const bindEvents = () => {
text: 'Spring',
icon: '.template-option .icon-spring',
},
+ dotnetcore: {
+ text: '.NET Core',
+ icon: '.template-option .icon-dotnet',
+ },
hugo: {
text: 'Pages/Hugo',
icon: '.template-option .icon-hugo',
@@ -145,6 +149,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/vue_merge_request_widget/components/deployment.vue b/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue
index 2f2a37347af..da0a9483f8e 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue
@@ -54,6 +54,12 @@ export default {
deployTimeago() {
return this.timeFormated(this.deployment.deployed_at);
},
+ deploymentExternalUrl() {
+ if (this.deployment.changes && this.deployment.changes.length === 1) {
+ return this.deployment.changes[0].external_url;
+ }
+ return this.deployment.external_url;
+ },
hasExternalUrls() {
return !!(this.deployment.external_url && this.deployment.external_url_formatted);
},
@@ -78,7 +84,7 @@ export default {
: '';
},
shouldRenderDropdown() {
- return this.deployment.changes && this.deployment.changes.length > 0;
+ return this.deployment.changes && this.deployment.changes.length > 1;
},
showMemoryUsage() {
return this.hasMetrics && this.showMetrics;
@@ -154,12 +160,12 @@ export default {
v-if="shouldRenderDropdown"
class="js-mr-wigdet-deployment-dropdown inline"
:items="deployment.changes"
- :main-action-link="deployment.external_url"
+ :main-action-link="deploymentExternalUrl"
filter-key="path"
>
<template slot="mainAction" slot-scope="slotProps">
<review-app-link
- :link="deployment.external_url"
+ :link="deploymentExternalUrl"
:css-class="`deploy-link js-deploy-url inline ${slotProps.className}`"
/>
</template>
@@ -183,7 +189,7 @@ export default {
</filtered-search-dropdown>
<review-app-link
v-else
- :link="deployment.external_url"
+ :link="deploymentExternalUrl"
css-class="js-deploy-url js-deploy-url-feature-flag deploy-link btn btn-default btn-sm inlin"
/>
</template>
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/projects/graphs_controller.rb b/app/controllers/projects/graphs_controller.rb
index 925b6ed9bfd..c80fce513f6 100644
--- a/app/controllers/projects/graphs_controller.rb
+++ b/app/controllers/projects/graphs_controller.rb
@@ -45,7 +45,14 @@ class Projects::GraphsController < Projects::ApplicationController
end
def get_languages
- @languages = @project.repository.languages
+ @languages =
+ if @project.repository_languages.present?
+ @project.repository_languages.map do |lang|
+ { value: lang.share, label: lang.name, color: lang.color, highlight: lang.color }
+ end
+ else
+ @project.repository.languages
+ end
end
def fetch_graph
diff --git a/app/controllers/projects/merge_requests/creations_controller.rb b/app/controllers/projects/merge_requests/creations_controller.rb
index 5639402a1e9..32cefe54613 100644
--- a/app/controllers/projects/merge_requests/creations_controller.rb
+++ b/app/controllers/projects/merge_requests/creations_controller.rb
@@ -89,7 +89,11 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap
def build_merge_request
params[:merge_request] ||= ActionController::Parameters.new(source_project: @project)
- @merge_request = ::MergeRequests::BuildService.new(project, current_user, merge_request_params.merge(diff_options: diff_options)).execute
+
+ # Gitaly N+1 issue: https://gitlab.com/gitlab-org/gitlab-ce/issues/58096
+ Gitlab::GitalyClient.allow_n_plus_1_calls do
+ @merge_request = ::MergeRequests::BuildService.new(project, current_user, merge_request_params.merge(diff_options: diff_options)).execute
+ end
end
def define_new_vars
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/graphql/types/issue_type.rb b/app/graphql/types/issue_type.rb
index 87f6b1f8278..5ad3ea52930 100644
--- a/app/graphql/types/issue_type.rb
+++ b/app/graphql/types/issue_type.rb
@@ -15,18 +15,16 @@ module Types
field :author, Types::UserType,
null: false,
- resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(User, obj.author_id).find } do
- authorize :read_user
- end
+ resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(User, obj.author_id).find },
+ authorize: :read_user
field :assignees, Types::UserType.connection_type, null: true
field :labels, Types::LabelType.connection_type, null: true
field :milestone, Types::MilestoneType,
null: true,
- resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Milestone, obj.milestone_id).find } do
- authorize :read_milestone
- end
+ resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Milestone, obj.milestone_id).find },
+ authorize: :read_milestone
field :due_date, Types::TimeType, null: true
field :confidential, GraphQL::BOOLEAN_TYPE, null: false
diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb
index 7827b6e3717..1ed27a14e33 100644
--- a/app/graphql/types/merge_request_type.rb
+++ b/app/graphql/types/merge_request_type.rb
@@ -48,9 +48,7 @@ module Types
field :downvotes, GraphQL::INT_TYPE, null: false
field :subscribed, GraphQL::BOOLEAN_TYPE, method: :subscribed?, null: false
- field :head_pipeline, Types::Ci::PipelineType, null: true, method: :actual_head_pipeline do
- authorize :read_pipeline
- end
+ field :head_pipeline, Types::Ci::PipelineType, null: true, method: :actual_head_pipeline, authorize: :read_pipeline
field :pipelines, Types::Ci::PipelineType.connection_type,
resolver: Resolvers::MergeRequestPipelinesResolver
end
diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb
index d25c8c8bd90..3ef0cc5020c 100644
--- a/app/graphql/types/project_type.rb
+++ b/app/graphql/types/project_type.rb
@@ -69,16 +69,14 @@ module Types
field :merge_requests,
Types::MergeRequestType.connection_type,
null: true,
- resolver: Resolvers::MergeRequestsResolver do
- authorize :read_merge_request
- end
+ resolver: Resolvers::MergeRequestsResolver,
+ authorize: :read_merge_request
field :merge_request,
Types::MergeRequestType,
null: true,
- resolver: Resolvers::MergeRequestsResolver.single do
- authorize :read_merge_request
- end
+ resolver: Resolvers::MergeRequestsResolver.single,
+ authorize: :read_merge_request
field :issues,
Types::IssueType.connection_type,
diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb
index 7c41716b82a..954bcc0a5a3 100644
--- a/app/graphql/types/query_type.rb
+++ b/app/graphql/types/query_type.rb
@@ -7,9 +7,8 @@ module Types
field :project, Types::ProjectType,
null: true,
resolver: Resolvers::ProjectResolver,
- description: "Find a project" do
- authorize :read_project
- end
+ description: "Find a project",
+ authorize: :read_project
field :echo, GraphQL::STRING_TYPE, null: false, function: Functions::Echo.new
end
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/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/project.rb b/app/models/project.rb
index c72d3a3b725..58254eb1bc9 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -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)
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/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/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/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/_sort_dropdown.html.haml b/app/views/shared/issuable/_sort_dropdown.html.haml
index b6ea9185b10..967f31c8325 100644
--- a/app/views/shared/issuable/_sort_dropdown.html.haml
+++ b/app/views/shared/issuable/_sort_dropdown.html.haml
@@ -5,7 +5,7 @@
.dropdown.inline.prepend-left-10.issue-sort-dropdown
.btn-group{ role: 'group' }
.btn-group{ role: 'group' }
- %button.dropdown-toggle{ type: 'button', data: { toggle: 'dropdown', display: 'static' }, class: 'btn btn-default' }
+ %button.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown', display: 'static' }, class: 'btn btn-default' }
= sort_title
= icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable.dropdown-menu-sort
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/55925-if-there-is-only-one-changed-page-in-review-app-go-directly-there.yml b/changelogs/unreleased/55925-if-there-is-only-one-changed-page-in-review-app-go-directly-there.yml
new file mode 100644
index 00000000000..ef3d9844acb
--- /dev/null
+++ b/changelogs/unreleased/55925-if-there-is-only-one-changed-page-in-review-app-go-directly-there.yml
@@ -0,0 +1,5 @@
+---
+title: Review App Link to Changed Page if Only One Change Present
+merge_request: 25048
+author:
+type: changed
diff --git a/changelogs/unreleased/57582-dropdown-icon-misalignment-on-issues-list-on-mobile-screen.yml b/changelogs/unreleased/57582-dropdown-icon-misalignment-on-issues-list-on-mobile-screen.yml
new file mode 100644
index 00000000000..5681309cb9e
--- /dev/null
+++ b/changelogs/unreleased/57582-dropdown-icon-misalignment-on-issues-list-on-mobile-screen.yml
@@ -0,0 +1,5 @@
+---
+title: Fix alignment of dropdown icon on issuable on mobile
+merge_request: 25205
+author: Takuya Noguchi
+type: fixed
diff --git a/changelogs/unreleased/57712-project-import-error-user-expected-got-hash.yml b/changelogs/unreleased/57712-project-import-error-user-expected-got-hash.yml
new file mode 100644
index 00000000000..6fb198e1552
--- /dev/null
+++ b/changelogs/unreleased/57712-project-import-error-user-expected-got-hash.yml
@@ -0,0 +1,5 @@
+---
+title: Fix project import error importing releases
+merge_request: 25495
+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/57794-project-template-for-net.yml b/changelogs/unreleased/57794-project-template-for-net.yml
new file mode 100644
index 00000000000..bc05ac10aff
--- /dev/null
+++ b/changelogs/unreleased/57794-project-template-for-net.yml
@@ -0,0 +1,5 @@
+---
+title: Add Project template for .NET Core
+merge_request: 25486
+author:
+type: changed
diff --git a/changelogs/unreleased/58020-fix-merge-api-endpoint-param.yml b/changelogs/unreleased/58020-fix-merge-api-endpoint-param.yml
new file mode 100644
index 00000000000..7cfeb4a0cd7
--- /dev/null
+++ b/changelogs/unreleased/58020-fix-merge-api-endpoint-param.yml
@@ -0,0 +1,5 @@
+---
+title: Respect the should_remove_source_branch parameter to the merge API
+merge_request: 25525
+author:
+type: fixed
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/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/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/refactor-56367-extract-resolve-with-issue-button-component.yml b/changelogs/unreleased/refactor-56367-extract-resolve-with-issue-button-component.yml
new file mode 100644
index 00000000000..082075506c0
--- /dev/null
+++ b/changelogs/unreleased/refactor-56367-extract-resolve-with-issue-button-component.yml
@@ -0,0 +1,5 @@
+---
+title: Extracted ResolveWithIssueButton to its own component
+merge_request: 25093
+author: Martin Hobert
+type: other
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/changelogs/unreleased/zj-load-languages-from-database.yml b/changelogs/unreleased/zj-load-languages-from-database.yml
new file mode 100644
index 00000000000..1688829b42c
--- /dev/null
+++ b/changelogs/unreleased/zj-load-languages-from-database.yml
@@ -0,0 +1,5 @@
+---
+title: Load repository language from the database if detected before
+merge_request: 25518
+author:
+type: performance
diff --git a/config/initializers/graphql.rb b/config/initializers/graphql.rb
new file mode 100644
index 00000000000..1ed93019329
--- /dev/null
+++ b/config/initializers/graphql.rb
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+
+GraphQL::Field.accepts_definitions(authorize: GraphQL::Define.assign_metadata_key(:authorize))
+Types::BaseField.accepts_definition(:authorize)
diff --git a/doc/administration/high_availability/redis.md b/doc/administration/high_availability/redis.md
index 55d85ad86e6..a52bc5c3b02 100644
--- a/doc/administration/high_availability/redis.md
+++ b/doc/administration/high_availability/redis.md
@@ -369,7 +369,7 @@ Now that the Redis servers are all set up, let's configure the Sentinel
servers.
If you are not sure if your Redis servers are working and replicating
-correctly, please read the [Troubleshooting Replication](#troubleshooting-replication)
+correctly, please read the [Troubleshooting Replication](#troubleshooting-redis-replication)
and fix it before proceeding with Sentinel setup.
You must have at least `3` Redis Sentinel servers, and they need to
diff --git a/doc/administration/high_availability/redis_source.md b/doc/administration/high_availability/redis_source.md
index 14e2784c419..be6b547372a 100644
--- a/doc/administration/high_availability/redis_source.md
+++ b/doc/administration/high_availability/redis_source.md
@@ -46,7 +46,7 @@ valuable information for the general setup.
Assuming that the Redis master instance IP is `10.0.0.1`:
-1. [Install Redis](../../install/installation.md#6-redis)
+1. [Install Redis](../../install/installation.md#7-redis).
1. Edit `/etc/redis/redis.conf`:
```conf
@@ -72,7 +72,7 @@ Assuming that the Redis master instance IP is `10.0.0.1`:
Assuming that the Redis slave instance IP is `10.0.0.2`:
-1. [Install Redis](../../install/installation.md#6-redis)
+1. [Install Redis](../../install/installation.md#7-redis).
1. Edit `/etc/redis/redis.conf`:
```conf
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/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/api_graphql_styleguide.md b/doc/development/api_graphql_styleguide.md
index 95722c027ba..501092ff2aa 100644
--- a/doc/development/api_graphql_styleguide.md
+++ b/doc/development/api_graphql_styleguide.md
@@ -12,24 +12,34 @@ add a `HTTP_PRIVATE_TOKEN` header.
### Authorization
Fields can be authorized using the same abilities used in the Rails
-app. This can be done using the `authorize` helper:
+app. This can be done by supplying the `authorize` option:
```ruby
module Types
class QueryType < BaseObject
graphql_name 'Query'
- field :project, Types::ProjectType, null: true, resolver: Resolvers::ProjectResolver do
- authorize :read_project
- end
+ field :project, Types::ProjectType, null: true, resolver: Resolvers::ProjectResolver, authorize: :read_project
end
+end
+```
+
+Fields can be authorized against multiple abilities, in which case all
+ability checks must pass. This requires explicitly passing a block to `field`:
+
+```ruby
+field :project, Types::ProjectType, null: true, resolver: Resolvers::ProjectResolver do
+ authorize [:read_project, :another_ability]
+end
```
The object found by the resolve call is used for authorization.
-This works for authorizing a single record, for authorizing
-collections, we should only load what the currently authenticated user
-is allowed to view. Preferably we use our existing finders for that.
+TIP: **Tip:**
+When authorizing collections, try to load only what the currently
+authenticated user is allowed to view with our existing finders first.
+This minimizes database queries and unnecessary authorization checks of
+the loaded records.
## Types
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 88e287bb44f..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).
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/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/university/high-availability/aws/README.md b/doc/university/high-availability/aws/README.md
index 4095f71e501..1906655dfa5 100644
--- a/doc/university/high-availability/aws/README.md
+++ b/doc/university/high-availability/aws/README.md
@@ -3,7 +3,7 @@ comments: false
---
> **Note**: We **do not** recommend using the AWS Elastic File System (EFS), as it can result
-in [significantly degraded performance](https://gitlab.com/gitlab-org/gitlab-ee/blob/master/doc/administration/high_availability/nfs.md#aws-elastic-file-system).
+in [significantly degraded performance](../../../administration/high_availability/nfs.md#avoid-using-awss-elastic-file-system-efs).
# High Availability on AWS
@@ -182,7 +182,7 @@ Another option is to build a simple NFS server using a vanilla Linux server back
by AWS Elastic Block Storage (EBS).
> **Note:** GitLab does not recommend using AWS Elastic File System (EFS). See
- details in [High Availability NFS documentation](../../../administration/high_availability/nfs.md#aws-elastic-file-system)
+ details in [High Availability NFS documentation](../../../administration/high_availability/nfs.md#avoid-using-awss-elastic-file-system-efs)
***
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 0797eb3b0de..e601d7b2ccc 100644
--- a/doc/user/project/clusters/index.md
+++ b/doc/user/project/clusters/index.md
@@ -92,7 +92,7 @@ To add an existing Kubernetes cluster to your project:
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** (requried) - A valid Kubernetes certificate is needed to authenticate to the EKS cluster. We will use the certificate created by default.
+ - **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:
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/index.md b/doc/user/project/clusters/serverless/index.md
index 139579a88ff..f7e72efa2ba 100644
--- a/doc/user/project/clusters/serverless/index.md
+++ b/doc/user/project/clusters/serverless/index.md
@@ -209,7 +209,7 @@ The sample function can now be triggered from any HTTP client using a simple `PO
```
2. Using a web-based tool (ie. postman, restlet, etc)
- ![function exection](img/function-execution.png)
+ ![function execution](img/function-execution.png)
## Deploying Serverless applications
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/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/merge_requests.rb b/lib/api/merge_requests.rb
index f8d2ba49d2f..03f6684226f 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -369,11 +369,11 @@ module API
merge_request.update(squash: params[:squash]) if params[:squash]
- merge_params = {
+ merge_params = HashWithIndifferentAccess.new(
commit_message: params[:merge_commit_message],
squash_commit_message: params[:squash_commit_message],
should_remove_source_branch: params[:should_remove_source_branch]
- }
+ )
if merge_when_pipeline_succeeds && merge_request.head_pipeline && merge_request.head_pipeline.active?
::MergeRequests::MergeWhenPipelineSucceedsService
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 6a93ef9f3ad..b23fe6cd4e7 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')
@@ -386,7 +388,11 @@ module API
desc 'Get languages in project repository'
get ':id/languages' do
- user_project.repository.languages.map { |language| language.values_at(:label, :value) }.to_h
+ if user_project.repository_languages.present?
+ user_project.repository_languages.map { |l| [l.name, l.share] }.to_h
+ else
+ user_project.repository.languages.map { |language| language.values_at(:label, :value) }.to_h
+ end
end
desc 'Remove a project'
diff --git a/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml b/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
index 7a9a11f1e33..c0d4d4400b3 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
@@ -698,6 +699,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/graphql/authorize.rb b/lib/gitlab/graphql/authorize.rb
index 5e48bf9043d..f62813db82c 100644
--- a/lib/gitlab/graphql/authorize.rb
+++ b/lib/gitlab/graphql/authorize.rb
@@ -10,21 +10,6 @@ module Gitlab
def self.use(schema_definition)
schema_definition.instrument(:field, Instrumentation.new)
end
-
- def required_permissions
- # If the `#authorize` call is used on multiple classes, we add the
- # permissions specified on a subclass, to the ones that were specified
- # on it's superclass.
- @required_permissions ||= if self.respond_to?(:superclass) && superclass.respond_to?(:required_permissions)
- superclass.required_permissions.dup
- else
- []
- end
- end
-
- def authorize(*permissions)
- required_permissions.concat(permissions)
- end
end
end
end
diff --git a/lib/gitlab/graphql/authorize/authorize_resource.rb b/lib/gitlab/graphql/authorize/authorize_resource.rb
index a56c4f6368d..b367a97105c 100644
--- a/lib/gitlab/graphql/authorize/authorize_resource.rb
+++ b/lib/gitlab/graphql/authorize/authorize_resource.rb
@@ -6,8 +6,21 @@ module Gitlab
module AuthorizeResource
extend ActiveSupport::Concern
- included do
- extend Gitlab::Graphql::Authorize
+ class_methods do
+ def required_permissions
+ # If the `#authorize` call is used on multiple classes, we add the
+ # permissions specified on a subclass, to the ones that were specified
+ # on it's superclass.
+ @required_permissions ||= if self.respond_to?(:superclass) && superclass.respond_to?(:required_permissions)
+ superclass.required_permissions.dup
+ else
+ []
+ end
+ end
+
+ def authorize(*permissions)
+ required_permissions.concat(permissions)
+ end
end
def find_object(*args)
diff --git a/lib/gitlab/graphql/authorize/instrumentation.rb b/lib/gitlab/graphql/authorize/instrumentation.rb
index 2a3d790d67b..593da8471dd 100644
--- a/lib/gitlab/graphql/authorize/instrumentation.rb
+++ b/lib/gitlab/graphql/authorize/instrumentation.rb
@@ -6,19 +6,15 @@ module Gitlab
class Instrumentation
# Replace the resolver for the field with one that will only return the
# resolved object if the permissions check is successful.
- #
- # Collections are not supported. Apply permissions checks for those at the
- # database level instead, to avoid loading superfluous data from the DB
def instrument(_type, field)
- field_definition = field.metadata[:type_class]
- return field unless field_definition.respond_to?(:required_permissions)
- return field if field_definition.required_permissions.empty?
+ required_permissions = Array.wrap(field.metadata[:authorize])
+ return field if required_permissions.empty?
old_resolver = field.resolve_proc
new_resolver = -> (obj, args, ctx) do
resolved_obj = old_resolver.call(obj, args, ctx)
- checker = build_checker(ctx[:current_user], field_definition.required_permissions)
+ checker = build_checker(ctx[:current_user], required_permissions)
if resolved_obj.respond_to?(:then)
resolved_obj.then(&checker)
diff --git a/lib/gitlab/import_export/import_export.yml b/lib/gitlab/import_export/import_export.yml
index 7f8c6d56627..fa54fc17d95 100644
--- a/lib/gitlab/import_export/import_export.yml
+++ b/lib/gitlab/import_export/import_export.yml
@@ -28,7 +28,6 @@ project_tree:
- notes:
:author
- releases:
- - :author
- :links
- project_members:
- :user
diff --git a/lib/gitlab/project_template.rb b/lib/gitlab/project_template.rb
index ef656e5b2ce..29c511524a2 100644
--- a/lib/gitlab/project_template.rb
+++ b/lib/gitlab/project_template.rb
@@ -28,11 +28,17 @@ module Gitlab
ProjectTemplate.new('rails', 'Ruby on Rails', _('Includes an MVC structure, Gemfile, Rakefile, along with many others, to help you get started.'), 'https://gitlab.com/gitlab-org/project-templates/rails', 'illustrations/logos/rails.svg'),
ProjectTemplate.new('spring', 'Spring', _('Includes an MVC structure, mvnw and pom.xml to help you get started.'), 'https://gitlab.com/gitlab-org/project-templates/spring', 'illustrations/logos/spring.svg'),
ProjectTemplate.new('express', 'NodeJS Express', _('Includes an MVC structure to help you get started.'), 'https://gitlab.com/gitlab-org/project-templates/express', 'illustrations/logos/express.svg'),
+ ProjectTemplate.new('dotnetcore', '.NET Core', _('A .NET Core console application template, customizable for any .NET Core project'), 'https://gitlab.com/gitlab-org/project-templates/dotnetcore', 'illustrations/logos/dotnet.svg'),
ProjectTemplate.new('hugo', 'Pages/Hugo', _('Everything you need to create a GitLab Pages site using Hugo.'), 'https://gitlab.com/pages/hugo'),
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/locale/gitlab.pot b/locale/gitlab.pot
index 5a8ab9aa996..bd45b01d463 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -285,6 +285,21 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+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 +315,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/projects/graphs_controller_spec.rb b/spec/controllers/projects/graphs_controller_spec.rb
index 73fb7307e11..8decd8f1382 100644
--- a/spec/controllers/projects/graphs_controller_spec.rb
+++ b/spec/controllers/projects/graphs_controller_spec.rb
@@ -24,4 +24,20 @@ describe Projects::GraphsController do
expect(response).to redirect_to action: :charts
end
end
+
+ describe 'charts' do
+ context 'when languages were previously detected' do
+ let!(:repository_language) { create(:repository_language, project: project) }
+
+ it 'sets the languages properly' do
+ get(:charts, params: { namespace_id: project.namespace.path, project_id: project.path, id: 'master' })
+
+ expect(assigns[:languages]).to eq(
+ [value: repository_language.share,
+ label: repository_language.name,
+ color: repository_language.color,
+ highlight: repository_language.color])
+ end
+ end
+ end
end
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/visual_tokens_spec.rb b/spec/features/issues/filtered_search/visual_tokens_spec.rb
index a4c34ce85f0..9fd661d80ae 100644
--- a/spec/features/issues/filtered_search/visual_tokens_spec.rb
+++ b/spec/features/issues/filtered_search/visual_tokens_spec.rb
@@ -59,13 +59,6 @@ describe 'Visual tokens', :js do
expect(page).to have_css('#js-dropdown-author', visible: false)
end
- it 'ends editing mode when scroll container is clicked' do
- find('.scroll-container').click
-
- expect_filtered_search_input_empty
- expect(page).to have_css('#js-dropdown-author', visible: false)
- end
-
describe 'selecting different author from dropdown' do
before do
filter_author_dropdown.find('.filter-dropdown-item .dropdown-light-content', text: "@#{user_rock.username}").click
@@ -109,13 +102,6 @@ describe 'Visual tokens', :js do
expect(page).to have_css('#js-dropdown-assignee', visible: false)
end
- it 'ends editing mode when scroll container is clicked' do
- find('.scroll-container').click
-
- expect_filtered_search_input_empty
- expect(page).to have_css('#js-dropdown-assignee', visible: false)
- end
-
describe 'selecting static option from dropdown' do
before do
find("#js-dropdown-assignee").find('.filter-dropdown-item', text: 'None').click
@@ -167,13 +153,6 @@ describe 'Visual tokens', :js do
expect_filtered_search_input_empty
expect(page).to have_css('#js-dropdown-milestone', visible: false)
end
-
- it 'ends editing mode when scroll container is clicked' do
- find('.scroll-container').click
-
- expect_filtered_search_input_empty
- expect(page).to have_css('#js-dropdown-milestone', visible: false)
- end
end
describe 'editing label token' do
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/graphql/features/authorization_spec.rb b/spec/graphql/features/authorization_spec.rb
new file mode 100644
index 00000000000..a229d29afa6
--- /dev/null
+++ b/spec/graphql/features/authorization_spec.rb
@@ -0,0 +1,106 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Gitlab::Graphql::Authorization' do
+ set(:user) { create(:user) }
+
+ let(:test_object) { double(name: 'My name') }
+ let(:object_type) { object_type_class }
+ let(:query_type) { query_type_class(object_type, test_object) }
+ let(:schema) { schema_class(query_type) }
+
+ let(:execute) do
+ schema.execute(
+ query_string,
+ context: { current_user: user },
+ variables: {}
+ )
+ end
+
+ let(:result) { execute['data'] }
+
+ before do
+ # By default, disallow all permissions.
+ allow(Ability).to receive(:allowed?).and_return(false)
+ end
+
+ describe 'authorizing with a single permission' do
+ let(:query_string) { '{ singlePermission() { name } }' }
+
+ subject { result['singlePermission'] }
+
+ it 'should return the protected field when user has permission' do
+ permit(:foo)
+
+ expect(subject['name']).to eq(test_object.name)
+ end
+
+ it 'should return nil when user is not authorized' do
+ expect(subject).to be_nil
+ end
+ end
+
+ describe 'authorizing with an Array of permissions' do
+ let(:query_string) { '{ permissionCollection() { name } }' }
+
+ subject { result['permissionCollection'] }
+
+ it 'should return the protected field when user has all permissions' do
+ permit(:foo, :bar)
+
+ expect(subject['name']).to eq(test_object.name)
+ end
+
+ it 'should return nil when user only has one of the permissions' do
+ permit(:foo)
+
+ expect(subject).to be_nil
+ end
+
+ it 'should return nil when user only has none of the permissions' do
+ expect(subject).to be_nil
+ end
+ end
+
+ private
+
+ def permit(*permissions)
+ permissions.each do |permission|
+ allow(Ability).to receive(:allowed?).with(user, permission, test_object).and_return(true)
+ end
+ end
+
+ def object_type_class
+ Class.new(Types::BaseObject) do
+ graphql_name 'TestObject'
+
+ field :name, GraphQL::STRING_TYPE, null: true
+ end
+ end
+
+ def query_type_class(type, object)
+ Class.new(Types::BaseObject) do
+ graphql_name 'TestQuery'
+
+ field :single_permission, type,
+ null: true,
+ authorize: :foo,
+ resolve: ->(obj, args, ctx) { object }
+
+ field :permission_collection, type,
+ null: true,
+ resolve: ->(obj, args, ctx) { object } do
+ authorize [:foo, :bar]
+ end
+ end
+ end
+
+ def schema_class(query)
+ Class.new(GraphQL::Schema) do
+ use Gitlab::Graphql::Authorize
+
+ query(query)
+ end
+ end
+end
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/javascripts/notes/components/discussion_resolve_with_issue_button_spec.js b/spec/javascripts/notes/components/discussion_resolve_with_issue_button_spec.js
new file mode 100644
index 00000000000..b2a91c9919a
--- /dev/null
+++ b/spec/javascripts/notes/components/discussion_resolve_with_issue_button_spec.js
@@ -0,0 +1,31 @@
+import { GlButton } from '@gitlab/ui';
+import ResolveWithIssueButton from '~/notes/components/discussion_resolve_with_issue_button.vue';
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import { TEST_HOST } from 'spec/test_constants';
+
+const localVue = createLocalVue();
+
+describe('ResolveWithIssueButton', () => {
+ let wrapper;
+ const url = `${TEST_HOST}/hello-world/`;
+
+ beforeEach(() => {
+ wrapper = shallowMount(ResolveWithIssueButton, {
+ localVue,
+ sync: false,
+ propsData: {
+ url,
+ },
+ });
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('it should have a link with the provided link property as href', () => {
+ const button = wrapper.find(GlButton);
+
+ expect(button.attributes().href).toBe(url);
+ });
+});
diff --git a/spec/javascripts/notes/components/noteable_discussion_spec.js b/spec/javascripts/notes/components/noteable_discussion_spec.js
index 2eae22e095f..2b93fb9fb45 100644
--- a/spec/javascripts/notes/components/noteable_discussion_spec.js
+++ b/spec/javascripts/notes/components/noteable_discussion_spec.js
@@ -2,6 +2,7 @@ import { shallowMount, createLocalVue } from '@vue/test-utils';
import createStore from '~/notes/stores';
import noteableDiscussion from '~/notes/components/noteable_discussion.vue';
import ReplyPlaceholder from '~/notes/components/discussion_reply_placeholder.vue';
+import ResolveWithIssueButton from '~/notes/components/discussion_resolve_with_issue_button.vue';
import '~/behaviors/markdown/render_gfm';
import { noteableDataMock, discussionMock, notesDataMock } from '../mock_data';
import mockDiffFile from '../../diffs/mock_data/diff_file';
@@ -238,4 +239,42 @@ describe('noteable_discussion component', () => {
});
});
});
+
+ describe('for resolved discussion', () => {
+ beforeEach(() => {
+ const discussion = getJSONFixture(discussionWithTwoUnresolvedNotes)[0];
+ wrapper.setProps({ discussion });
+ });
+
+ it('does not display a button to resolve with issue', () => {
+ const button = wrapper.find(ResolveWithIssueButton);
+
+ expect(button.exists()).toBe(false);
+ });
+ });
+
+ describe('for unresolved discussion', () => {
+ beforeEach(done => {
+ const discussion = {
+ ...getJSONFixture(discussionWithTwoUnresolvedNotes)[0],
+ expanded: true,
+ };
+ discussion.notes = discussion.notes.map(note => ({
+ ...note,
+ resolved: false,
+ }));
+
+ wrapper.setProps({ discussion });
+ wrapper.vm
+ .$nextTick()
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('displays a button to resolve with issue', () => {
+ const button = wrapper.find(ResolveWithIssueButton);
+
+ expect(button.exists()).toBe(true);
+ });
+ });
});
diff --git a/spec/javascripts/vue_mr_widget/components/deployment_spec.js b/spec/javascripts/vue_mr_widget/components/deployment_spec.js
index e355416bd27..42bf3b7df09 100644
--- a/spec/javascripts/vue_mr_widget/components/deployment_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/deployment_spec.js
@@ -6,32 +6,36 @@ import mountComponent from '../../helpers/vue_mount_component_helper';
describe('Deployment component', () => {
const Component = Vue.extend(deploymentComponent);
- const deploymentMockData = {
- id: 15,
- name: 'review/diplo',
- url: '/root/review-apps/environments/15',
- stop_url: '/root/review-apps/environments/15/stop',
- metrics_url: '/root/review-apps/environments/15/deployments/1/metrics',
- metrics_monitoring_url: '/root/review-apps/environments/15/metrics',
- external_url: 'http://gitlab.com.',
- external_url_formatted: 'gitlab',
- deployed_at: '2017-03-22T22:44:42.258Z',
- deployed_at_formatted: 'Mar 22, 2017 10:44pm',
- changes: [
- {
- path: 'index.html',
- external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/index.html',
- },
- {
- path: 'imgs/gallery.html',
- external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/imgs/gallery.html',
- },
- {
- path: 'about/',
- external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/about/',
- },
- ],
- };
+ let deploymentMockData;
+
+ beforeEach(() => {
+ deploymentMockData = {
+ id: 15,
+ name: 'review/diplo',
+ url: '/root/review-apps/environments/15',
+ stop_url: '/root/review-apps/environments/15/stop',
+ metrics_url: '/root/review-apps/environments/15/deployments/1/metrics',
+ metrics_monitoring_url: '/root/review-apps/environments/15/metrics',
+ external_url: 'http://gitlab.com.',
+ external_url_formatted: 'gitlab',
+ deployed_at: '2017-03-22T22:44:42.258Z',
+ deployed_at_formatted: 'Mar 22, 2017 10:44pm',
+ changes: [
+ {
+ path: 'index.html',
+ external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/index.html',
+ },
+ {
+ path: 'imgs/gallery.html',
+ external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/imgs/gallery.html',
+ },
+ {
+ path: 'about/',
+ external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/about/',
+ },
+ ],
+ };
+ });
let vm;
@@ -207,6 +211,31 @@ describe('Deployment component', () => {
});
});
+ describe('with a single change', () => {
+ beforeEach(() => {
+ deploymentMockData.changes = deploymentMockData.changes.slice(0, 1);
+
+ vm = mountComponent(Component, {
+ deployment: { ...deploymentMockData },
+ showMetrics: true,
+ });
+ });
+
+ it('renders the link to the review app without dropdown', () => {
+ expect(vm.$el.querySelector('.js-mr-wigdet-deployment-dropdown')).toBeNull();
+ expect(vm.$el.querySelector('.js-deploy-url-feature-flag')).not.toBeNull();
+ });
+
+ it('renders the link to the review app linked to to the first change', () => {
+ const expectedUrl = deploymentMockData.changes[0].external_url;
+ const deployUrl = vm.$el.querySelector('.js-deploy-url-feature-flag');
+
+ expect(vm.$el.querySelector('.js-mr-wigdet-deployment-dropdown')).toBeNull();
+ expect(deployUrl).not.toBeNull();
+ expect(deployUrl.href).toEqual(expectedUrl);
+ });
+ });
+
describe('deployment status', () => {
describe('running', () => {
beforeEach(() => {
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/graphql/authorize/authorize_resource_spec.rb b/spec/lib/gitlab/graphql/authorize/authorize_resource_spec.rb
index 95bf7685ade..13cf52fd795 100644
--- a/spec/lib/gitlab/graphql/authorize/authorize_resource_spec.rb
+++ b/spec/lib/gitlab/graphql/authorize/authorize_resource_spec.rb
@@ -100,4 +100,22 @@ describe Gitlab::Graphql::Authorize::AuthorizeResource do
expect { fake_class.new.find_object }.to raise_error(/Implement #find_object in #{fake_class.name}/)
end
end
+
+ describe '#authorize' do
+ it 'adds permissions from subclasses to those of superclasses when used on classes' do
+ base_class = Class.new do
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+
+ authorize :base_authorization
+ end
+
+ sub_class = Class.new(base_class) do
+ authorize :sub_authorization
+ end
+
+ expect(base_class.required_permissions).to contain_exactly(:base_authorization)
+ expect(sub_class.required_permissions)
+ .to contain_exactly(:base_authorization, :sub_authorization)
+ end
+ end
end
diff --git a/spec/lib/gitlab/graphql/authorize_spec.rb b/spec/lib/gitlab/graphql/authorize_spec.rb
deleted file mode 100644
index 9c17a3b0e4b..00000000000
--- a/spec/lib/gitlab/graphql/authorize_spec.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::Graphql::Authorize do
- describe '#authorize' do
- it 'adds permissions from subclasses to those of superclasses when used on classes' do
- base_class = Class.new do
- extend Gitlab::Graphql::Authorize
-
- authorize :base_authorization
- end
- sub_class = Class.new(base_class) do
- authorize :sub_authorization
- end
-
- expect(base_class.required_permissions).to contain_exactly(:base_authorization)
- expect(sub_class.required_permissions)
- .to contain_exactly(:base_authorization, :sub_authorization)
- end
- end
-end
diff --git a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
index 46fdfba953b..cfc3e0ce926 100644
--- a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
@@ -78,6 +78,14 @@ describe Gitlab::ImportExport::ProjectTreeSaver do
expect(saved_project_json['releases']).not_to be_empty
end
+ it 'has no author on releases' do
+ expect(saved_project_json['releases'].first['author']).to be_nil
+ end
+
+ it 'has the author ID on releases' do
+ expect(saved_project_json['releases'].first['author_id']).not_to be_nil
+ end
+
it 'has issues' do
expect(saved_project_json['issues']).not_to be_empty
end
diff --git a/spec/lib/gitlab/project_template_spec.rb b/spec/lib/gitlab/project_template_spec.rb
index 1cc2bde50e9..0cee100b64e 100644
--- a/spec/lib/gitlab/project_template_spec.rb
+++ b/spec/lib/gitlab/project_template_spec.rb
@@ -7,11 +7,17 @@ describe Gitlab::ProjectTemplate do
described_class.new('rails', 'Ruby on Rails', 'Includes an MVC structure, .gitignore, Gemfile, and more great stuff', 'https://gitlab.com/gitlab-org/project-templates/rails'),
described_class.new('spring', 'Spring', 'Includes an MVC structure, .gitignore, Gemfile, and more great stuff', 'https://gitlab.com/gitlab-org/project-templates/spring'),
described_class.new('express', 'NodeJS Express', 'Includes an MVC structure, .gitignore, Gemfile, and more great stuff', 'https://gitlab.com/gitlab-org/project-templates/express'),
+ described_class.new('dotnetcore', '.NET Core', _('A .NET Core console application template, customizable for any .NET Core project'), 'https://gitlab.com/gitlab-org/project-templates/dotnetcore'),
described_class.new('hugo', 'Pages/Hugo', 'Everything you need to get started using a Hugo Pages site.', 'https://gitlab.com/pages/hugo'),
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/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/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/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index b8426126bc6..b4cd3130dc5 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -984,6 +984,21 @@ describe API::MergeRequests do
expect(squash_commit.message).to eq(merge_request.default_squash_commit_message)
end
end
+
+ describe "the should_remove_source_branch param" do
+ let(:source_repository) { merge_request.source_project.repository }
+ let(:source_branch) { merge_request.source_branch }
+
+ it 'removes the source branch when set' do
+ put(
+ api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/merge", user),
+ params: { should_remove_source_branch: true }
+ )
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(source_repository.branch_exists?(source_branch)).to be_falsy
+ end
+ end
end
describe "PUT /projects/:id/merge_requests/:merge_request_iid" do
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index cfa7a1a31a3..792abdb2972 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -4,6 +4,15 @@ require 'spec_helper'
shared_examples 'languages and percentages JSON response' do
let(:expected_languages) { project.repository.languages.map { |language| language.values_at(:label, :value)}.to_h }
+ before do
+ allow(project.repository).to receive(:languages).and_return(
+ [{ value: 66.69, label: "Ruby", color: "#701516", highlight: "#701516" },
+ { value: 22.98, label: "JavaScript", color: "#f1e05a", highlight: "#f1e05a" },
+ { value: 7.91, label: "HTML", color: "#e34c26", highlight: "#e34c26" },
+ { value: 2.42, label: "CoffeeScript", color: "#244776", highlight: "#244776" }]
+ )
+ end
+
it 'returns expected language values' do
get api("/projects/#{project.id}/languages", user)
@@ -11,6 +20,23 @@ shared_examples 'languages and percentages JSON response' do
expect(json_response).to eq(expected_languages)
expect(json_response.count).to be > 1
end
+
+ context 'when the languages were detected before' do
+ before do
+ Projects::DetectRepositoryLanguagesService.new(project, project.owner).execute
+ end
+
+ it 'returns the detection from the database' do
+ # Allow this to happen once, so the expected languages can be determined
+ expect(project.repository).to receive(:languages).once
+
+ get api("/projects/#{project.id}/languages", user)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response).to eq(expected_languages)
+ expect(json_response.count).to be > 1
+ end
+ end
end
describe API::Projects do
@@ -1995,6 +2021,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 +2041,7 @@ describe API::Projects do
before do
project.add_reporter(user2)
+ project2.add_reporter(user2)
end
context 'when authenticated' do
@@ -2124,6 +2156,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/matchers/graphql_matchers.rb b/spec/support/matchers/graphql_matchers.rb
index 7be84838e00..7894484f590 100644
--- a/spec/support/matchers/graphql_matchers.rb
+++ b/spec/support/matchers/graphql_matchers.rb
@@ -1,8 +1,6 @@
RSpec::Matchers.define :require_graphql_authorizations do |*expected|
match do |field|
- field_definition = field.metadata[:type_class]
- expect(field_definition).to respond_to(:required_permissions)
- expect(field_definition.required_permissions).to contain_exactly(*expected)
+ expect(field.metadata[:authorize]).to eq(*expected)
end
end
diff --git a/spec/support/shared_examples/services/boards/issues_move_service.rb b/spec/support/shared_examples/services/boards/issues_move_service.rb
index ec44b99d10e..9dbd1d8e867 100644
--- a/spec/support/shared_examples/services/boards/issues_move_service.rb
+++ b/spec/support/shared_examples/services/boards/issues_move_service.rb
@@ -39,6 +39,22 @@ shared_examples 'issues move service' do |group|
end
end
+ context 'when moving to backlog' do
+ let(:milestone) { create(:milestone, project: project) }
+ let!(:backlog) { create(:backlog_list, board: board1) }
+
+ let(:issue) { create(:labeled_issue, project: project, labels: [bug, development, testing, regression], milestone: milestone) }
+ let(:params) { { board_id: board1.id, from_list_id: list2.id, to_list_id: backlog.id } }
+
+ it 'keeps labels and milestone' do
+ described_class.new(parent, user, params).execute(issue)
+ issue.reload
+
+ expect(issue.labels).to contain_exactly(bug, regression)
+ expect(issue.milestone).to eq(milestone)
+ end
+ end
+
context 'when moving from closed' do
let(:issue) { create(:labeled_issue, :closed, project: project, labels: [bug]) }
let(:params) { { board_id: board1.id, from_list_id: closed.id, to_list_id: list2.id } }
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/dotnetcore.tar.gz b/vendor/project_templates/dotnetcore.tar.gz
new file mode 100644
index 00000000000..b5dae407f83
--- /dev/null
+++ b/vendor/project_templates/dotnetcore.tar.gz
Binary files differ
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