Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml24
-rw-r--r--CHANGELOG.md15
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock6
-rw-r--r--app/assets/javascripts/notes/components/discussion_filter.vue2
-rw-r--r--app/assets/javascripts/notes/components/note_actions/reply_button.vue2
-rw-r--r--app/assets/javascripts/notes/discussion_filters.js13
-rw-r--r--app/assets/javascripts/notes/index.js7
-rw-r--r--app/assets/stylesheets/framework/blank.scss28
-rw-r--r--app/assets/stylesheets/framework/common.scss6
-rw-r--r--app/assets/stylesheets/framework/icons.scss1
-rw-r--r--app/assets/stylesheets/framework/system_messages.scss2
-rw-r--r--app/assets/stylesheets/framework/variables.scss2
-rw-r--r--app/assets/stylesheets/pages/builds.scss12
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss1
-rw-r--r--app/assets/stylesheets/pages/pipelines.scss7
-rw-r--r--app/assets/stylesheets/pages/profile.scss15
-rw-r--r--app/assets/stylesheets/pages/status.scss1
-rw-r--r--app/controllers/concerns/notes_actions.rb2
-rw-r--r--app/controllers/groups/group_members_controller.rb1
-rw-r--r--app/controllers/omniauth_callbacks_controller.rb4
-rw-r--r--app/controllers/profiles/accounts_controller.rb2
-rw-r--r--app/controllers/profiles/two_factor_auths_controller.rb25
-rw-r--r--app/helpers/auth_helper.rb8
-rw-r--r--app/helpers/projects_helper.rb5
-rw-r--r--app/models/broadcast_message.rb2
-rw-r--r--app/models/ci/build.rb26
-rw-r--r--app/models/ci/pipeline.rb13
-rw-r--r--app/models/ci/stage.rb7
-rw-r--r--app/models/clusters/platforms/kubernetes.rb2
-rw-r--r--app/models/commit_collection.rb35
-rw-r--r--app/models/commit_status.rb21
-rw-r--r--app/models/commit_status_enums.rb3
-rw-r--r--app/models/concerns/cache_markdown_field.rb22
-rw-r--r--app/models/concerns/has_status.rb17
-rw-r--r--app/models/deployment.rb4
-rw-r--r--app/models/project_services/kubernetes_service.rb4
-rw-r--r--app/policies/identity_provider_policy.rb15
-rw-r--r--app/presenters/commit_status_presenter.rb3
-rw-r--r--app/services/ci/prepare_build_service.rb25
-rw-r--r--app/views/admin/dashboard/index.html.haml5
-rw-r--r--app/views/profiles/_email_settings.html.haml16
-rw-r--r--app/views/profiles/accounts/_providers.html.haml21
-rw-r--r--app/views/profiles/accounts/show.html.haml19
-rw-r--r--app/views/profiles/notifications/_email_settings.html.haml6
-rw-r--r--app/views/profiles/notifications/show.html.haml4
-rw-r--r--app/views/profiles/show.html.haml13
-rw-r--r--app/views/search/_results.html.haml2
-rw-r--r--app/views/shared/projects/_project.html.haml2
-rw-r--r--app/workers/all_queues.yml1
-rw-r--r--app/workers/ci/build_prepare_worker.rb16
-rw-r--r--app/workers/cluster_configure_worker.rb2
-rw-r--r--app/workers/cluster_project_configure_worker.rb2
-rw-r--r--changelogs/unreleased/57115-just-in-time-k8s-resource-creation.yml5
-rw-r--r--changelogs/unreleased/58410-change-pixel-size-of-instance-header-footer-message-to-16px.yml5
-rw-r--r--changelogs/unreleased/58883-fix-fetching-comments.yml5
-rw-r--r--changelogs/unreleased/59057-buttons-on-top-from-a-user-profile-page-on-mobile.yml5
-rw-r--r--changelogs/unreleased/59208-fix-error-500-on-every-page-when-active-broadcast-message-present-after-upgrading-to-11-9-0.yml6
-rw-r--r--changelogs/unreleased/avoid_es_loading_project_ci_status.yml5
-rw-r--r--changelogs/unreleased/do-not-force-2fa.yml6
-rw-r--r--changelogs/unreleased/k8s_new_deployment_labels.yml5
-rw-r--r--changelogs/unreleased/security-2826-fix-project-serialization-in-quick-actions.yml5
-rw-r--r--doc/administration/container_registry.md1
-rw-r--r--doc/administration/high_availability/redis.md3
-rw-r--r--doc/administration/job_artifacts.md2
-rw-r--r--doc/administration/monitoring/prometheus/index.md1
-rw-r--r--doc/administration/pages/index.md1
-rw-r--r--doc/administration/repository_storage_types.md5
-rw-r--r--doc/administration/uploads.md2
-rw-r--r--doc/api/group_milestones.md1
-rw-r--r--doc/api/project_snippets.md1
-rw-r--r--doc/api/services.md7
-rw-r--r--doc/ci/chatops/README.md4
-rw-r--r--doc/ci/docker/using_docker_build.md1
-rw-r--r--doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md18
-rw-r--r--doc/development/testing_guide/frontend_testing.md10
-rw-r--r--doc/raketasks/backup_restore.md2
-rw-r--r--doc/topics/autodevops/index.md4
-rw-r--r--doc/user/discussions/index.md5
-rw-r--r--doc/user/group/index.md5
-rw-r--r--doc/user/profile/account/two_factor_authentication.md1
-rw-r--r--doc/user/project/bulk_editing.md1
-rw-r--r--doc/user/project/clusters/index.md25
-rw-r--r--doc/user/project/container_registry.md6
-rw-r--r--doc/user/project/integrations/jira.md2
-rw-r--r--doc/user/project/merge_requests/versions.md1
-rw-r--r--doc/user/project/new_ci_build_permissions_model.md1
-rw-r--r--doc/user/project/pages/introduction.md1
-rw-r--r--doc/user/project/pipelines/job_artifacts.md7
-rw-r--r--doc/user/project/pipelines/schedules.md1
-rw-r--r--lib/gitlab/auth/ldap/person.rb2
-rw-r--r--lib/gitlab/badge/pipeline/template.rb1
-rw-r--r--lib/gitlab/ci/build/prerequisite/base.rb27
-rw-r--r--lib/gitlab/ci/build/prerequisite/factory.rb33
-rw-r--r--lib/gitlab/ci/build/prerequisite/kubernetes_namespace.rb40
-rw-r--r--lib/gitlab/ci/status/build/factory.rb1
-rw-r--r--lib/gitlab/ci/status/build/failed.rb3
-rw-r--r--lib/gitlab/ci/status/build/preparing.rb28
-rw-r--r--lib/gitlab/ci/status/preparing.rb33
-rw-r--r--lib/gitlab/ci/templates/Android-Fastlane.gitlab-ci.yml1
-rw-r--r--lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml4
-rw-r--r--lib/gitlab/ci/templates/Bash.gitlab-ci.yml14
-rw-r--r--lib/gitlab/ci/templates/C++.gitlab-ci.yml6
-rw-r--r--lib/gitlab/ci/templates/Chef.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Clojure.gitlab-ci.yml8
-rw-r--r--lib/gitlab/ci/templates/Code-Quality.gitlab-ci.yml17
-rw-r--r--lib/gitlab/ci/templates/Django.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Gradle.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Grails.gitlab-ci.yml10
-rw-r--r--lib/gitlab/ci/templates/Julia.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Laravel.gitlab-ci.yml15
-rw-r--r--lib/gitlab/ci/templates/Maven.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Mono.gitlab-ci.yml4
-rw-r--r--lib/gitlab/ci/templates/Pages/Brunch.gitlab-ci.yml1
-rw-r--r--lib/gitlab/ci/templates/Pages/Harp.gitlab-ci.yml1
-rw-r--r--lib/gitlab/ci/templates/Pages/Hugo.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Pages/JBake.gitlab-ci.yml3
-rw-r--r--lib/gitlab/ci/templates/Pages/Jigsaw.gitlab-ci.yml13
-rw-r--r--lib/gitlab/ci/templates/Pages/Metalsmith.gitlab-ci.yml1
-rw-r--r--lib/gitlab/ci/templates/Python.gitlab-ci.yml4
-rw-r--r--lib/gitlab/ci/templates/Ruby.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/dotNET.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/iOS-Fastlane.gitlab-ci.yml2
-rw-r--r--lib/gitlab/git/commit.rb15
-rw-r--r--lib/gitlab/json_cache.rb19
-rw-r--r--lib/gitlab/kubernetes.rb24
-rw-r--r--lib/sentry/client.rb2
-rw-r--r--locale/gitlab.pot38
-rw-r--r--package.json1
-rw-r--r--qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb6
-rw-r--r--spec/controllers/omniauth_callbacks_controller_spec.rb27
-rw-r--r--spec/controllers/projects/notes_controller_spec.rb31
-rw-r--r--spec/factories/ci/builds.rb4
-rw-r--r--spec/factories/ci/pipelines.rb8
-rw-r--r--spec/factories/clusters/clusters.rb4
-rw-r--r--spec/factories/commit_statuses.rb4
-rw-r--r--spec/features/issues/gfm_autocomplete_spec.rb84
-rw-r--r--spec/features/issues_spec.rb11
-rw-r--r--spec/features/projects/badges/pipeline_badge_spec.rb19
-rw-r--r--spec/features/projects/pipelines/pipeline_spec.rb23
-rw-r--r--spec/features/projects/pipelines/pipelines_spec.rb24
-rw-r--r--spec/features/user_opens_link_to_comment.rb33
-rw-r--r--spec/features/users/login_spec.rb29
-rw-r--r--spec/frontend/gfm_auto_complete_spec.js99
-rw-r--r--spec/frontend/helpers/fixtures.js24
-rw-r--r--spec/helpers/auth_helper_spec.rb38
-rw-r--r--spec/javascripts/fixtures/autocomplete_sources.rb40
-rw-r--r--spec/lib/gitlab/badge/pipeline/template_spec.rb10
-rw-r--r--spec/lib/gitlab/ci/build/prerequisite/factory_spec.rb34
-rw-r--r--spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb85
-rw-r--r--spec/lib/gitlab/ci/status/build/preparing_spec.rb33
-rw-r--r--spec/lib/gitlab/ci/status/preparing_spec.rb29
-rw-r--r--spec/lib/gitlab/git/commit_spec.rb12
-rw-r--r--spec/lib/gitlab/json_cache_spec.rb54
-rw-r--r--spec/lib/gitlab/kubernetes_spec.rb34
-rw-r--r--spec/models/broadcast_message_spec.rb6
-rw-r--r--spec/models/ci/build_spec.rb87
-rw-r--r--spec/models/ci/pipeline_spec.rb68
-rw-r--r--spec/models/clusters/platforms/kubernetes_spec.rb6
-rw-r--r--spec/models/commit_collection_spec.rb86
-rw-r--r--spec/models/commit_status_spec.rb23
-rw-r--r--spec/models/concerns/cache_markdown_field_spec.rb14
-rw-r--r--spec/models/concerns/has_status_spec.rb22
-rw-r--r--spec/models/deployment_spec.rb28
-rw-r--r--spec/models/merge_request_spec.rb37
-rw-r--r--spec/models/project_services/kubernetes_service_spec.rb12
-rw-r--r--spec/policies/identity_provider_policy_spec.rb30
-rw-r--r--spec/requests/api/project_clusters_spec.rb1
-rw-r--r--spec/requests/api/runner_spec.rb9
-rw-r--r--spec/serializers/merge_request_widget_entity_spec.rb15
-rw-r--r--spec/services/ci/prepare_build_service_spec.rb54
-rw-r--r--spec/services/projects/create_service_spec.rb1
-rw-r--r--spec/services/projects/transfer_service_spec.rb1
-rw-r--r--spec/support/helpers/kubernetes_helpers.rb16
-rw-r--r--spec/workers/ci/build_prepare_worker_spec.rb30
-rw-r--r--spec/workers/cluster_configure_worker_spec.rb16
-rw-r--r--spec/workers/cluster_project_configure_worker_spec.rb21
177 files changed, 2001 insertions, 435 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 2de6c0d2cc7..ad5c194bffe 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,5 +1,8 @@
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.5.3-golang-1.11-git-2.18-chrome-71.0-node-10.x-yarn-1.12-postgresql-9.6-graphicsmagick-1.3.29"
+include:
+ - local: /lib/gitlab/ci/templates/Code-Quality.gitlab-ci.yml
+
.dedicated-runner: &dedicated-runner
retry: 1
tags:
@@ -774,31 +777,14 @@ jest:
code_quality:
<<: *dedicated-no-docs-no-db-pull-cache-job
- image: docker:stable
- allow_failure: true
# gitlab-org runners set `privileged: false` but we need to have it set to true
# since we're using Docker in Docker
tags: []
before_script: []
- services:
- - docker:stable-dind
- variables:
- SETUP_DB: "false"
- DOCKER_DRIVER: overlay2
cache: {}
dependencies: []
- script:
- # Extract "MAJOR.MINOR" from CI_SERVER_VERSION and generate "MAJOR-MINOR-stable" for Security Products
- - export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
- - docker run
- --env SOURCE_CODE="$PWD"
- --volume "$PWD":/code
- --volume /var/run/docker.sock:/var/run/docker.sock
- "registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code
- artifacts:
- reports:
- codequality: gl-code-quality-report.json
- expire_in: 1 week
+ variables:
+ SETUP_DB: "false"
sast:
<<: *dedicated-no-docs-no-db-pull-cache-job
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bec6a8b3e21..0c33596d9c1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,13 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 11.8.3 (2019-03-19)
+
+### Security (1 change)
+
+- Remove project serialization in quick actions response.
+
+
## 11.8.2 (2019-03-13)
### Security (1 change)
@@ -264,6 +271,14 @@ entry.
- Creates mixin to reduce code duplication between CE and EE in graph component.
+## 11.7.7 (2019-03-19)
+
+### Security (2 changes)
+
+- Remove project serialization in quick actions response.
+- Fixed ability to see private groups by users not belonging to given group.
+
+
## 11.7.5 (2019-02-06)
### Fixed (8 changes)
diff --git a/Gemfile b/Gemfile
index 1ae063d8558..d44a99b2913 100644
--- a/Gemfile
+++ b/Gemfile
@@ -170,7 +170,7 @@ gem 'gitlab-sidekiq-fetcher', '~> 0.4.0', require: 'sidekiq-reliable-fetch'
gem 'fugit', '~> 1.1'
# HTTP requests
-gem 'httparty', '~> 0.13.3'
+gem 'httparty', '~> 0.16.4'
# Colored output to console
gem 'rainbow', '~> 3.0'
diff --git a/Gemfile.lock b/Gemfile.lock
index face24489f1..6943428a7d8 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -379,8 +379,8 @@ GEM
domain_name (~> 0.5)
http-form_data (2.1.1)
http_parser.rb (0.6.0)
- httparty (0.13.7)
- json (~> 1.8)
+ httparty (0.16.4)
+ mime-types (~> 3.0)
multi_xml (>= 0.5.2)
httpclient (2.8.3)
i18n (1.2.0)
@@ -1040,7 +1040,7 @@ DEPENDENCIES
health_check (~> 2.6.0)
html-pipeline (~> 2.8)
html2text
- httparty (~> 0.13.3)
+ httparty (~> 0.16.4)
icalendar
influxdb (~> 0.2)
jaeger-client (~> 0.10.0)
diff --git a/app/assets/javascripts/notes/components/discussion_filter.vue b/app/assets/javascripts/notes/components/discussion_filter.vue
index 31164f74201..47951591e82 100644
--- a/app/assets/javascripts/notes/components/discussion_filter.vue
+++ b/app/assets/javascripts/notes/components/discussion_filter.vue
@@ -22,7 +22,7 @@ export default {
},
selectedValue: {
type: Number,
- default: null,
+ default: DISCUSSION_FILTERS_DEFAULT_VALUE,
required: false,
},
},
diff --git a/app/assets/javascripts/notes/components/note_actions/reply_button.vue b/app/assets/javascripts/notes/components/note_actions/reply_button.vue
index f50cab81efe..be8e42af9ea 100644
--- a/app/assets/javascripts/notes/components/note_actions/reply_button.vue
+++ b/app/assets/javascripts/notes/components/note_actions/reply_button.vue
@@ -18,7 +18,7 @@ export default {
<div class="note-actions-item">
<gl-button
ref="button"
- v-gl-tooltip.bottom
+ v-gl-tooltip
class="note-action-button"
variant="transparent"
:title="__('Reply to comment')"
diff --git a/app/assets/javascripts/notes/discussion_filters.js b/app/assets/javascripts/notes/discussion_filters.js
index 5c5f38a3fb0..cdf9a46c5aa 100644
--- a/app/assets/javascripts/notes/discussion_filters.js
+++ b/app/assets/javascripts/notes/discussion_filters.js
@@ -6,12 +6,16 @@ export default store => {
if (discussionFilterEl) {
const { defaultFilter, notesFilters } = discussionFilterEl.dataset;
- const selectedValue = defaultFilter ? parseInt(defaultFilter, 10) : null;
const filterValues = notesFilters ? JSON.parse(notesFilters) : {};
const filters = Object.keys(filterValues).map(entry => ({
title: entry,
value: filterValues[entry],
}));
+ const props = { filters };
+
+ if (defaultFilter) {
+ props.selectedValue = parseInt(defaultFilter, 10);
+ }
return new Vue({
el: discussionFilterEl,
@@ -21,12 +25,7 @@ export default store => {
},
store,
render(createElement) {
- return createElement('discussion-filter', {
- props: {
- filters,
- selectedValue,
- },
- });
+ return createElement('discussion-filter', { props });
},
});
}
diff --git a/app/assets/javascripts/notes/index.js b/app/assets/javascripts/notes/index.js
index 4883266dae5..30372103590 100644
--- a/app/assets/javascripts/notes/index.js
+++ b/app/assets/javascripts/notes/index.js
@@ -6,9 +6,8 @@ import createStore from './stores';
document.addEventListener('DOMContentLoaded', () => {
const store = createStore();
- initDiscussionFilters(store);
-
- return new Vue({
+ // eslint-disable-next-line no-new
+ new Vue({
el: '#js-vue-notes',
components: {
notesApp,
@@ -49,4 +48,6 @@ document.addEventListener('DOMContentLoaded', () => {
});
},
});
+
+ initDiscussionFilters(store);
});
diff --git a/app/assets/stylesheets/framework/blank.scss b/app/assets/stylesheets/framework/blank.scss
index 91dbb2a6365..cbd390e7145 100644
--- a/app/assets/stylesheets/framework/blank.scss
+++ b/app/assets/stylesheets/framework/blank.scss
@@ -69,6 +69,7 @@
@include media-breakpoint-up(sm) {
display: flex;
+ height: 100%;
align-items: center;
padding: 50px 30px;
}
@@ -99,3 +100,30 @@
}
}
}
+
+@include media-breakpoint-up(lg) {
+ .column-large {
+ flex: 2;
+ }
+
+ .column-small {
+ flex: 1;
+ margin-bottom: 15px;
+
+ .blank-state {
+ max-width: 400px;
+ flex-wrap: wrap;
+ margin-left: 15px;
+ }
+
+ .blank-state-icon {
+ margin-bottom: 30px;
+ }
+ }
+}
+
+@include media-breakpoint-down(xs) {
+ .blank-state-icon svg {
+ width: 315px;
+ }
+}
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index 5efd484b51b..97a9a55c968 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -120,7 +120,7 @@ hr {
text-overflow: ellipsis;
white-space: nowrap;
- > div,
+ > div:not(.block),
.str-truncated {
display: inline;
}
@@ -381,6 +381,7 @@ img.emoji {
.prepend-left-5 { margin-left: 5px; }
.prepend-left-8 { margin-left: 8px; }
.prepend-left-10 { margin-left: 10px; }
+.prepend-left-15 { margin-left: 15px; }
.prepend-left-default { margin-left: $gl-padding; }
.prepend-left-20 { margin-left: 20px; }
.prepend-left-32 { margin-left: 32px; }
@@ -388,6 +389,7 @@ img.emoji {
.append-right-5 { margin-right: 5px; }
.append-right-8 { margin-right: 8px; }
.append-right-10 { margin-right: 10px; }
+.append-right-15 { margin-right: 15px; }
.append-right-default { margin-right: $gl-padding; }
.append-right-20 { margin-right: 20px; }
.prepend-right-32 { margin-right: 32px; }
@@ -402,6 +404,8 @@ img.emoji {
.prepend-bottom-32 { margin-bottom: 32px; }
.inline { display: inline-block; }
.center { text-align: center; }
+.block { display: block; }
+.flex { display: flex; }
.vertical-align-middle { vertical-align: middle; }
.vertical-align-sub { vertical-align: sub; }
.flex-align-self-center { align-self: center; }
diff --git a/app/assets/stylesheets/framework/icons.scss b/app/assets/stylesheets/framework/icons.scss
index 49b9b7014ae..3ab61cc5c47 100644
--- a/app/assets/stylesheets/framework/icons.scss
+++ b/app/assets/stylesheets/framework/icons.scss
@@ -31,6 +31,7 @@
}
}
+.ci-status-icon-preparing,
.ci-status-icon-running {
svg {
fill: $blue-400;
diff --git a/app/assets/stylesheets/framework/system_messages.scss b/app/assets/stylesheets/framework/system_messages.scss
index 3d66136938f..e5edddec71e 100644
--- a/app/assets/stylesheets/framework/system_messages.scss
+++ b/app/assets/stylesheets/framework/system_messages.scss
@@ -12,7 +12,7 @@
p {
@include str-truncated(100%);
- margin-top: 0;
+ margin-top: -1px;
margin-bottom: 0;
}
}
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 0333b9445c5..08dbe3d5b0f 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -277,7 +277,7 @@ $general-hover-transition-duration: 100ms;
$general-hover-transition-curve: linear;
$highlight-changes-color: rgb(235, 255, 232);
$performance-bar-height: 35px;
-$system-header-height: 35px;
+$system-header-height: 16px;
$system-footer-height: $system-header-height;
$flash-height: 52px;
$context-header-height: 60px;
diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss
index fa5a182243c..916f6cd3137 100644
--- a/app/assets/stylesheets/pages/builds.scss
+++ b/app/assets/stylesheets/pages/builds.scss
@@ -50,7 +50,6 @@
position: relative;
}
-
.build-trace {
@include build-trace();
}
@@ -392,3 +391,14 @@
right: 0;
margin-top: -17px;
}
+
+@include media-breakpoint-down(sm) {
+ .top-bar {
+ .truncated-info {
+ white-space: nowrap;
+ overflow: hidden;
+ max-width: 220px;
+ text-overflow: ellipsis;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index df3a4be6559..7f8b8ea8100 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -166,6 +166,7 @@
float: left;
.accept-merge-request {
+ &.ci-preparing,
&.ci-pending,
&.ci-running {
@include btn-blue;
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index 2b6319ddd4f..bb08440fda8 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -498,7 +498,8 @@
list-style: none;
}
- &:last-child {
+ // when downstream pipelines are present, the last stage isn't the last column
+ &:last-child:not(.has-downstream) {
.build {
// Remove right connecting horizontal line from first build in last stage
&:first-child::after {
@@ -515,7 +516,8 @@
}
}
- &:first-child {
+ // when upstream pipelines are present, the first stage isn't the first column
+ &:first-child:not(.has-upstream) {
.build {
// Remove left curved connectors from all builds in first stage
&:not(:first-child)::before {
@@ -793,6 +795,7 @@
@include mini-pipeline-graph-color($white, $orange-100, $orange-200, $orange-500, $orange-600, $orange-700);
}
+ &.ci-status-icon-preparing,
&.ci-status-icon-running {
@include mini-pipeline-graph-color($white, $blue-100, $blue-200, $blue-500, $blue-600, $blue-700);
}
diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss
index ab26259c007..8e933b62dd9 100644
--- a/app/assets/stylesheets/pages/profile.scss
+++ b/app/assets/stylesheets/pages/profile.scss
@@ -266,21 +266,6 @@
padding-top: 20px;
}
- .cover-controls {
- position: static;
- padding: 0 16px;
- margin-bottom: 20px;
- display: flex;
-
- .btn {
- flex-grow: 1;
-
- &:first-child {
- margin-left: 0;
- }
- }
- }
-
.user-profile-nav {
a {
margin-right: 0;
diff --git a/app/assets/stylesheets/pages/status.scss b/app/assets/stylesheets/pages/status.scss
index f4d568d02ac..a59bb31bdcb 100644
--- a/app/assets/stylesheets/pages/status.scss
+++ b/app/assets/stylesheets/pages/status.scss
@@ -44,6 +44,7 @@
}
&.ci-info,
+ &.ci-preparing,
&.ci-running {
@include status-color($blue-100, $blue-500, $blue-600);
}
diff --git a/app/controllers/concerns/notes_actions.rb b/app/controllers/concerns/notes_actions.rb
index b4fee93713b..f96d1821095 100644
--- a/app/controllers/concerns/notes_actions.rb
+++ b/app/controllers/concerns/notes_actions.rb
@@ -48,7 +48,7 @@ module NotesActions
respond_to do |format|
format.json do
json = {
- commands_changes: @note.commands_changes
+ commands_changes: @note.commands_changes&.slice(:emoji_award, :time_estimate, :spend_time)
}
if @note.persisted? && return_discussion?
diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb
index 0bc082246a1..f1d6fb00cfc 100644
--- a/app/controllers/groups/group_members_controller.rb
+++ b/app/controllers/groups/group_members_controller.rb
@@ -12,6 +12,7 @@ class Groups::GroupMembersController < Groups::ApplicationController
# Authorize
before_action :authorize_admin_group_member!, except: admin_not_required_endpoints
+ skip_before_action :check_two_factor_requirement, only: :leave
skip_cross_project_access_check :index, :create, :update, :destroy, :request_access,
:approve_access_request, :leave, :resend_invite,
:override
diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb
index cc2bb99f55b..e90e8278c13 100644
--- a/app/controllers/omniauth_callbacks_controller.rb
+++ b/app/controllers/omniauth_callbacks_controller.rb
@@ -3,6 +3,7 @@
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
include AuthenticatesWithTwoFactor
include Devise::Controllers::Rememberable
+ include AuthHelper
protect_from_forgery except: [:kerberos, :saml, :cas3, :failure], with: :exception, prepend: true
@@ -80,10 +81,11 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
end
if current_user
+ return render_403 unless link_provider_allowed?(oauth['provider'])
+
log_audit_event(current_user, with: oauth['provider'])
identity_linker ||= auth_module::IdentityLinker.new(current_user, oauth)
-
identity_linker.link
if identity_linker.changed?
diff --git a/app/controllers/profiles/accounts_controller.rb b/app/controllers/profiles/accounts_controller.rb
index b0d65f284af..0d2a6145d0e 100644
--- a/app/controllers/profiles/accounts_controller.rb
+++ b/app/controllers/profiles/accounts_controller.rb
@@ -14,7 +14,7 @@ class Profiles::AccountsController < Profiles::ApplicationController
return render_404 unless identity
- if unlink_allowed?(provider)
+ if unlink_provider_allowed?(provider)
identity.destroy
else
flash[:alert] = "You are not allowed to unlink your primary login account"
diff --git a/app/controllers/profiles/two_factor_auths_controller.rb b/app/controllers/profiles/two_factor_auths_controller.rb
index ba94196b2f9..83e14275a8b 100644
--- a/app/controllers/profiles/two_factor_auths_controller.rb
+++ b/app/controllers/profiles/two_factor_auths_controller.rb
@@ -18,21 +18,16 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
two_factor_authentication_reason(
global: lambda do
flash.now[:alert] =
- 'The global settings require you to enable Two-Factor Authentication for your account.'
+ s_('The global settings require you to enable Two-Factor Authentication for your account.')
end,
group: lambda do |groups|
- group_links = groups.map { |group| view_context.link_to group.full_name, group_path(group) }.to_sentence
-
- flash.now[:alert] = %{
- The group settings for #{group_links} require you to enable
- Two-Factor Authentication for your account.
- }.html_safe
+ flash.now[:alert] = groups_notification(groups)
end
)
unless two_factor_grace_period_expired?
grace_period_deadline = current_user.otp_grace_period_started_at + two_factor_grace_period.hours
- flash.now[:alert] = flash.now[:alert] + " You need to do this before #{l(grace_period_deadline)}."
+ flash.now[:alert] = flash.now[:alert] + s_(" You need to do this before %{grace_period_deadline}.") % { grace_period_deadline: l(grace_period_deadline) }
end
end
@@ -49,7 +44,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
render 'create'
else
- @error = 'Invalid pin code'
+ @error = s_('Invalid pin code')
@qr_code = build_qr_code
setup_u2f_registration
render 'show'
@@ -63,7 +58,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
if @u2f_registration.persisted?
session.delete(:challenges)
- redirect_to profile_two_factor_auth_path, notice: "Your U2F device was registered!"
+ redirect_to profile_two_factor_auth_path, notice: s_("Your U2F device was registered!")
else
@qr_code = build_qr_code
setup_u2f_registration
@@ -85,7 +80,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
def skip
if two_factor_grace_period_expired?
- redirect_to new_profile_two_factor_auth_path, alert: 'Cannot skip two factor authentication setup'
+ redirect_to new_profile_two_factor_auth_path, alert: s_('Cannot skip two factor authentication setup')
else
session[:skip_two_factor] = current_user.otp_grace_period_started_at + two_factor_grace_period.hours
redirect_to root_path
@@ -126,4 +121,12 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
def u2f_registration_params
params.require(:u2f_registration).permit(:device_response, :name)
end
+
+ def groups_notification(groups)
+ group_links = groups.map { |group| view_context.link_to group.full_name, group_path(group) }.to_sentence
+ leave_group_links = groups.map { |group| view_context.link_to (s_("leave %{group_name}") % { group_name: group.full_name }), leave_group_members_path(group), remote: false, method: :delete}.to_sentence
+
+ s_(%{The group settings for %{group_links} require you to enable Two-Factor Authentication for your account. You can %{leave_group_links}.})
+ .html_safe % { group_links: group_links.html_safe, leave_group_links: leave_group_links.html_safe }
+ end
end
diff --git a/app/helpers/auth_helper.rb b/app/helpers/auth_helper.rb
index 2b1d6f49878..b4ee648361c 100644
--- a/app/helpers/auth_helper.rb
+++ b/app/helpers/auth_helper.rb
@@ -100,8 +100,12 @@ module AuthHelper
end
# rubocop: enable CodeReuse/ActiveRecord
- def unlink_allowed?(provider)
- %w(saml cas3).exclude?(provider.to_s)
+ def unlink_provider_allowed?(provider)
+ IdentityProviderPolicy.new(current_user, provider).can?(:unlink)
+ end
+
+ def link_provider_allowed?(provider)
+ IdentityProviderPolicy.new(current_user, provider).can?(:link)
end
extend self
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 5496aa4908c..f2abb241753 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -169,7 +169,7 @@ module ProjectsHelper
translation.html_safe
end
- def project_list_cache_key(project)
+ def project_list_cache_key(project, pipeline_status: true)
key = [
project.route.cache_key,
project.cache_key,
@@ -179,10 +179,11 @@ module ProjectsHelper
Gitlab::CurrentSettings.cache_key,
"cross-project:#{can?(current_user, :read_cross_project)}",
max_project_member_access_cache_key(project),
+ pipeline_status,
'v2.6'
]
- key << pipeline_status_cache_key(project.pipeline_status) if project.pipeline_status.has_status?
+ key << pipeline_status_cache_key(project.pipeline_status) if pipeline_status && project.pipeline_status.has_status?
key
end
diff --git a/app/models/broadcast_message.rb b/app/models/broadcast_message.rb
index 2d237383e60..1c95abdd9ee 100644
--- a/app/models/broadcast_message.rb
+++ b/app/models/broadcast_message.rb
@@ -4,7 +4,7 @@ class BroadcastMessage < ActiveRecord::Base
include CacheMarkdownField
include Sortable
- cache_markdown_field :message, pipeline: :broadcast_message
+ cache_markdown_field :message, pipeline: :broadcast_message, whitelisted: true
validates :message, presence: true
validates :starts_at, presence: true
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index a629db82c19..59f47effff7 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -172,6 +172,10 @@ module Ci
end
state_machine :status do
+ event :enqueue do
+ transition [:created, :skipped, :manual, :scheduled] => :preparing, if: :any_unmet_prerequisites?
+ end
+
event :actionize do
transition created: :manual
end
@@ -185,8 +189,12 @@ module Ci
end
event :enqueue_scheduled do
+ transition scheduled: :preparing, if: ->(build) do
+ build.scheduled_at&.past? && build.any_unmet_prerequisites?
+ end
+
transition scheduled: :pending, if: ->(build) do
- build.scheduled_at && build.scheduled_at < Time.now
+ build.scheduled_at&.past? && !build.any_unmet_prerequisites?
end
end
@@ -204,6 +212,12 @@ module Ci
end
end
+ after_transition any => [:preparing] do |build|
+ build.run_after_commit do
+ Ci::BuildPrepareWorker.perform_async(id)
+ end
+ end
+
after_transition any => [:pending] do |build|
build.run_after_commit do
BuildQueueWorker.perform_async(id)
@@ -355,6 +369,16 @@ module Ci
!retried?
end
+ def any_unmet_prerequisites?
+ return false unless Feature.enabled?(:ci_preparing_state, default_enabled: true)
+
+ prerequisites.present?
+ end
+
+ def prerequisites
+ Gitlab::Ci::Build::Prerequisite::Factory.new(self).unmet
+ end
+
def expanded_environment_name
return unless has_environment?
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index adffdc0355e..ae74f569415 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -82,10 +82,14 @@ module Ci
state_machine :status, initial: :created do
event :enqueue do
- transition [:created, :skipped, :scheduled] => :pending
+ transition [:created, :preparing, :skipped, :scheduled] => :pending
transition [:success, :failed, :canceled] => :running
end
+ event :prepare do
+ transition any - [:preparing] => :preparing
+ end
+
event :run do
transition any - [:running] => :running
end
@@ -118,7 +122,7 @@ module Ci
# Do not add any operations to this state_machine
# Create a separate worker for each new operation
- before_transition [:created, :pending] => :running do |pipeline|
+ before_transition [:created, :preparing, :pending] => :running do |pipeline|
pipeline.started_at = Time.now
end
@@ -141,7 +145,7 @@ module Ci
end
end
- after_transition [:created, :pending] => :running do |pipeline|
+ after_transition [:created, :preparing, :pending] => :running do |pipeline|
pipeline.run_after_commit { PipelineMetricsWorker.perform_async(pipeline.id) }
end
@@ -149,7 +153,7 @@ module Ci
pipeline.run_after_commit { PipelineMetricsWorker.perform_async(pipeline.id) }
end
- after_transition [:created, :pending, :running] => :success do |pipeline|
+ after_transition [:created, :preparing, :pending, :running] => :success do |pipeline|
pipeline.run_after_commit { PipelineSuccessWorker.perform_async(pipeline.id) }
end
@@ -597,6 +601,7 @@ module Ci
retry_optimistic_lock(self) do
case latest_builds_status.to_s
when 'created' then nil
+ when 'preparing' then prepare
when 'pending' then enqueue
when 'running' then run
when 'success' then succeed
diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb
index 0389945191e..098f5189517 100644
--- a/app/models/ci/stage.rb
+++ b/app/models/ci/stage.rb
@@ -39,10 +39,14 @@ module Ci
state_machine :status, initial: :created do
event :enqueue do
- transition created: :pending
+ transition [:created, :preparing] => :pending
transition [:success, :failed, :canceled, :skipped] => :running
end
+ event :prepare do
+ transition any - [:preparing] => :preparing
+ end
+
event :run do
transition any - [:running] => :running
end
@@ -76,6 +80,7 @@ module Ci
retry_optimistic_lock(self) do
case statuses.latest.status
when 'created' then nil
+ when 'preparing' then prepare
when 'pending' then enqueue
when 'running' then run
when 'success' then succeed
diff --git a/app/models/clusters/platforms/kubernetes.rb b/app/models/clusters/platforms/kubernetes.rb
index fb2221b601f..7786b48429c 100644
--- a/app/models/clusters/platforms/kubernetes.rb
+++ b/app/models/clusters/platforms/kubernetes.rb
@@ -110,7 +110,7 @@ module Clusters
# short time later
def terminals(environment)
with_reactive_cache do |data|
- pods = filter_by_label(data[:pods], app: environment.slug)
+ pods = filter_by_project_environment(data[:pods], project.full_path_slug, environment.slug)
terminals = pods.flat_map { |pod| terminals_for_pod(api_url, actual_namespace, pod) }.compact
terminals.each { |terminal| add_terminal_auth(terminal, terminal_auth) }
end
diff --git a/app/models/commit_collection.rb b/app/models/commit_collection.rb
index a9a2e9c81eb..52524456439 100644
--- a/app/models/commit_collection.rb
+++ b/app/models/commit_collection.rb
@@ -28,10 +28,43 @@ class CommitCollection
def without_merge_commits
strong_memoize(:without_merge_commits) do
- commits.reject(&:merge_commit?)
+ # `#enrich!` the collection to ensure all commits contain
+ # the necessary parent data
+ enrich!.commits.reject(&:merge_commit?)
end
end
+ def unenriched
+ commits.reject(&:gitaly_commit?)
+ end
+
+ def fully_enriched?
+ unenriched.empty?
+ end
+
+ # Batch load any commits that are not backed by full gitaly data, and
+ # replace them in the collection.
+ def enrich!
+ # A project is needed in order to fetch data from gitaly. Projects
+ # can be absent from commits in certain rare situations (like when
+ # viewing a MR of a deleted fork). In these cases, assume that the
+ # enriched data is not needed.
+ return self if project.blank? || fully_enriched?
+
+ # Batch load full Commits from the repository
+ # and map to a Hash of id => Commit
+ replacements = Hash[unenriched.map do |c|
+ [c.id, Commit.lazy(project, c.id)]
+ end.compact]
+
+ # Replace the commits, keeping the same order
+ @commits = @commits.map do |c|
+ replacements.fetch(c.id, c)
+ end
+
+ self
+ end
+
# Sets the pipeline status for every commit.
#
# Setting this status ahead of time removes the need for running a query for
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
index 7f6562b63e5..5f66a661324 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -66,7 +66,10 @@ class CommitStatus < ActiveRecord::Base
end
event :enqueue do
- transition [:created, :skipped, :manual, :scheduled] => :pending
+ # A CommitStatus will never have prerequisites, but this event
+ # is shared by Ci::Build, which cannot progress unless prerequisites
+ # are satisfied.
+ transition [:created, :preparing, :skipped, :manual, :scheduled] => :pending, unless: :any_unmet_prerequisites?
end
event :run do
@@ -74,26 +77,26 @@ class CommitStatus < ActiveRecord::Base
end
event :skip do
- transition [:created, :pending] => :skipped
+ transition [:created, :preparing, :pending] => :skipped
end
event :drop do
- transition [:created, :pending, :running, :scheduled] => :failed
+ transition [:created, :preparing, :pending, :running, :scheduled] => :failed
end
event :success do
- transition [:created, :pending, :running] => :success
+ transition [:created, :preparing, :pending, :running] => :success
end
event :cancel do
- transition [:created, :pending, :running, :manual, :scheduled] => :canceled
+ transition [:created, :preparing, :pending, :running, :manual, :scheduled] => :canceled
end
- before_transition [:created, :skipped, :manual, :scheduled] => :pending do |commit_status|
+ before_transition [:created, :preparing, :skipped, :manual, :scheduled] => :pending do |commit_status|
commit_status.queued_at = Time.now
end
- before_transition [:created, :pending] => :running do |commit_status|
+ before_transition [:created, :preparing, :pending] => :running do |commit_status|
commit_status.started_at = Time.now
end
@@ -180,6 +183,10 @@ class CommitStatus < ActiveRecord::Base
false
end
+ def any_unmet_prerequisites?
+ false
+ end
+
def auto_canceled?
canceled? && auto_canceled_by_id?
end
diff --git a/app/models/commit_status_enums.rb b/app/models/commit_status_enums.rb
index 152105d9429..45e08fa18fe 100644
--- a/app/models/commit_status_enums.rb
+++ b/app/models/commit_status_enums.rb
@@ -14,7 +14,8 @@ module CommitStatusEnums
runner_unsupported: 6,
stale_schedule: 7,
job_execution_timeout: 8,
- archived_failure: 9
+ archived_failure: 9,
+ unmet_prerequisites: 10
}
end
end
diff --git a/app/models/concerns/cache_markdown_field.rb b/app/models/concerns/cache_markdown_field.rb
index 1a8570b80c3..15d8d58b9b5 100644
--- a/app/models/concerns/cache_markdown_field.rb
+++ b/app/models/concerns/cache_markdown_field.rb
@@ -7,6 +7,7 @@
# cache_markdown_field :foo
# cache_markdown_field :bar
# cache_markdown_field :baz, pipeline: :single_line
+# cache_markdown_field :baz, whitelisted: true
#
# Corresponding foo_html, bar_html and baz_html fields should exist.
module CacheMarkdownField
@@ -37,7 +38,15 @@ module CacheMarkdownField
end
def html_fields
- markdown_fields.map {|field| html_field(field) }
+ markdown_fields.map { |field| html_field(field) }
+ end
+
+ def html_fields_whitelisted
+ markdown_fields.each_with_object([]) do |field, fields|
+ if @data[field].fetch(:whitelisted, false)
+ fields << html_field(field)
+ end
+ end
end
end
@@ -149,13 +158,18 @@ module CacheMarkdownField
alias_method :attributes_before_markdown_cache, :attributes
def attributes
attrs = attributes_before_markdown_cache
+ html_fields = cached_markdown_fields.html_fields
+ whitelisted = cached_markdown_fields.html_fields_whitelisted
+ exclude_fields = html_fields - whitelisted
- attrs.delete('cached_markdown_version')
-
- cached_markdown_fields.html_fields.each do |field|
+ exclude_fields.each do |field|
attrs.delete(field)
end
+ if whitelisted.empty?
+ attrs.delete('cached_markdown_version')
+ end
+
attrs
end
diff --git a/app/models/concerns/has_status.rb b/app/models/concerns/has_status.rb
index 0d2be4c61ab..8882f48c281 100644
--- a/app/models/concerns/has_status.rb
+++ b/app/models/concerns/has_status.rb
@@ -5,14 +5,14 @@ module HasStatus
DEFAULT_STATUS = 'created'.freeze
BLOCKED_STATUS = %w[manual scheduled].freeze
- AVAILABLE_STATUSES = %w[created pending running success failed canceled skipped manual scheduled].freeze
+ AVAILABLE_STATUSES = %w[created preparing pending running success failed canceled skipped manual scheduled].freeze
STARTED_STATUSES = %w[running success failed skipped manual scheduled].freeze
- ACTIVE_STATUSES = %w[pending running].freeze
+ ACTIVE_STATUSES = %w[preparing pending running].freeze
COMPLETED_STATUSES = %w[success failed canceled skipped].freeze
- ORDERED_STATUSES = %w[failed pending running manual scheduled canceled success skipped created].freeze
+ ORDERED_STATUSES = %w[failed preparing pending running manual scheduled canceled success skipped created].freeze
STATUSES_ENUM = { created: 0, pending: 1, running: 2, success: 3,
failed: 4, canceled: 5, skipped: 6, manual: 7,
- scheduled: 8 }.freeze
+ scheduled: 8, preparing: 9 }.freeze
UnknownStatusError = Class.new(StandardError)
@@ -26,6 +26,7 @@ module HasStatus
success = scope_relevant.success.select('count(*)').to_sql
manual = scope_relevant.manual.select('count(*)').to_sql
scheduled = scope_relevant.scheduled.select('count(*)').to_sql
+ preparing = scope_relevant.preparing.select('count(*)').to_sql
pending = scope_relevant.pending.select('count(*)').to_sql
running = scope_relevant.running.select('count(*)').to_sql
skipped = scope_relevant.skipped.select('count(*)').to_sql
@@ -37,12 +38,14 @@ module HasStatus
WHEN (#{builds})=(#{skipped}) THEN 'skipped'
WHEN (#{builds})=(#{success}) THEN 'success'
WHEN (#{builds})=(#{created}) THEN 'created'
+ WHEN (#{builds})=(#{preparing}) THEN 'preparing'
WHEN (#{builds})=(#{success})+(#{skipped}) THEN 'success'
WHEN (#{builds})=(#{success})+(#{skipped})+(#{canceled}) THEN 'canceled'
WHEN (#{builds})=(#{created})+(#{skipped})+(#{pending}) THEN 'pending'
WHEN (#{running})+(#{pending})>0 THEN 'running'
WHEN (#{manual})>0 THEN 'manual'
WHEN (#{scheduled})>0 THEN 'scheduled'
+ WHEN (#{preparing})>0 THEN 'preparing'
WHEN (#{created})>0 THEN 'running'
ELSE 'failed'
END)"
@@ -70,6 +73,7 @@ module HasStatus
state_machine :status, initial: :created do
state :created, value: 'created'
+ state :preparing, value: 'preparing'
state :pending, value: 'pending'
state :running, value: 'running'
state :failed, value: 'failed'
@@ -81,6 +85,7 @@ module HasStatus
end
scope :created, -> { where(status: 'created') }
+ scope :preparing, -> { where(status: 'preparing') }
scope :relevant, -> { where(status: AVAILABLE_STATUSES - ['created']) }
scope :running, -> { where(status: 'running') }
scope :pending, -> { where(status: 'pending') }
@@ -90,14 +95,14 @@ module HasStatus
scope :skipped, -> { where(status: 'skipped') }
scope :manual, -> { where(status: 'manual') }
scope :scheduled, -> { where(status: 'scheduled') }
- scope :alive, -> { where(status: [:created, :pending, :running]) }
+ scope :alive, -> { where(status: [:created, :preparing, :pending, :running]) }
scope :created_or_pending, -> { where(status: [:created, :pending]) }
scope :running_or_pending, -> { where(status: [:running, :pending]) }
scope :finished, -> { where(status: [:success, :failed, :canceled]) }
scope :failed_or_canceled, -> { where(status: [:failed, :canceled]) }
scope :cancelable, -> do
- where(status: [:running, :pending, :created, :scheduled])
+ where(status: [:running, :preparing, :pending, :created, :scheduled])
end
end
diff --git a/app/models/deployment.rb b/app/models/deployment.rb
index 811e623b7f7..428edfd88de 100644
--- a/app/models/deployment.rb
+++ b/app/models/deployment.rb
@@ -78,6 +78,10 @@ class Deployment < ActiveRecord::Base
Commit.truncate_sha(sha)
end
+ def cluster
+ project.deployment_platform(environment: environment.name)&.cluster
+ end
+
def last?
self == environment.last_deployment
end
diff --git a/app/models/project_services/kubernetes_service.rb b/app/models/project_services/kubernetes_service.rb
index 4cf3a7f3d84..f650dbd3726 100644
--- a/app/models/project_services/kubernetes_service.rb
+++ b/app/models/project_services/kubernetes_service.rb
@@ -131,8 +131,8 @@ class KubernetesService < DeploymentService
# short time later
def terminals(environment)
with_reactive_cache do |data|
- pods = filter_by_label(data[:pods], app: environment.slug)
- terminals = pods.flat_map { |pod| terminals_for_pod(api_url, actual_namespace, pod) }
+ pods = filter_by_project_environment(data[:pods], project.full_path_slug, environment.slug)
+ terminals = pods.flat_map { |pod| terminals_for_pod(api_url, actual_namespace, pod) }.compact
terminals.each { |terminal| add_terminal_auth(terminal, terminal_auth) }
end
end
diff --git a/app/policies/identity_provider_policy.rb b/app/policies/identity_provider_policy.rb
new file mode 100644
index 00000000000..d34cdd5bdd4
--- /dev/null
+++ b/app/policies/identity_provider_policy.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class IdentityProviderPolicy < BasePolicy
+ desc "Provider is SAML or CAS3"
+ condition(:protected_provider, scope: :subject, score: 0) { %w(saml cas3).include?(@subject.to_s) }
+
+ rule { anonymous }.prevent_all
+
+ rule { default }.policy do
+ enable :unlink
+ enable :link
+ end
+
+ rule { protected_provider }.prevent(:unlink)
+end
diff --git a/app/presenters/commit_status_presenter.rb b/app/presenters/commit_status_presenter.rb
index 0cd77da6303..28a25c8b7a3 100644
--- a/app/presenters/commit_status_presenter.rb
+++ b/app/presenters/commit_status_presenter.rb
@@ -11,7 +11,8 @@ class CommitStatusPresenter < Gitlab::View::Presenter::Delegated
runner_unsupported: 'Your runner is outdated, please upgrade your runner',
stale_schedule: 'Delayed job could not be executed by some reason, please try again',
job_execution_timeout: 'The script exceeded the maximum execution time set for the job',
- archived_failure: 'The job is archived and cannot be run'
+ archived_failure: 'The job is archived and cannot be run',
+ unmet_prerequisites: 'The job failed to complete prerequisite tasks'
}.freeze
private_constant :CALLOUT_FAILURE_MESSAGES
diff --git a/app/services/ci/prepare_build_service.rb b/app/services/ci/prepare_build_service.rb
new file mode 100644
index 00000000000..32f11438b79
--- /dev/null
+++ b/app/services/ci/prepare_build_service.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Ci
+ class PrepareBuildService
+ attr_reader :build
+
+ def initialize(build)
+ @build = build
+ end
+
+ def execute
+ prerequisites.each(&:complete!)
+
+ unless build.enqueue
+ build.drop!(:unmet_prerequisites)
+ end
+ end
+
+ private
+
+ def prerequisites
+ build.prerequisites
+ end
+ end
+end
diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml
index 6756299cf43..2a1d2c2aeab 100644
--- a/app/views/admin/dashboard/index.html.haml
+++ b/app/views/admin/dashboard/index.html.haml
@@ -22,9 +22,10 @@
%h3.text-center
Users:
= approximate_count_with_delimiters(@counts, User)
- = render_if_exists 'admin/dashboard/users_statistics'
%hr
- = link_to 'New user', new_admin_user_path, class: "btn btn-success"
+ .btn-group.d-flex{ role: 'group' }
+ = link_to 'New user', new_admin_user_path, class: "btn btn-success"
+ = render_if_exists 'admin/dashboard/users_statistics'
.col-sm-4
.info-well.dark-well
.well-segment.well-centered
diff --git a/app/views/profiles/_email_settings.html.haml b/app/views/profiles/_email_settings.html.haml
new file mode 100644
index 00000000000..fb4da08e129
--- /dev/null
+++ b/app/views/profiles/_email_settings.html.haml
@@ -0,0 +1,16 @@
+- form = local_assigns.fetch(:form)
+- readonly = @user.read_only_attribute?(:email)
+- email_change_disabled = local_assigns.fetch(:email_change_disabled, nil)
+- read_only_help_text = readonly ? s_("Profiles|Your email address was automatically set based on your %{provider_label} account") % { provider_label: attribute_provider_label(:email) } : user_email_help_text(@user)
+- help_text = email_change_disabled ? s_("Your account uses dedicated credentials for the \"%{group_name}\" group and can only be updated through SSO.") % { group_name: @user.managing_group.name } : read_only_help_text
+
+= form.text_field :email, required: true, class: 'input-lg', value: (@user.email unless @user.temp_oauth_email?), help: help_text.html_safe, readonly: readonly || email_change_disabled
+= form.select :public_email, options_for_select(@user.all_emails, selected: @user.public_email),
+ { help: s_("Profiles|This email will be displayed on your public profile"), include_blank: s_("Profiles|Do not show on profile") },
+ control_class: 'select2 input-lg', disabled: email_change_disabled
+- commit_email_link_url = help_page_path('user/profile/index', anchor: 'commit-email', target: '_blank')
+- commit_email_link_start = '<a href="%{url}">'.html_safe % { url: commit_email_link_url }
+- commit_email_docs_link = s_('Profiles|This email will be used for web based operations, such as edits and merges. %{commit_email_link_start}Learn more%{commit_email_link_end}').html_safe % { commit_email_link_start: commit_email_link_start, commit_email_link_end: '</a>'.html_safe }
+= form.select :commit_email, options_for_select(commit_email_select_options(@user), selected: selected_commit_email(@user)),
+ { help: commit_email_docs_link },
+ control_class: 'select2 input-lg', disabled: email_change_disabled
diff --git a/app/views/profiles/accounts/_providers.html.haml b/app/views/profiles/accounts/_providers.html.haml
new file mode 100644
index 00000000000..068f9cc70f7
--- /dev/null
+++ b/app/views/profiles/accounts/_providers.html.haml
@@ -0,0 +1,21 @@
+%label.label-bold
+ = s_('Profiles|Connected Accounts')
+ %p= s_('Profiles|Click on icon to activate signin with one of the following services')
+ - providers.each do |provider|
+ - unlink_allowed = unlink_provider_allowed?(provider)
+ - link_allowed = link_provider_allowed?(provider)
+ - if unlink_allowed || link_allowed
+ .provider-btn-group
+ .provider-btn-image
+ = provider_image_tag(provider)
+ - if auth_active?(provider)
+ - if unlink_allowed
+ = link_to unlink_profile_account_path(provider: provider), method: :delete, class: 'provider-btn' do
+ = s_('Profiles|Disconnect')
+ - else
+ %a.provider-btn
+ = s_('Profiles|Active')
+ - elsif link_allowed
+ = link_to omniauth_authorize_path(:user, provider), method: :post, class: 'provider-btn not-active' do
+ = s_('Profiles|Connect')
+ = render_if_exists 'profiles/accounts/group_saml_unlink_buttons', group_saml_identities: group_saml_identities
diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml
index ee2c5a13b8a..e6380817c8f 100644
--- a/app/views/profiles/accounts/show.html.haml
+++ b/app/views/profiles/accounts/show.html.haml
@@ -29,24 +29,7 @@
%p
= s_('Profiles|Activate signin with one of the following services')
.col-lg-8
- %label.label-bold
- = s_('Profiles|Connected Accounts')
- %p= s_('Profiles|Click on icon to activate signin with one of the following services')
- - button_based_providers.each do |provider|
- .provider-btn-group
- .provider-btn-image
- = provider_image_tag(provider)
- - if auth_active?(provider)
- - if unlink_allowed?(provider)
- = link_to unlink_profile_account_path(provider: provider), method: :delete, class: 'provider-btn' do
- = s_('Profiles|Disconnect')
- - else
- %a.provider-btn
- = s_('Profiles|Active')
- - else
- = link_to omniauth_authorize_path(:user, provider), method: :post, class: 'provider-btn not-active' do
- = s_('Profiles|Connect')
- = render_if_exists 'profiles/accounts/group_saml_unlink_buttons', group_saml_identities: local_assigns[:group_saml_identities]
+ = render 'providers', providers: button_based_providers, group_saml_identities: local_assigns[:group_saml_identities]
%hr
- if current_user.can_change_username?
.row.prepend-top-default
diff --git a/app/views/profiles/notifications/_email_settings.html.haml b/app/views/profiles/notifications/_email_settings.html.haml
new file mode 100644
index 00000000000..34dcf8f5402
--- /dev/null
+++ b/app/views/profiles/notifications/_email_settings.html.haml
@@ -0,0 +1,6 @@
+- form = local_assigns.fetch(:form)
+.form-group
+ = form.label :notification_email, class: "label-bold"
+ = form.select :notification_email, @user.all_emails, { include_blank: false }, class: "select2", disabled: local_assigns.fetch(:email_change_disabled, nil)
+ .help-block
+ = local_assigns.fetch(:help_text, nil)
diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml
index 712eb2a4573..e616e5546b3 100644
--- a/app/views/profiles/notifications/show.html.haml
+++ b/app/views/profiles/notifications/show.html.haml
@@ -22,9 +22,7 @@
Global notification settings
= form_for @user, url: profile_notifications_path, method: :put, html: { class: 'update-notifications prepend-top-default' } do |f|
- .form-group
- = f.label :notification_email, class: "label-bold"
- = f.select :notification_email, @user.all_emails, { include_blank: false }, class: "select2"
+ = render_if_exists 'profiles/notifications/email_settings', form: f
= label_tag :global_notification_level, "Global notification level", class: "label-bold"
%br
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index 4d3d92d09c0..1fffea08dae 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -83,18 +83,7 @@
= f.text_field :name, label: 'Full name', required: true, title: s_("Profiles|Using emojis in names seems fun, but please try to set a status message instead"), wrapper: { class: 'col-md-9 qa-full-name' }, help: s_("Profiles|Enter your name, so people you know can recognize you")
= f.text_field :id, readonly: true, label: 'User ID', wrapper: { class: 'col-md-3' }
- - if @user.read_only_attribute?(:email)
- = f.text_field :email, required: true, class: 'input-lg', readonly: true, help: s_("Profiles|Your email address was automatically set based on your %{provider_label} account") % { provider_label: attribute_provider_label(:email) }
- - else
- = f.text_field :email, required: true, class: 'input-lg', value: (@user.email unless @user.temp_oauth_email?),
- help: user_email_help_text(@user)
- = f.select :public_email, options_for_select(@user.all_emails, selected: @user.public_email),
- { help: s_("Profiles|This email will be displayed on your public profile"), include_blank: s_("Profiles|Do not show on profile") },
- control_class: 'select2 input-lg'
- - commit_email_docs_link = link_to s_('Profiles|Learn more'), help_page_path('user/profile/index', anchor: 'commit-email', target: '_blank')
- = f.select :commit_email, options_for_select(commit_email_select_options(@user), selected: selected_commit_email(@user)),
- { help: s_("Profiles|This email will be used for web based operations, such as edits and merges. %{learn_more}").html_safe % { learn_more: commit_email_docs_link } },
- control_class: 'select2 input-lg'
+ = render_if_exists 'profiles/email_settings', form: f
= f.text_field :skype, class: 'input-md', placeholder: s_("Profiles|username")
= f.text_field :linkedin, class: 'input-md', help: s_("Profiles|Your LinkedIn profile name from linkedin.com/in/profilename")
= f.text_field :twitter, class: 'input-md', placeholder: s_("Profiles|@username")
diff --git a/app/views/search/_results.html.haml b/app/views/search/_results.html.haml
index be7a2436d16..2e62039b90a 100644
--- a/app/views/search/_results.html.haml
+++ b/app/views/search/_results.html.haml
@@ -20,7 +20,7 @@
.search-results
- if @scope == 'projects'
.term
- = render 'shared/projects/list', projects: @search_objects
+ = render 'shared/projects/list', projects: @search_objects, pipeline_status: false
- else
= render partial: "search/results/#{@scope.singularize}", collection: @search_objects
diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml
index d2b1be29eb9..90fb067e75d 100644
--- a/app/views/shared/projects/_project.html.haml
+++ b/app/views/shared/projects/_project.html.haml
@@ -10,7 +10,7 @@
- show_last_commit_as_description = false unless local_assigns[:show_last_commit_as_description] == true && can_show_last_commit_in_list?(project)
- css_class = '' unless local_assigns[:css_class]
- css_class += " no-description" if project.description.blank? && !show_last_commit_as_description
-- cache_key = project_list_cache_key(project)
+- cache_key = project_list_cache_key(project, pipeline_status: pipeline_status)
- 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'
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index b2d88567e0e..6ebd756d3da 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -71,6 +71,7 @@
- pipeline_hooks:build_hooks
- pipeline_hooks:pipeline_hooks
- pipeline_processing:build_finished
+- pipeline_processing:ci_build_prepare
- pipeline_processing:build_queue
- pipeline_processing:build_success
- pipeline_processing:pipeline_process
diff --git a/app/workers/ci/build_prepare_worker.rb b/app/workers/ci/build_prepare_worker.rb
new file mode 100644
index 00000000000..1a35a74ae53
--- /dev/null
+++ b/app/workers/ci/build_prepare_worker.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module Ci
+ class BuildPrepareWorker
+ include ApplicationWorker
+ include PipelineQueue
+
+ queue_namespace :pipeline_processing
+
+ def perform(build_id)
+ Ci::Build.find_by_id(build_id).try do |build|
+ Ci::PrepareBuildService.new(build).execute
+ end
+ end
+ end
+end
diff --git a/app/workers/cluster_configure_worker.rb b/app/workers/cluster_configure_worker.rb
index 63e6cc147be..b984dee5b21 100644
--- a/app/workers/cluster_configure_worker.rb
+++ b/app/workers/cluster_configure_worker.rb
@@ -5,6 +5,8 @@ class ClusterConfigureWorker
include ClusterQueue
def perform(cluster_id)
+ return if Feature.enabled?(:ci_preparing_state, default_enabled: true)
+
Clusters::Cluster.find_by_id(cluster_id).try do |cluster|
Clusters::RefreshService.create_or_update_namespaces_for_cluster(cluster)
end
diff --git a/app/workers/cluster_project_configure_worker.rb b/app/workers/cluster_project_configure_worker.rb
index 497e57c0d0b..d7bea69a01c 100644
--- a/app/workers/cluster_project_configure_worker.rb
+++ b/app/workers/cluster_project_configure_worker.rb
@@ -5,6 +5,8 @@ class ClusterProjectConfigureWorker
include ClusterQueue
def perform(project_id)
+ return if Feature.enabled?(:ci_preparing_state, default_enabled: true)
+
project = Project.find(project_id)
::Clusters::RefreshService.create_or_update_namespaces_for_project(project)
diff --git a/changelogs/unreleased/57115-just-in-time-k8s-resource-creation.yml b/changelogs/unreleased/57115-just-in-time-k8s-resource-creation.yml
new file mode 100644
index 00000000000..2141c75ec72
--- /dev/null
+++ b/changelogs/unreleased/57115-just-in-time-k8s-resource-creation.yml
@@ -0,0 +1,5 @@
+---
+title: Create Kubernetes resources for projects when their deployment jobs run.
+merge_request: 25586
+author:
+type: changed
diff --git a/changelogs/unreleased/58410-change-pixel-size-of-instance-header-footer-message-to-16px.yml b/changelogs/unreleased/58410-change-pixel-size-of-instance-header-footer-message-to-16px.yml
new file mode 100644
index 00000000000..3e494847e75
--- /dev/null
+++ b/changelogs/unreleased/58410-change-pixel-size-of-instance-header-footer-message-to-16px.yml
@@ -0,0 +1,5 @@
+---
+title: Reduce height of instance system header and footer
+merge_request: 25752
+author:
+type: changed
diff --git a/changelogs/unreleased/58883-fix-fetching-comments.yml b/changelogs/unreleased/58883-fix-fetching-comments.yml
new file mode 100644
index 00000000000..14c0f1687f2
--- /dev/null
+++ b/changelogs/unreleased/58883-fix-fetching-comments.yml
@@ -0,0 +1,5 @@
+---
+title: Fix error shown when loading links to specific comments
+merge_request: 26092
+author:
+type: fixed
diff --git a/changelogs/unreleased/59057-buttons-on-top-from-a-user-profile-page-on-mobile.yml b/changelogs/unreleased/59057-buttons-on-top-from-a-user-profile-page-on-mobile.yml
new file mode 100644
index 00000000000..febbbce2139
--- /dev/null
+++ b/changelogs/unreleased/59057-buttons-on-top-from-a-user-profile-page-on-mobile.yml
@@ -0,0 +1,5 @@
+---
+title: Improve mobile UI on User Profile page
+merge_request: 26240
+author: Takuya Noguchi
+type: other
diff --git a/changelogs/unreleased/59208-fix-error-500-on-every-page-when-active-broadcast-message-present-after-upgrading-to-11-9-0.yml b/changelogs/unreleased/59208-fix-error-500-on-every-page-when-active-broadcast-message-present-after-upgrading-to-11-9-0.yml
new file mode 100644
index 00000000000..3c9feae5a04
--- /dev/null
+++ b/changelogs/unreleased/59208-fix-error-500-on-every-page-when-active-broadcast-message-present-after-upgrading-to-11-9-0.yml
@@ -0,0 +1,6 @@
+---
+title: Gracefully handles excluded fields from attributes during serialization on
+ JsonCache
+merge_request: 26368
+author:
+type: fixed
diff --git a/changelogs/unreleased/avoid_es_loading_project_ci_status.yml b/changelogs/unreleased/avoid_es_loading_project_ci_status.yml
new file mode 100644
index 00000000000..514909c730d
--- /dev/null
+++ b/changelogs/unreleased/avoid_es_loading_project_ci_status.yml
@@ -0,0 +1,5 @@
+---
+title: Avoid loading pipeline status in project search
+merge_request: 26342
+author:
+type: performance
diff --git a/changelogs/unreleased/do-not-force-2fa.yml b/changelogs/unreleased/do-not-force-2fa.yml
new file mode 100644
index 00000000000..f9be40e8f37
--- /dev/null
+++ b/changelogs/unreleased/do-not-force-2fa.yml
@@ -0,0 +1,6 @@
+---
+title: Add link on two-factor authorization settings page to leave group that enforces
+ two-factor authorization
+merge_request: 25731
+author:
+type: changed
diff --git a/changelogs/unreleased/k8s_new_deployment_labels.yml b/changelogs/unreleased/k8s_new_deployment_labels.yml
new file mode 100644
index 00000000000..e9ef3ee0082
--- /dev/null
+++ b/changelogs/unreleased/k8s_new_deployment_labels.yml
@@ -0,0 +1,5 @@
+---
+title: Update deploy boards to additionally select on "app.gitlab.com" annotations
+merge_request: 25623
+author:
+type: changed
diff --git a/changelogs/unreleased/security-2826-fix-project-serialization-in-quick-actions.yml b/changelogs/unreleased/security-2826-fix-project-serialization-in-quick-actions.yml
new file mode 100644
index 00000000000..272f8a95957
--- /dev/null
+++ b/changelogs/unreleased/security-2826-fix-project-serialization-in-quick-actions.yml
@@ -0,0 +1,5 @@
+---
+title: Remove project serialization in quick actions response
+merge_request:
+author:
+type: security
diff --git a/doc/administration/container_registry.md b/doc/administration/container_registry.md
index a1ac4a2a57c..b21bfafc096 100644
--- a/doc/administration/container_registry.md
+++ b/doc/administration/container_registry.md
@@ -1,6 +1,7 @@
# GitLab Container Registry administration
> **Notes:**
+>
> - [Introduced][ce-4040] in GitLab 8.8.
> - Container Registry manifest `v1` support was added in GitLab 8.9 to support
> Docker versions earlier than 1.10.
diff --git a/doc/administration/high_availability/redis.md b/doc/administration/high_availability/redis.md
index a52bc5c3b02..3daebc4d84b 100644
--- a/doc/administration/high_availability/redis.md
+++ b/doc/administration/high_availability/redis.md
@@ -14,6 +14,7 @@ a hosted cloud solution or you can use the one that comes bundled with
Omnibus GitLab packages.
> **Notes:**
+>
> - Redis requires authentication for High Availability. See
> [Redis Security](https://redis.io/topics/security) documentation for more
> information. We recommend using a combination of a Redis password and tight
@@ -55,6 +56,7 @@ components below.
### High Availability with Sentinel
> **Notes:**
+>
> - Starting with GitLab `8.11`, you can configure a list of Redis Sentinel
> servers that will monitor a group of Redis servers to provide failover support.
> - Starting with GitLab `8.14`, the Omnibus GitLab Enterprise Edition package
@@ -231,6 +233,7 @@ Pick the one that suits your needs.
This is the section where we install and set up the new Redis instances.
> **Notes:**
+>
> - We assume that you have installed GitLab and all HA components from scratch. If you
> already have it installed and running, read how to
> [switch from a single-machine installation to Redis HA](#switching-from-an-existing-single-machine-installation-to-redis-ha).
diff --git a/doc/administration/job_artifacts.md b/doc/administration/job_artifacts.md
index 8522d046a92..e7792106f81 100644
--- a/doc/administration/job_artifacts.md
+++ b/doc/administration/job_artifacts.md
@@ -1,6 +1,7 @@
# Jobs artifacts administration
> **Notes:**
+>
> - Introduced in GitLab 8.2 and GitLab Runner 0.7.0.
> - Starting with GitLab 8.4 and GitLab Runner 1.0, the artifacts archive format changed to `ZIP`.
> - Starting with GitLab 8.17, builds are renamed to jobs.
@@ -86,6 +87,7 @@ _The artifacts are stored by default in
### Using object storage
> **Notes:**
+>
> - [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/1762) in
> [GitLab Premium](https://about.gitlab.com/pricing/) 9.4.
> - Since version 9.5, artifacts are [browsable](../user/project/pipelines/job_artifacts.md#browsing-artifacts),
diff --git a/doc/administration/monitoring/prometheus/index.md b/doc/administration/monitoring/prometheus/index.md
index 20d7ef9bb74..f2ac155a694 100644
--- a/doc/administration/monitoring/prometheus/index.md
+++ b/doc/administration/monitoring/prometheus/index.md
@@ -1,6 +1,7 @@
# Monitoring GitLab with Prometheus
> **Notes:**
+>
> - Prometheus and the various exporters listed in this page are bundled in the
> Omnibus GitLab package. Check each exporter's documentation for the timeline
> they got added. For installations from source you will have to install them
diff --git a/doc/administration/pages/index.md b/doc/administration/pages/index.md
index 279ad018aed..288ce376687 100644
--- a/doc/administration/pages/index.md
+++ b/doc/administration/pages/index.md
@@ -5,6 +5,7 @@ description: 'Learn how to administer GitLab Pages.'
# GitLab Pages administration
> **Notes:**
+>
> - [Introduced][ee-80] in GitLab EE 8.3.
> - Custom CNAMEs with TLS support were [introduced][ee-173] in GitLab EE 8.5.
> - GitLab Pages [were ported][ce-14605] to Community Edition in GitLab 8.17.
diff --git a/doc/administration/repository_storage_types.md b/doc/administration/repository_storage_types.md
index 40f7c5566ac..25c3d564560 100644
--- a/doc/administration/repository_storage_types.md
+++ b/doc/administration/repository_storage_types.md
@@ -86,6 +86,11 @@ by another folder with the next 2 characters. They are both stored in a special
### Hashed object pools
+CAUTION: **Beta:**
+Hashed objects pools are considered beta, and are not ready for production use.
+Follow [gitaly#1548](https://gitlab.com/gitlab-org/gitaly/issues/1548) for
+updates.
+
For deduplication of public forks and their parent repository, objects are pooled
in an object pool. These object pools are a third repository where shared objects
are stored.
diff --git a/doc/administration/uploads.md b/doc/administration/uploads.md
index 8c0c7a36736..708b59a273b 100644
--- a/doc/administration/uploads.md
+++ b/doc/administration/uploads.md
@@ -18,7 +18,7 @@ below.
>**Notes:**
For historical reasons, uploads are stored into a base directory, which by default is `uploads/-/system`. It is strongly discouraged to change this configuration option on an existing GitLab installation.
-_The uploads are stored by default in `/var/opt/gitlab/gitlab-rails/uploads/-/system`._
+_The uploads are stored by default in `/var/opt/gitlab/gitlab-rails/uploads`._
1. To change the storage path for example to `/mnt/storage/uploads`, edit
`/etc/gitlab/gitlab.rb` and add the following line:
diff --git a/doc/api/group_milestones.md b/doc/api/group_milestones.md
index eb974267084..1c2f56581eb 100644
--- a/doc/api/group_milestones.md
+++ b/doc/api/group_milestones.md
@@ -1,6 +1,5 @@
# Group milestones API
-> **Notes:**
> [Introduced][ce-12819] in GitLab 9.5.
## List group milestones
diff --git a/doc/api/project_snippets.md b/doc/api/project_snippets.md
index f02674adfe2..0ccb0517e08 100644
--- a/doc/api/project_snippets.md
+++ b/doc/api/project_snippets.md
@@ -121,7 +121,6 @@ Parameters:
## Get user agent details
-> **Notes:**
> [Introduced][ce-29508] in GitLab 9.4.
Available only for admins.
diff --git a/doc/api/services.md b/doc/api/services.md
index c44f5cc5781..03d0a80aa64 100644
--- a/doc/api/services.md
+++ b/doc/api/services.md
@@ -505,10 +505,9 @@ GET /projects/:id/services/jira
Set JIRA service for a project.
-> **Notes:**
-> - Starting with GitLab 8.14, `api_url`, `issues_url`, `new_issue_url` and
-> `project_url` are replaced by `project_key`, `url`. If you are using an
-> older version, [follow this documentation][old-jira-api].
+> Starting with GitLab 8.14, `api_url`, `issues_url`, `new_issue_url` and
+> `project_url` are replaced by `project_key`, `url`. If you are using an
+> older version, [follow this documentation][old-jira-api].
```
PUT /projects/:id/services/jira
diff --git a/doc/ci/chatops/README.md b/doc/ci/chatops/README.md
index df7fb8a4912..a06fe6961a7 100644
--- a/doc/ci/chatops/README.md
+++ b/doc/ci/chatops/README.md
@@ -2,9 +2,9 @@
> **Notes:**
>
-> * [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/4466) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 10.6. [Moved](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/24780) to [GitLab Core](https://about.gitlab.com/pricing/) in 11.9.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/4466) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 10.6. [Moved](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/24780) to [GitLab Core](https://about.gitlab.com/pricing/) in 11.9.
>
-> * ChatOps is currently in alpha, with some important features missing like access control.
+> - ChatOps is currently in alpha, with some important features missing like access control.
GitLab ChatOps provides a method to interact with CI/CD jobs through chat services like Slack. Many organizations' discussion, collaboration, and troubleshooting is taking place in chat services these days, and having a method to run CI/CD jobs with output posted back to the channel can significantly augment a team's workflow.
diff --git a/doc/ci/docker/using_docker_build.md b/doc/ci/docker/using_docker_build.md
index 4f9efb57b8d..9266c4511be 100644
--- a/doc/ci/docker/using_docker_build.md
+++ b/doc/ci/docker/using_docker_build.md
@@ -389,6 +389,7 @@ If you're running multiple Runners you will have to modify all configuration fil
## Using the GitLab Container Registry
> **Notes:**
+>
> - This feature requires GitLab 8.8 and GitLab Runner 1.2.
> - Starting from GitLab 8.12, if you have [2FA] enabled in your account, you need
> to pass a [personal access token][pat] instead of your password in order to
diff --git a/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md b/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md
index 90a8f5917f8..908cf85980e 100644
--- a/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md
+++ b/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md
@@ -4,7 +4,7 @@ author_gitlab: blitzgren
level: intermediate
article_type: tutorial
date: 2018-03-07
-last_updated: 2019-03-06
+last_updated: 2019-03-11
---
# DevOps and Game Dev with GitLab CI/CD
@@ -21,7 +21,7 @@ and the basics of game development.
Our [demo game](http://gitlab-game-demo.s3-website-us-east-1.amazonaws.com/) consists of a simple spaceship traveling in space that shoots by clicking the mouse in a given direction.
-Creating a strong CI/CD pipeline at the beginning of developing another game, [Dark Nova](http://darknova.io/about),
+Creating a strong CI/CD pipeline at the beginning of developing another game, [Dark Nova](http://darknova.io/),
was essential for the fast pace the team worked at. This tutorial will build upon my
[previous introductory article](https://ryanhallcs.wordpress.com/2017/03/15/devops-and-game-dev/) and go through the following steps:
@@ -38,8 +38,8 @@ This will also provide
boilerplate code for starting a browser-based game with the following components:
- Written in [Typescript](https://www.typescriptlang.org/) and [PhaserJs](https://phaser.io)
-- Building, running, and testing with [Gulp](http://gulpjs.com/)
-- Unit tests with [Chai](http://chaijs.com/) and [Mocha](https://mochajs.org/)
+- Building, running, and testing with [Gulp](https://gulpjs.com)
+- Unit tests with [Chai](https://www.chaijs.com) and [Mocha](https://mochajs.org/)
- CI/CD with GitLab
- Hosting the codebase on GitLab.com
- Hosting the game on AWS
@@ -318,7 +318,7 @@ allowing us to run our tests by every push.
Our entire `.gitlab-ci.yml` file should now look like this:
```yml
-image: node:6
+image: node:10
build:
stage: build
@@ -413,7 +413,7 @@ use root security credentials. Proper IAM credential management is beyond the sc
article, but AWS will remind you that using root credentials is unadvised and against their
best practices, as they should. Feel free to follow best practices and use a custom IAM user's
credentials, which will be the same two credentials (Key ID and Secret). It's a good idea to
-fully understand [IAM Best Practices in AWS](http://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html). We need to add these credentials to GitLab:
+fully understand [IAM Best Practices in AWS](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html). We need to add these credentials to GitLab:
1. Log into your AWS account and go to the [Security Credentials page](https://console.aws.amazon.com/iam/home#/security_credential)
1. Click the **Access Keys** section and **Create New Access Key**. Create the key and keep the id and secret around, you'll need them later
@@ -455,7 +455,7 @@ Be sure to update the region and S3 URL in that last script command to fit your
Our final configuration file `.gitlab-ci.yml` looks like:
```yml
-image: node:6
+image: node:10
build:
stage: build
@@ -503,7 +503,7 @@ deploy:
## Conclusion
Within the [demo repository](https://gitlab.com/blitzgren/gitlab-game-demo) you can also find a handful of boilerplate code to get
-[Typescript](https://www.typescriptlang.org/), [Mocha](https://mochajs.org/), [Gulp](http://gulpjs.com/) and [Phaser](https://phaser.io) all playing
+[Typescript](https://www.typescriptlang.org/), [Mocha](https://mochajs.org/), [Gulp](https://gulpjs.com/) and [Phaser](https://phaser.io) all playing
together nicely with GitLab CI/CD, which is the result of lessons learned while making [Dark Nova](http://darknova.io/).
Using a combination of free and open source software, we have a full CI/CD pipeline, a game foundation,
and unit tests, all running and deployed at every push to master - with shockingly little code.
@@ -522,6 +522,6 @@ Here are some ideas to further investigate that can speed up or improve your pip
- [Yarn](https://yarnpkg.com) instead of npm
- Set up a custom [Docker](../../../ci/docker/using_docker_images.md#define-image-and-services-from-gitlab-ciyml) image that can preload dependencies and tools (like AWS CLI)
-- Forward a [custom domain](http://docs.aws.amazon.com/AmazonS3/latest/dev/website-hosting-custom-domain-walkthrough.html) to your game's S3 static website
+- Forward a [custom domain](https:/docs.aws.amazon.com/AmazonS3/latest/dev/website-hosting-custom-domain-walkthrough.html) to your game's S3 static website
- Combine jobs if you find it unnecessary for a small project
- Avoid the queues and set up your own [custom GitLab CI/CD runner](https://about.gitlab.com/2016/03/01/gitlab-runner-with-docker/)
diff --git a/doc/development/testing_guide/frontend_testing.md b/doc/development/testing_guide/frontend_testing.md
index 3634e648219..71c9637e72c 100644
--- a/doc/development/testing_guide/frontend_testing.md
+++ b/doc/development/testing_guide/frontend_testing.md
@@ -225,14 +225,12 @@ See this [section][vue-test].
### Running frontend tests
-`rake karma` runs the frontend-only (JavaScript) tests.
-It consists of two subtasks:
+For running the frontend tests, you need the following commands:
-- `rake karma:fixtures` (re-)generates fixtures
-- `rake karma:tests` actually executes the tests
+- `rake karma:fixtures` (re-)generates fixtures.
+- `yarn test` executes the tests.
-As long as the fixtures don't change, `rake karma:tests` (or `yarn karma`)
-is sufficient (and saves you some time).
+As long as the fixtures don't change, `yarn test` is sufficient (and saves you some time).
### Live testing and focused testing
diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md
index c78cfc41678..574ba961cb0 100644
--- a/doc/raketasks/backup_restore.md
+++ b/doc/raketasks/backup_restore.md
@@ -409,6 +409,8 @@ an access key from the Google console first:
1. Select "Interoperability" and create an access key
1. Make note of the "Access Key" and "Secret" and replace them in the
configurations below
+1. In the buckets advanced settings ensure the Access Control option "Set object-level
+ and bucket-level permissions" is selected
1. Make sure you already have a bucket created
For Omnibus GitLab packages:
diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md
index 43aa4163ab1..fb3f9711711 100644
--- a/doc/topics/autodevops/index.md
+++ b/doc/topics/autodevops/index.md
@@ -1022,6 +1022,10 @@ planned for a subsequent release.
buildpack](#custom-buildpacks).
- Auto Test may fail because of a mismatch between testing frameworks. In this
case, you may need to customize your `.gitlab-ci.yml` with your test commands.
+- Auto Deploy may fail if it is unable to create a Kubernetes namespace and
+ service account for your project. See the
+ [troubleshooting failed deployments](../../user/project/clusters/index.md#troubleshooting-failed-deployment-jobs)
+ section to debug why these resources were not created.
### Disable the banner instance wide
diff --git a/doc/user/discussions/index.md b/doc/user/discussions/index.md
index a15e1a59921..cf0dc51ea23 100644
--- a/doc/user/discussions/index.md
+++ b/doc/user/discussions/index.md
@@ -19,13 +19,14 @@ comment at any time, and anyone with [Maintainer access level][permissions] or
higher can also edit a comment made by someone else.
You can also reply to a comment notification email to reply to the comment if
-[Reply by email] is configured for your GitLab instance. Replying to a standard comment
+[Reply by email] is configured for your GitLab instance. Replying to a standard comment
creates another standard comment. Replying to a discussion comment creates a reply in the
discussion thread. Email replies support [Markdown] and [quick actions], just as if you replied from the web.
## Resolvable comments and discussions
> **Notes:**
+>
> - The main feature was [introduced][ce-5022] in GitLab 8.11.
> - Resolvable discussions can be added only to merge request diffs.
@@ -357,7 +358,7 @@ Clicking on the **Reply to comment** button will bring the reply area into focus
![Reply to comment feature](img/reply_to_comment.gif)
-Relying to a non-discussion comment will convert the non-discussion comment to a
+Relying to a non-discussion comment will convert the non-discussion comment to a
threaded discussion once the reply is submitted. This conversion is considered an edit
to the original comment, so a note about when it was last edited will appear underneath it.
diff --git a/doc/user/group/index.md b/doc/user/group/index.md
index 1fe8017adbc..3bcfd30079d 100644
--- a/doc/user/group/index.md
+++ b/doc/user/group/index.md
@@ -63,9 +63,8 @@ together in a single list view.
## Create a new group
-> **Notes:**
-> - For a list of words that are not allowed to be used as group names see the
-> [reserved names](../reserved_names.md).
+> For a list of words that are not allowed to be used as group names see the
+> [reserved names](../reserved_names.md).
You can create a group in GitLab from:
diff --git a/doc/user/profile/account/two_factor_authentication.md b/doc/user/profile/account/two_factor_authentication.md
index 8e5fe4b0fb9..b74bd81d467 100644
--- a/doc/user/profile/account/two_factor_authentication.md
+++ b/doc/user/profile/account/two_factor_authentication.md
@@ -59,6 +59,7 @@ of recovery codes.
### Enable 2FA via U2F device
> **Notes:**
+>
> - GitLab officially only supports [Yubikey] U2F devices.
> - Support for U2F devices was added in GitLab 8.8.
diff --git a/doc/user/project/bulk_editing.md b/doc/user/project/bulk_editing.md
index fead99c5e88..d0c7daf4692 100644
--- a/doc/user/project/bulk_editing.md
+++ b/doc/user/project/bulk_editing.md
@@ -1,6 +1,7 @@
# Bulk editing issues and merge requests
> **Notes:**
+>
> - A permission level of `Reporter` or higher is required in order to manage
> issues.
> - A permission level of `Developer` or higher is required in order to manage
diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md
index b05e8777b40..44b9e5cccb5 100644
--- a/doc/user/project/clusters/index.md
+++ b/doc/user/project/clusters/index.md
@@ -556,26 +556,27 @@ NOTE: **NOTE:**
Prior to GitLab 11.5, `KUBE_TOKEN` was the Kubernetes token of the main
service account of the cluster integration.
-### Troubleshooting missing `KUBECONFIG` or `KUBE_TOKEN`
+### Troubleshooting failed deployment jobs
-GitLab will create a new service account specifically for your CI builds. The
-new service account is created when the cluster is added to the project.
-Sometimes there may be errors that cause the service account creation to fail.
+GitLab will create a namespace and service account specifically for your
+deployment jobs. These resources are created just before the deployment
+job starts. Sometimes there may be errors that cause their creation to fail.
-In such instances, your build will not be passed the `KUBECONFIG` or
-`KUBE_TOKEN` variables and, if you are using Auto DevOps, your Auto DevOps
-pipelines will no longer trigger a `production` deploy build. You will need to
-check the [logs](../../../administration/logs.md) to debug why the service
-account creation failed.
+In such instances, your job will fail with the message:
+
+```The job failed to complete prerequisite tasks```
+
+You will need to check the [logs](../../../administration/logs.md) to debug
+why the namespace and service account creation failed.
A common reason for failure is that the token you gave GitLab did not have
[`cluster-admin`](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#user-facing-roles)
privileges as GitLab expects.
-Another common problem for why these variables are not being passed to your
-builds is that they must have a matching
+Another common problem is caused by a missing `KUBECONFIG` or `KUBE_TOKEN`.
+To be passed to your job, it must have a matching
[`environment:name`](../../../ci/environments.md#defining-environments). If
-your build has no `environment:name` set, it will not be passed the Kubernetes
+your job has no `environment:name` set, it will not be passed the Kubernetes
credentials.
## Monitoring your Kubernetes cluster **[ULTIMATE]**
diff --git a/doc/user/project/container_registry.md b/doc/user/project/container_registry.md
index dec6eac2508..83b268db967 100644
--- a/doc/user/project/container_registry.md
+++ b/doc/user/project/container_registry.md
@@ -1,7 +1,8 @@
# GitLab Container Registry
> **Notes:**
-> [Introduced][ce-4040] in GitLab 8.8.
+>
+> - [Introduced][ce-4040] in GitLab 8.8.
> - Docker Registry manifest `v1` support was added in GitLab 8.9 to support Docker
> versions earlier than 1.10.
> - This document is about the user guide. To learn how to enable GitLab Container
@@ -10,7 +11,7 @@
> - Starting from GitLab 8.12, if you have 2FA enabled in your account, you need
> to pass a [personal access token][pat] instead of your password in order to
> login to GitLab's Container Registry.
-> - Multiple level image names support was added in GitLab 9.1
+> - Multiple level image names support was added in GitLab 9.1.
With the Docker Container Registry integrated into GitLab, every project can
have its own space to store its Docker images.
@@ -41,6 +42,7 @@ to enable it.
## Build and push images
> **Notes:**
+>
> - Moving or renaming existing container registry repositories is not supported
> once you have pushed images because the images are signed, and the
> signature includes the repository name.
diff --git a/doc/user/project/integrations/jira.md b/doc/user/project/integrations/jira.md
index 754711f5919..a90167b9767 100644
--- a/doc/user/project/integrations/jira.md
+++ b/doc/user/project/integrations/jira.md
@@ -56,6 +56,7 @@ When connecting to **JIRA Cloud**, which supports authentication via API token,
### Configuring GitLab
> **Notes:**
+>
> - The currently supported Jira versions are `v6.x` and `v7.x.`. GitLab 7.8 or
> higher is required.
> - GitLab 8.14 introduced a new way to integrate with Jira which greatly simplified
@@ -142,6 +143,7 @@ the same goal:
where `PROJECT-1` is the issue ID of the Jira project.
> **Notes:**
+>
> - Only commits and merges into the project's default branch (usually **master**) will
> close an issue in Jira. You can change your projects default branch under
> [project settings](img/jira_project_settings.png).
diff --git a/doc/user/project/merge_requests/versions.md b/doc/user/project/merge_requests/versions.md
index 90500fd9c21..70bd1e60594 100644
--- a/doc/user/project/merge_requests/versions.md
+++ b/doc/user/project/merge_requests/versions.md
@@ -1,6 +1,7 @@
# Merge requests versions
> **Notes:**
+>
> - [Introduced][ce-5467] in GitLab 8.12.
> - Comments are disabled while viewing outdated merge versions or comparing to
> versions other than base.
diff --git a/doc/user/project/new_ci_build_permissions_model.md b/doc/user/project/new_ci_build_permissions_model.md
index d41b65f7985..6c3fa5eb463 100644
--- a/doc/user/project/new_ci_build_permissions_model.md
+++ b/doc/user/project/new_ci_build_permissions_model.md
@@ -205,6 +205,7 @@ With the update permission model we also extended the support for accessing
Container Registries for private projects.
> **Notes:**
+>
> - GitLab Runner versions prior to 1.8 don't incorporate the introduced changes
> for permissions. This makes the `image:` directive to not work with private
> projects automatically and it needs to be configured manually on Runner's host
diff --git a/doc/user/project/pages/introduction.md b/doc/user/project/pages/introduction.md
index f67ef1e6a46..ccb0300e23e 100644
--- a/doc/user/project/pages/introduction.md
+++ b/doc/user/project/pages/introduction.md
@@ -1,6 +1,7 @@
# Exploring GitLab Pages
> **Notes:**
+>
> - This feature was [introduced][ee-80] in GitLab EE 8.3.
> - Custom CNAMEs with TLS support were [introduced][ee-173] in GitLab EE 8.5.
> - GitLab Pages [was ported][ce-14605] to Community Edition in GitLab 8.17.
diff --git a/doc/user/project/pipelines/job_artifacts.md b/doc/user/project/pipelines/job_artifacts.md
index 5271c76fc24..a3f40c20192 100644
--- a/doc/user/project/pipelines/job_artifacts.md
+++ b/doc/user/project/pipelines/job_artifacts.md
@@ -1,6 +1,7 @@
# Introduction to job artifacts
> **Notes:**
+>
> - Since GitLab 8.2 and GitLab Runner 0.7.0, job artifacts that are created by
> GitLab Runner are uploaded to GitLab and are downloadable as a single archive
> (`tar.gz`) using the GitLab UI.
@@ -152,7 +153,7 @@ For example:
https://gitlab.com/gitlab-org/gitlab-ce/-/jobs/artifacts/master/browse?job=coverage
```
-There is also a URL to specific files, including html files that
+There is also a URL to specific files, including html files that
are shown in [GitLab Pages](../../../administration/pages/index.md):
```
@@ -191,9 +192,9 @@ artifacts and the job's trace.
1. Click the trash icon at the top right of the job's trace.
1. Confirm the deletion.
-## Retrieve artifacts of private projects when using GitLab CI
+## Retrieve artifacts of private projects when using GitLab CI
In order to retrieve a job artifact of a different project, you might need to use a private token in order to [authenticate and download](../../../api/jobs.md#get-job-artifacts) the artifacts.
[expiry date]: ../../../ci/yaml/README.md#artifactsexpire_in
-[ce-14399]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/14399 \ No newline at end of file
+[ce-14399]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/14399
diff --git a/doc/user/project/pipelines/schedules.md b/doc/user/project/pipelines/schedules.md
index 58a0fbc97cd..07ce4f3f5da 100644
--- a/doc/user/project/pipelines/schedules.md
+++ b/doc/user/project/pipelines/schedules.md
@@ -1,6 +1,7 @@
# Pipeline schedules
> **Notes**:
+>
> - This feature was introduced in 9.1 as [Trigger Schedule][ce-10533].
> - In 9.2, the feature was [renamed to Pipeline Schedule][ce-10853].
> - Cron notation is parsed by [Fugit](https://github.com/floraison/fugit).
diff --git a/lib/gitlab/auth/ldap/person.rb b/lib/gitlab/auth/ldap/person.rb
index 13d67e0f871..c1517222956 100644
--- a/lib/gitlab/auth/ldap/person.rb
+++ b/lib/gitlab/auth/ldap/person.rb
@@ -69,7 +69,7 @@ module Gitlab
end
def name
- attribute_value(:name).first
+ attribute_value(:name)&.first
end
def uid
diff --git a/lib/gitlab/badge/pipeline/template.rb b/lib/gitlab/badge/pipeline/template.rb
index 64c3dfcd10b..2c5f9654496 100644
--- a/lib/gitlab/badge/pipeline/template.rb
+++ b/lib/gitlab/badge/pipeline/template.rb
@@ -15,6 +15,7 @@ module Gitlab
failed: '#e05d44',
running: '#dfb317',
pending: '#dfb317',
+ preparing: '#dfb317',
canceled: '#9f9f9f',
skipped: '#9f9f9f',
unknown: '#9f9f9f'
diff --git a/lib/gitlab/ci/build/prerequisite/base.rb b/lib/gitlab/ci/build/prerequisite/base.rb
new file mode 100644
index 00000000000..156aa22d95b
--- /dev/null
+++ b/lib/gitlab/ci/build/prerequisite/base.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Build
+ module Prerequisite
+ class Base
+ include Utils::StrongMemoize
+
+ attr_reader :build
+
+ def initialize(build)
+ @build = build
+ end
+
+ def unmet?
+ raise NotImplementedError
+ end
+
+ def complete!
+ raise NotImplementedError
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/build/prerequisite/factory.rb b/lib/gitlab/ci/build/prerequisite/factory.rb
new file mode 100644
index 00000000000..60cdf7af418
--- /dev/null
+++ b/lib/gitlab/ci/build/prerequisite/factory.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Build
+ module Prerequisite
+ class Factory
+ attr_reader :build
+
+ def self.prerequisites
+ [KubernetesNamespace]
+ end
+
+ def initialize(build)
+ @build = build
+ end
+
+ def unmet
+ build_prerequisites.select(&:unmet?)
+ end
+
+ private
+
+ def build_prerequisites
+ self.class.prerequisites.map do |prerequisite|
+ prerequisite.new(build)
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/build/prerequisite/kubernetes_namespace.rb b/lib/gitlab/ci/build/prerequisite/kubernetes_namespace.rb
new file mode 100644
index 00000000000..41135ae62bb
--- /dev/null
+++ b/lib/gitlab/ci/build/prerequisite/kubernetes_namespace.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Build
+ module Prerequisite
+ class KubernetesNamespace < Base
+ def unmet?
+ deployment_cluster.present? && kubernetes_namespace.new_record?
+ end
+
+ def complete!
+ return unless unmet?
+
+ create_or_update_namespace
+ end
+
+ private
+
+ def deployment_cluster
+ build.deployment&.cluster
+ end
+
+ def kubernetes_namespace
+ strong_memoize(:kubernetes_namespace) do
+ deployment_cluster.find_or_initialize_kubernetes_namespace_for_project(build.project)
+ end
+ end
+
+ def create_or_update_namespace
+ Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService.new(
+ cluster: deployment_cluster,
+ kubernetes_namespace: kubernetes_namespace
+ ).execute
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/status/build/factory.rb b/lib/gitlab/ci/status/build/factory.rb
index 6e4bfe23f2b..f7d0715e617 100644
--- a/lib/gitlab/ci/status/build/factory.rb
+++ b/lib/gitlab/ci/status/build/factory.rb
@@ -11,6 +11,7 @@ module Gitlab
Status::Build::Manual,
Status::Build::Canceled,
Status::Build::Created,
+ Status::Build::Preparing,
Status::Build::Pending,
Status::Build::Skipped],
[Status::Build::Cancelable,
diff --git a/lib/gitlab/ci/status/build/failed.rb b/lib/gitlab/ci/status/build/failed.rb
index d40454df737..76dfe7b7639 100644
--- a/lib/gitlab/ci/status/build/failed.rb
+++ b/lib/gitlab/ci/status/build/failed.rb
@@ -15,7 +15,8 @@ module Gitlab
runner_unsupported: 'unsupported runner',
stale_schedule: 'stale schedule',
job_execution_timeout: 'job execution timeout',
- archived_failure: 'archived failure'
+ archived_failure: 'archived failure',
+ unmet_prerequisites: 'unmet prerequisites'
}.freeze
private_constant :REASONS
diff --git a/lib/gitlab/ci/status/build/preparing.rb b/lib/gitlab/ci/status/build/preparing.rb
new file mode 100644
index 00000000000..1fddcb05f79
--- /dev/null
+++ b/lib/gitlab/ci/status/build/preparing.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Status
+ module Build
+ class Preparing < Status::Extended
+ ##
+ # TODO: image is shared with 'pending'
+ # until we get a dedicated one
+ #
+ def illustration
+ {
+ image: 'illustrations/job_not_triggered.svg',
+ size: 'svg-306',
+ title: _('This job is preparing to start'),
+ content: _('This job is performing tasks that must complete before it can start')
+ }
+ end
+
+ def self.matches?(build, _)
+ build.preparing?
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/status/preparing.rb b/lib/gitlab/ci/status/preparing.rb
new file mode 100644
index 00000000000..62985d0a9f9
--- /dev/null
+++ b/lib/gitlab/ci/status/preparing.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Status
+ class Preparing < Status::Core
+ def text
+ s_('CiStatusText|preparing')
+ end
+
+ def label
+ s_('CiStatusLabel|preparing')
+ end
+
+ ##
+ # TODO: shared with 'created'
+ # until we get one for 'preparing'
+ #
+ def icon
+ 'status_created'
+ end
+
+ ##
+ # TODO: shared with 'created'
+ # until we get one for 'preparing'
+ #
+ def favicon
+ 'favicon_status_created'
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/templates/Android-Fastlane.gitlab-ci.yml b/lib/gitlab/ci/templates/Android-Fastlane.gitlab-ci.yml
index 9c534b2b8e7..8e767b22360 100644
--- a/lib/gitlab/ci/templates/Android-Fastlane.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Android-Fastlane.gitlab-ci.yml
@@ -118,4 +118,3 @@ promoteProduction:
- master
script:
- bundle exec fastlane promote_beta_to_production
- \ No newline at end of file
diff --git a/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml b/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
index cbe466a1c37..7ec786b6d5d 100644
--- a/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
@@ -697,6 +697,8 @@ rollout 100%:
helm upgrade --install \
--wait \
--set service.enabled="$service_enabled" \
+ --set gitlab.app="$CI_PROJECT_PATH_SLUG" \
+ --set gitlab.env="$CI_ENVIRONMENT_SLUG" \
--set releaseOverride="$CI_ENVIRONMENT_SLUG" \
--set image.repository="$CI_APPLICATION_REPOSITORY" \
--set image.tag="$CI_APPLICATION_TAG" \
@@ -734,6 +736,8 @@ rollout 100%:
helm upgrade --install \
--wait \
--set service.enabled="$service_enabled" \
+ --set gitlab.app="$CI_PROJECT_PATH_SLUG" \
+ --set gitlab.env="$CI_ENVIRONMENT_SLUG" \
--set releaseOverride="$CI_ENVIRONMENT_SLUG" \
--set image.repository="$CI_APPLICATION_REPOSITORY" \
--set image.tag="$CI_APPLICATION_TAG" \
diff --git a/lib/gitlab/ci/templates/Bash.gitlab-ci.yml b/lib/gitlab/ci/templates/Bash.gitlab-ci.yml
index 2d218b2e164..368069844ea 100644
--- a/lib/gitlab/ci/templates/Bash.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Bash.gitlab-ci.yml
@@ -7,28 +7,28 @@ before_script:
- echo "Before script section"
- echo "For example you might run an update here or install a build dependency"
- echo "Or perhaps you might print out some debugging details"
-
+
after_script:
- echo "After script section"
- echo "For example you might do some cleanup here"
-
+
build1:
stage: build
script:
- echo "Do your build here"
-
+
test1:
stage: test
- script:
+ script:
- echo "Do a test here"
- echo "For example run a test suite"
-
+
test2:
stage: test
- script:
+ script:
- echo "Do another parallel test here"
- echo "For example run a lint test"
-
+
deploy1:
stage: deploy
script:
diff --git a/lib/gitlab/ci/templates/C++.gitlab-ci.yml b/lib/gitlab/ci/templates/C++.gitlab-ci.yml
index c83c49d8c95..9a8fa9d7091 100644
--- a/lib/gitlab/ci/templates/C++.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/C++.gitlab-ci.yml
@@ -7,9 +7,9 @@ build:
stage: build
# instead of calling g++ directly you can also use some build toolkit like make
# install the necessary build tools when needed
- # before_script:
- # - apt update && apt -y install make autoconf
- script:
+ # before_script:
+ # - apt update && apt -y install make autoconf
+ script:
- g++ helloworld.cpp -o mybinary
artifacts:
paths:
diff --git a/lib/gitlab/ci/templates/Chef.gitlab-ci.yml b/lib/gitlab/ci/templates/Chef.gitlab-ci.yml
index 4d5b6484d6e..33507aa58e4 100644
--- a/lib/gitlab/ci/templates/Chef.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Chef.gitlab-ci.yml
@@ -41,7 +41,7 @@ chefspec:
# - apt-get -y install rsync
# script:
# - kitchen verify default-centos-6 --destroy=always
-#
+#
#verify-centos-7:
# stage: functional
# before_script:
diff --git a/lib/gitlab/ci/templates/Clojure.gitlab-ci.yml b/lib/gitlab/ci/templates/Clojure.gitlab-ci.yml
index f066285b1ad..0610cb9ccc0 100644
--- a/lib/gitlab/ci/templates/Clojure.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Clojure.gitlab-ci.yml
@@ -5,9 +5,9 @@ image: clojure:lein-2.7.0
# Make sure you configure the connection as well
before_script:
- # If you need to install any external applications, like a
+ # If you need to install any external applications, like a
# postgres client, you may want to uncomment the line below:
- #
+ #
#- apt-get update -y
#
# Retrieve project dependencies
@@ -17,6 +17,6 @@ before_script:
test:
script:
- # If you need to run any migrations or configure the database, this
- # would be the point to do it.
+ # If you need to run any migrations or configure the database, this
+ # would be the point to do it.
- lein test
diff --git a/lib/gitlab/ci/templates/Code-Quality.gitlab-ci.yml b/lib/gitlab/ci/templates/Code-Quality.gitlab-ci.yml
new file mode 100644
index 00000000000..10b25af904f
--- /dev/null
+++ b/lib/gitlab/ci/templates/Code-Quality.gitlab-ci.yml
@@ -0,0 +1,17 @@
+code_quality:
+ image: docker:stable
+ allow_failure: true
+ services:
+ - docker:stable-dind
+ variables:
+ DOCKER_DRIVER: overlay2
+ script:
+ - docker run
+ --env SOURCE_CODE="$PWD"
+ --volume "$PWD":/code
+ --volume /var/run/docker.sock:/var/run/docker.sock
+ "registry.gitlab.com/gitlab-org/security-products/codequality:11-8-stable" /code
+ artifacts:
+ reports:
+ codequality: gl-code-quality-report.json
+ expire_in: 1 week
diff --git a/lib/gitlab/ci/templates/Django.gitlab-ci.yml b/lib/gitlab/ci/templates/Django.gitlab-ci.yml
index 57afcbbe8b5..1d8be6f017e 100644
--- a/lib/gitlab/ci/templates/Django.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Django.gitlab-ci.yml
@@ -21,7 +21,7 @@ cache:
# This is a basic example for a gem or script which doesn't use
# services such as redis or postgres
before_script:
- - python -V # Print out python version for debugging
+ - python -V # Print out python version for debugging
# Uncomment next line if your Django app needs a JS runtime:
# - apt-get update -q && apt-get install nodejs -yqq
- pip install -r requirements.txt
diff --git a/lib/gitlab/ci/templates/Gradle.gitlab-ci.yml b/lib/gitlab/ci/templates/Gradle.gitlab-ci.yml
index 48d98dddfad..cbf4d58bdad 100644
--- a/lib/gitlab/ci/templates/Gradle.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Gradle.gitlab-ci.yml
@@ -23,7 +23,6 @@ build:
- build
- .gradle
-
test:
stage: test
script: gradle check
@@ -33,4 +32,3 @@ test:
paths:
- build
- .gradle
-
diff --git a/lib/gitlab/ci/templates/Grails.gitlab-ci.yml b/lib/gitlab/ci/templates/Grails.gitlab-ci.yml
index 7fc698d50cf..dbc868238f8 100644
--- a/lib/gitlab/ci/templates/Grails.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Grails.gitlab-ci.yml
@@ -13,7 +13,7 @@ image: java:8
variables:
GRAILS_VERSION: "3.1.9"
GRADLE_VERSION: "2.13"
-
+
# We use SDKMan as tool for managing versions
before_script:
- apt-get update -qq && apt-get install -y -qq unzip
@@ -23,10 +23,10 @@ before_script:
- sdk install gradle $GRADLE_VERSION < /dev/null
- sdk use gradle $GRADLE_VERSION
# As it's not a good idea to version gradle.properties feel free to add your
-# environments variable here
+# environments variable here
- echo grailsVersion=$GRAILS_VERSION > gradle.properties
- echo gradleWrapperVersion=2.14 >> gradle.properties
-# refresh dependencies from your project
+# refresh dependencies from your project
- ./gradlew --refresh-dependencies
# Be aware that if you are using Angular profile,
# Bower cannot be run as root if you don't allow it before.
@@ -36,5 +36,5 @@ before_script:
# This build job does the full grails pipeline
# (compile, test, integrationTest, war, assemble).
build:
- script:
- - ./gradlew build \ No newline at end of file
+ script:
+ - ./gradlew build
diff --git a/lib/gitlab/ci/templates/Julia.gitlab-ci.yml b/lib/gitlab/ci/templates/Julia.gitlab-ci.yml
index 04c21b4725d..2c4683fbfbb 100644
--- a/lib/gitlab/ci/templates/Julia.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Julia.gitlab-ci.yml
@@ -30,7 +30,7 @@
test:0.7:
image: julia:0.7
<<: *test_definition
-
+
test:1.0:
image: julia:1.0
<<: *test_definition
diff --git a/lib/gitlab/ci/templates/Laravel.gitlab-ci.yml b/lib/gitlab/ci/templates/Laravel.gitlab-ci.yml
index d0cad285572..e1cd29ecc94 100644
--- a/lib/gitlab/ci/templates/Laravel.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Laravel.gitlab-ci.yml
@@ -22,33 +22,25 @@ cache:
# This is a basic example for a gem or script which doesn't use
# services such as redis or postgres
before_script:
- # Update packages
+ # Update packages
- apt-get update -yqq
-
# Prep for Node
- apt-get install gnupg -yqq
-
# Upgrade to Node 8
- curl -sL https://deb.nodesource.com/setup_8.x | bash -
-
# Install dependencies
- apt-get install git nodejs libcurl4-gnutls-dev libicu-dev libmcrypt-dev libvpx-dev libjpeg-dev libpng-dev libxpm-dev zlib1g-dev libfreetype6-dev libxml2-dev libexpat1-dev libbz2-dev libgmp3-dev libldap2-dev unixodbc-dev libpq-dev libsqlite3-dev libaspell-dev libsnmp-dev libpcre3-dev libtidy-dev -yqq
-
# Install php extensions
- docker-php-ext-install mbstring pdo_mysql curl json intl gd xml zip bz2 opcache
-
# Install & enable Xdebug for code coverage reports
- pecl install xdebug
- docker-php-ext-enable xdebug
-
# Install Composer and project dependencies.
- curl -sS https://getcomposer.org/installer | php
- php composer.phar install
-
# Install Node dependencies.
# comment this out if you don't have a node dependency
- npm install
-
# Copy over testing configuration.
# Don't forget to set the database config in .env.testing correctly
# DB_HOST=mysql
@@ -56,20 +48,16 @@ before_script:
# DB_USERNAME=root
# DB_PASSWORD=secret
- cp .env.testing .env
-
# Run npm build
# comment this out if you don't have a frontend build
# you can change this to to your frontend building script like
# npm run build
- npm run dev
-
# Generate an application key. Re-cache.
- php artisan key:generate
- php artisan config:cache
-
# Run database migrations.
- php artisan migrate
-
# Run database seed
- php artisan db:seed
@@ -77,7 +65,6 @@ test:
script:
# run laravel tests
- php vendor/bin/phpunit --coverage-text --colors=never
-
# run frontend tests
# if you have any task for testing frontend
# set it in your package.json script
diff --git a/lib/gitlab/ci/templates/Maven.gitlab-ci.yml b/lib/gitlab/ci/templates/Maven.gitlab-ci.yml
index 492b3d03db2..c9838c7a7ff 100644
--- a/lib/gitlab/ci/templates/Maven.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Maven.gitlab-ci.yml
@@ -66,7 +66,6 @@ verify:jdk8:
<<: *verify
image: maven:3.3.9-jdk-8
-
# For `master` branch run `mvn deploy` automatically.
# Here you need to decide whether you want to use JDK7 or 8.
# To get this working you need to define a volume while configuring your gitlab-ci-multi-runner.
@@ -85,7 +84,6 @@ deploy:jdk8:
- target/staging
image: maven:3.3.9-jdk-8
-
pages:
image: busybox:latest
stage: deploy
diff --git a/lib/gitlab/ci/templates/Mono.gitlab-ci.yml b/lib/gitlab/ci/templates/Mono.gitlab-ci.yml
index 3585f99760f..86d62b93313 100644
--- a/lib/gitlab/ci/templates/Mono.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Mono.gitlab-ci.yml
@@ -32,11 +32,11 @@ release:
# The output path is relative to the position of the csproj-file
- msbuild /p:Configuration="Release" /p:Platform="Any CPU"
/p:OutputPath="./../../build/release/" "MyProject.sln"
-
+
debug:
stage: test
script:
# The output path is relative to the position of the csproj-file
- msbuild /p:Configuration="Debug" /p:Platform="Any CPU"
/p:OutputPath="./../../build/debug/" "MyProject.sln"
- - mono packages/NUnit.ConsoleRunner.3.6.0/tools/nunit3-console.exe build/debug/MyProject.Test.dll \ No newline at end of file
+ - mono packages/NUnit.ConsoleRunner.3.6.0/tools/nunit3-console.exe build/debug/MyProject.Test.dll
diff --git a/lib/gitlab/ci/templates/Pages/Brunch.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Brunch.gitlab-ci.yml
index 7fcc0b436b5..d6de8cab5d1 100644
--- a/lib/gitlab/ci/templates/Pages/Brunch.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Brunch.gitlab-ci.yml
@@ -5,7 +5,6 @@ pages:
cache:
paths:
- node_modules/
-
script:
- npm install -g brunch
- brunch build --production
diff --git a/lib/gitlab/ci/templates/Pages/Harp.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Harp.gitlab-ci.yml
index dd3ef149668..4b58003ee10 100644
--- a/lib/gitlab/ci/templates/Pages/Harp.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Harp.gitlab-ci.yml
@@ -5,7 +5,6 @@ pages:
cache:
paths:
- node_modules
-
script:
- npm install -g harp
- harp compile ./ public
diff --git a/lib/gitlab/ci/templates/Pages/Hugo.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Hugo.gitlab-ci.yml
index b8cfb0f56f6..f9ddcc6fb0a 100644
--- a/lib/gitlab/ci/templates/Pages/Hugo.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Hugo.gitlab-ci.yml
@@ -9,7 +9,7 @@ pages:
- public
only:
- master
-
+
test:
script:
- hugo
diff --git a/lib/gitlab/ci/templates/Pages/JBake.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/JBake.gitlab-ci.yml
index 7abfaf53e8e..7a485f8d135 100644
--- a/lib/gitlab/ci/templates/Pages/JBake.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/JBake.gitlab-ci.yml
@@ -13,7 +13,6 @@ image: java:8
variables:
JBAKE_VERSION: 2.5.1
-
# We use SDKMan as tool for managing versions
before_script:
- apt-get update -qq && apt-get install -y -qq unzip zip
@@ -29,4 +28,4 @@ pages:
- jbake . public
artifacts:
paths:
- - public \ No newline at end of file
+ - public
diff --git a/lib/gitlab/ci/templates/Pages/Jigsaw.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Jigsaw.gitlab-ci.yml
index 0e5fb410a4e..5ca4619e200 100644
--- a/lib/gitlab/ci/templates/Pages/Jigsaw.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Jigsaw.gitlab-ci.yml
@@ -11,24 +11,19 @@ cache:
- node_modules/
before_script:
- # Update packages
+ # Update packages
- apt-get update -yqq
-
# Install dependencies
- apt-get install -yqq gnupg zlib1g-dev libpng-dev
-
# Install Node 8
- curl -sL https://deb.nodesource.com/setup_8.x | bash -
- apt-get install -yqq nodejs
-
# Install php extensions
- docker-php-ext-install zip
-
- # Install Composer and project dependencies.
+ # Install Composer and project dependencies
- curl -sS https://getcomposer.org/installer | php
- - php composer.phar install
-
- # Install Node dependencies.
+ - php composer.phar install
+ # Install Node dependencies
- npm install
pages:
diff --git a/lib/gitlab/ci/templates/Pages/Metalsmith.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Metalsmith.gitlab-ci.yml
index 50e8b7ccd46..c6ded272150 100644
--- a/lib/gitlab/ci/templates/Pages/Metalsmith.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Metalsmith.gitlab-ci.yml
@@ -5,7 +5,6 @@ pages:
cache:
paths:
- node_modules/
-
script:
- npm install -g metalsmith
- npm install
diff --git a/lib/gitlab/ci/templates/Python.gitlab-ci.yml b/lib/gitlab/ci/templates/Python.gitlab-ci.yml
index 098abe4daf5..3eaed4e91cd 100644
--- a/lib/gitlab/ci/templates/Python.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Python.gitlab-ci.yml
@@ -18,7 +18,7 @@ cache:
- venv/
before_script:
- - python -V # Print out python version for debugging
+ - python -V # Print out python version for debugging
- pip install virtualenv
- virtualenv venv
- source venv/bin/activate
@@ -26,7 +26,7 @@ before_script:
test:
script:
- python setup.py test
- - pip install tox flake8 # you can also use tox
+ - pip install tox flake8 # you can also use tox
- tox -e py36,flake8
run:
diff --git a/lib/gitlab/ci/templates/Ruby.gitlab-ci.yml b/lib/gitlab/ci/templates/Ruby.gitlab-ci.yml
index 0d12cbc6460..93196dbd475 100644
--- a/lib/gitlab/ci/templates/Ruby.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Ruby.gitlab-ci.yml
@@ -21,7 +21,7 @@ cache:
# This is a basic example for a gem or script which doesn't use
# services such as redis or postgres
before_script:
- - ruby -v # Print out ruby version for debugging
+ - ruby -v # Print out ruby version for debugging
# Uncomment next line if your rails app needs a JS runtime:
# - apt-get update -q && apt-get install nodejs -yqq
- bundle install -j $(nproc) --path vendor # Install dependencies into ./vendor/ruby
diff --git a/lib/gitlab/ci/templates/dotNET.gitlab-ci.yml b/lib/gitlab/ci/templates/dotNET.gitlab-ci.yml
index 25a32ba0f74..5e128b793d0 100644
--- a/lib/gitlab/ci/templates/dotNET.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/dotNET.gitlab-ci.yml
@@ -26,7 +26,6 @@ variables:
MSI_RELEASE_FOLDER: 'Setup\bin\Release'
TEST_FOLDER: 'Tests\bin\Release'
DEPLOY_FOLDER: 'P:\Projects\YourApp\Builds'
-
NUGET_PATH: 'C:\NuGet\nuget.exe'
MSBUILD_PATH: 'C:\Program Files (x86)\MSBuild\14.0\Bin\msbuild.exe'
NUNIT_PATH: 'C:\Program Files (x86)\NUnit.org\nunit-console\nunit3-console.exe'
@@ -84,4 +83,3 @@ deploy_job:
dependencies:
- build_job
- test_job
- \ No newline at end of file
diff --git a/lib/gitlab/ci/templates/iOS-Fastlane.gitlab-ci.yml b/lib/gitlab/ci/templates/iOS-Fastlane.gitlab-ci.yml
index 245e6bec60a..df6ac4d340d 100644
--- a/lib/gitlab/ci/templates/iOS-Fastlane.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/iOS-Fastlane.gitlab-ci.yml
@@ -17,7 +17,7 @@ variables:
LC_ALL: "en_US.UTF-8"
LANG: "en_US.UTF-8"
GIT_STRATEGY: clone
-
+
build:
stage: build
script:
diff --git a/lib/gitlab/git/commit.rb b/lib/gitlab/git/commit.rb
index e5bbd500e98..8fac3621df9 100644
--- a/lib/gitlab/git/commit.rb
+++ b/lib/gitlab/git/commit.rb
@@ -184,11 +184,12 @@ module Gitlab
end
end
- def initialize(repository, raw_commit, head = nil)
+ def initialize(repository, raw_commit, head = nil, lazy_load_parents: false)
raise "Nil as raw commit passed" unless raw_commit
@repository = repository
@head = head
+ @lazy_load_parents = lazy_load_parents
init_commit(raw_commit)
end
@@ -225,6 +226,12 @@ module Gitlab
author_name != committer_name || author_email != committer_email
end
+ def parent_ids
+ return @parent_ids unless @lazy_load_parents
+
+ @parent_ids ||= @repository.commit(id).parent_ids
+ end
+
def parent_id
parent_ids.first
end
@@ -311,6 +318,10 @@ module Gitlab
parent_ids.size > 1
end
+ def gitaly_commit?
+ raw_commit.is_a?(Gitaly::GitCommit)
+ end
+
def tree_entry(path)
return unless path.present?
@@ -333,7 +344,7 @@ module Gitlab
end
def to_gitaly_commit
- return raw_commit if raw_commit.is_a?(Gitaly::GitCommit)
+ return raw_commit if gitaly_commit?
message_split = raw_commit.message.split("\n", 2)
Gitaly::GitCommit.new(
diff --git a/lib/gitlab/json_cache.rb b/lib/gitlab/json_cache.rb
index 24daad638f4..e4bc437d787 100644
--- a/lib/gitlab/json_cache.rb
+++ b/lib/gitlab/json_cache.rb
@@ -80,8 +80,23 @@ module Gitlab
# when the new_record? method incorrectly returns false.
#
# See https://gitlab.com/gitlab-org/gitlab-ee/issues/9903#note_145329964
- attributes = klass.attributes_builder.build_from_database(raw, {})
- klass.allocate.init_with("attributes" => attributes, "new_record" => new_record?(raw, klass))
+ klass
+ .allocate
+ .init_with(
+ "attributes" => attributes_for(klass, raw),
+ "new_record" => new_record?(raw, klass)
+ )
+ end
+
+ def attributes_for(klass, raw)
+ # We have models that leave out some fields from the JSON export for
+ # security reasons, e.g. models that include the CacheMarkdownField.
+ # The ActiveRecord::AttributeSet we build from raw does know about
+ # these columns so we need manually set them.
+ missing_attributes = (klass.columns.map(&:name) - raw.keys)
+ missing_attributes.each { |column| raw[column] = nil }
+
+ klass.attributes_builder.build_from_database(raw, {})
end
def new_record?(raw, klass)
diff --git a/lib/gitlab/kubernetes.rb b/lib/gitlab/kubernetes.rb
index a9957a85d48..d46b5e3aee3 100644
--- a/lib/gitlab/kubernetes.rb
+++ b/lib/gitlab/kubernetes.rb
@@ -24,6 +24,30 @@ module Gitlab
end
end
+ # Filters an array of pods (as returned by the kubernetes API) by their annotations
+ def filter_by_annotation(items, annotations = {})
+ items.select do |item|
+ metadata = item.fetch("metadata", {})
+ item_annotations = metadata.fetch("annotations", nil)
+ next unless item_annotations
+
+ annotations.all? { |k, v| item_annotations[k.to_s] == v }
+ end
+ end
+
+ # Filters an array of pods (as returned by the kubernetes API) by their project and environment
+ def filter_by_project_environment(items, app, env)
+ pods = filter_by_annotation(items, {
+ 'app.gitlab.com/app' => app,
+ 'app.gitlab.com/env' => env
+ })
+ return pods unless pods.empty?
+
+ filter_by_label(items, {
+ 'app' => env, # deprecated: replaced by app.gitlab.com/env
+ })
+ end
+
# Converts a pod (as returned by the kubernetes API) into a terminal
def terminals_for_pod(api_url, namespace, pod)
metadata = pod.fetch("metadata", {})
diff --git a/lib/sentry/client.rb b/lib/sentry/client.rb
index 49ec196b103..5e0c9101de5 100644
--- a/lib/sentry/client.rb
+++ b/lib/sentry/client.rb
@@ -57,7 +57,7 @@ module Sentry
raise Client::Error, "Sentry response status code: #{response.code}"
end
- response.as_json
+ response
end
def projects_api_url
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index d9b50e5e3b9..0dfcf62b36d 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -19,6 +19,9 @@ msgstr ""
msgid " Status"
msgstr ""
+msgid " You need to do this before %{grace_period_deadline}."
+msgstr ""
+
msgid " or "
msgstr ""
@@ -1380,6 +1383,9 @@ msgstr ""
msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
msgstr ""
+msgid "Cannot skip two factor authentication setup"
+msgstr ""
+
msgid "Certificate"
msgstr ""
@@ -1512,6 +1518,9 @@ msgstr ""
msgid "CiStatusLabel|pending"
msgstr ""
+msgid "CiStatusLabel|preparing"
+msgstr ""
+
msgid "CiStatusLabel|skipped"
msgstr ""
@@ -1545,6 +1554,9 @@ msgstr ""
msgid "CiStatusText|pending"
msgstr ""
+msgid "CiStatusText|preparing"
+msgstr ""
+
msgid "CiStatusText|skipped"
msgstr ""
@@ -4328,6 +4340,9 @@ msgstr ""
msgid "Invalid input, please avoid emojis"
msgstr ""
+msgid "Invalid pin code"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -5977,7 +5992,7 @@ msgstr ""
msgid "Profiles|This email will be displayed on your public profile"
msgstr ""
-msgid "Profiles|This email will be used for web based operations, such as edits and merges. %{learn_more}"
+msgid "Profiles|This email will be used for web based operations, such as edits and merges. %{commit_email_link_start}Learn more%{commit_email_link_end}"
msgstr ""
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
@@ -7612,6 +7627,12 @@ msgstr ""
msgid "The fork relationship has been removed."
msgstr ""
+msgid "The global settings require you to enable Two-Factor Authentication for your account."
+msgstr ""
+
+msgid "The group settings for %{group_links} require you to enable Two-Factor Authentication for your account. You can %{leave_group_links}."
+msgstr ""
+
msgid "The import will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
msgstr ""
@@ -7864,6 +7885,12 @@ msgstr ""
msgid "This job is in pending state and is waiting to be picked by a runner"
msgstr ""
+msgid "This job is performing tasks that must complete before it can start"
+msgstr ""
+
+msgid "This job is preparing to start"
+msgstr ""
+
msgid "This job is stuck because you don't have any active runners online with any of these tags assigned to them:"
msgstr ""
@@ -9008,6 +9035,12 @@ msgstr ""
msgid "Your U2F device needs to be set up. Plug it in (if not already) and click the button on the left."
msgstr ""
+msgid "Your U2F device was registered!"
+msgstr ""
+
+msgid "Your account uses dedicated credentials for the \"%{group_name}\" group and can only be updated through SSO."
+msgstr ""
+
msgid "Your applications (%{size})"
msgstr ""
@@ -9195,6 +9228,9 @@ msgstr ""
msgid "latest version"
msgstr ""
+msgid "leave %{group_name}"
+msgstr ""
+
msgid "manual"
msgstr ""
diff --git a/package.json b/package.json
index fa980fa173e..d830d83b963 100644
--- a/package.json
+++ b/package.json
@@ -17,6 +17,7 @@
"prettier-all": "node ./scripts/frontend/prettier.js check-all",
"prettier-all-save": "node ./scripts/frontend/prettier.js save-all",
"stylelint": "node node_modules/stylelint/bin/stylelint.js app/assets/stylesheets/**/*.* --custom-formatter node_modules/stylelint-error-string-formatter",
+ "test": "yarn jest && yarn karma",
"webpack": "webpack --config config/webpack.config.js",
"webpack-prod": "NODE_ENV=production webpack --config config/webpack.config.js"
},
diff --git a/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb b/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb
index 16aca7c0e22..0b92ea29ca4 100644
--- a/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb
+++ b/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb
@@ -116,12 +116,6 @@ module QA
p.description = 'Project with AutoDevOps'
end
- project.visit!
-
- Page::Alert::AutoDevopsAlert.perform do |alert|
- expect(alert).to have_text(/.*The Auto DevOps pipeline has been enabled.*/)
- end
-
# Create AutoDevOps repo
Resource::Repository::ProjectPush.fabricate! do |push|
push.project = project
diff --git a/spec/controllers/omniauth_callbacks_controller_spec.rb b/spec/controllers/omniauth_callbacks_controller_spec.rb
index e0da23ca0b8..06c6f49f7cc 100644
--- a/spec/controllers/omniauth_callbacks_controller_spec.rb
+++ b/spec/controllers/omniauth_callbacks_controller_spec.rb
@@ -113,6 +113,33 @@ describe OmniauthCallbacksController, type: :controller do
expect(request.env['warden']).to be_authenticated
end
+ context 'when user has no linked provider' do
+ let(:user) { create(:user) }
+
+ before do
+ sign_in user
+ end
+
+ it 'links identity' do
+ expect do
+ post provider
+ user.reload
+ end.to change { user.identities.count }.by(1)
+ end
+
+ context 'and is not allowed to link the provider' do
+ before do
+ allow_any_instance_of(IdentityProviderPolicy).to receive(:can?).with(:link).and_return(false)
+ end
+
+ it 'returns 403' do
+ post provider
+
+ expect(response).to have_gitlab_http_status(403)
+ end
+ end
+ end
+
shared_context 'sign_up' do
let(:user) { double(email: 'new@example.com') }
diff --git a/spec/controllers/projects/notes_controller_spec.rb b/spec/controllers/projects/notes_controller_spec.rb
index 0b0f5117784..deecb7fefe9 100644
--- a/spec/controllers/projects/notes_controller_spec.rb
+++ b/spec/controllers/projects/notes_controller_spec.rb
@@ -413,6 +413,37 @@ describe Projects::NotesController do
end
end
end
+
+ context 'when creating a note with quick actions' do
+ context 'with commands that return changes' do
+ let(:note_text) { "/award :thumbsup:\n/estimate 1d\n/spend 3h" }
+
+ it 'includes changes in commands_changes ' do
+ post :create, params: request_params.merge(note: { note: note_text }, format: :json)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['commands_changes']).to include('emoji_award', 'time_estimate', 'spend_time')
+ expect(json_response['commands_changes']).not_to include('target_project', 'title')
+ end
+ end
+
+ context 'with commands that do not return changes' do
+ let(:issue) { create(:issue, project: project) }
+ let(:other_project) { create(:project) }
+ let(:note_text) { "/move #{other_project.full_path}\n/title AAA" }
+
+ before do
+ other_project.add_developer(user)
+ end
+
+ it 'does not include changes in commands_changes' do
+ post :create, params: request_params.merge(note: { note: note_text }, target_type: 'issue', target_id: issue.id, format: :json)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['commands_changes']).not_to include('target_project', 'title')
+ end
+ end
+ end
end
describe 'PUT update' do
diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb
index 0b3e67b4987..067391c1179 100644
--- a/spec/factories/ci/builds.rb
+++ b/spec/factories/ci/builds.rb
@@ -75,6 +75,10 @@ FactoryBot.define do
status 'created'
end
+ trait :preparing do
+ status 'preparing'
+ end
+
trait :scheduled do
schedulable
status 'scheduled'
diff --git a/spec/factories/ci/pipelines.rb b/spec/factories/ci/pipelines.rb
index ee5d27355f1..aa5ccbda6cd 100644
--- a/spec/factories/ci/pipelines.rb
+++ b/spec/factories/ci/pipelines.rb
@@ -50,6 +50,14 @@ FactoryBot.define do
failure_reason :config_error
end
+ trait :created do
+ status :created
+ end
+
+ trait :preparing do
+ status :preparing
+ end
+
trait :blocked do
status :manual
end
diff --git a/spec/factories/clusters/clusters.rb b/spec/factories/clusters/clusters.rb
index a2e5f4862db..1cc3c0e03d8 100644
--- a/spec/factories/clusters/clusters.rb
+++ b/spec/factories/clusters/clusters.rb
@@ -12,7 +12,7 @@ FactoryBot.define do
cluster_type { Clusters::Cluster.cluster_types[:project_type] }
before(:create) do |cluster, evaluator|
- cluster.projects << create(:project, :repository)
+ cluster.projects << create(:project, :repository) unless cluster.projects.present?
end
end
@@ -20,7 +20,7 @@ FactoryBot.define do
cluster_type { Clusters::Cluster.cluster_types[:group_type] }
before(:create) do |cluster, evalutor|
- cluster.groups << create(:group)
+ cluster.groups << create(:group) unless cluster.groups.present?
end
end
diff --git a/spec/factories/commit_statuses.rb b/spec/factories/commit_statuses.rb
index 381bf07f6a0..848a31e96c1 100644
--- a/spec/factories/commit_statuses.rb
+++ b/spec/factories/commit_statuses.rb
@@ -33,6 +33,10 @@ FactoryBot.define do
status 'pending'
end
+ trait :preparing do
+ status 'preparing'
+ end
+
trait :created do
status 'created'
end
diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb
index 986f3823275..8eb413bdd8d 100644
--- a/spec/features/issues/gfm_autocomplete_spec.rb
+++ b/spec/features/issues/gfm_autocomplete_spec.rb
@@ -278,12 +278,7 @@ describe 'GFM autocomplete', :js do
end
end
- # This context has just one example in each contexts in order to improve spec performance.
- context 'labels', :quarantine do
- let!(:backend) { create(:label, project: project, title: 'backend') }
- let!(:bug) { create(:label, project: project, title: 'bug') }
- let!(:feature_proposal) { create(:label, project: project, title: 'feature proposal') }
-
+ context 'labels' do
it 'opens autocomplete menu for Labels when field starts with text with item escaping HTML characters' do
create(:label, project: project, title: label_xss_title)
@@ -298,83 +293,6 @@ describe 'GFM autocomplete', :js do
expect(find('.atwho-view-ul').text).to have_content('alert label')
end
end
-
- context 'when no labels are assigned' do
- it 'shows labels' do
- note = find('#note-body')
-
- # It should show all the labels on "~".
- type(note, '~')
- wait_for_requests
- expect_labels(shown: [backend, bug, feature_proposal])
-
- # It should show all the labels on "/label ~".
- type(note, '/label ~')
- expect_labels(shown: [backend, bug, feature_proposal])
-
- # It should show all the labels on "/relabel ~".
- type(note, '/relabel ~')
- expect_labels(shown: [backend, bug, feature_proposal])
-
- # It should show no labels on "/unlabel ~".
- type(note, '/unlabel ~')
- expect_labels(not_shown: [backend, bug, feature_proposal])
- end
- end
-
- context 'when some labels are assigned' do
- before do
- issue.labels << [backend]
- end
-
- it 'shows labels' do
- note = find('#note-body')
-
- # It should show all the labels on "~".
- type(note, '~')
- wait_for_requests
- expect_labels(shown: [backend, bug, feature_proposal])
-
- # It should show only unset labels on "/label ~".
- type(note, '/label ~')
- expect_labels(shown: [bug, feature_proposal], not_shown: [backend])
-
- # It should show all the labels on "/relabel ~".
- type(note, '/relabel ~')
- expect_labels(shown: [backend, bug, feature_proposal])
-
- # It should show only set labels on "/unlabel ~".
- type(note, '/unlabel ~')
- expect_labels(shown: [backend], not_shown: [bug, feature_proposal])
- end
- end
-
- context 'when all labels are assigned' do
- before do
- issue.labels << [backend, bug, feature_proposal]
- end
-
- it 'shows labels' do
- note = find('#note-body')
-
- # It should show all the labels on "~".
- type(note, '~')
- wait_for_requests
- expect_labels(shown: [backend, bug, feature_proposal])
-
- # It should show no labels on "/label ~".
- type(note, '/label ~')
- expect_labels(not_shown: [backend, bug, feature_proposal])
-
- # It should show all the labels on "/relabel ~".
- type(note, '/relabel ~')
- expect_labels(shown: [backend, bug, feature_proposal])
-
- # It should show all the labels on "/unlabel ~".
- type(note, '/unlabel ~')
- expect_labels(shown: [backend, bug, feature_proposal])
- end
- end
end
shared_examples 'autocomplete suggestions' do
diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb
index 9bc340ed4bb..51508b78649 100644
--- a/spec/features/issues_spec.rb
+++ b/spec/features/issues_spec.rb
@@ -497,12 +497,21 @@ describe 'Issues' do
it 'allows user to unselect themselves', :js do
issue2 = create(:issue, project: project, author: user)
+
visit project_issue_path(project, issue2)
+ def close_dropdown_menu_if_visible
+ find('.dropdown-menu-toggle', visible: :all).tap do |toggle|
+ toggle.click if toggle.visible?
+ end
+ end
+
page.within '.assignee' do
click_link 'Edit'
click_link user.name
+ close_dropdown_menu_if_visible
+
page.within '.value .author' do
expect(page).to have_content user.name
end
@@ -510,6 +519,8 @@ describe 'Issues' do
click_link 'Edit'
click_link user.name
+ close_dropdown_menu_if_visible
+
page.within '.value .assign-yourself' do
expect(page).to have_content "No assignee"
end
diff --git a/spec/features/projects/badges/pipeline_badge_spec.rb b/spec/features/projects/badges/pipeline_badge_spec.rb
index dee81898928..4ac4e8f0fcb 100644
--- a/spec/features/projects/badges/pipeline_badge_spec.rb
+++ b/spec/features/projects/badges/pipeline_badge_spec.rb
@@ -41,6 +41,25 @@ describe 'Pipeline Badge' do
end
end
+ context 'when the pipeline is preparing' do
+ let!(:job) { create(:ci_build, status: 'created', pipeline: pipeline) }
+
+ before do
+ # Prevent skipping directly to 'pending'
+ allow(Ci::BuildPrepareWorker).to receive(:perform_async)
+ allow(job).to receive(:prerequisites).and_return([double])
+ end
+
+ it 'displays the preparing badge' do
+ job.enqueue
+
+ visit pipeline_project_badges_path(project, ref: ref, format: :svg)
+
+ expect(page.status_code).to eq(200)
+ expect_badge('preparing')
+ end
+ end
+
context 'when the pipeline is running' do
it 'shows displays so on the badge' do
create(:ci_build, pipeline: pipeline, name: 'second build', status_event: 'run')
diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb
index 5d2a99f40b7..9fdf78baa1e 100644
--- a/spec/features/projects/pipelines/pipeline_spec.rb
+++ b/spec/features/projects/pipelines/pipeline_spec.rb
@@ -24,6 +24,11 @@ describe 'Pipeline', :js do
pipeline: pipeline, stage: 'test', name: 'test')
end
+ let!(:build_preparing) do
+ create(:ci_build, :preparing,
+ pipeline: pipeline, stage: 'deploy', name: 'prepare')
+ end
+
let!(:build_running) do
create(:ci_build, :running,
pipeline: pipeline, stage: 'deploy', name: 'deploy')
@@ -109,6 +114,24 @@ describe 'Pipeline', :js do
end
end
+ context 'when pipeline has preparing builds' do
+ it 'shows a preparing icon and a cancel action' do
+ page.within('#ci-badge-prepare') do
+ expect(page).to have_selector('.js-ci-status-icon-preparing')
+ expect(page).to have_selector('.js-icon-cancel')
+ expect(page).to have_content('prepare')
+ end
+ end
+
+ it 'cancels the preparing build and shows retry button' do
+ find('#ci-badge-deploy .ci-action-icon-container').click
+
+ page.within('#ci-badge-deploy') do
+ expect(page).to have_css('.js-icon-retry')
+ end
+ end
+ end
+
context 'when pipeline has successful builds' do
it 'shows the success icon and a retry action for the successful build' do
page.within('#ci-badge-build') do
diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb
index 0e151efa9df..7ca3b3d8edd 100644
--- a/spec/features/projects/pipelines/pipelines_spec.rb
+++ b/spec/features/projects/pipelines/pipelines_spec.rb
@@ -377,6 +377,30 @@ describe 'Pipelines', :js do
end
context 'for generic statuses' do
+ context 'when preparing' do
+ let!(:pipeline) do
+ create(:ci_empty_pipeline,
+ status: 'preparing', project: project)
+ end
+
+ let!(:status) do
+ create(:generic_commit_status,
+ :preparing, pipeline: pipeline)
+ end
+
+ before do
+ visit_project_pipelines
+ end
+
+ it 'is cancelable' do
+ expect(page).to have_selector('.js-pipelines-cancel-button')
+ end
+
+ it 'shows the pipeline as preparing' do
+ expect(page).to have_selector('.ci-preparing')
+ end
+ end
+
context 'when running' do
let!(:running) do
create(:generic_commit_status,
diff --git a/spec/features/user_opens_link_to_comment.rb b/spec/features/user_opens_link_to_comment.rb
new file mode 100644
index 00000000000..f1e07e55799
--- /dev/null
+++ b/spec/features/user_opens_link_to_comment.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'User opens link to comment', :js do
+ let(:project) { create(:project, :public) }
+ let(:note) { create(:note_on_issue, project: project) }
+
+ context 'authenticated user' do
+ let(:user) { create(:user) }
+
+ before do
+ sign_in(user)
+ end
+
+ it 'switches to all activity and does not show error message' do
+ create(:user_preference, user: user, issue_notes_filter: UserPreference::NOTES_FILTERS[:only_activity])
+
+ visit Gitlab::UrlBuilder.build(note)
+
+ expect(page.find('#discussion-filter-dropdown')).to have_content('Show all activity')
+ expect(page).not_to have_content('Something went wrong while fetching comments')
+ end
+ end
+
+ context 'anonymous user' do
+ it 'does not show error message' do
+ visit Gitlab::UrlBuilder.build(note)
+
+ expect(page).not_to have_content('Something went wrong while fetching comments')
+ end
+ end
+end
diff --git a/spec/features/users/login_spec.rb b/spec/features/users/login_spec.rb
index ad856bd062e..368a814874f 100644
--- a/spec/features/users/login_spec.rb
+++ b/spec/features/users/login_spec.rb
@@ -434,16 +434,22 @@ describe 'Login' do
context 'within the grace period' do
it 'redirects to two-factor configuration page' do
- expect(authentication_metrics)
- .to increment(:user_authenticated_counter)
-
- gitlab_sign_in(user)
-
- expect(current_path).to eq profile_two_factor_auth_path
- expect(page).to have_content(
- 'The group settings for Group 1 and Group 2 require you to enable ' \
- 'Two-Factor Authentication for your account. You need to do this ' \
- 'before ')
+ Timecop.freeze do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+
+ gitlab_sign_in(user)
+
+ expect(current_path).to eq profile_two_factor_auth_path
+ expect(page).to have_content(
+ 'The group settings for Group 1 and Group 2 require you to enable '\
+ 'Two-Factor Authentication for your account. '\
+ 'You can leave Group 1 and leave Group 2. '\
+ 'You need to do this '\
+ 'before '\
+ "#{(Time.zone.now + 2.days).strftime("%a, %-d %b %Y %H:%M:%S %z")}"
+ )
+ end
end
it 'allows skipping two-factor configuration', :js do
@@ -500,7 +506,8 @@ describe 'Login' do
expect(current_path).to eq profile_two_factor_auth_path
expect(page).to have_content(
'The group settings for Group 1 and Group 2 require you to enable ' \
- 'Two-Factor Authentication for your account.'
+ 'Two-Factor Authentication for your account. '\
+ 'You can leave Group 1 and leave Group 2.'
)
end
end
diff --git a/spec/frontend/gfm_auto_complete_spec.js b/spec/frontend/gfm_auto_complete_spec.js
index 27c679c749b..ed12af925f1 100644
--- a/spec/frontend/gfm_auto_complete_spec.js
+++ b/spec/frontend/gfm_auto_complete_spec.js
@@ -6,17 +6,26 @@ import GfmAutoComplete from '~/gfm_auto_complete';
import 'jquery.caret';
import 'at.js';
+import { TEST_HOST } from 'helpers/test_constants';
+import { setTestTimeout } from 'helpers/timeout';
+import { getJSONFixture } from 'helpers/fixtures';
+
+setTestTimeout(500);
+
+const labelsFixture = getJSONFixture('autocomplete_sources/labels.json');
+
describe('GfmAutoComplete', () => {
const gfmAutoCompleteCallbacks = GfmAutoComplete.prototype.getDefaultCallbacks.call({
fetchData: () => {},
});
let atwhoInstance;
- let items;
let sorterValue;
describe('DefaultOptions.sorter', () => {
describe('assets loading', () => {
+ let items;
+
beforeEach(() => {
jest.spyOn(GfmAutoComplete, 'isLoading').mockReturnValue(true);
@@ -61,7 +70,7 @@ describe('GfmAutoComplete', () => {
atwhoInstance = { setting: {} };
const query = 'query';
- items = [];
+ const items = [];
const searchKey = 'searchKey';
gfmAutoCompleteCallbacks.sorter.call(atwhoInstance, query, items, searchKey);
@@ -250,4 +259,90 @@ describe('GfmAutoComplete', () => {
).toBe('<li><small>grp/proj#5</small> Some Issue</li>');
});
});
+
+ describe('labels', () => {
+ const dataSources = {
+ labels: `${TEST_HOST}/autocomplete_sources/labels`,
+ };
+
+ const allLabels = labelsFixture;
+ const assignedLabels = allLabels.filter(label => label.set);
+ const unassignedLabels = allLabels.filter(label => !label.set);
+
+ let autocomplete;
+ let $textarea;
+
+ beforeEach(() => {
+ autocomplete = new GfmAutoComplete(dataSources);
+ $textarea = $('<textarea></textarea>');
+ autocomplete.setup($textarea, { labels: true });
+ });
+
+ afterEach(() => {
+ autocomplete.destroy();
+ });
+
+ const triggerDropdown = text => {
+ $textarea
+ .trigger('focus')
+ .val(text)
+ .caret('pos', -1);
+ $textarea.trigger('keyup');
+
+ return new Promise(window.requestAnimationFrame);
+ };
+
+ const getDropdownItems = () => {
+ const dropdown = document.getElementById('at-view-labels');
+ const items = dropdown.getElementsByTagName('li');
+ return [].map.call(items, item => item.textContent.trim());
+ };
+
+ const expectLabels = ({ input, output }) =>
+ triggerDropdown(input).then(() => {
+ expect(getDropdownItems()).toEqual(output.map(label => label.title));
+ });
+
+ describe('with no labels assigned', () => {
+ beforeEach(() => {
+ autocomplete.cachedData['~'] = [...unassignedLabels];
+ });
+
+ it.each`
+ input | output
+ ${'~'} | ${unassignedLabels}
+ ${'/label ~'} | ${unassignedLabels}
+ ${'/relabel ~'} | ${unassignedLabels}
+ ${'/unlabel ~'} | ${[]}
+ `('$input shows $output.length labels', expectLabels);
+ });
+
+ describe('with some labels assigned', () => {
+ beforeEach(() => {
+ autocomplete.cachedData['~'] = allLabels;
+ });
+
+ it.each`
+ input | output
+ ${'~'} | ${allLabels}
+ ${'/label ~'} | ${unassignedLabels}
+ ${'/relabel ~'} | ${allLabels}
+ ${'/unlabel ~'} | ${assignedLabels}
+ `('$input shows $output.length labels', expectLabels);
+ });
+
+ describe('with all labels assigned', () => {
+ beforeEach(() => {
+ autocomplete.cachedData['~'] = [...assignedLabels];
+ });
+
+ it.each`
+ input | output
+ ${'~'} | ${assignedLabels}
+ ${'/label ~'} | ${[]}
+ ${'/relabel ~'} | ${assignedLabels}
+ ${'/unlabel ~'} | ${assignedLabels}
+ `('$input shows $output.length labels', expectLabels);
+ });
+ });
});
diff --git a/spec/frontend/helpers/fixtures.js b/spec/frontend/helpers/fixtures.js
new file mode 100644
index 00000000000..f96f27c4d80
--- /dev/null
+++ b/spec/frontend/helpers/fixtures.js
@@ -0,0 +1,24 @@
+/* eslint-disable import/prefer-default-export, global-require, import/no-dynamic-require */
+
+import fs from 'fs';
+import path from 'path';
+
+// jest-util is part of Jest
+// eslint-disable-next-line import/no-extraneous-dependencies
+import { ErrorWithStack } from 'jest-util';
+
+const fixturesBasePath = path.join(process.cwd(), 'spec', 'javascripts', 'fixtures');
+
+export function getJSONFixture(relativePath, ee = false) {
+ const absolutePath = path.join(fixturesBasePath, ee ? 'ee' : '', relativePath);
+ if (!fs.existsSync(absolutePath)) {
+ throw new ErrorWithStack(
+ `Fixture file ${relativePath} does not exist.
+
+Did you run bin/rake karma:fixtures?`,
+ getJSONFixture,
+ );
+ }
+
+ return require(absolutePath);
+}
diff --git a/spec/helpers/auth_helper_spec.rb b/spec/helpers/auth_helper_spec.rb
index f0c2e4768ec..2ba8b3dbf22 100644
--- a/spec/helpers/auth_helper_spec.rb
+++ b/spec/helpers/auth_helper_spec.rb
@@ -97,17 +97,37 @@ describe AuthHelper do
end
end
- describe 'unlink_allowed?' do
- [:saml, :cas3].each do |provider|
- it "returns true if the provider is #{provider}" do
- expect(helper.unlink_allowed?(provider)).to be false
- end
+ describe '#link_provider_allowed?' do
+ let(:policy) { instance_double('IdentityProviderPolicy') }
+ let(:current_user) { instance_double('User') }
+ let(:provider) { double }
+
+ before do
+ allow(helper).to receive(:current_user).and_return(current_user)
+ allow(IdentityProviderPolicy).to receive(:new).with(current_user, provider).and_return(policy)
end
- [:twitter, :facebook, :google_oauth2, :gitlab, :github, :bitbucket, :crowd, :auth0, :authentiq].each do |provider|
- it "returns false if the provider is #{provider}" do
- expect(helper.unlink_allowed?(provider)).to be true
- end
+ it 'delegates to identity provider policy' do
+ allow(policy).to receive(:can?).with(:link).and_return('policy_link_result')
+
+ expect(helper.link_provider_allowed?(provider)).to eq 'policy_link_result'
+ end
+ end
+
+ describe '#unlink_provider_allowed?' do
+ let(:policy) { instance_double('IdentityProviderPolicy') }
+ let(:current_user) { instance_double('User') }
+ let(:provider) { double }
+
+ before do
+ allow(helper).to receive(:current_user).and_return(current_user)
+ allow(IdentityProviderPolicy).to receive(:new).with(current_user, provider).and_return(policy)
+ end
+
+ it 'delegates to identity provider policy' do
+ allow(policy).to receive(:can?).with(:unlink).and_return('policy_unlink_result')
+
+ expect(helper.unlink_provider_allowed?(provider)).to eq 'policy_unlink_result'
end
end
end
diff --git a/spec/javascripts/fixtures/autocomplete_sources.rb b/spec/javascripts/fixtures/autocomplete_sources.rb
new file mode 100644
index 00000000000..c117fb7cd24
--- /dev/null
+++ b/spec/javascripts/fixtures/autocomplete_sources.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Projects::AutocompleteSourcesController, '(JavaScript fixtures)', type: :controller do
+ include JavaScriptFixturesHelpers
+
+ set(:admin) { create(:admin) }
+ set(:group) { create(:group, name: 'frontend-fixtures') }
+ set(:project) { create(:project, namespace: group, path: 'autocomplete-sources-project') }
+ set(:issue) { create(:issue, project: project) }
+
+ before(:all) do
+ clean_frontend_fixtures('autocomplete_sources/')
+ end
+
+ before do
+ sign_in(admin)
+ end
+
+ it 'autocomplete_sources/labels.json' do |example|
+ issue.labels << create(:label, project: project, title: 'bug')
+ issue.labels << create(:label, project: project, title: 'critical')
+
+ create(:label, project: project, title: 'feature')
+ create(:label, project: project, title: 'documentation')
+
+ get :labels,
+ format: :json,
+ params: {
+ namespace_id: group.path,
+ project_id: project.path,
+ type: issue.class.name,
+ type_id: issue.id
+ }
+
+ expect(response).to be_success
+ store_frontend_fixture(response, example.description)
+ end
+end
diff --git a/spec/lib/gitlab/badge/pipeline/template_spec.rb b/spec/lib/gitlab/badge/pipeline/template_spec.rb
index 20fa4f879c3..bcef0b7e120 100644
--- a/spec/lib/gitlab/badge/pipeline/template_spec.rb
+++ b/spec/lib/gitlab/badge/pipeline/template_spec.rb
@@ -59,6 +59,16 @@ describe Gitlab::Badge::Pipeline::Template do
end
end
+ context 'when status is preparing' do
+ before do
+ allow(badge).to receive(:status).and_return('preparing')
+ end
+
+ it 'has expected color' do
+ expect(template.value_color).to eq '#dfb317'
+ end
+ end
+
context 'when status is unknown' do
before do
allow(badge).to receive(:status).and_return('unknown')
diff --git a/spec/lib/gitlab/ci/build/prerequisite/factory_spec.rb b/spec/lib/gitlab/ci/build/prerequisite/factory_spec.rb
new file mode 100644
index 00000000000..5187f99a441
--- /dev/null
+++ b/spec/lib/gitlab/ci/build/prerequisite/factory_spec.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Ci::Build::Prerequisite::Factory do
+ let(:build) { create(:ci_build) }
+
+ describe '.for_build' do
+ let(:kubernetes_namespace) do
+ instance_double(
+ Gitlab::Ci::Build::Prerequisite::KubernetesNamespace,
+ unmet?: unmet)
+ end
+
+ subject { described_class.new(build).unmet }
+
+ before do
+ expect(Gitlab::Ci::Build::Prerequisite::KubernetesNamespace)
+ .to receive(:new).with(build).and_return(kubernetes_namespace)
+ end
+
+ context 'prerequisite is unmet' do
+ let(:unmet) { true }
+
+ it { is_expected.to eq [kubernetes_namespace] }
+ end
+
+ context 'prerequisite is met' do
+ let(:unmet) { false }
+
+ it { is_expected.to be_empty }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb b/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb
new file mode 100644
index 00000000000..62dcd80fad7
--- /dev/null
+++ b/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb
@@ -0,0 +1,85 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Ci::Build::Prerequisite::KubernetesNamespace do
+ let(:build) { create(:ci_build) }
+
+ describe '#unmet?' do
+ subject { described_class.new(build).unmet? }
+
+ context 'build has no deployment' do
+ before do
+ expect(build.deployment).to be_nil
+ end
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'build has a deployment' do
+ let!(:deployment) { create(:deployment, deployable: build) }
+
+ context 'and a cluster to deploy to' do
+ let(:cluster) { create(:cluster, projects: [build.project]) }
+
+ before do
+ allow(build.deployment).to receive(:cluster).and_return(cluster)
+ end
+
+ it { is_expected.to be_truthy }
+
+ context 'and a namespace is already created for this project' do
+ let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, cluster: cluster, project: build.project) }
+
+ it { is_expected.to be_falsey }
+ end
+ end
+
+ context 'and no cluster to deploy to' do
+ before do
+ expect(deployment.cluster).to be_nil
+ end
+
+ it { is_expected.to be_falsey }
+ end
+ end
+ end
+
+ describe '#complete!' do
+ let!(:deployment) { create(:deployment, deployable: build) }
+ let(:service) { double(execute: true) }
+
+ subject { described_class.new(build).complete! }
+
+ context 'completion is required' do
+ let(:cluster) { create(:cluster, projects: [build.project]) }
+
+ before do
+ allow(build.deployment).to receive(:cluster).and_return(cluster)
+ end
+
+ it 'creates a kubernetes namespace' do
+ expect(Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService)
+ .to receive(:new)
+ .with(cluster: cluster, kubernetes_namespace: instance_of(Clusters::KubernetesNamespace))
+ .and_return(service)
+
+ expect(service).to receive(:execute).once
+
+ subject
+ end
+ end
+
+ context 'completion is not required' do
+ before do
+ expect(deployment.cluster).to be_nil
+ end
+
+ it 'does not create a namespace' do
+ expect(Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService).not_to receive(:new)
+
+ subject
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/build/preparing_spec.rb b/spec/lib/gitlab/ci/status/build/preparing_spec.rb
new file mode 100644
index 00000000000..4d8945845ba
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/build/preparing_spec.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Build::Preparing do
+ subject do
+ described_class.new(double('subject'))
+ end
+
+ describe '#illustration' do
+ it { expect(subject.illustration).to include(:image, :size, :title, :content) }
+ end
+
+ describe '.matches?' do
+ subject { described_class.matches?(build, nil) }
+
+ context 'when build is preparing' do
+ let(:build) { create(:ci_build, :preparing) }
+
+ it 'is a correct match' do
+ expect(subject).to be true
+ end
+ end
+
+ context 'when build is not preparing' do
+ let(:build) { create(:ci_build, :success) }
+
+ it 'does not match' do
+ expect(subject).to be false
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/preparing_spec.rb b/spec/lib/gitlab/ci/status/preparing_spec.rb
new file mode 100644
index 00000000000..7211c0e506d
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/preparing_spec.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Preparing do
+ subject do
+ described_class.new(double('subject'), nil)
+ end
+
+ describe '#text' do
+ it { expect(subject.text).to eq 'preparing' }
+ end
+
+ describe '#label' do
+ it { expect(subject.label).to eq 'preparing' }
+ end
+
+ describe '#icon' do
+ it { expect(subject.icon).to eq 'status_created' }
+ end
+
+ describe '#favicon' do
+ it { expect(subject.favicon).to eq 'favicon_status_created' }
+ end
+
+ describe '#group' do
+ it { expect(subject.group).to eq 'preparing' }
+ end
+end
diff --git a/spec/lib/gitlab/git/commit_spec.rb b/spec/lib/gitlab/git/commit_spec.rb
index 3fb41a626b2..4a4ac833e39 100644
--- a/spec/lib/gitlab/git/commit_spec.rb
+++ b/spec/lib/gitlab/git/commit_spec.rb
@@ -537,6 +537,18 @@ describe Gitlab::Git::Commit, :seed_helper do
end
end
+ describe '#gitaly_commit?' do
+ context 'when the commit data comes from gitaly' do
+ it { expect(commit.gitaly_commit?).to eq(true) }
+ end
+
+ context 'when the commit data comes from a Hash' do
+ let(:commit) { described_class.new(repository, sample_commit_hash) }
+
+ it { expect(commit.gitaly_commit?).to eq(false) }
+ end
+ end
+
describe '#has_zero_stats?' do
it { expect(commit.has_zero_stats?).to eq(false) }
end
diff --git a/spec/lib/gitlab/json_cache_spec.rb b/spec/lib/gitlab/json_cache_spec.rb
index 2cae8ec031a..b82c09af306 100644
--- a/spec/lib/gitlab/json_cache_spec.rb
+++ b/spec/lib/gitlab/json_cache_spec.rb
@@ -7,7 +7,7 @@ describe Gitlab::JsonCache do
let(:namespace) { 'geo' }
let(:key) { 'foo' }
let(:expanded_key) { "#{namespace}:#{key}:#{Rails.version}" }
- let(:broadcast_message) { create(:broadcast_message) }
+ set(:broadcast_message) { create(:broadcast_message) }
subject(:cache) { described_class.new(namespace: namespace, backend: backend) }
@@ -146,6 +146,18 @@ describe Gitlab::JsonCache do
expect(cache.read(key, BroadcastMessage)).to be_nil
end
+
+ it 'gracefully handles excluded fields from attributes during serialization' do
+ allow(backend).to receive(:read)
+ .with(expanded_key)
+ .and_return(broadcast_message.attributes.except("message_html").to_json)
+
+ result = cache.read(key, BroadcastMessage)
+
+ BroadcastMessage.cached_markdown_fields.html_fields.each do |field|
+ expect(result.public_send(field)).to be_nil
+ end
+ end
end
context 'when the cached value is an array' do
@@ -321,6 +333,46 @@ describe Gitlab::JsonCache do
expect(result).to be_new_record
end
+
+ it 'gracefully handles bad cached entry' do
+ allow(backend).to receive(:read)
+ .with(expanded_key)
+ .and_return('{')
+
+ result = cache.fetch(key, as: BroadcastMessage) { 'block result' }
+
+ expect(result).to eq 'block result'
+ end
+
+ it 'gracefully handles an empty hash' do
+ allow(backend).to receive(:read)
+ .with(expanded_key)
+ .and_return('{}')
+
+ expect(cache.fetch(key, as: BroadcastMessage)).to be_a(BroadcastMessage)
+ end
+
+ it 'gracefully handles unknown attributes' do
+ allow(backend).to receive(:read)
+ .with(expanded_key)
+ .and_return(broadcast_message.attributes.merge(unknown_attribute: 1).to_json)
+
+ result = cache.fetch(key, as: BroadcastMessage) { 'block result' }
+
+ expect(result).to eq 'block result'
+ end
+
+ it 'gracefully handles excluded fields from attributes during serialization' do
+ allow(backend).to receive(:read)
+ .with(expanded_key)
+ .and_return(broadcast_message.attributes.except("message_html").to_json)
+
+ result = cache.fetch(key, as: BroadcastMessage) { 'block result' }
+
+ BroadcastMessage.cached_markdown_fields.html_fields.each do |field|
+ expect(result.public_send(field)).to be_nil
+ end
+ end
end
it "returns the result of the block when 'as' option is nil" do
diff --git a/spec/lib/gitlab/kubernetes_spec.rb b/spec/lib/gitlab/kubernetes_spec.rb
index f326d57e9c6..57b570a9166 100644
--- a/spec/lib/gitlab/kubernetes_spec.rb
+++ b/spec/lib/gitlab/kubernetes_spec.rb
@@ -40,10 +40,40 @@ describe Gitlab::Kubernetes do
describe '#filter_by_label' do
it 'returns matching labels' do
- matching_items = [kube_pod(app: 'foo')]
+ matching_items = [kube_pod(track: 'foo'), kube_deployment(track: 'foo')]
+ items = matching_items + [kube_pod, kube_deployment]
+
+ expect(filter_by_label(items, 'track' => 'foo')).to eq(matching_items)
+ end
+ end
+
+ describe '#filter_by_annotation' do
+ it 'returns matching labels' do
+ matching_items = [kube_pod(environment_slug: 'foo'), kube_deployment(environment_slug: 'foo')]
+ items = matching_items + [kube_pod, kube_deployment]
+
+ expect(filter_by_annotation(items, 'app.gitlab.com/env' => 'foo')).to eq(matching_items)
+ end
+ end
+
+ describe '#filter_by_project_environment' do
+ let(:matching_pod) { kube_pod(environment_slug: 'production', project_slug: 'my-cool-app') }
+
+ it 'returns matching legacy env label' do
+ matching_pod['metadata']['annotations'].delete('app.gitlab.com/app')
+ matching_pod['metadata']['annotations'].delete('app.gitlab.com/env')
+ matching_pod['metadata']['labels']['app'] = 'production'
+ matching_items = [matching_pod]
+ items = matching_items + [kube_pod]
+
+ expect(filter_by_project_environment(items, 'my-cool-app', 'production')).to eq(matching_items)
+ end
+
+ it 'returns matching env label' do
+ matching_items = [matching_pod]
items = matching_items + [kube_pod]
- expect(filter_by_label(items, app: 'foo')).to eq(matching_items)
+ expect(filter_by_project_environment(items, 'my-cool-app', 'production')).to eq(matching_items)
end
end
diff --git a/spec/models/broadcast_message_spec.rb b/spec/models/broadcast_message_spec.rb
index 89839709131..30ca07d5d2c 100644
--- a/spec/models/broadcast_message_spec.rb
+++ b/spec/models/broadcast_message_spec.rb
@@ -95,6 +95,12 @@ describe BroadcastMessage do
end
end
+ describe '#attributes' do
+ it 'includes message_html field' do
+ expect(subject.attributes.keys).to include("cached_markdown_version", "message_html")
+ end
+ end
+
describe '#active?' do
it 'is truthy when started and not ended' do
message = build(:broadcast_message)
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 9ca4241d7d8..7500e6ae5b1 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -186,6 +186,37 @@ describe Ci::Build do
end
end
+ describe '#enqueue' do
+ let(:build) { create(:ci_build, :created) }
+
+ subject { build.enqueue }
+
+ before do
+ allow(build).to receive(:any_unmet_prerequisites?).and_return(has_prerequisites)
+ allow(Ci::PrepareBuildService).to receive(:perform_async)
+ end
+
+ context 'build has unmet prerequisites' do
+ let(:has_prerequisites) { true }
+
+ it 'transitions to preparing' do
+ subject
+
+ expect(build).to be_preparing
+ end
+ end
+
+ context 'build has no prerequisites' do
+ let(:has_prerequisites) { false }
+
+ it 'transitions to pending' do
+ subject
+
+ expect(build).to be_pending
+ end
+ end
+ end
+
describe '#actionize' do
context 'when build is a created' do
before do
@@ -344,6 +375,18 @@ describe Ci::Build do
expect(build).to be_pending
end
+
+ context 'build has unmet prerequisites' do
+ before do
+ allow(build).to receive(:prerequisites).and_return([double])
+ end
+
+ it 'transits to preparing' do
+ subject
+
+ expect(build).to be_preparing
+ end
+ end
end
end
@@ -2876,6 +2919,36 @@ describe Ci::Build do
end
end
+ describe '#any_unmet_prerequisites?' do
+ let(:build) { create(:ci_build, :created) }
+
+ subject { build.any_unmet_prerequisites? }
+
+ context 'build has prerequisites' do
+ before do
+ allow(build).to receive(:prerequisites).and_return([double])
+ end
+
+ it { is_expected.to be_truthy }
+
+ context 'and the ci_preparing_state feature is disabled' do
+ before do
+ stub_feature_flags(ci_preparing_state: false)
+ end
+
+ it { is_expected.to be_falsey }
+ end
+ end
+
+ context 'build does not have prerequisites' do
+ before do
+ allow(build).to receive(:prerequisites).and_return([])
+ end
+
+ it { is_expected.to be_falsey }
+ end
+ end
+
describe '#yaml_variables' do
let(:build) { create(:ci_build, pipeline: pipeline, yaml_variables: variables) }
@@ -2928,6 +3001,20 @@ describe Ci::Build do
end
end
+ describe 'state transition: any => [:preparing]' do
+ let(:build) { create(:ci_build, :created) }
+
+ before do
+ allow(build).to receive(:prerequisites).and_return([double])
+ end
+
+ it 'queues BuildPrepareWorker' do
+ expect(Ci::BuildPrepareWorker).to receive(:perform_async).with(build.id)
+
+ build.enqueue
+ end
+ end
+
describe 'state transition: any => [:pending]' do
let(:build) { create(:ci_build, :created) }
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 7eeaa7a18ef..2ac056f63b2 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -1201,16 +1201,28 @@ describe Ci::Pipeline, :mailer do
end
describe '#started_at' do
- it 'updates on transitioning to running' do
- build.run
+ let(:pipeline) { create(:ci_empty_pipeline, status: from_status) }
+
+ %i[created preparing pending].each do |status|
+ context "from #{status}" do
+ let(:from_status) { status }
- expect(pipeline.reload.started_at).not_to be_nil
+ it 'updates on transitioning to running' do
+ pipeline.run
+
+ expect(pipeline.started_at).not_to be_nil
+ end
+ end
end
- it 'does not update on transitioning to success' do
- build.success
+ context 'from created' do
+ let(:from_status) { :created }
+
+ it 'does not update on transitioning to success' do
+ pipeline.succeed
- expect(pipeline.reload.started_at).to be_nil
+ expect(pipeline.started_at).to be_nil
+ end
end
end
@@ -1229,27 +1241,49 @@ describe Ci::Pipeline, :mailer do
end
describe 'merge request metrics' do
- let(:project) { create(:project, :repository) }
- let(:pipeline) { FactoryBot.create(:ci_empty_pipeline, status: 'created', project: project, ref: 'master', sha: project.repository.commit('master').id) }
- let!(:merge_request) { create(:merge_request, source_project: project, source_branch: pipeline.ref) }
+ let(:pipeline) { create(:ci_empty_pipeline, status: from_status) }
before do
expect(PipelineMetricsWorker).to receive(:perform_async).with(pipeline.id)
end
context 'when transitioning to running' do
- it 'schedules metrics workers' do
- pipeline.run
+ %i[created preparing pending].each do |status|
+ context "from #{status}" do
+ let(:from_status) { status }
+
+ it 'schedules metrics workers' do
+ pipeline.run
+ end
+ end
end
end
context 'when transitioning to success' do
+ let(:from_status) { 'created' }
+
it 'schedules metrics workers' do
pipeline.succeed
end
end
end
+ describe 'merge on success' do
+ let(:pipeline) { create(:ci_empty_pipeline, status: from_status) }
+
+ %i[created preparing pending running].each do |status|
+ context "from #{status}" do
+ let(:from_status) { status }
+
+ it 'schedules pipeline success worker' do
+ expect(PipelineSuccessWorker).to receive(:perform_async).with(pipeline.id)
+
+ pipeline.succeed
+ end
+ end
+ end
+ end
+
describe 'pipeline caching' do
it 'performs ExpirePipelinesCacheWorker' do
expect(ExpirePipelineCacheWorker).to receive(:perform_async).with(pipeline.id)
@@ -1768,6 +1802,18 @@ describe Ci::Pipeline, :mailer do
subject { pipeline.reload.status }
+ context 'on prepare' do
+ before do
+ # Prevent skipping directly to 'pending'
+ allow(build).to receive(:prerequisites).and_return([double])
+ allow(Ci::BuildPrepareWorker).to receive(:perform_async)
+
+ build.enqueue
+ end
+
+ it { is_expected.to eq('preparing') }
+ end
+
context 'on queuing' do
before do
build.enqueue
diff --git a/spec/models/clusters/platforms/kubernetes_spec.rb b/spec/models/clusters/platforms/kubernetes_spec.rb
index cc93a1b4965..af65530e663 100644
--- a/spec/models/clusters/platforms/kubernetes_spec.rb
+++ b/spec/models/clusters/platforms/kubernetes_spec.rb
@@ -375,14 +375,14 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
end
context 'with valid pods' do
- let(:pod) { kube_pod(app: environment.slug) }
- let(:pod_with_no_terminal) { kube_pod(app: environment.slug, status: "Pending") }
+ let(:pod) { kube_pod(environment_slug: environment.slug, project_slug: project.full_path_slug) }
+ let(:pod_with_no_terminal) { kube_pod(environment_slug: environment.slug, project_slug: project.full_path_slug, status: "Pending") }
let(:terminals) { kube_terminals(service, pod) }
before do
stub_reactive_cache(
service,
- pods: [pod, pod, pod_with_no_terminal, kube_pod(app: "should-be-filtered-out")]
+ pods: [pod, pod, pod_with_no_terminal, kube_pod(environment_slug: "should-be-filtered-out")]
)
end
diff --git a/spec/models/commit_collection_spec.rb b/spec/models/commit_collection_spec.rb
index 0f5d03ff458..30c504ebea8 100644
--- a/spec/models/commit_collection_spec.rb
+++ b/spec/models/commit_collection_spec.rb
@@ -37,12 +37,92 @@ describe CommitCollection do
describe '#without_merge_commits' do
it 'returns all commits except merge commits' do
+ merge_commit = project.commit("60ecb67744cb56576c30214ff52294f8ce2def98")
+ expect(merge_commit).to receive(:merge_commit?).and_return(true)
+
collection = described_class.new(project, [
- build(:commit),
- build(:commit, :merge_commit)
+ commit,
+ merge_commit
])
- expect(collection.without_merge_commits.size).to eq(1)
+ expect(collection.without_merge_commits).to contain_exactly(commit)
+ end
+ end
+
+ describe 'enrichment methods' do
+ let(:gitaly_commit) { commit }
+ let(:hash_commit) { Commit.from_hash(gitaly_commit.to_hash, project) }
+
+ describe '#unenriched' do
+ it 'returns all commits that are not backed by gitaly data' do
+ collection = described_class.new(project, [gitaly_commit, hash_commit])
+
+ expect(collection.unenriched).to contain_exactly(hash_commit)
+ end
+ end
+
+ describe '#fully_enriched?' do
+ it 'returns true when all commits are backed by gitaly data' do
+ collection = described_class.new(project, [gitaly_commit, gitaly_commit])
+
+ expect(collection.fully_enriched?).to eq(true)
+ end
+
+ it 'returns false when any commits are not backed by gitaly data' do
+ collection = described_class.new(project, [gitaly_commit, hash_commit])
+
+ expect(collection.fully_enriched?).to eq(false)
+ end
+
+ it 'returns true when the collection is empty' do
+ collection = described_class.new(project, [])
+
+ expect(collection.fully_enriched?).to eq(true)
+ end
+ end
+
+ describe '#enrich!' do
+ it 'replaces commits in the collection with those backed by gitaly data' do
+ collection = described_class.new(project, [hash_commit])
+
+ collection.enrich!
+
+ new_commit = collection.commits.first
+ expect(new_commit.id).to eq(hash_commit.id)
+ expect(hash_commit.gitaly_commit?).to eq(false)
+ expect(new_commit.gitaly_commit?).to eq(true)
+ end
+
+ it 'maintains the original order of the commits' do
+ gitaly_commits = [gitaly_commit] * 3
+ hash_commits = [hash_commit] * 3
+ # Interleave the gitaly and hash commits together
+ original_commits = gitaly_commits.zip(hash_commits).flatten
+ collection = described_class.new(project, original_commits)
+
+ collection.enrich!
+
+ original_commits.each_with_index do |original_commit, i|
+ new_commit = collection.commits[i]
+ expect(original_commit.id).to eq(new_commit.id)
+ end
+ end
+
+ it 'fetches data if there are unenriched commits' do
+ collection = described_class.new(project, [hash_commit])
+
+ expect(Commit).to receive(:lazy).exactly(:once)
+
+ collection.enrich!
+ end
+
+ it 'does not fetch data if all commits are enriched' do
+ collection = described_class.new(project, [gitaly_commit])
+
+ expect(Commit).not_to receive(:lazy)
+
+ collection.enrich!
+ end
end
end
diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb
index 8b7c88805c1..e2b7f5c6ee2 100644
--- a/spec/models/commit_status_spec.rb
+++ b/spec/models/commit_status_spec.rb
@@ -49,6 +49,16 @@ describe CommitStatus do
commit_status.success!
end
+
+ describe 'transitioning to running' do
+ let(:commit_status) { create(:commit_status, :pending, started_at: nil) }
+
+ it 'records the started at time' do
+ commit_status.run!
+
+ expect(commit_status.started_at).to be_present
+ end
+ end
end
describe '#started?' do
@@ -479,6 +489,12 @@ describe CommitStatus do
it { is_expected.to be_script_failure }
end
+
+ context 'when failure_reason is unmet_prerequisites' do
+ let(:reason) { :unmet_prerequisites }
+
+ it { is_expected.to be_unmet_prerequisites }
+ end
end
describe 'ensure stage assignment' do
@@ -555,6 +571,7 @@ describe CommitStatus do
before do
allow(Time).to receive(:now).and_return(current_time)
+ expect(commit_status.any_unmet_prerequisites?).to eq false
end
shared_examples 'commit status enqueued' do
@@ -569,6 +586,12 @@ describe CommitStatus do
it_behaves_like 'commit status enqueued'
end
+ context 'when initial state is :preparing' do
+ let(:commit_status) { create(:commit_status, :preparing) }
+
+ it_behaves_like 'commit status enqueued'
+ end
+
context 'when initial state is :skipped' do
let(:commit_status) { create(:commit_status, :skipped) }
diff --git a/spec/models/concerns/cache_markdown_field_spec.rb b/spec/models/concerns/cache_markdown_field_spec.rb
index 447279f19a8..7d555f15e39 100644
--- a/spec/models/concerns/cache_markdown_field_spec.rb
+++ b/spec/models/concerns/cache_markdown_field_spec.rb
@@ -23,6 +23,7 @@ describe CacheMarkdownField do
include CacheMarkdownField
cache_markdown_field :foo
cache_markdown_field :baz, pipeline: :single_line
+ cache_markdown_field :zoo, whitelisted: true
def self.add_attr(name)
self.attribute_names += [name]
@@ -35,7 +36,7 @@ describe CacheMarkdownField do
add_attr :cached_markdown_version
- [:foo, :foo_html, :bar, :baz, :baz_html].each do |name|
+ [:foo, :foo_html, :bar, :baz, :baz_html, :zoo, :zoo_html].each do |name|
add_attr(name)
end
@@ -84,8 +85,8 @@ describe CacheMarkdownField do
end
describe '.attributes' do
- it 'excludes cache attributes' do
- expect(thing.attributes.keys.sort).to eq(%w[bar baz foo])
+ it 'excludes cache attributes that is blacklisted by default' do
+ expect(thing.attributes.keys.sort).to eq(%w[bar baz cached_markdown_version foo zoo zoo_html])
end
end
@@ -297,7 +298,12 @@ describe CacheMarkdownField do
it 'saves the changes using #update_columns' do
expect(thing).to receive(:persisted?).and_return(true)
expect(thing).to receive(:update_columns)
- .with("foo_html" => updated_html, "baz_html" => "", "cached_markdown_version" => cache_version)
+ .with(
+ "foo_html" => updated_html,
+ "baz_html" => "",
+ "zoo_html" => "",
+ "cached_markdown_version" => cache_version
+ )
thing.refresh_markdown_cache!
end
diff --git a/spec/models/concerns/has_status_spec.rb b/spec/models/concerns/has_status_spec.rb
index 6b1038cb8fd..e8b1eba67cc 100644
--- a/spec/models/concerns/has_status_spec.rb
+++ b/spec/models/concerns/has_status_spec.rb
@@ -34,6 +34,22 @@ describe HasStatus do
it { is_expected.to eq 'running' }
end
+ context 'all preparing' do
+ let!(:statuses) do
+ [create(type, status: :preparing), create(type, status: :preparing)]
+ end
+
+ it { is_expected.to eq 'preparing' }
+ end
+
+ context 'at least one preparing' do
+ let!(:statuses) do
+ [create(type, status: :success), create(type, status: :preparing)]
+ end
+
+ it { is_expected.to eq 'preparing' }
+ end
+
context 'success and failed but allowed to fail' do
let!(:statuses) do
[create(type, status: :success),
@@ -188,7 +204,7 @@ describe HasStatus do
end
end
- %i[created running pending success
+ %i[created preparing running pending success
failed canceled skipped].each do |status|
it_behaves_like 'having a job', status
end
@@ -234,7 +250,7 @@ describe HasStatus do
describe '.alive' do
subject { CommitStatus.alive }
- %i[running pending created].each do |status|
+ %i[running pending preparing created].each do |status|
it_behaves_like 'containing the job', status
end
@@ -270,7 +286,7 @@ describe HasStatus do
describe '.cancelable' do
subject { CommitStatus.cancelable }
- %i[running pending created scheduled].each do |status|
+ %i[running pending preparing created scheduled].each do |status|
it_behaves_like 'containing the job', status
end
diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb
index a8d53cfcd7d..5fce9504334 100644
--- a/spec/models/deployment_spec.rb
+++ b/spec/models/deployment_spec.rb
@@ -356,4 +356,32 @@ describe Deployment do
end
end
end
+
+ describe '#cluster' do
+ let(:deployment) { create(:deployment) }
+ let(:project) { deployment.project }
+ let(:environment) { deployment.environment }
+
+ subject { deployment.cluster }
+
+ before do
+ expect(project).to receive(:deployment_platform)
+ .with(environment: environment.name).and_call_original
+ end
+
+ context 'project has no deployment platform' do
+ before do
+ expect(project.clusters).to be_empty
+ end
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'project has a deployment platform' do
+ let!(:cluster) { create(:cluster, projects: [project]) }
+ let!(:platform) { create(:cluster_platform_kubernetes, cluster: cluster) }
+
+ it { is_expected.to eq cluster }
+ end
+ end
end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 42c49e330cc..fad73613989 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -84,32 +84,27 @@ describe MergeRequest do
describe '#default_squash_commit_message' do
let(:project) { subject.project }
-
- def commit_collection(commit_hashes)
- raw_commits = commit_hashes.map { |raw| Commit.from_hash(raw, project) }
-
- CommitCollection.new(project, raw_commits)
- end
+ let(:is_multiline) { -> (c) { c.description.present? } }
+ let(:multiline_commits) { subject.commits.select(&is_multiline) }
+ let(:singleline_commits) { subject.commits.reject(&is_multiline) }
it 'returns the oldest multiline commit message' do
- commits = commit_collection([
- { message: 'Singleline', parent_ids: [] },
- { message: "Second multiline\nCommit message", parent_ids: [] },
- { message: "First multiline\nCommit message", parent_ids: [] }
- ])
-
- expect(subject).to receive(:commits).and_return(commits)
-
- expect(subject.default_squash_commit_message).to eq("First multiline\nCommit message")
+ expect(subject.default_squash_commit_message).to eq(multiline_commits.last.message)
end
it 'returns the merge request title if there are no multiline commits' do
- commits = commit_collection([
- { message: 'Singleline', parent_ids: [] }
- ])
+ expect(subject).to receive(:commits).and_return(
+ CommitCollection.new(project, singleline_commits)
+ )
+
+ expect(subject.default_squash_commit_message).to eq(subject.title)
+ end
- expect(subject).to receive(:commits).and_return(commits)
+ it 'does not return commit messages from multiline merge commits' do
+ collection = CommitCollection.new(project, multiline_commits).enrich!
+ expect(collection.commits).to all( receive(:merge_commit?).and_return(true) )
+ expect(subject).to receive(:commits).and_return(collection)
expect(subject.default_squash_commit_message).to eq(subject.title)
end
end
@@ -1045,7 +1040,7 @@ describe MergeRequest do
describe '#commit_authors' do
it 'returns all the authors of every commit in the merge request' do
- users = subject.commits.map(&:author_email).uniq.map do |email|
+ users = subject.commits.without_merge_commits.map(&:author_email).uniq.map do |email|
create(:user, email: email)
end
@@ -1059,7 +1054,7 @@ describe MergeRequest do
describe '#authors' do
it 'returns a list with all the commit authors in the merge request and author' do
- users = subject.commits.map(&:author_email).uniq.map do |email|
+ users = subject.commits.without_merge_commits.map(&:author_email).uniq.map do |email|
create(:user, email: email)
end
diff --git a/spec/models/project_services/kubernetes_service_spec.rb b/spec/models/project_services/kubernetes_service_spec.rb
index 47f70e6648a..56e587262ef 100644
--- a/spec/models/project_services/kubernetes_service_spec.rb
+++ b/spec/models/project_services/kubernetes_service_spec.rb
@@ -323,13 +323,14 @@ describe KubernetesService, :use_clean_rails_memory_store_caching do
end
context 'with valid pods' do
- let(:pod) { kube_pod(app: environment.slug) }
+ let(:pod) { kube_pod(environment_slug: environment.slug, project_slug: project.full_path_slug) }
+ let(:pod_with_no_terminal) { kube_pod(environment_slug: environment.slug, project_slug: project.full_path_slug, status: "Pending") }
let(:terminals) { kube_terminals(service, pod) }
before do
stub_reactive_cache(
service,
- pods: [pod, pod, kube_pod(app: "should-be-filtered-out")]
+ pods: [pod, pod, pod_with_no_terminal, kube_pod(environment_slug: "should-be-filtered-out")]
)
end
@@ -360,14 +361,16 @@ describe KubernetesService, :use_clean_rails_memory_store_caching do
context 'when kubernetes responds with valid pods' do
before do
stub_kubeclient_pods
+ stub_kubeclient_deployments # Used by EE
end
- it { is_expected.to eq(pods: [kube_pod]) }
+ it { is_expected.to include(pods: [kube_pod]) }
end
context 'when kubernetes responds with 500s' do
before do
stub_kubeclient_pods(status: 500)
+ stub_kubeclient_deployments(status: 500) # Used by EE
end
it { expect { subject }.to raise_error(Kubeclient::HttpError) }
@@ -376,9 +379,10 @@ describe KubernetesService, :use_clean_rails_memory_store_caching do
context 'when kubernetes responds with 404s' do
before do
stub_kubeclient_pods(status: 404)
+ stub_kubeclient_deployments(status: 404) # Used by EE
end
- it { is_expected.to eq(pods: []) }
+ it { is_expected.to include(pods: []) }
end
end
diff --git a/spec/policies/identity_provider_policy_spec.rb b/spec/policies/identity_provider_policy_spec.rb
new file mode 100644
index 00000000000..2520469d4e7
--- /dev/null
+++ b/spec/policies/identity_provider_policy_spec.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe IdentityProviderPolicy do
+ subject(:policy) { described_class.new(user, provider) }
+ let(:user) { User.new }
+ let(:provider) { :a_provider }
+
+ describe '#rules' do
+ it { is_expected.to be_allowed(:link) }
+ it { is_expected.to be_allowed(:unlink) }
+
+ context 'when user is anonymous' do
+ let(:user) { nil }
+
+ it { is_expected.not_to be_allowed(:link) }
+ it { is_expected.not_to be_allowed(:unlink) }
+ end
+
+ %w[saml cas3].each do |provider_name|
+ context "when provider is #{provider_name}" do
+ let(:provider) { provider_name }
+
+ it { is_expected.to be_allowed(:link) }
+ it { is_expected.not_to be_allowed(:unlink) }
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/project_clusters_spec.rb b/spec/requests/api/project_clusters_spec.rb
index 9bab1f95150..4e42e233b4c 100644
--- a/spec/requests/api/project_clusters_spec.rb
+++ b/spec/requests/api/project_clusters_spec.rb
@@ -331,7 +331,6 @@ describe API::ProjectClusters do
it 'should update cluster attributes' do
expect(cluster.platform_kubernetes.namespace).to eq('new-namespace')
- expect(cluster.kubernetes_namespace.namespace).to eq('new-namespace')
end
end
diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb
index 9087cccb759..3ccedd8dd06 100644
--- a/spec/requests/api/runner_spec.rb
+++ b/spec/requests/api/runner_spec.rb
@@ -918,6 +918,15 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
it { expect(job).to be_job_execution_timeout }
end
+
+ context 'when failure_reason is unmet_prerequisites' do
+ before do
+ update_job(state: 'failed', failure_reason: 'unmet_prerequisites')
+ job.reload
+ end
+
+ it { expect(job).to be_unmet_prerequisites }
+ end
end
context 'when trace is given' do
diff --git a/spec/serializers/merge_request_widget_entity_spec.rb b/spec/serializers/merge_request_widget_entity_spec.rb
index 4dbd79f2fc0..727fd8951f2 100644
--- a/spec/serializers/merge_request_widget_entity_spec.rb
+++ b/spec/serializers/merge_request_widget_entity_spec.rb
@@ -279,13 +279,18 @@ describe MergeRequestWidgetEntity do
end
describe 'commits_without_merge_commits' do
+ def find_matching_commit(short_id)
+ resource.commits.find { |c| c.short_id == short_id }
+ end
+
it 'should not include merge commits' do
- # Mock all but the first 5 commits to be merge commits
- resource.commits.each_with_index do |commit, i|
- expect(commit).to receive(:merge_commit?).at_least(:once).and_return(i > 4)
- end
+ commits_in_widget = subject[:commits_without_merge_commits]
- expect(subject[:commits_without_merge_commits].size).to eq(5)
+ expect(commits_in_widget.length).to be < resource.commits.length
+ expect(commits_in_widget.length).to eq(resource.commits.without_merge_commits.length)
+ commits_in_widget.each do |c|
+ expect(find_matching_commit(c[:short_id]).merge_commit?).to eq(false)
+ end
end
end
end
diff --git a/spec/services/ci/prepare_build_service_spec.rb b/spec/services/ci/prepare_build_service_spec.rb
new file mode 100644
index 00000000000..1797f8f964f
--- /dev/null
+++ b/spec/services/ci/prepare_build_service_spec.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Ci::PrepareBuildService do
+ describe '#execute' do
+ let(:build) { create(:ci_build, :preparing) }
+
+ subject { described_class.new(build).execute }
+
+ before do
+ allow(build).to receive(:prerequisites).and_return(prerequisites)
+ end
+
+ shared_examples 'build enqueueing' do
+ it 'enqueues the build' do
+ expect(build).to receive(:enqueue).once
+
+ subject
+ end
+ end
+
+ context 'build has unmet prerequisites' do
+ let(:prerequisite) { double(complete!: true) }
+ let(:prerequisites) { [prerequisite] }
+
+ it 'completes each prerequisite' do
+ expect(prerequisites).to all(receive(:complete!))
+
+ subject
+ end
+
+ include_examples 'build enqueueing'
+
+ context 'prerequisites fail to complete' do
+ before do
+ allow(build).to receive(:enqueue).and_return(false)
+ end
+
+ it 'drops the build' do
+ expect(build).to receive(:drop!).with(:unmet_prerequisites).once
+
+ subject
+ end
+ end
+ end
+
+ context 'build has no prerequisites' do
+ let(:prerequisites) { [] }
+
+ include_examples 'build enqueueing'
+ end
+ end
+end
diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb
index d1b110b9806..e8418b09dc2 100644
--- a/spec/services/projects/create_service_spec.rb
+++ b/spec/services/projects/create_service_spec.rb
@@ -276,6 +276,7 @@ describe Projects::CreateService, '#execute' do
before do
group.add_owner(user)
+ stub_feature_flags(ci_preparing_state: false)
expect(Clusters::Gcp::Kubernetes::CreateOrUpdateServiceAccountService).to receive(:namespace_creator).and_return(service_account_creator)
expect(Clusters::Gcp::Kubernetes::FetchKubernetesTokenService).to receive(:new).and_return(secrets_fetcher)
end
diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb
index aae50d5307f..4efd360cb30 100644
--- a/spec/services/projects/transfer_service_spec.rb
+++ b/spec/services/projects/transfer_service_spec.rb
@@ -83,6 +83,7 @@ describe Projects::TransferService do
subject { transfer_project(project, user, group) }
before do
+ stub_feature_flags(ci_preparing_state: false)
expect(Clusters::Gcp::Kubernetes::CreateOrUpdateServiceAccountService).to receive(:namespace_creator).and_return(service_account_creator)
expect(Clusters::Gcp::Kubernetes::FetchKubernetesTokenService).to receive(:new).and_return(secrets_fetcher)
end
diff --git a/spec/support/helpers/kubernetes_helpers.rb b/spec/support/helpers/kubernetes_helpers.rb
index cca11e112c9..ac52acb6570 100644
--- a/spec/support/helpers/kubernetes_helpers.rb
+++ b/spec/support/helpers/kubernetes_helpers.rb
@@ -250,16 +250,19 @@ module KubernetesHelpers
# This is a partial response, it will have many more elements in reality but
# these are the ones we care about at the moment
- def kube_pod(name: "kube-pod", app: "valid-pod-label", status: "Running", track: nil)
+ def kube_pod(name: "kube-pod", environment_slug: "production", project_slug: "project-path-slug", status: "Running", track: nil)
{
"metadata" => {
"name" => name,
"generate_name" => "generated-name-with-suffix",
"creationTimestamp" => "2016-11-25T19:55:19Z",
+ "annotations" => {
+ "app.gitlab.com/env" => environment_slug,
+ "app.gitlab.com/app" => project_slug
+ },
"labels" => {
- "app" => app,
"track" => track
- }
+ }.compact
},
"spec" => {
"containers" => [
@@ -293,13 +296,16 @@ module KubernetesHelpers
}
end
- def kube_deployment(name: "kube-deployment", app: "valid-deployment-label", track: nil)
+ def kube_deployment(name: "kube-deployment", environment_slug: "production", project_slug: "project-path-slug", track: nil)
{
"metadata" => {
"name" => name,
"generation" => 4,
+ "annotations" => {
+ "app.gitlab.com/env" => environment_slug,
+ "app.gitlab.com/app" => project_slug
+ },
"labels" => {
- "app" => app,
"track" => track
}.compact
},
diff --git a/spec/workers/ci/build_prepare_worker_spec.rb b/spec/workers/ci/build_prepare_worker_spec.rb
new file mode 100644
index 00000000000..9f76696ee66
--- /dev/null
+++ b/spec/workers/ci/build_prepare_worker_spec.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Ci::BuildPrepareWorker do
+ subject { described_class.new.perform(build_id) }
+
+ context 'build exists' do
+ let(:build) { create(:ci_build) }
+ let(:build_id) { build.id }
+ let(:service) { double(execute: true) }
+
+ it 'calls the prepare build service' do
+ expect(Ci::PrepareBuildService).to receive(:new).with(build).and_return(service)
+ expect(service).to receive(:execute).once
+
+ subject
+ end
+ end
+
+ context 'build does not exist' do
+ let(:build_id) { -1 }
+
+ it 'does not attempt to prepare the build' do
+ expect(Ci::PrepareBuildService).not_to receive(:new)
+
+ subject
+ end
+ end
+end
diff --git a/spec/workers/cluster_configure_worker_spec.rb b/spec/workers/cluster_configure_worker_spec.rb
index 6918ee3d7d8..83f76809435 100644
--- a/spec/workers/cluster_configure_worker_spec.rb
+++ b/spec/workers/cluster_configure_worker_spec.rb
@@ -4,6 +4,11 @@ require 'spec_helper'
describe ClusterConfigureWorker, '#perform' do
let(:worker) { described_class.new }
+ let(:ci_preparing_state_enabled) { false }
+
+ before do
+ stub_feature_flags(ci_preparing_state: ci_preparing_state_enabled)
+ end
context 'when group cluster' do
let(:cluster) { create(:cluster, :group, :provided_by_gcp) }
@@ -66,4 +71,15 @@ describe ClusterConfigureWorker, '#perform' do
described_class.new.perform(123)
end
end
+
+ context 'ci_preparing_state feature is enabled' do
+ let(:cluster) { create(:cluster) }
+ let(:ci_preparing_state_enabled) { true }
+
+ it 'does not configure the cluster' do
+ expect(Clusters::RefreshService).not_to receive(:create_or_update_namespaces_for_cluster)
+
+ described_class.new.perform(cluster.id)
+ end
+ end
end
diff --git a/spec/workers/cluster_project_configure_worker_spec.rb b/spec/workers/cluster_project_configure_worker_spec.rb
new file mode 100644
index 00000000000..afdea55adf4
--- /dev/null
+++ b/spec/workers/cluster_project_configure_worker_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe ClusterProjectConfigureWorker, '#perform' do
+ let(:worker) { described_class.new }
+
+ context 'ci_preparing_state feature is enabled' do
+ let(:cluster) { create(:cluster) }
+
+ before do
+ stub_feature_flags(ci_preparing_state: true)
+ end
+
+ it 'does not configure the cluster' do
+ expect(Clusters::RefreshService).not_to receive(:create_or_update_namespaces_for_project)
+
+ described_class.new.perform(cluster.id)
+ end
+ end
+end