Commit 9ad5803b authored by Stan Hu's avatar Stan Hu

Merge branch 'ce-to-ee-2018-10-16' into 'master'

CE upstream - 2018-10-16 18:21 UTC

Closes gitlab-ce#52487

See merge request gitlab-org/gitlab-ee!7955
parents bbcacef1 a4411a75
<script> <script>
import $ from 'jquery'; import $ from 'jquery';
import IssueCardWeight from 'ee/boards/components/issue_card_weight.vue';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import IssueCardWeight from 'ee/boards/components/issue_card_weight.vue';
import UserAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue'; import UserAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
import eventHub from '../eventhub'; import eventHub from '../eventhub';
import tooltip from '../../vue_shared/directives/tooltip'; import tooltip from '../../vue_shared/directives/tooltip';
...@@ -10,8 +10,8 @@ ...@@ -10,8 +10,8 @@
export default { export default {
components: { components: {
UserAvatarLink, UserAvatarLink,
IssueCardWeight,
Icon, Icon,
IssueCardWeight,
}, },
directives: { directives: {
tooltip, tooltip,
......
import Visibility from 'visibilityjs'; import Visibility from 'visibilityjs';
import Vue from 'vue'; import Vue from 'vue';
import PersistentUserCallout from '../persistent_user_callout'; import initDismissableCallout from '~/dismissable_callout';
import { s__, sprintf } from '../locale'; import { s__, sprintf } from '../locale';
import Flash from '../flash'; import Flash from '../flash';
import Poll from '../lib/utils/poll'; import Poll from '../lib/utils/poll';
...@@ -62,7 +62,7 @@ export default class Clusters { ...@@ -62,7 +62,7 @@ export default class Clusters {
this.showTokenButton = document.querySelector('.js-show-cluster-token'); this.showTokenButton = document.querySelector('.js-show-cluster-token');
this.tokenField = document.querySelector('.js-cluster-token'); this.tokenField = document.querySelector('.js-cluster-token');
Clusters.initDismissableCallout(); initDismissableCallout('.js-cluster-security-warning');
initSettingsPanels(); initSettingsPanels();
setupToggleButtons(document.querySelector('.js-cluster-enable-toggle-area')); setupToggleButtons(document.querySelector('.js-cluster-enable-toggle-area'));
this.initApplications(); this.initApplications();
...@@ -105,12 +105,6 @@ export default class Clusters { ...@@ -105,12 +105,6 @@ export default class Clusters {
}); });
} }
static initDismissableCallout() {
const callout = document.querySelector('.js-cluster-security-warning');
if (callout) new PersistentUserCallout(callout); // eslint-disable-line no-new
}
addListeners() { addListeners() {
if (this.showTokenButton) this.showTokenButton.addEventListener('click', this.showToken); if (this.showTokenButton) this.showTokenButton.addEventListener('click', this.showToken);
eventHub.$on('installApplication', this.installApplication); eventHub.$on('installApplication', this.installApplication);
......
import createFlash from '~/flash'; import createFlash from '~/flash';
import { __ } from '~/locale'; import { __ } from '~/locale';
import setupToggleButtons from '~/toggle_buttons'; import setupToggleButtons from '~/toggle_buttons';
import PersistentUserCallout from '../persistent_user_callout'; import initDismissableCallout from '~/dismissable_callout';
import ClustersService from './services/clusters_service'; import ClustersService from './services/clusters_service';
export default () => { export default () => {
const clusterList = document.querySelector('.js-clusters-list'); const clusterList = document.querySelector('.js-clusters-list');
const callout = document.querySelector('.gcp-signup-offer'); initDismissableCallout('.gcp-signup-offer');
if (callout) new PersistentUserCallout(callout); // eslint-disable-line no-new
// The empty state won't have a clusterList // The empty state won't have a clusterList
if (clusterList) { if (clusterList) {
......
import $ from 'jquery';
import axios from '~/lib/utils/axios_utils';
import { __ } from '~/locale';
import Flash from '~/flash';
export default function initDismissableCallout(alertSelector) {
const alertEl = document.querySelector(alertSelector);
if (!alertEl) {
return;
}
const closeButtonEl = alertEl.getElementsByClassName('close')[0];
const { dismissEndpoint, featureId } = closeButtonEl.dataset;
closeButtonEl.addEventListener('click', () => {
axios
.post(dismissEndpoint, {
feature_name: featureId,
})
.then(() => {
$(alertEl).alert('close');
})
.catch(() => {
Flash(__('An error occurred while dismissing the alert. Refresh the page and try again.'));
});
});
}
...@@ -13,6 +13,7 @@ export default { ...@@ -13,6 +13,7 @@ export default {
}, },
[types.SET_SELECTED_TEMPLATE_TYPE](state, type) { [types.SET_SELECTED_TEMPLATE_TYPE](state, type) {
state.selectedTemplateType = type; state.selectedTemplateType = type;
state.templates = [];
}, },
[types.SET_UPDATE_SUCCESS](state, success) { [types.SET_UPDATE_SUCCESS](state, success) {
state.updateSuccess = success; state.updateSuccess = success;
......
...@@ -53,10 +53,7 @@ export const slugify = str => str.trim().toLowerCase(); ...@@ -53,10 +53,7 @@ export const slugify = str => str.trim().toLowerCase();
* @param {String} str * @param {String} str
* @returns {String} * @returns {String}
*/ */
export const slugifyWithHyphens = str => { export const slugifyWithHyphens = str => str.toLowerCase().replace(/\s+/g, '-');
const regex = new RegExp(/\s+/, 'g');
return str.toLowerCase().replace(regex, '-');
};
/** /**
* Truncates given text * Truncates given text
......
import initDismissableCallout from '~/dismissable_callout';
import initGkeDropdowns from '~/projects/gke_cluster_dropdowns'; import initGkeDropdowns from '~/projects/gke_cluster_dropdowns';
import PersistentUserCallout from '../../persistent_user_callout';
import Project from './project'; import Project from './project';
import ShortcutsNavigation from '../../behaviors/shortcuts/shortcuts_navigation'; import ShortcutsNavigation from '../../behaviors/shortcuts/shortcuts_navigation';
...@@ -12,9 +12,7 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -12,9 +12,7 @@ document.addEventListener('DOMContentLoaded', () => {
]; ];
if (newClusterViews.indexOf(page) > -1) { if (newClusterViews.indexOf(page) > -1) {
const callout = document.querySelector('.gcp-signup-offer'); initDismissableCallout('.gcp-signup-offer');
if (callout) new PersistentUserCallout(callout); // eslint-disable-line no-new
initGkeDropdowns(); initGkeDropdowns();
} }
......
// if the "projects dashboard" is a user's default dashboard, when they visit the
// instance root index, the dashboard will be served by the root controller instead
// of a dashboard controller. The root index redirects for all other default dashboards.
import '../dashboard/projects/index';
import axios from './lib/utils/axios_utils';
import { __ } from './locale';
import Flash from './flash';
export default class PersistentUserCallout {
constructor(container) {
const { dismissEndpoint, featureId } = container.dataset;
this.container = container;
this.dismissEndpoint = dismissEndpoint;
this.featureId = featureId;
this.init();
}
init() {
const closeButton = this.container.querySelector('.js-close');
closeButton.addEventListener('click', event => this.dismiss(event));
}
dismiss(event) {
event.preventDefault();
axios
.post(this.dismissEndpoint, {
feature_name: this.featureId,
})
.then(() => {
this.container.remove();
})
.catch(() => {
Flash(__('An error occurred while dismissing the alert. Refresh the page and try again.'));
});
}
}
...@@ -269,11 +269,12 @@ export default { ...@@ -269,11 +269,12 @@ export default {
:key="deployment.id" :key="deployment.id"
:deployment="deployment" :deployment="deployment"
/> />
<div class="mr-section-container">
<grouped-test-reports-app <grouped-test-reports-app
v-if="mr.testResultsPath" v-if="mr.testResultsPath"
class="js-reports-container"
:endpoint="mr.testResultsPath" :endpoint="mr.testResultsPath"
/> />
<div class="mr-section-container">
<div class="mr-widget-section"> <div class="mr-widget-section">
<component <component
:is="componentName" :is="componentName"
......
...@@ -1013,6 +1013,7 @@ ...@@ -1013,6 +1013,7 @@
.with-performance-bar & { .with-performance-bar & {
top: 135px; top: 135px;
max-height: calc(100vh - 135px);
} }
} }
......
...@@ -201,7 +201,6 @@ ...@@ -201,7 +201,6 @@
.mr-widget-icon { .mr-widget-icon {
font-size: 22px; font-size: 22px;
margin-right: $gl-btn-padding;
} }
.ci-status-icon svg { .ci-status-icon svg {
......
...@@ -12,7 +12,9 @@ class ApplicationController < ActionController::Base ...@@ -12,7 +12,9 @@ class ApplicationController < ActionController::Base
include WorkhorseHelper include WorkhorseHelper
include EnforcesTwoFactorAuthentication include EnforcesTwoFactorAuthentication
include WithPerformanceBar include WithPerformanceBar
include InvalidUTF8ErrorHandler # this can be removed after switching to rails 5
# https://gitlab.com/gitlab-org/gitlab-ce/issues/51908
include InvalidUTF8ErrorHandler unless Gitlab.rails5?
before_action :authenticate_sessionless_user! before_action :authenticate_sessionless_user!
before_action :authenticate_user! before_action :authenticate_user!
......
...@@ -9,7 +9,7 @@ class Projects::BuildArtifactsController < Projects::ApplicationController ...@@ -9,7 +9,7 @@ class Projects::BuildArtifactsController < Projects::ApplicationController
before_action :validate_artifacts!, except: [:download] before_action :validate_artifacts!, except: [:download]
def download def download
redirect_to download_project_job_artifacts_path(project, job) redirect_to download_project_job_artifacts_path(project, job, params: request.query_parameters)
end end
def browse def browse
......
...@@ -21,29 +21,6 @@ module DashboardHelper ...@@ -21,29 +21,6 @@ module DashboardHelper
links.any? { |link| dashboard_nav_link?(link) } links.any? { |link| dashboard_nav_link?(link) }
end end
def controller_action_to_child_dashboards(controller = controller_name, action = action_name)
case "#{controller}##{action}"
when 'projects#index', 'root#index', 'projects#starred', 'projects#trending'
%w(projects stars)
when 'dashboard#activity'
%w(starred_project_activity project_activity)
when 'groups#index'
%w(groups)
when 'todos#index'
%w(todos)
when 'dashboard#issues'
%w(issues)
when 'dashboard#merge_requests'
%w(merge_requests)
else
[]
end
end
def user_default_dashboard?(user = current_user)
controller_action_to_child_dashboards.any? {|dashboard| dashboard == user.dashboard }
end
private private
def get_dashboard_nav_links def get_dashboard_nav_links
......
...@@ -120,6 +120,7 @@ class Notify < BaseMailer ...@@ -120,6 +120,7 @@ class Notify < BaseMailer
add_unsubscription_headers_and_links add_unsubscription_headers_and_links
headers["X-GitLab-#{model.class.name}-ID"] = model.id headers["X-GitLab-#{model.class.name}-ID"] = model.id
headers["X-GitLab-#{model.class.name}-IID"] = model.iid if model.respond_to?(:iid)
headers['X-GitLab-Reply-Key'] = reply_key headers['X-GitLab-Reply-Key'] = reply_key
@reason = headers['X-GitLab-NotificationReason'] @reason = headers['X-GitLab-NotificationReason']
......
...@@ -52,8 +52,11 @@ class Project < ActiveRecord::Base ...@@ -52,8 +52,11 @@ class Project < ActiveRecord::Base
attachments: 2 attachments: 2
}.freeze }.freeze
# Valids ports to import from VALID_IMPORT_PORTS = [80, 443].freeze
VALID_IMPORT_PORTS = [22, 80, 443].freeze VALID_IMPORT_PROTOCOLS = %w(http https git).freeze
VALID_MIRROR_PORTS = [22, 80, 443].freeze
VALID_MIRROR_PROTOCOLS = %w(http https ssh git).freeze
cache_markdown_field :description, pipeline: :description cache_markdown_field :description, pipeline: :description
...@@ -310,10 +313,10 @@ class Project < ActiveRecord::Base ...@@ -310,10 +313,10 @@ class Project < ActiveRecord::Base
validates :namespace, presence: true validates :namespace, presence: true
validates :name, uniqueness: { scope: :namespace_id } validates :name, uniqueness: { scope: :namespace_id }
validates :import_url, url: { protocols: %w(http https ssh git), validates :import_url, url: { protocols: ->(project) { project.persisted? ? VALID_MIRROR_PROTOCOLS : VALID_IMPORT_PROTOCOLS },
ports: ->(project) { project.persisted? ? VALID_MIRROR_PORTS : VALID_IMPORT_PORTS },
allow_localhost: false, allow_localhost: false,
enforce_user: true, enforce_user: true }, if: [:external_import?, :import_url_changed?]
ports: VALID_IMPORT_PORTS }, if: [:external_import?, :import_url_changed?]
validates :star_count, numericality: { greater_than_or_equal_to: 0 } validates :star_count, numericality: { greater_than_or_equal_to: 0 }
validate :check_limit, on: :create validate :check_limit, on: :create
validate :check_repository_path_availability, on: :update, if: ->(project) { project.renamed? } validate :check_repository_path_availability, on: :update, if: ->(project) { project.renamed? }
......
...@@ -6,8 +6,7 @@ class UserCallout < ActiveRecord::Base ...@@ -6,8 +6,7 @@ class UserCallout < ActiveRecord::Base
enum feature_name: { enum feature_name: {
gke_cluster_integration: 1, gke_cluster_integration: 1,
gcp_signup_offer: 2, gcp_signup_offer: 2,
cluster_security_warning: 3, cluster_security_warning: 3
gold_trial: 4
} }
validates :user, presence: true validates :user, presence: true
......
...@@ -4,9 +4,6 @@ ...@@ -4,9 +4,6 @@
= content_for :meta_tags do = content_for :meta_tags do
= auto_discovery_link_tag(:atom, dashboard_projects_url(rss_url_options), title: "All activity") = auto_discovery_link_tag(:atom, dashboard_projects_url(rss_url_options), title: "All activity")
= content_for :above_breadcrumbs_content do
= render_if_exists "shared/gold_trial_callout"
- page_title "Activity" - page_title "Activity"
- header_title "Activity", activity_dashboard_path - header_title "Activity", activity_dashboard_path
......
...@@ -3,9 +3,6 @@ ...@@ -3,9 +3,6 @@
- header_title "Groups", dashboard_groups_path - header_title "Groups", dashboard_groups_path
= render 'dashboard/groups_head' = render 'dashboard/groups_head'
= content_for :above_breadcrumbs_content do
= render_if_exists "shared/gold_trial_callout"
- if params[:filter].blank? && @groups.empty? - if params[:filter].blank? && @groups.empty?
= render 'shared/groups/empty_state' = render 'shared/groups/empty_state'
- else - else
......
...@@ -4,9 +4,6 @@ ...@@ -4,9 +4,6 @@
= content_for :meta_tags do = content_for :meta_tags do
= auto_discovery_link_tag(:atom, safe_params.merge(rss_url_options).to_h, title: "#{current_user.name} issues") = auto_discovery_link_tag(:atom, safe_params.merge(rss_url_options).to_h, title: "#{current_user.name} issues")
= content_for :above_breadcrumbs_content do
= render_if_exists "shared/gold_trial_callout"
.top-area .top-area
= render 'shared/issuable/nav', type: :issues, display_count: !@no_filters_set = render 'shared/issuable/nav', type: :issues, display_count: !@no_filters_set
.nav-controls .nav-controls
......
...@@ -2,9 +2,6 @@ ...@@ -2,9 +2,6 @@
- page_title _("Merge Requests") - page_title _("Merge Requests")
- @breadcrumb_link = merge_requests_dashboard_path(assignee_id: current_user.id) - @breadcrumb_link = merge_requests_dashboard_path(assignee_id: current_user.id)
= content_for :above_breadcrumbs_content do
= render_if_exists "shared/gold_trial_callout"
.top-area .top-area
= render 'shared/issuable/nav', type: :merge_requests, display_count: !@no_filters_set = render 'shared/issuable/nav', type: :merge_requests, display_count: !@no_filters_set
.nav-controls .nav-controls
......
...@@ -4,9 +4,6 @@ ...@@ -4,9 +4,6 @@
= content_for :meta_tags do = content_for :meta_tags do
= auto_discovery_link_tag(:atom, dashboard_projects_url(rss_url_options), title: "All activity") = auto_discovery_link_tag(:atom, dashboard_projects_url(rss_url_options), title: "All activity")
= content_for :above_breadcrumbs_content do
= render_if_exists "shared/gold_trial_callout"
- page_title "Projects" - page_title "Projects"
- header_title "Projects", dashboard_projects_path - header_title "Projects", dashboard_projects_path
......
...@@ -4,9 +4,6 @@ ...@@ -4,9 +4,6 @@
- page_title "Starred Projects" - page_title "Starred Projects"
- header_title "Projects", dashboard_projects_path - header_title "Projects", dashboard_projects_path
= content_for :above_breadcrumbs_content do
= render_if_exists "shared/gold_trial_callout"
%div{ class: container_class } %div{ class: container_class }
= render "projects/last_push" = render "projects/last_push"
= render 'dashboard/projects_head' = render 'dashboard/projects_head'
......
...@@ -2,9 +2,6 @@ ...@@ -2,9 +2,6 @@
- page_title "Todos" - page_title "Todos"
- header_title "Todos", dashboard_todos_path - header_title "Todos", dashboard_todos_path
= content_for :above_breadcrumbs_content do
= render_if_exists "shared/gold_trial_callout"
- if current_user.todos.any? - if current_user.todos.any?
.top-area .top-area
%ul.nav-links.mobile-separator.nav.nav-tabs %ul.nav-links.mobile-separator.nav.nav-tabs
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
%p.gl-field-hint.text-secondary Minimum length is #{@minimum_password_length} characters %p.gl-field-hint.text-secondary Minimum length is #{@minimum_password_length} characters
- if Gitlab::CurrentSettings.current_application_settings.enforce_terms? - if Gitlab::CurrentSettings.current_application_settings.enforce_terms?
.form-group .form-group
= check_box_tag :terms_opt_in, '1', false, required: true = check_box_tag :terms_opt_in, '1', false, required: true, class: 'qa-new-user-accept-terms'
= label_tag :terms_opt_in do = label_tag :terms_opt_in do
- terms_link = link_to s_("I accept the|Terms of Service and Privacy Policy"), terms_path, target: "_blank" - terms_link = link_to s_("I accept the|Terms of Service and Privacy Policy"), terms_path, target: "_blank"
- accept_terms_label = _("I accept the %{terms_link}") % { terms_link: terms_link } - accept_terms_label = _("I accept the %{terms_link}") % { terms_link: terms_link }
......
...@@ -2,9 +2,6 @@ ...@@ -2,9 +2,6 @@
- page_title _("Groups") - page_title _("Groups")
- header_title _("Groups"), dashboard_groups_path - header_title _("Groups"), dashboard_groups_path
= content_for :above_breadcrumbs_content do
= render_if_exists "shared/gold_trial_callout"
- if current_user - if current_user
= render 'dashboard/groups_head' = render 'dashboard/groups_head'
- else - else
......
...@@ -2,9 +2,6 @@ ...@@ -2,9 +2,6 @@
- page_title _("Projects") - page_title _("Projects")
- header_title _("Projects"), dashboard_projects_path - header_title _("Projects"), dashboard_projects_path
= content_for :above_breadcrumbs_content do
= render_if_exists "shared/gold_trial_callout"
- if current_user - if current_user
= render 'dashboard/projects_head' = render 'dashboard/projects_head'
- else - else
......
...@@ -2,9 +2,6 @@ ...@@ -2,9 +2,6 @@
- page_title _("Projects") - page_title _("Projects")
- header_title _("Projects"), dashboard_projects_path - header_title _("Projects"), dashboard_projects_path
= content_for :above_breadcrumbs_content do
= render_if_exists "shared/gold_trial_callout"
- if current_user - if current_user
= render 'dashboard/projects_head' = render 'dashboard/projects_head'
- else - else
......
...@@ -2,9 +2,6 @@ ...@@ -2,9 +2,6 @@
- page_title _("Projects") - page_title _("Projects")
- header_title _("Projects"), dashboard_projects_path - header_title _("Projects"), dashboard_projects_path
= content_for :above_breadcrumbs_content do
= render_if_exists "shared/gold_trial_callout"
- if current_user - if current_user
= render 'dashboard/projects_head' = render 'dashboard/projects_head'
- else - else
......
- container = @no_breadcrumb_container ? 'container-fluid' : container_class - container = @no_breadcrumb_container ? 'container-fluid' : container_class
- hide_top_links = @hide_top_links || false - hide_top_links = @hide_top_links || false
= yield :above_breadcrumbs_content
%nav.breadcrumbs{ role: "navigation", class: [container, @content_class] } %nav.breadcrumbs{ role: "navigation", class: [container, @content_class] }
.breadcrumbs-container .breadcrumbs-container
- if defined?(@left_sidebar) - if defined?(@left_sidebar)
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
= s_("ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details") = s_("ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details")
- if show_cluster_security_warning? - if show_cluster_security_warning?
.js-cluster-security-warning.alert.alert-block.alert-dismissable.bs-callout.bs-callout-warning{ data: { feature_id: UserCalloutsHelper::CLUSTER_SECURITY_WARNING, dismiss_endpoint: user_callouts_path } } .js-cluster-security-warning.alert.alert-block.alert-dismissable.bs-callout.bs-callout-warning
%button.close.js-close{ type: "button" } &times; %button.close{ type: "button", data: { feature_id: UserCalloutsHelper::CLUSTER_SECURITY_WARNING, dismiss_endpoint: user_callouts_path } } &times;
= s_("ClusterIntegration|The default cluster configuration grants access to many functionalities needed to successfully build and deploy a containerised application.") = s_("ClusterIntegration|The default cluster configuration grants access to many functionalities needed to successfully build and deploy a containerised application.")
= link_to s_("More information"), help_page_path('user/project/clusters/index.md', anchor: 'security-implications') = link_to s_("More information"), help_page_path('user/project/clusters/index.md', anchor: 'security-implications')
- link = link_to(s_('ClusterIntegration|sign up'), 'https://console.cloud.google.com/freetrial?utm_campaign=2018_cpanel&utm_source=gitlab&utm_medium=referral', target: '_blank', rel: 'noopener noreferrer') - link = link_to(s_('ClusterIntegration|sign up'), 'https://console.cloud.google.com/freetrial?utm_campaign=2018_cpanel&utm_source=gitlab&utm_medium=referral', target: '_blank', rel: 'noopener noreferrer')
.bs-callout.gcp-signup-offer.alert.alert-block.alert-dismissable.prepend-top-default.append-bottom-default{ role: 'alert', data: { feature_id: UserCalloutsHelper::GCP_SIGNUP_OFFER, dismiss_endpoint: user_callouts_path } } .bs-callout.gcp-signup-offer.alert.alert-block.alert-dismissable.prepend-top-default.append-bottom-default{ role: 'alert' }
%button.close.js-close{ type: "button" } &times; %button.close{ type: "button", data: { feature_id: UserCalloutsHelper::GCP_SIGNUP_OFFER, dismiss_endpoint: user_callouts_path } } &times;
.gcp-signup-offer--content .gcp-signup-offer--content
.gcp-signup-offer--icon.append-right-8 .gcp-signup-offer--icon.append-right-8
= sprite_icon("information", size: 16) = sprite_icon("information", size: 16)
......
---
title: Removes extra border from test reports in the merge request widget
merge_request:
author:
type: fixed
---
title: Add IID headers to E-Mail notifications
merge_request: 22263
author:
type: changed
---
title: Use literal instead of constructor for creating regex
merge_request: 22367
author:
type: other
---
title: Does not allow a SSH URI when importing new projects
merge_request: 22309
author:
type: fixed
---
title: Fixed diff stats not showing when performance bar is enabled
merge_request:
author:
type: fixed
---
title: Clear fetched file templates when changing template type in Web IDE
merge_request:
author:
type: fixed
...@@ -33,7 +33,7 @@ provides solutions for all the stages of the DevOps lifecycle: ...@@ -33,7 +33,7 @@ provides solutions for all the stages of the DevOps lifecycle:
[plan](#plan), [create](#create), [verify](#verify), [package](#package), [plan](#plan), [create](#create), [verify](#verify), [package](#package),
[release](#release), [configure](#configure), [monitor](#monitor). [release](#release), [configure](#configure), [monitor](#monitor).
![DevOps Lifecycle](img/devops_lifecycle.png) <img class="image-noshadow" src="img/devops_lifecycle.png" alt="DevOps Lifecycle">
### Plan ### Plan
......
...@@ -197,7 +197,7 @@ stage has a job with a manual action. ...@@ -197,7 +197,7 @@ stage has a job with a manual action.
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/21767) in GitLab 11.4. > [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/21767) in GitLab 11.4.
When you do not want to run a job immediately, you can [delay the job to run after a certain period](yaml/README.md#delayed). When you do not want to run a job immediately, you can [delay the job to run after a certain period](yaml/README.md#when-delayed).
This is especially useful for timed incremental rollout that new code is rolled out gradually. This is especially useful for timed incremental rollout that new code is rolled out gradually.
For example, if you start rolling out new code and users do not experience trouble, GitLab automatically completes the deployment from 0% to 100%. For example, if you start rolling out new code and users do not experience trouble, GitLab automatically completes the deployment from 0% to 100%.
Alternatively, if you start rolling out and you noticed that a few users experience trouble with the version, Alternatively, if you start rolling out and you noticed that a few users experience trouble with the version,
......
...@@ -9,6 +9,7 @@ Most issues will have labels for at least one of the following: ...@@ -9,6 +9,7 @@ Most issues will have labels for at least one of the following:
- Type: ~"feature proposal", ~bug, ~customer, etc. - Type: ~"feature proposal", ~bug, ~customer, etc.
- Subject: ~wiki, ~"container registry", ~ldap, ~api, ~frontend, etc. - Subject: ~wiki, ~"container registry", ~ldap, ~api, ~frontend, etc.
- Team: ~"CI/CD", ~Plan, ~Manage, ~Quality, etc. - Team: ~"CI/CD", ~Plan, ~Manage, ~Quality, etc.
- Stage: ~"devops:plan", ~"devops:create", etc.
- Release Scoping: ~Deliverable, ~Stretch, ~"Next Patch Release" - Release Scoping: ~Deliverable, ~Stretch, ~"Next Patch Release"
- Priority: ~P1, ~P2, ~P3, ~P4 - Priority: ~P1, ~P2, ~P3, ~P4
- Severity: ~S1, ~S2, ~S3, ~S4 - Severity: ~S1, ~S2, ~S3, ~S4
...@@ -83,6 +84,39 @@ indicate if an issue needs backend work, frontend work, or both. ...@@ -83,6 +84,39 @@ indicate if an issue needs backend work, frontend work, or both.
Team labels are always capitalized so that they show up as the first label for Team labels are always capitalized so that they show up as the first label for
any issue. any issue.
## Stage labels
Stage labels specify which [DevOps stage][devops-stages] the issue belongs to.
The current stage labels are:
- ~"devops:manage"
- ~"devops:plan"
- ~"devops:create"
- ~"devops:verify"
- ~"devops:package"
- ~"devops:release"
- ~"devops:configure"
- ~"devops:monitor"
- ~"devops:secure"
These labels should be mutually exclusive. If an issue belongs to multiple
stages, the most relevant should be used.
They differ from the [Team labels](#team-labels) because teams may work on
issues outside their stage.
Normally there is a 1:1 relationship between Stage labels and Team labels, but
any issue can be picked up by any team, depending on current priorities.
So, an issue labeled ~"devops:create" may be scheduled by the ~Plan team, for
example. In such cases, it's usual to include both team labels so each team can
be aware of the progress.
The Stage labels are used to generate the [direction pages][direction-pages] automatically.
[devops-stages]: https://about.gitlab.com/direction/#devops-stages
[direction-pages]: https://about.gitlab.com/direction/
## Release Scoping labels ## Release Scoping labels
Release Scoping labels help us clearly communicate expectations of the work for the Release Scoping labels help us clearly communicate expectations of the work for the
......
...@@ -75,7 +75,7 @@ Please see the table below for some examples: ...@@ -75,7 +75,7 @@ Please see the table below for some examples:
| Latest stable version | Your version | Recommended upgrade path | Note | | Latest stable version | Your version | Recommended upgrade path | Note |
| -------------- | ------------ | ------------------------ | ---------------- | | -------------- | ------------ | ------------------------ | ---------------- |
| 9.4.5 | 8.13.4 | `8.13.4` -> `8.17.7` -> `9.4.5` | `8.17.7` is the last version in version `8` | | 9.4.5 | 8.13.4 | `8.13.4` -> `8.17.7` -> `9.4.5` | `8.17.7` is the last version in version `8` |
| 10.1.4 | 8.13.4 | `8.13.4 -> 8.17.7 -> 9.5.8 -> 10.1.4` | `8.17.7` is the last version in version `8`, `9.5.10` is the last version in version `9` | | 10.1.4 | 8.13.4 | `8.13.4 -> 8.17.7 -> 9.5.10 -> 10.1.4` | `8.17.7` is the last version in version `8`, `9.5.10` is the last version in version `9` |
| 11.3.4 | 8.13.4 | `8.13.4` -> `8.17.7` -> `9.5.10` -> `10.8.7` -> `11.3.4` | `8.17.7` is the last version in version `8`, `9.5.10` is the last version in version `9`, `10.8.7` is the last version in version `10` | | 11.3.4 | 8.13.4 | `8.13.4` -> `8.17.7` -> `9.5.10` -> `10.8.7` -> `11.3.4` | `8.17.7` is the last version in version `8`, `9.5.10` is the last version in version `9`, `10.8.7` is the last version in version `10` |
More information about the release procedures can be found in our More information about the release procedures can be found in our
......
...@@ -299,7 +299,6 @@ request to add new user to project through API will not be possible. ...@@ -299,7 +299,6 @@ request to add new user to project through API will not be possible.
- **Projects**: view all projects within that group, add members to each project, - **Projects**: view all projects within that group, add members to each project,
access each project's settings, and remove any project from the same screen. access each project's settings, and remove any project from the same screen.
- **Webhooks**: configure [webhooks](../project/integrations/webhooks.md) to your group. - **Webhooks**: configure [webhooks](../project/integrations/webhooks.md) to your group.
- **Push rules**: configure [push rules](https://docs.gitlab.com/ee/push_rules/push_rules.html#push-rules) to your group. **[STARTER]**
- **Audit Events**: view [Audit Events](https://docs.gitlab.com/ee/administration/audit_events.html#audit-events) - **Audit Events**: view [Audit Events](https://docs.gitlab.com/ee/administration/audit_events.html#audit-events)
for the group. **[STARTER ONLY]** for the group. **[STARTER ONLY]**
- **Pipelines quota**: keep track of the [pipeline quota](../admin_area/settings/continuous_integration.md) for the group - **Pipelines quota**: keep track of the [pipeline quota](../admin_area/settings/continuous_integration.md) for the group
......
File mode changed from 100755 to 100644
File mode changed from 100755 to 100644
...@@ -166,7 +166,11 @@ minutes. ...@@ -166,7 +166,11 @@ minutes.
![Repository Languages bar](img/repository_languages.png) ![Repository Languages bar](img/repository_languages.png)
Not all files are detected, among others; documentation, Not all files are detected, among others; documentation,
vendored code, and most markup languages are excluded. vendored code, and most markup languages are excluded. This behaviour can be
adjusted by overriding the default. For example, to enable `.proto` files to be
detected, add the following to `.gitattributes` in the root of your repository.
> *.proto linguist-detectable=true
## Compare ## Compare
......
...@@ -270,6 +270,7 @@ export default { ...@@ -270,6 +270,7 @@ export default {
<div class="mr-section-container"> <div class="mr-section-container">
<grouped-test-reports-app <grouped-test-reports-app
v-if="mr.testResultsPath" v-if="mr.testResultsPath"
class="js-reports-container"
:endpoint="mr.testResultsPath" :endpoint="mr.testResultsPath"
/> />
<div class="mr-widget-section"> <div class="mr-widget-section">
......
...@@ -2,11 +2,21 @@ FROM ruby:2.4-stretch ...@@ -2,11 +2,21 @@ FROM ruby:2.4-stretch
LABEL maintainer "Grzegorz Bizon <grzegorz@gitlab.com>" LABEL maintainer "Grzegorz Bizon <grzegorz@gitlab.com>"
ENV DEBIAN_FRONTEND noninteractive ENV DEBIAN_FRONTEND noninteractive
##
# Add support for stretch-backports
#
RUN echo "deb http://ftp.debian.org/debian stretch-backports main" >> /etc/apt/sources.list
## ##
# Update APT sources and install some dependencies # Update APT sources and install some dependencies
# #
RUN sed -i "s/httpredir.debian.org/ftp.us.debian.org/" /etc/apt/sources.list RUN sed -i "s/httpredir.debian.org/ftp.us.debian.org/" /etc/apt/sources.list
RUN apt-get update && apt-get install -y wget git unzip xvfb RUN apt-get update && apt-get install -y wget unzip xvfb
##
# Install some packages from backports
#
RUN apt-get -y -t stretch-backports install git git-lfs
## ##
# Install Docker # Install Docker
......
...@@ -11,6 +11,7 @@ module QA ...@@ -11,6 +11,7 @@ module QA
element :new_user_email_confirmation element :new_user_email_confirmation
element :new_user_password element :new_user_password
element :new_user_register_button element :new_user_register_button
element :new_user_accept_terms
end end
def sign_up!(user) def sign_up!(user)
...@@ -20,6 +21,8 @@ module QA ...@@ -20,6 +21,8 @@ module QA
fill_element :new_user_email_confirmation, user.email fill_element :new_user_email_confirmation, user.email
fill_element :new_user_password, user.password fill_element :new_user_password, user.password
check_element :new_user_accept_terms if has_element?(:new_user_accept_terms)
signed_in = with_retry do signed_in = with_retry do
click_element :new_user_register_button click_element :new_user_register_button
......
...@@ -685,17 +685,28 @@ describe ApplicationController do ...@@ -685,17 +685,28 @@ describe ApplicationController do
end end
context 'html' do context 'html' do
subject { get :index, text: "hi \255" }
it 'renders 412' do it 'renders 412' do
get :index, text: "hi \255" if Gitlab.rails5?
expect { subject }.to raise_error(ActionController::BadRequest)
else
subject
expect(response).to have_gitlab_http_status(412) expect(response).to have_gitlab_http_status(412)
expect(response).to render_template :precondition_failed expect(response).to render_template :precondition_failed
end end
end end
end
context 'js' do context 'js' do
subject { get :index, text: "hi \255", format: :js }
it 'renders 412' do it 'renders 412' do
get :index, text: "hi \255", format: :js if Gitlab.rails5?
expect { subject }.to raise_error(ActionController::BadRequest)
else
subject
json_response = JSON.parse(response.body) json_response = JSON.parse(response.body)
...@@ -704,6 +715,7 @@ describe ApplicationController do ...@@ -704,6 +715,7 @@ describe ApplicationController do
end end
end end
end end
end
context 'X-GitLab-Custom-Error header' do context 'X-GitLab-Custom-Error header' do
before do before do
......
...@@ -6,8 +6,6 @@ describe Projects::ClustersController do ...@@ -6,8 +6,6 @@ describe Projects::ClustersController do
set(:project) { create(:project) } set(:project) { create(:project) }
describe 'GET index' do
describe 'functionality' do
let(:user) { create(:user) } let(:user) { create(:user) }
before do before do
...@@ -15,6 +13,12 @@ describe Projects::ClustersController do ...@@ -15,6 +13,12 @@ describe Projects::ClustersController do
sign_in(user) sign_in(user)
end end
describe 'GET index' do
def go(params = {})
get :index, params.reverse_merge(namespace_id: project.namespace.to_param, project_id: project)
end
describe 'functionality' do
context 'when project has one or more clusters' do context 'when project has one or more clusters' do
let(:project) { create(:project) } let(:project) { create(:project) }
let!(:enabled_cluster) { create(:cluster, :provided_by_gcp, projects: [project]) } let!(:enabled_cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
...@@ -33,10 +37,11 @@ describe Projects::ClustersController do ...@@ -33,10 +37,11 @@ describe Projects::ClustersController do
before do before do
allow(Clusters::Cluster).to receive(:paginates_per).and_return(1) allow(Clusters::Cluster).to receive(:paginates_per).and_return(1)
create_list(:cluster, 2, :provided_by_gcp, :production_environment, projects: [project]) create_list(:cluster, 2, :provided_by_gcp, :production_environment, projects: [project])
get :index, namespace_id: project.namespace, project_id: project, page: last_page
end end
it 'redirects to the page' do it 'redirects to the page' do
go(page: last_page)
expect(response).to have_gitlab_http_status(:ok) expect(response).to have_gitlab_http_status(:ok)
expect(assigns(:clusters).current_page).to eq(last_page) expect(assigns(:clusters).current_page).to eq(last_page)
end end
...@@ -68,21 +73,14 @@ describe Projects::ClustersController do ...@@ -68,21 +73,14 @@ describe Projects::ClustersController do
it { expect { go }.to be_denied_for(:user) } it { expect { go }.to be_denied_for(:user) }
it { expect { go }.to be_denied_for(:external) } it { expect { go }.to be_denied_for(:external) }
end end
def go
get :index, namespace_id: project.namespace.to_param, project_id: project
end
end end
describe 'GET new' do describe 'GET new' do
describe 'functionality for new cluster' do def go
let(:user) { create(:user) } get :new, namespace_id: project.namespace, project_id: project
before do
project.add_maintainer(user)
sign_in(user)
end end
describe 'functionality for new cluster' do
context 'when omniauth has been configured' do context 'when omniauth has been configured' do
let(:key) { 'secret-key' } let(:key) { 'secret-key' }
let(:session_key_for_redirect_uri) do let(:session_key_for_redirect_uri) do
...@@ -139,13 +137,6 @@ describe Projects::ClustersController do ...@@ -139,13 +137,6 @@ describe Projects::ClustersController do
end end
describe 'functionality for existing cluster' do describe 'functionality for existing cluster' do
let(:user) { create(:user) }
before do
project.add_maintainer(user)
sign_in(user)
end
it 'has new object' do it 'has new object' do
go go
...@@ -163,10 +154,6 @@ describe Projects::ClustersController do ...@@ -163,10 +154,6 @@ describe Projects::ClustersController do
it { expect { go }.to be_denied_for(:user) } it { expect { go }.to be_denied_for(:user) }
it { expect { go }.to be_denied_for(:external) } it { expect { go }.to be_denied_for(:external) }
end end
def go
get :new, namespace_id: project.namespace, project_id: project
end
end end
describe 'POST create for new cluster' do describe 'POST create for new cluster' do
...@@ -183,14 +170,11 @@ describe Projects::ClustersController do ...@@ -183,14 +170,11 @@ describe Projects::ClustersController do
} }
end end
describe 'functionality' do def go
let(:user) { create(:user) } post :create_gcp, params.merge(namespace_id: project.namespace, project_id: project)
before do
project.add_maintainer(user)
sign_in(user)
end end
describe 'functionality' do
context 'when access token is valid' do context 'when access token is valid' do
before do before do
stub_google_api_validate_token stub_google_api_validate_token
...@@ -257,10 +241,6 @@ describe Projects::ClustersController do ...@@ -257,10 +241,6 @@ describe Projects::ClustersController do
it { expect { go }.to be_denied_for(:user) } it { expect { go }.to be_denied_for(:user) }
it { expect { go }.to be_denied_for(:external) } it { expect { go }.to be_denied_for(:external) }
end end
def go
post :create_gcp, params.merge(namespace_id: project.namespace, project_id: project)
end
end end
describe 'POST create for existing cluster' do describe 'POST create for existing cluster' do
...@@ -277,14 +257,11 @@ describe Projects::ClustersController do ...@@ -277,14 +257,11 @@ describe Projects::ClustersController do
} }
end end
describe 'functionality' do def go
let(:user) { create(:user) } post :create_user, params.merge(namespace_id: project.namespace, project_id: project)
before do
project.add_maintainer(user)
sign_in(user)
end end
describe 'functionality' do
context 'when creates a cluster' do context 'when creates a cluster' do
it 'creates a new cluster' do it 'creates a new cluster' do
expect(ClusterProvisionWorker).to receive(:perform_async) expect(ClusterProvisionWorker).to receive(:perform_async)
...@@ -339,23 +316,19 @@ describe Projects::ClustersController do ...@@ -339,23 +316,19 @@ describe Projects::ClustersController do
it { expect { go }.to be_denied_for(:user) } it { expect { go }.to be_denied_for(:user) }
it { expect { go }.to be_denied_for(:external) } it { expect { go }.to be_denied_for(:external) }
end end
def go
post :create_user, params.merge(namespace_id: project.namespace, project_id: project)
end
end end
describe 'GET status' do describe 'GET status' do
let(:cluster) { create(:cluster, :providing_by_gcp, projects: [project]) } let(:cluster) { create(:cluster, :providing_by_gcp, projects: [project]) }
describe 'functionality' do def go
let(:user) { create(:user) } get :status, namespace_id: project.namespace,
project_id: project,
before do id: cluster,
project.add_maintainer(user) format: :json
sign_in(user)
end end
describe 'functionality' do
it "responds with matching schema" do it "responds with matching schema" do
go go
...@@ -380,26 +353,18 @@ describe Projects::ClustersController do ...@@ -380,26 +353,18 @@ describe Projects::ClustersController do
it { expect { go }.to be_denied_for(:user) } it { expect { go }.to be_denied_for(:user) }
it { expect { go }.to be_denied_for(:external) } it { expect { go }.to be_denied_for(:external) }
end end
def go
get :status, namespace_id: project.namespace,
project_id: project,
id: cluster,
format: :json
end
end end
describe 'GET show' do describe 'GET show' do
let(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) } let(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
describe 'functionality' do def go
let(:user) { create(:user) } get :show, namespace_id: project.namespace,
project_id: project,
before do id: cluster
project.add_maintainer(user)
sign_in(user)
end end
describe 'functionality' do
it "renders view" do it "renders view" do
go go
...@@ -418,25 +383,11 @@ describe Projects::ClustersController do ...@@ -418,25 +383,11 @@ describe Projects::ClustersController do
it { expect { go }.to be_denied_for(:user) } it { expect { go }.to be_denied_for(:user) }
it { expect { go }.to be_denied_for(:external) } it { expect { go }.to be_denied_for(:external) }
end end
def go
get :show, namespace_id: project.namespace,
project_id: project,
id: cluster
end
end end
describe 'PUT update' do describe 'PUT update' do
context 'when cluster is provided by GCP' do
let(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) } let(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
let(:user) { create(:user) }
before do
project.add_maintainer(user)
sign_in(user)
end
context 'when changing parameters' do
let(:params) do let(:params) do
{ {
cluster: { cluster: {
...@@ -449,6 +400,15 @@ describe Projects::ClustersController do ...@@ -449,6 +400,15 @@ describe Projects::ClustersController do
} }
end end
def go(format: :html)
put :update, params.merge(namespace_id: project.namespace,
project_id: project,
id: cluster,
format: format
)
end
context 'when cluster is provided by GCP' do
it "updates and redirects back to show page" do it "updates and redirects back to show page" do
go go
...@@ -461,6 +421,7 @@ describe Projects::ClustersController do ...@@ -461,6 +421,7 @@ describe Projects::ClustersController do
it "does not change cluster name" do it "does not change cluster name" do
go go
cluster.reload
expect(cluster.name).to eq('test-cluster') expect(cluster.name).to eq('test-cluster')
end end
...@@ -476,20 +437,10 @@ describe Projects::ClustersController do ...@@ -476,20 +437,10 @@ describe Projects::ClustersController do
end end
end end
end end
end
context 'when cluster is provided by user' do context 'when cluster is provided by user' do
let(:cluster) { create(:cluster, :provided_by_user, projects: [project]) } let(:cluster) { create(:cluster, :provided_by_user, projects: [project]) }
let(:user) { create(:user) }
before do
project.add_maintainer(user)
sign_in(user)
end
context 'when format is json' do
context 'when changing parameters' do
context 'when valid parameters are used' do
let(:params) do let(:params) do
{ {
cluster: { cluster: {
...@@ -503,60 +454,59 @@ describe Projects::ClustersController do ...@@ -503,60 +454,59 @@ describe Projects::ClustersController do
end end
it "updates and redirects back to show page" do it "updates and redirects back to show page" do
go_json go
cluster.reload cluster.reload
expect(response).to have_http_status(:no_content) expect(response).to redirect_to(project_cluster_path(project, cluster))
expect(flash[:notice]).to eq('Kubernetes cluster was successfully updated.')
expect(cluster.enabled).to be_falsey expect(cluster.enabled).to be_falsey
expect(cluster.name).to eq('my-new-cluster-name') expect(cluster.name).to eq('my-new-cluster-name')
expect(cluster.platform_kubernetes.namespace).to eq('my-namespace') expect(cluster.platform_kubernetes.namespace).to eq('my-namespace')
end end
end
context 'when invalid parameters are used' do context 'when format is json' do
context 'when changing parameters' do
context 'when valid parameters are used' do
let(:params) do let(:params) do
{ {
cluster: { cluster: {
enabled: false, enabled: false,
name: 'my-new-cluster-name',
platform_kubernetes_attributes: { platform_kubernetes_attributes: {
namespace: 'my invalid namespace #@' namespace: 'my-namespace'
} }
} }
} }
end end
it "rejects changes" do it "updates and redirects back to show page" do
go_json go(format: :json)
expect(response).to have_http_status(:bad_request) cluster.reload
end expect(response).to have_http_status(:no_content)
end expect(cluster.enabled).to be_falsey
expect(cluster.name).to eq('my-new-cluster-name')
expect(cluster.platform_kubernetes.namespace).to eq('my-namespace')
end end
end end
context 'when format is html' do context 'when invalid parameters are used' do
context 'when update enabled' do
let(:params) do let(:params) do
{ {
cluster: { cluster: {
enabled: false, enabled: false,
name: 'my-new-cluster-name',
platform_kubernetes_attributes: { platform_kubernetes_attributes: {
namespace: 'my-namespace' namespace: 'my invalid namespace #@'
} }
} }
} }
end end
it "updates and redirects back to show page" do it "rejects changes" do
go go(format: :json)
cluster.reload expect(response).to have_http_status(:bad_request)
expect(response).to redirect_to(project_cluster_path(project, cluster)) end
expect(flash[:notice]).to eq('Kubernetes cluster was successfully updated.')
expect(cluster.enabled).to be_falsey
expect(cluster.name).to eq('my-new-cluster-name')
expect(cluster.platform_kubernetes.namespace).to eq('my-namespace')
end end
end end
end end
...@@ -565,10 +515,6 @@ describe Projects::ClustersController do ...@@ -565,10 +515,6 @@ describe Projects::ClustersController do
describe 'security' do describe 'security' do
set(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) } set(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
let(:params) do
{ cluster: { enabled: false } }
end
it { expect { go }.to be_allowed_for(:admin) } it { expect { go }.to be_allowed_for(:admin) }
it { expect { go }.to be_allowed_for(:owner).of(project) } it { expect { go }.to be_allowed_for(:owner).of(project) }
it { expect { go }.to be_allowed_for(:maintainer).of(project) } it { expect { go }.to be_allowed_for(:maintainer).of(project) }
...@@ -578,34 +524,20 @@ describe Projects::ClustersController do ...@@ -578,34 +524,20 @@ describe Projects::ClustersController do
it { expect { go }.to be_denied_for(:user) } it { expect { go }.to be_denied_for(:user) }
it { expect { go }.to be_denied_for(:external) } it { expect { go }.to be_denied_for(:external) }
end end
def go
put :update, params.merge(namespace_id: project.namespace,
project_id: project,
id: cluster)
end
def go_json
put :update, params.merge(namespace_id: project.namespace,
project_id: project,
id: cluster,
format: :json)
end
end end
describe 'DELETE destroy' do describe 'DELETE destroy' do
describe 'functionality' do let!(:cluster) { create(:cluster, :provided_by_gcp, :production_environment, projects: [project]) }
let(:user) { create(:user) }
before do def go
project.add_maintainer(user) delete :destroy, namespace_id: project.namespace,
sign_in(user) project_id: project,
id: cluster
end end
describe 'functionality' do
context 'when cluster is provided by GCP' do context 'when cluster is provided by GCP' do
context 'when cluster is created' do context 'when cluster is created' do
let!(:cluster) { create(:cluster, :provided_by_gcp, :production_environment, projects: [project]) }
it "destroys and redirects back to clusters list" do it "destroys and redirects back to clusters list" do
expect { go } expect { go }
.to change { Clusters::Cluster.count }.by(-1) .to change { Clusters::Cluster.count }.by(-1)
...@@ -658,11 +590,5 @@ describe Projects::ClustersController do ...@@ -658,11 +590,5 @@ describe Projects::ClustersController do
it { expect { go }.to be_denied_for(:user) } it { expect { go }.to be_denied_for(:user) }
it { expect { go }.to be_denied_for(:external) } it { expect { go }.to be_denied_for(:external) }
end end
def go
delete :destroy, namespace_id: project.namespace,
project_id: project,
id: cluster
end
end end
end end
...@@ -423,7 +423,7 @@ describe 'Merge request > User sees merge widget', :js do ...@@ -423,7 +423,7 @@ describe 'Merge request > User sees merge widget', :js do
end end
it 'shows test reports summary which includes the new failure' do it 'shows test reports summary which includes the new failure' do
within(".mr-section-container") do within(".js-reports-container") do
click_button 'Expand' click_button 'Expand'
expect(page).to have_content('Test summary contained 1 failed test result out of 2 total tests') expect(page).to have_content('Test summary contained 1 failed test result out of 2 total tests')
...@@ -438,7 +438,7 @@ describe 'Merge request > User sees merge widget', :js do ...@@ -438,7 +438,7 @@ describe 'Merge request > User sees merge widget', :js do
context 'when user clicks the new failure' do context 'when user clicks the new failure' do
it 'shows the test report detail' do it 'shows the test report detail' do
within(".mr-section-container") do within(".js-reports-container") do
click_button 'Expand' click_button 'Expand'
within(".js-report-section-container") do within(".js-report-section-container") do
...@@ -468,7 +468,7 @@ describe 'Merge request > User sees merge widget', :js do ...@@ -468,7 +468,7 @@ describe 'Merge request > User sees merge widget', :js do
end end
it 'shows test reports summary which includes the existing failure' do it 'shows test reports summary which includes the existing failure' do
within(".mr-section-container") do within(".js-reports-container") do
click_button 'Expand' click_button 'Expand'
expect(page).to have_content('Test summary contained 1 failed test result out of 2 total tests') expect(page).to have_content('Test summary contained 1 failed test result out of 2 total tests')
...@@ -483,7 +483,7 @@ describe 'Merge request > User sees merge widget', :js do ...@@ -483,7 +483,7 @@ describe 'Merge request > User sees merge widget', :js do
context 'when user clicks the existing failure' do context 'when user clicks the existing failure' do
it 'shows test report detail of it' do it 'shows test report detail of it' do
within(".mr-section-container") do within(".js-reports-container") do
click_button 'Expand' click_button 'Expand'
within(".js-report-section-container") do within(".js-report-section-container") do
...@@ -519,7 +519,7 @@ describe 'Merge request > User sees merge widget', :js do ...@@ -519,7 +519,7 @@ describe 'Merge request > User sees merge widget', :js do
end end
it 'shows test reports summary which includes the resolved failure' do it 'shows test reports summary which includes the resolved failure' do
within(".mr-section-container") do within(".js-reports-container") do
click_button 'Expand' click_button 'Expand'
expect(page).to have_content('Test summary contained 1 fixed test result out of 2 total tests') expect(page).to have_content('Test summary contained 1 fixed test result out of 2 total tests')
...@@ -533,7 +533,7 @@ describe 'Merge request > User sees merge widget', :js do ...@@ -533,7 +533,7 @@ describe 'Merge request > User sees merge widget', :js do
context 'when user clicks the resolved failure' do context 'when user clicks the resolved failure' do
it 'shows test report detail of it' do it 'shows test report detail of it' do
within(".mr-section-container") do within(".js-reports-container") do
click_button 'Expand' click_button 'Expand'
within(".js-report-section-container") do within(".js-report-section-container") do
......
...@@ -49,6 +49,14 @@ describe('IDE file templates mutations', () => { ...@@ -49,6 +49,14 @@ describe('IDE file templates mutations', () => {
expect(state.selectedTemplateType).toBe('type'); expect(state.selectedTemplateType).toBe('type');
}); });
it('clears templates', () => {
state.templates = ['test'];
mutations[types.SET_SELECTED_TEMPLATE_TYPE](state, 'type');
expect(state.templates).toEqual([]);
});
}); });
describe(types.SET_UPDATE_SUCCESS, () => { describe(types.SET_UPDATE_SUCCESS, () => {
......
...@@ -11,7 +11,7 @@ describe Gitlab::BitbucketImport::ProjectCreator do ...@@ -11,7 +11,7 @@ describe Gitlab::BitbucketImport::ProjectCreator do
owner: "asd", owner: "asd",
full_name: 'Vim repo', full_name: 'Vim repo',
visibility_level: Gitlab::VisibilityLevel::PRIVATE, visibility_level: Gitlab::VisibilityLevel::PRIVATE,
clone_url: 'ssh://git@bitbucket.org/asd/vim.git', clone_url: 'http://bitbucket.org/asd/vim.git',
has_wiki?: false) has_wiki?: false)
end end
...@@ -32,7 +32,7 @@ describe Gitlab::BitbucketImport::ProjectCreator do ...@@ -32,7 +32,7 @@ describe Gitlab::BitbucketImport::ProjectCreator do
project_creator = described_class.new(repo, 'vim', namespace, user, access_params) project_creator = described_class.new(repo, 'vim', namespace, user, access_params)
project = project_creator.execute project = project_creator.execute
expect(project.import_url).to eq("ssh://git@bitbucket.org/asd/vim.git") expect(project.import_url).to eq("http://bitbucket.org/asd/vim.git")
expect(project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE) expect(project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE)
end end
end end
...@@ -256,54 +256,75 @@ describe Project do ...@@ -256,54 +256,75 @@ describe Project do
end end
it 'does not allow an invalid URI as import_url' do it 'does not allow an invalid URI as import_url' do
project2 = build(:project, import_url: 'invalid://') project = build(:project, import_url: 'invalid://')
expect(project2).not_to be_valid expect(project).not_to be_valid
end
it 'does allow a SSH URI as import_url for persisted projects' do
project = create(:project)
project.import_url = 'ssh://test@gitlab.com/project.git'
expect(project).to be_valid
end
it 'does not allow a SSH URI as import_url for new projects' do
project = build(:project, import_url: 'ssh://test@gitlab.com/project.git')
expect(project).not_to be_valid
end end
it 'does allow a valid URI as import_url' do it 'does allow a valid URI as import_url' do
project2 = build(:project, import_url: 'ssh://test@gitlab.com/project.git') project = build(:project, import_url: 'http://gitlab.com/project.git')
expect(project2).to be_valid expect(project).to be_valid
end end
it 'allows an empty URI' do it 'allows an empty URI' do
project2 = build(:project, import_url: '') project = build(:project, import_url: '')
expect(project2).to be_valid expect(project).to be_valid
end end
it 'does not produce import data on an empty URI' do it 'does not produce import data on an empty URI' do
project2 = build(:project, import_url: '') project = build(:project, import_url: '')
expect(project2.import_data).to be_nil expect(project.import_data).to be_nil
end end
it 'does not produce import data on an invalid URI' do it 'does not produce import data on an invalid URI' do
project2 = build(:project, import_url: 'test://') project = build(:project, import_url: 'test://')
expect(project2.import_data).to be_nil expect(project.import_data).to be_nil
end end
it "does not allow import_url pointing to localhost" do it "does not allow import_url pointing to localhost" do
project2 = build(:project, import_url: 'http://localhost:9000/t.git') project = build(:project, import_url: 'http://localhost:9000/t.git')
expect(project2).to be_invalid expect(project).to be_invalid
expect(project2.errors[:import_url].first).to include('Requests to localhost are not allowed') expect(project.errors[:import_url].first).to include('Requests to localhost are not allowed')
end end
it "does not allow import_url with invalid ports" do it "does not allow import_url with invalid ports for new projects" do
project2 = build(:project, import_url: 'http://github.com:25/t.git') project = build(:project, import_url: 'http://github.com:25/t.git')
expect(project).to be_invalid
expect(project.errors[:import_url].first).to include('Only allowed ports are 80, 443')
end
it "does not allow import_url with invalid ports for persisted projects" do
project = create(:project)
project.import_url = 'http://github.com:25/t.git'
expect(project2).to be_invalid expect(project).to be_invalid
expect(project2.errors[:import_url].first).to include('Only allowed ports are 22, 80, 443') expect(project.errors[:import_url].first).to include('Only allowed ports are 22, 80, 443')
end end
it "does not allow import_url with invalid user" do it "does not allow import_url with invalid user" do
project2 = build(:project, import_url: 'http://$user:password@github.com/t.git') project = build(:project, import_url: 'http://$user:password@github.com/t.git')
expect(project2).to be_invalid expect(project).to be_invalid
expect(project2.errors[:import_url].first).to include('Username needs to start with an alphanumeric character') expect(project.errors[:import_url].first).to include('Username needs to start with an alphanumeric character')
end end
it 'creates import state when mirror gets enabled' do it 'creates import state when mirror gets enabled' do
......
...@@ -235,7 +235,7 @@ describe Projects::ImportService do ...@@ -235,7 +235,7 @@ describe Projects::ImportService do
result = described_class.new(project, user).execute result = described_class.new(project, user).execute
expect(result[:status]).to eq :error expect(result[:status]).to eq :error
expect(result[:message]).to include('Only allowed ports are 22, 80, 443') expect(result[:message]).to include('Only allowed ports are 80, 443')
end end
end end
......
...@@ -45,6 +45,20 @@ shared_examples 'an email that contains a header with author username' do ...@@ -45,6 +45,20 @@ shared_examples 'an email that contains a header with author username' do
end end
end end
shared_examples 'an email with X-GitLab headers containing IDs' do
it 'has X-GitLab-*-ID header' do
is_expected.to have_header "X-GitLab-#{model.class.name}-ID", "#{model.id}"
end
it 'has X-GitLab-*-IID header if model has iid defined' do
if model.respond_to?(:iid)
is_expected.to have_header "X-GitLab-#{model.class.name}-IID", "#{model.iid}"
else
expect(subject.header["X-GitLab-#{model.class.name}-IID"]).to eq nil
end
end
end
shared_examples 'an email with X-GitLab headers containing project details' do shared_examples 'an email with X-GitLab headers containing project details' do
it 'has X-GitLab-Project headers' do it 'has X-GitLab-Project headers' do
aggregate_failures do aggregate_failures do
...@@ -69,6 +83,7 @@ end ...@@ -69,6 +83,7 @@ end
shared_examples 'a thread answer email with reply-by-email enabled' do shared_examples 'a thread answer email with reply-by-email enabled' do
include_examples 'an email with X-GitLab headers containing project details' include_examples 'an email with X-GitLab headers containing project details'
include_examples 'an email with X-GitLab headers containing IDs'
it 'has the characteristics of a threaded reply' do it 'has the characteristics of a threaded reply' do
host = Gitlab.config.gitlab.host host = Gitlab.config.gitlab.host
...@@ -85,6 +100,7 @@ end ...@@ -85,6 +100,7 @@ end
shared_examples 'an email starting a new thread with reply-by-email enabled' do shared_examples 'an email starting a new thread with reply-by-email enabled' do
include_examples 'an email with X-GitLab headers containing project details' include_examples 'an email with X-GitLab headers containing project details'
include_examples 'an email with X-GitLab headers containing IDs'
include_examples 'a new thread email with reply-by-email enabled' include_examples 'a new thread email with reply-by-email enabled'
it 'includes "Reply to this email directly or <View it on GitLab>"' do it 'includes "Reply to this email directly or <View it on GitLab>"' do
...@@ -109,6 +125,7 @@ end ...@@ -109,6 +125,7 @@ end
shared_examples 'an answer to an existing thread with reply-by-email enabled' do shared_examples 'an answer to an existing thread with reply-by-email enabled' do
include_examples 'an email with X-GitLab headers containing project details' include_examples 'an email with X-GitLab headers containing project details'
include_examples 'an email with X-GitLab headers containing IDs'
include_examples 'a thread answer email with reply-by-email enabled' include_examples 'a thread answer email with reply-by-email enabled'
context 'when reply-by-email is enabled with incoming address with %{key}' do context 'when reply-by-email is enabled with incoming address with %{key}' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment