Commit 5b24739e authored by Shinya Maeda's avatar Shinya Maeda

Merge branch 'master-ce' into check-unique-values-of-pipeline-enum

parents 45a5ed6d 2b2f9369
......@@ -2,6 +2,22 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
## 11.5.2 (2018-12-03)
### Removed (1 change)
- Removed Site Statistics optimization as it was causing problems. !23314
### Fixed (6 changes, 1 of them is from the community)
- Display impersonation token value only after creation. !22916
- Fix not render emoji in filter dropdown. !23112 (Hiroyuki Sato)
- Fixes stuck tooltip on stop env button. !23244
- Correctly handle data-loss scenarios when encrypting columns. !23306
- Clear BatchLoader context between Sidekiq jobs. !23308
- Fix handling of filenames with hash characters in tree view. !23368
## 11.5.1 (2018-11-26)
### Security (17 changes)
......@@ -287,6 +303,14 @@ entry.
- Disables stop environment button while the deploy is in progress.
## 11.4.9 (2018-12-03)
### Fixed (2 changes)
- Display impersonation token value only after creation. !22916
- Correctly handle data-loss scenarios when encrypting columns. !23306
## 11.4.8 (2018-11-27)
### Security (24 changes)
......
......@@ -181,4 +181,4 @@ This [documentation](doc/development/contributing/merge_request_workflow.md) has
## Style guides
This [documentation](doc/development/contributing/design.md) has been moved.
This [documentation](doc/development/contributing/style_guides.md) has been moved.
......@@ -82,7 +82,7 @@ gem 'validates_hostname', '~> 1.0.6'
gem 'browser', '~> 2.5'
# GPG
gem 'gpgme'
gem 'gpgme', '~> 2.0.18'
# LDAP Auth
# GitLab fork with several improvements to original library. For full list of changes
......
......@@ -313,8 +313,8 @@ GEM
multi_json (~> 1.11)
os (>= 0.9, < 2.0)
signet (~> 0.7)
gpgme (2.0.13)
mini_portile2 (~> 2.1)
gpgme (2.0.18)
mini_portile2 (~> 2.3)
grape (1.1.0)
activesupport
builder
......@@ -1016,7 +1016,7 @@ DEPENDENCIES
gon (~> 6.2)
google-api-client (~> 0.23)
google-protobuf (~> 3.6)
gpgme
gpgme (~> 2.0.18)
grape (~> 1.1.0)
grape-entity (~> 0.7.1)
grape-path-helpers (~> 1.0)
......
......@@ -310,8 +310,8 @@ GEM
multi_json (~> 1.11)
os (>= 0.9, < 2.0)
signet (~> 0.7)
gpgme (2.0.13)
mini_portile2 (~> 2.1)
gpgme (2.0.18)
mini_portile2 (~> 2.3)
grape (1.1.0)
activesupport
builder
......@@ -1007,7 +1007,7 @@ DEPENDENCIES
gon (~> 6.2)
google-api-client (~> 0.23)
google-protobuf (~> 3.6)
gpgme
gpgme (~> 2.0.18)
grape (~> 1.1.0)
grape-entity (~> 0.7.1)
grape-path-helpers (~> 1.0)
......
......@@ -88,10 +88,15 @@ export const conditions = [
value: 'started',
},
{
url: 'label_name[]=No+Label',
url: 'label_name[]=None',
tokenKey: 'label',
value: 'none',
},
{
url: 'label_name[]=Any',
tokenKey: 'any',
value: 'any',
},
{
url: 'my_reaction_emoji=None',
tokenKey: 'my-reaction',
......
......@@ -105,7 +105,7 @@ export default {
:key="tabView.name"
class="h-100"
>
<component :is="tabView.name" />
<component :is="tabView.component || tabView.name" />
</div>
</resizable-panel>
<nav class="ide-activity-bar">
......
......@@ -30,6 +30,7 @@ export default class MirrorRepos {
this.$password.on('input.updateUrl', () => this.debouncedUpdateUrl());
this.initMirrorSSH();
this.updateProtectedBranches();
}
initMirrorSSH() {
......
......@@ -18,23 +18,19 @@ export default {
required: true,
},
},
computed: {
graph() {
return this.pipeline.details && this.pipeline.details.stages;
},
},
methods: {
capitalizeStageName(name) {
const escapedName = _.escape(name);
return escapedName.charAt(0).toUpperCase() + escapedName.slice(1);
},
isFirstColumn(index) {
return index === 0;
},
stageConnectorClass(index, stage) {
let className;
......@@ -48,7 +44,6 @@ export default {
return className;
},
refreshPipelineGraph() {
this.$emit('refreshPipelineGraph');
},
......
......@@ -84,10 +84,6 @@ export default {
return textBuilder.join(' ');
},
tooltipBoundary() {
return this.dropdownLength < 5 ? 'viewport' : null;
},
/**
* Verifies if the provided job has an action path
*
......@@ -108,7 +104,7 @@ export default {
<div class="ci-job-component">
<gl-link
v-if="status.has_details"
v-gl-tooltip="{ boundary: tooltipBoundary }"
v-gl-tooltip
:href="status.details_path"
:title="tooltipText"
:class="cssClassJobName"
......
......@@ -23,11 +23,11 @@ export default class Star {
if (isStarred) {
$starSpan.removeClass('starred').text(s__('StarProject|Star'));
$startIcon.remove();
$this.prepend(spriteIcon('star-o'));
$this.prepend(spriteIcon('star-o', 'icon'));
} else {
$starSpan.addClass('starred').text(__('Unstar'));
$startIcon.remove();
$this.prepend(spriteIcon('star'));
$this.prepend(spriteIcon('star', 'icon'));
}
})
.catch(() => Flash('Star toggle failed. Try again later.'));
......
......@@ -112,7 +112,7 @@ export default {
</script>
<template>
<div class="mr-widget-heading deploy-heading append-bottom-default">
<div class="deploy-heading">
<div class="ci-widget media">
<div class="media-body">
<div class="deploy-body">
......
<template>
<div class="mr-widget-heading">
<div class="mr-widget-content"><slot name="default"></slot></div>
<slot name="footer"></slot>
</div>
</template>
......@@ -6,6 +6,7 @@ import Icon from '~/vue_shared/components/icon.vue';
import clipboardButton from '~/vue_shared/components/clipboard_button.vue';
import tooltip from '~/vue_shared/directives/tooltip';
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue';
import MrWidgetIcon from './mr_widget_icon.vue';
export default {
name: 'MRWidgetHeader',
......@@ -13,6 +14,7 @@ export default {
Icon,
clipboardButton,
TooltipOnTruncate,
MrWidgetIcon,
},
directives: {
tooltip,
......@@ -76,7 +78,7 @@ export default {
</script>
<template>
<div class="mr-source-target append-bottom-default">
<div class="git-merge-icon-container append-right-default"><icon name="git-merge" /></div>
<mr-widget-icon name="git-merge" />
<div class="git-merge-container d-flex">
<div class="normal">
<strong>
......
<script>
import Icon from '~/vue_shared/components/icon.vue';
export default {
components: { Icon },
props: {
name: {
type: String,
required: true,
},
},
};
</script>
<template>
<div class="circle-icon-container append-right-default"><icon :name="name" /></div>
</template>
......@@ -79,8 +79,7 @@ export default {
</script>
<template>
<div v-if="hasPipeline || hasCIError" class="mr-widget-heading append-bottom-default">
<div class="ci-widget media">
<div v-if="hasPipeline || hasCIError" class="ci-widget media">
<template v-if="hasCIError">
<div
class="add-border ci-status-icon ci-status-icon-failed ci-error
......@@ -141,5 +140,4 @@ export default {
</div>
</template>
</div>
</div>
</template>
<script>
import Deployment from './deployment.vue';
import MrWidgetContainer from './mr_widget_container.vue';
import MrWidgetPipeline from './mr_widget_pipeline.vue';
/**
* Renders the pipeline and related deployments from the store.
*
* | Props | Description
* |---------------|-------------
* | `mr` | This is the mr_widget store
* | `isPostMerge` | If true, show the "post merge" pipeline and deployments
*/
export default {
name: 'MrWidgetPipelineContainer',
components: {
Deployment,
MrWidgetContainer,
MrWidgetPipeline,
},
props: {
mr: {
type: Object,
required: true,
},
isPostMerge: {
type: Boolean,
required: false,
default: false,
},
},
computed: {
pipeline() {
return this.isPostMerge ? this.mr.mergePipeline : this.mr.pipeline;
},
branch() {
return this.isPostMerge ? this.mr.targetBranch : this.mr.sourceBranch;
},
branchLink() {
return this.isPostMerge ? this.mr.targetBranch : this.mr.sourceBranchLink;
},
deployments() {
return this.isPostMerge ? this.mr.postMergeDeployments : this.mr.deployments;
},
deploymentClass() {
return this.isPostMerge ? 'js-post-deployment' : 'js-pre-deployment';
},
hasDeploymentMetrics() {
return this.isPostMerge;
},
},
};
</script>
<template>
<mr-widget-container>
<mr-widget-pipeline
:pipeline="pipeline"
:ci-status="mr.ciStatus"
:has-ci="mr.hasCI"
:source-branch="branch"
:source-branch-link="branchLink"
:troubleshooting-docs-path="mr.troubleshootingDocsPath"
/>
<div v-if="deployments.length" slot="footer" class="mr-widget-extension">
<deployment
v-for="deployment in deployments"
:key="deployment.id"
:class="deploymentClass"
:deployment="deployment"
:show-metrics="hasDeploymentMetrics"
/>
</div>
</mr-widget-container>
</template>
......@@ -6,7 +6,7 @@ import SmartInterval from '~/smart_interval';
import createFlash from '../flash';
import WidgetHeader from './components/mr_widget_header.vue';
import WidgetMergeHelp from './components/mr_widget_merge_help.vue';
import WidgetPipeline from './components/mr_widget_pipeline.vue';
import MrWidgetPipelineContainer from './components/mr_widget_pipeline_container.vue';
import Deployment from './components/deployment.vue';
import WidgetRelatedLinks from './components/mr_widget_related_links.vue';
import MergedState from './components/states/mr_widget_merged.vue';
......@@ -44,7 +44,7 @@ export default {
components: {
'mr-widget-header': WidgetHeader,
'mr-widget-merge-help': WidgetMergeHelp,
'mr-widget-pipeline': WidgetPipeline,
MrWidgetPipelineContainer,
Deployment,
'mr-widget-related-links': WidgetRelatedLinks,
'mr-widget-merged': MergedState,
......@@ -296,23 +296,12 @@ export default {
<template>
<div class="mr-state-widget prepend-top-default">
<mr-widget-header :mr="mr" />
<mr-widget-pipeline
<mr-widget-pipeline-container
v-if="shouldRenderPipelines"
:pipeline="mr.pipeline"
:ci-status="mr.ciStatus"
:has-ci="mr.hasCI"
:source-branch="mr.sourceBranch"
:source-branch-link="mr.sourceBranchLink"
:troubleshooting-docs-path="mr.troubleshootingDocsPath"
class="mr-widget-workflow"
:mr="mr"
/>
<deployment
v-for="deployment in mr.deployments"
:key="`pre-merge-deploy-${deployment.id}`"
class="js-pre-merge-deploy"
:deployment="deployment"
:show-metrics="false"
/>
<div class="mr-section-container">
<div class="mr-section-container mr-widget-workflow">
<grouped-test-reports-app
v-if="mr.testResultsPath"
class="js-reports-container"
......@@ -336,24 +325,11 @@ export default {
</div>
<div v-if="shouldRenderMergeHelp" class="mr-widget-footer"><mr-widget-merge-help /></div>
</div>
<template v-if="shouldRenderMergedPipeline">
<mr-widget-pipeline
class="js-post-merge-pipeline prepend-top-default"
:pipeline="mr.mergePipeline"
:ci-status="mr.ciStatus"
:has-ci="mr.hasCI"
:source-branch="mr.targetBranch"
:source-branch-link="mr.targetBranch"
:troubleshooting-docs-path="mr.troubleshootingDocsPath"
/>
<deployment
v-for="postMergeDeployment in mr.postMergeDeployments"
:key="`post-merge-deploy-${postMergeDeployment.id}`"
:deployment="postMergeDeployment"
:show-metrics="true"
class="js-post-deployment"
<mr-widget-pipeline-container
v-if="shouldRenderMergedPipeline"
class="js-post-merge-pipeline mr-widget-workflow"
:mr="mr"
:is-post-merge="true"
/>
</template>
</div>
</template>
......@@ -6,6 +6,7 @@
@import 'bootstrap_migration';
@import 'framework/layout';
@import 'framework/alerts';
@import 'framework/animations';
@import 'framework/vue_transitions';
@import 'framework/avatar';
......
.alert-tip {
background-color: $theme-gray-100;
color: $theme-gray-900;
}
......@@ -363,6 +363,12 @@
background-color: $white-light;
border-top: 0;
}
.filter-dropdown-container {
.dropdown {
margin-left: 0;
}
}
}
@include media-breakpoint-down(sm) {
......@@ -372,16 +378,6 @@
.dropdown-menu {
width: 100%;
}
.dropdown {
margin-left: 0;
}
.fa-chevron-down {
position: absolute;
right: 10px;
top: 10px;
}
}
}
......
......@@ -42,6 +42,7 @@
padding: 10px;
text-align: right;
float: left;
line-height: 1;
a {
font-family: $monospace-font;
......
......@@ -80,3 +80,15 @@
.user-avatar-link {
text-decoration: none;
}
.circle-icon-container {
$border-size: 1px;
display: flex;
align-items: center;
justify-content: center;
border: $border-size solid $theme-gray-400;
border-radius: 50%;
padding: $gl-padding-8 - $border-size;
color: $theme-gray-700;
}
......@@ -158,6 +158,10 @@
width: 100%;
}
.dropdown-menu-toggle {
margin-bottom: 0;
}
form {
display: block;
height: auto;
......
......@@ -50,9 +50,19 @@
.mr-widget-heading {
position: relative;
border: 1px solid $border-color;
border-radius: 4px;
border-radius: $border-radius-default;
}
.mr-widget-extension {
border-top: 1px solid $border-color;
background-color: $gray-light;
}
&:not(.deploy-heading)::before {
.mr-widget-workflow {
margin-top: $gl-padding;
position: relative;
&::before {
content: '';
border-left: 1px solid $theme-gray-200;
position: absolute;
......@@ -68,8 +78,8 @@
border-top: 0;
}
.mr-widget-heading,
.mr-widget-section,
.mr-widget-content,
.mr-widget-footer {
padding: $gl-padding;
}
......@@ -560,19 +570,6 @@
color: $gl-text-color;
}
.git-merge-icon-container {
border: 1px solid $theme-gray-400;
border-radius: 50%;
height: 32px;
width: 32px;
color: $theme-gray-700;
line-height: 28px;
.ic-git-merge {
vertical-align: middle;
width: 31px;
}
}
.git-merge-container {
justify-content: space-between;
......@@ -854,11 +851,6 @@
}
.deploy-heading {
margin-top: -19px;
border-top-left-radius: 0;
border-top-right-radius: 0;
background-color: $gray-light;
@include media-breakpoint-up(md) {
padding: $gl-padding-8 $gl-padding;
}
......@@ -868,6 +860,10 @@
font-size: 12px;
margin-left: 48px;
}
&:not(:last-child) {
border-bottom: 1px solid $border-color;
}
}
.deploy-body {
......
......@@ -11,7 +11,7 @@ class Admin::RequestsProfilesController < Admin::ApplicationController
profile = Gitlab::RequestProfiler::Profile.find(clean_name)
if profile
render text: profile.content
render html: profile.content
else
redirect_to admin_requests_profiles_path, alert: 'Profile not found'
end
......
......@@ -15,7 +15,7 @@ class ChaosController < ActionController::Base
duration_taken = (Time.now - start).seconds
Kernel.sleep duration_s - duration_taken if duration_s > duration_taken
render text: "OK", content_type: 'text/plain'
render plain: "OK"
end
def cpuspin
......@@ -24,14 +24,14 @@ class ChaosController < ActionController::Base
rand while Time.now < end_time
render text: "OK", content_type: 'text/plain'
render plain: "OK"
end
def sleep
duration_s = (params[:duration_s]&.to_i || 30).seconds
Kernel.sleep duration_s
render text: "OK", content_type: 'text/plain'
render plain: "OK"
end
def kill
......@@ -44,13 +44,13 @@ class ChaosController < ActionController::Base
secret = ENV['GITLAB_CHAOS_SECRET']
# GITLAB_CHAOS_SECRET is required unless you're running in Development mode
if !secret && !Rails.env.development?
render text: "chaos misconfigured: please configure GITLAB_CHAOS_SECRET when using GITLAB_ENABLE_CHAOS_ENDPOINTS outside of a development environment", content_type: 'text/plain', status: 500
render plain: "chaos misconfigured: please configure GITLAB_CHAOS_SECRET when using GITLAB_ENABLE_CHAOS_ENDPOINTS outside of a development environment", status: :internal_server_error
end
return unless secret
unless request.headers["HTTP_X_CHAOS_SECRET"] == secret
render text: "To experience chaos, please set X-Chaos-Secret header", content_type: 'text/plain', status: 401
render plain: "To experience chaos, please set X-Chaos-Secret header", status: :unauthorized
end
end
end
......@@ -15,7 +15,7 @@ class MetricsController < ActionController::Base
"# Metrics are disabled, see: #{help_page}\n"
end
render text: response, content_type: 'text/plain; version=0.0.4'
render plain: response, content_type: 'text/plain; version=0.0.4'
end
private
......
......@@ -41,12 +41,12 @@ class Profiles::KeysController < Profiles::ApplicationController
user = UserFinder.new(params[:username]).find_by_username
if user.present?
headers['Content-Disposition'] = 'attachment'
render text: user.all_ssh_keys.join("\n"), content_type: 'text/plain'
render plain: user.all_ssh_keys.join("\n")
else
return render_404
end
rescue => e
render text: e.message
render html: e.message
end
else
return render_404
......
......@@ -122,7 +122,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController
set_workhorse_internal_api_content_type
render json: Gitlab::Workhorse.terminal_websocket(terminal)
else
render text: 'Not found', status: :not_found
render html: 'Not found', status: :not_found
end
end
......
......@@ -26,6 +26,8 @@ module Ci
has_many :builds, foreign_key: :commit_id, inverse_of: :pipeline
has_many :trigger_requests, dependent: :destroy, foreign_key: :commit_id # rubocop:disable Cop/ActiveRecordDependent
has_many :variables, class_name: 'Ci::PipelineVariable'
has_many :deployments, through: :builds
has_many :environments, -> { distinct }, through: :deployments
# Merge requests for which the current pipeline is running against
# the merge request's latest commit.
......@@ -523,10 +525,6 @@ module Ci
yaml_errors.present?
end
def environments
builds.where.not(environment: nil).success.pluck(:environment).uniq
end
# Manually set the notes for a Ci::Pipeline
# There is no ActiveRecord relation between Ci::Pipeline and notes
# as they are related to a commit sha. This method helps importing
......
......@@ -56,7 +56,11 @@ module Clusters
def specification
{
"ingress" => {
"hosts" => [hostname]
"hosts" => [hostname],
"tls" => [{
"hosts" => [hostname],
"secretName" => "jupyter-cert"
}]
},
"hub" => {
"extraEnv" => {
......
......@@ -12,13 +12,13 @@ class EnvironmentStatus
delegate :deployed_at, to: :deployment, allow_nil: true
def self.for_merge_request(mr, user)
build_environments_status(mr, user, mr.diff_head_sha)
build_environments_status(mr, user, mr.actual_head_pipeline)
end
def self.after_merge_request(mr, user)
return [] unless mr.merged?
build_environments_status(mr, user, mr.merge_commit_sha)
build_environments_status(mr, user, mr.merge_pipeline)
end
def initialize(environment, merge_request, sha)
......@@ -61,13 +61,13 @@ class EnvironmentStatus
}
end
def self.build_environments_status(mr, user, sha)
Environment.where(project_id: [mr.source_project_id, mr.target_project_id])
.available
.with_deployment(sha).map do |environment|
def self.build_environments_status(mr, user, pipeline)
return [] unless pipeline
pipeline.environments.available.map do |environment|
next unless Ability.allowed?(user, :read_environment, environment)
EnvironmentStatus.new(environment, mr, sha)
EnvironmentStatus.new(environment, mr, pipeline.sha)
end.compact
end
private_class_method :build_environments_status
......
......@@ -5,7 +5,7 @@ class NotificationSetting < ActiveRecord::Base
ignore_column :events
enum level: { global: 3, watch: 2, mention: 4, participating: 1, disabled: 0, custom: 5 }
enum level: { global: 3, watch: 2, participating: 1, mention: 4, disabled: 0, custom: 5 }
default_value_for :level, NotificationSetting.levels[:global]
......
- sorted_by = sort_options_hash[@sort]
.dropdown.inline.prepend-left-10
%button.dropdown-toggle{ type: 'button', data: { toggle: 'dropdown', display: 'static' } }
%button.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown', display: 'static' } }
= sorted_by
= icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable.dropdown-menu-sort
......
- if current_user
.dropdown
%button.dropdown-toggle{ href: '#', "data-toggle" => "dropdown", 'data-display' => 'static' }
= icon('globe')
%span.light= _("Visibility:")
%button.dropdown-menu-toggle{ href: '#', "data-toggle" => "dropdown", 'data-display' => 'static' }
= icon('globe', class: 'mt-1')
%span.light.ml-3= _("Visibility:")
- if params[:visibility_level].present?
= visibility_level_label(params[:visibility_level].to_i)
- else
......
......@@ -9,7 +9,7 @@
spellcheck: false, data: { 'filter-selector' => 'span.namespace-name' }
.dropdown
%button.dropdown-toggle{ type: 'button', 'data-toggle' => 'dropdown' }
%button.dropdown-menu-toggle{ type: 'button', 'data-toggle' => 'dropdown' }
%span.light sort:
- if @sort.present?
= sort_options_hash[@sort]
......
......@@ -32,7 +32,7 @@
= link_to icon('question-circle'), help_page_path('user/project/protected_branches')
.panel-footer
= f.submit _('Mirror repository'), class: 'btn btn-success', name: :update_remote_mirror
= f.submit _('Mirror repository'), class: 'btn btn-success js-mirror-submit', name: :update_remote_mirror
.panel.panel-default
.table-responsive
......
......@@ -14,7 +14,7 @@
= search_field_tag :search, params[:search], { placeholder: s_('TagsPage|Filter by tag name'), id: 'tag-search', class: 'form-control search-text-input input-short', spellcheck: false }
.dropdown
%button.dropdown-toggle{ type: 'button', data: { toggle: 'dropdown'} }
%button.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown'} }
%span.light
= tags_sort_options_hash[@sort]
= icon('chevron-down')
......
- if show_auto_devops_implicitly_enabled_banner?(project)
.auto-devops-implicitly-enabled-banner.alert.alert-warning
- more_information_link = link_to _('More information'), 'https://docs.gitlab.com/ee/topics/autodevops/', class: 'alert-link'
- more_information_link = link_to _('More information'), help_page_path('topics/autodevops/index.md'), target: '_blank', class: 'alert-link'
- auto_devops_message = s_("AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}") % { more_information_link: more_information_link }
= auto_devops_message.html_safe
.alert-link-group
......
.dropdown.inline.prepend-left-10
%button.dropdown-toggle{ type: 'button', data: { toggle: 'dropdown' } }
%button.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown' } }
%span.light
- if @sort.present?
= milestone_sort_options_hash[@sort]
......
......@@ -2,7 +2,7 @@
- viewing_issues = controller.controller_name == 'issues' || controller.action_name == 'issues'
.dropdown.inline.prepend-left-10
%button.dropdown-toggle{ type: 'button', data: { toggle: 'dropdown', display: 'static' } }
%button.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown', display: 'static' } }
= sorted_by
= icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable.dropdown-menu-sort
......
......@@ -9,7 +9,7 @@
- default_sort_by = sort_value_recently_created
.dropdown.inline.js-group-filter-dropdown-wrap.append-right-10
%button.dropdown-toggle{ type: 'button', 'data-toggle' => 'dropdown' }
%button.dropdown-menu-toggle{ type: 'button', 'data-toggle' => 'dropdown' }
%span.dropdown-label
= options_hash[default_sort_by]
= icon('chevron-down')
......
......@@ -95,7 +95,10 @@
%ul{ data: { dropdown: true } }
%li.filter-dropdown-item{ data: { value: 'none' } }
%button.btn.btn-link{ type: 'button' }
= _('No Label')
= _('None')
%li.filter-dropdown-item{ data: { value: 'any' } }
%button.btn.btn-link{ type: 'button' }
= _('Any')
%li.divider.droplab-item-ignore
%ul.filter-dropdown{ data: { dynamic: true, dropdown: true } }
%li.filter-dropdown-item
......
- sort_title = label_sort_options_hash[@sort] || sort_title_name_desc
.dropdown.inline
%button.dropdown-toggle{ type: 'button', data: { toggle: 'dropdown' } }
%button.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown' } }
= sort_title
= icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-sort
......
---
title: Redesign of MR header sections (CE)
merge_request: 23465
author:
type: changed
---
title: Adds Any option to label filters
merge_request: 23111
author: Jacopo Beschi @jacopo-beschi
type: added
---
title: 'Fix: Unstar icon button is misaligned'
merge_request: 23444
author:
type: fixed
---
title: Correctly handle data-loss scenarios when encrypting columns
merge_request: 23306
author:
type: fixed
---
title: Removed Site Statistics optimization as it was causing problems
merge_request: 23314
author:
type: removed
---
title: Fix line height of numbers in file blame view
merge_request: 23090
author: Johann Hubert Sonntagbauer
type: fixed
---
title: Fixes stuck tooltip on stop env button
merge_request: 23244
author:
type: fixed
---
title: Fix Order By dropdown menu styling in tablet and mobile screens
merge_request: 23446
author:
type: fixed
---
title: Bump gpgme gem version from 2.0.13 to 2.0.18
merge_request:
author: asaparov
type: other
---
title: Display impersonation token value only after creation
merge_request: 22916
author:
type: fixed
---
title: Clear BatchLoader context between Sidekiq jobs
merge_request: 23308
author:
type: fixed
---
title: Fix unrelated deployment status in MR widget
merge_request: 23175
author:
type: fixed
---
title: Fix not render emoji in filter dropdown
merge_request: 23112
author: Hiroyuki Sato
type: fixed
---
title: 'Auto DevOps: Add echo for each branch of the deploy() function where we run
helm upgrade'
merge_request: 23499
author:
type: changed
---
title: "#52753: HTTPS for JupyterHub installation"
merge_request: 23479
author: Amit Rathi
type: added
---
title: reorder notification settings by noisy-ness
merge_request:
author: C.J. Jameson
type: changed
---
title: 'Fix deprecation: render :text is deprecated because it does not actually render
a text/plain response'
merge_request: 23425
author: Jasper Maes
type: other
---
title: Fix handling of filenames with hash characters in tree view
merge_request: 23368
author:
type: fixed
---
title: Fix "protected branches only" checkbox not set properly at init
merge_request: 23409
author:
type: fixed
......@@ -108,14 +108,18 @@ Get an archive of the repository. This endpoint can be accessed without
authentication if the repository is publicly accessible.
```
GET /projects/:id/repository/archive
GET /projects/:id/repository/archive[.format]
```
`format` is an optional suffix for the archive format. Default is
`tar.gz`. Options are `tar.gz`, `tar.bz2`, `tbz`, 'tbz2`, `tb2`,
`bz2`, `tar`, and `zip`. For example, specifying `archive.zip`
would send an archive in ZIP format.
Parameters:
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `sha` (optional) - The commit SHA to download. A tag, branch reference or sha can be used. This defaults to the tip of the default branch if not specified
- `format` (optional) - The archive format. Default is `tar.gz`. Options are `tar.gz`, `tar.bz2`, `tbz`, `tbz2`, `tb2`, `bz2`, `tar`, `zip`
## Compare branches, tags or commits
......
......@@ -273,6 +273,8 @@ The `releases` directory will hold all our deployments:
echo 'Cloning repository'
[ -d {{ $releases_dir }} ] || mkdir {{ $releases_dir }}
git clone --depth 1 {{ $repository }} {{ $new_release_dir }}
cd {{ $releases_dir }}
git reset --hard {{ $commit }}
@endtask
...
......@@ -349,6 +351,8 @@ At the end, our `Envoy.blade.php` file will look like this:
echo 'Cloning repository'
[ -d {{ $releases_dir }} ] || mkdir {{ $releases_dir }}
git clone --depth 1 {{ $repository }} {{ $new_release_dir }}
cd {{ $releases_dir }}
git reset --hard {{ $commit }}
@endtask
@task('run_composer')
......@@ -519,7 +523,7 @@ deploy_production:
- mkdir -p ~/.ssh
- '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
- ~/.composer/vendor/bin/envoy run deploy
- ~/.composer/vendor/bin/envoy run deploy --commit="$CI_COMMIT_SHA"
environment:
name: production
url: http://192.168.1.1
......
......@@ -1590,7 +1590,7 @@ Possible values for `when` are:
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22631) in GitLab 11.5.
`parallel` allows you to configure how many instances of a job to run in
parallel. This value has to be greater than or equal to two (2) and less or equal than 50.
parallel. This value has to be greater than or equal to two (2) and less than or equal to 50.
This creates N instances of the same job that run in parallel. They're named
sequentially from `job_name 1/N` to `job_name N/N`.
......
......@@ -55,7 +55,7 @@ GitLab can be considered to have two layers from a process perspective:
### gitaly
- [Omnibus confiugration options](https://gitlab.com/gitlab-org/gitaly/tree/master/doc/configuration)
- [Omnibus configuration options](https://gitlab.com/gitlab-org/gitaly/tree/master/doc/configuration)
- Layer: Core Service (Data)
Gitaly is a service designed by GitLab to remove our need for NFS for Git storage in distributed deployments of GitLab. (Think GitLab.com or High Availablity Deployments) As of 11.3.0, This service handles all Git level access in GitLab. You can read more about the project [in the project's readme](https://gitlab.com/gitlab-org/gitaly).
......
......@@ -479,14 +479,23 @@ no longer be valid as soon as the deployment job finishes. This means that
Kubernetes can run the application, but in case it should be restarted or
executed somewhere else, it cannot be accessed again.
#### Migrations
> [Introduced][ce-21955] in GitLab 11.4
Database initialization and migrations for PostgreSQL can be configured to run
within the application pod by setting the project variables `DB_INITIALIZE` and
`DB_MIGRATE` respectively.
If present, `DB_INITIALIZE` will be run as a shell command within an application pod as a helm
post-install hook. Note that this means that if any deploy succeeds,
If present, `DB_INITIALIZE` will be run as a shell command within an
application pod as a helm post-install hook. As some applications will
not run without a successful database initialization step, GitLab will
deploy the first release without the application deployment and only the
database initialization step. After the database initialization completes,
GitLab will deploy a second release with the application deployment as
normal.
Note that a post-install hook means that if any deploy succeeds,
`DB_INITIALIZE` will not be processed thereafter.
If present, `DB_MIGRATE` will be run as a shell command within an application pod as
......
......@@ -1022,7 +1022,7 @@ A link starting with a `/` is relative to the wiki root.
[rouge]: http://rouge.jneen.net/ "Rouge website"
[redcarpet]: https://github.com/vmg/redcarpet "Redcarpet website"
[katex]: https://github.com/Khan/KaTeX "KaTeX website"
[katex-subset]: https://github.com/Khan/KaTeX/wiki/Function-Support-in-KaTeX "Macros supported by KaTeX"
[katex-subset]: https://katex.org/docs/supported.html "Macros supported by KaTeX"
[asciidoctor-manual]: http://asciidoctor.org/docs/user-manual/#activating-stem-support "Asciidoctor user manual"
[commonmarker]: https://github.com/gjtorikian/commonmarker
[commonmark-spec]: https://spec.commonmark.org/current/
......@@ -658,6 +658,7 @@ rollout 100%:
fi
if [[ -n "$DB_INITIALIZE" && -z "$(helm ls -q "^$name$")" ]]; then
echo "Deploying first release with database initialization..."
helm upgrade --install \
--wait \
--set service.enabled="$service_enabled" \
......@@ -680,6 +681,7 @@ rollout 100%:
"$name" \
chart/
echo "Deploying second release..."
helm upgrade --reuse-values \
--wait \
--set application.initializeCommand="" \
......@@ -688,6 +690,7 @@ rollout 100%:
"$name" \
chart/
else
echo "Deploying new release..."
helm upgrade --install \
--wait \
--set service.enabled="$service_enabled" \
......
......@@ -4297,9 +4297,6 @@ msgstr ""
msgid "No"
msgstr ""
msgid "No Label"
msgstr ""
msgid "No assignee"
msgstr ""
......
......@@ -114,7 +114,7 @@ describe ApplicationController do
skip_before_action :authenticate_user!, only: :index
def index
render text: 'authenticated'
render html: 'authenticated'
end
end
......@@ -401,7 +401,7 @@ describe ApplicationController do
context 'terms' do
controller(described_class) do
def index
render text: 'authenticated'
render html: 'authenticated'
end
end
......@@ -444,7 +444,7 @@ describe ApplicationController do
attr_reader :last_payload
def index
render text: 'authenticated'
render html: 'authenticated'
end
def append_info_to_payload(payload)
......
......@@ -176,7 +176,7 @@ describe 'Issue Boards add issue modal filtering', :js do
it 'filters by no label' do
set_filter('label')
click_filter_link('No Label')
click_filter_link('None')
submit_filter
page.within('.add-issues-modal') do
......
......@@ -45,7 +45,8 @@ describe 'Dropdown label', :js do
bug_label = create(:label, project: project, title: 'bug-label')
init_label_search
filtered_search.native.send_keys(:down, :down, :enter)
# navigate to the bug_label option and selects it
filtered_search.native.send_keys(:down, :down, :down, :enter)
expect_tokens([label_token(bug_label.title)])
expect_filtered_search_input_empty
......@@ -234,12 +235,20 @@ describe 'Dropdown label', :js do
end
it 'selects `no label`' do
find("#{js_dropdown_label} .filter-dropdown-item", text: 'No Label').click
find("#{js_dropdown_label} .filter-dropdown-item", text: 'None').click
expect(page).not_to have_css(js_dropdown_label)
expect_tokens([label_token('none', false)])
expect_filtered_search_input_empty
end
it 'selects `any label`' do
find("#{js_dropdown_label} .filter-dropdown-item", text: 'Any').click
expect(page).not_to have_css(js_dropdown_label)
expect_tokens([label_token('any', false)])
expect_filtered_search_input_empty
end
end
describe 'input has existing content' do
......
......@@ -430,10 +430,10 @@ describe 'Filter issues', :js do
expect_issues_list_count(2)
sort_toggle = find('.filtered-search-wrapper .dropdown-toggle')
sort_toggle = find('.filter-dropdown-container .dropdown-menu-toggle')
sort_toggle.click
find('.filtered-search-wrapper .dropdown-menu li a', text: 'Created date').click
find('.filter-dropdown-container .dropdown-menu li a', text: 'Created date').click
wait_for_requests
expect(find('.issues-list .issue:first-of-type .issue-title-text a')).to have_content(new_issue.title)
......
......@@ -20,7 +20,7 @@ describe "User sorts issues" do
end
it 'keeps the sort option' do
find('button.dropdown-toggle').click
find('.filter-dropdown-container button.dropdown-menu-toggle').click
page.within('.content ul.dropdown-menu.dropdown-menu-right li') do
click_link('Milestone')
......@@ -40,7 +40,7 @@ describe "User sorts issues" do
end
it "sorts by popularity" do
find("button.dropdown-toggle").click
find(".filter-dropdown-container button.dropdown-menu-toggle").click
page.within(".content ul.dropdown-menu.dropdown-menu-right li") do
click_link("Popularity")
......
......@@ -29,6 +29,22 @@ describe 'Merge request > User sees deployment widget', :js do
expect(page).to have_content("Deployed to #{environment.name}")
expect(find('.js-deploy-time')['data-original-title']).to eq(deployment.created_at.to_time.in_time_zone.to_s(:medium))
end
context 'when a user created a new merge request with the same SHA' do
let(:pipeline2) { create(:ci_pipeline_without_jobs, sha: sha, project: project, ref: 'new-patch-1') }
let(:build2) { create(:ci_build, :success, pipeline: pipeline2) }
let(:environment2) { create(:environment, project: project) }
let!(:deployment2) { create(:deployment, environment: environment2, sha: sha, ref: 'new-patch-1', deployable: build2) }
it 'displays one environment which is related to the pipeline' do
visit project_merge_request_path(project, merge_request)
wait_for_requests
expect(page).to have_selector('.js-deployment-info', count: 1)
expect(page).to have_content("#{environment.name}")
expect(page).not_to have_content("#{environment2.name}")
end
end
end
context 'when deployment failed' do
......
......@@ -60,7 +60,7 @@ describe 'Merge request > User sees merge widget', :js do
it 'shows environments link' do
wait_for_requests
page.within('.js-pre-merge-deploy') do
page.within('.js-pre-deployment') do
expect(page).to have_content("Deployed to #{environment.name}")
expect(find('.js-deploy-url')[:href]).to include(environment.formatted_external_url)
end
......
......@@ -19,7 +19,7 @@ describe 'User sorts merge requests' do
end
it 'keeps the sort option' do
find('button.dropdown-toggle').click
find('.filter-dropdown-container button.dropdown-menu-toggle').click
page.within('.content ul.dropdown-menu.dropdown-menu-right li') do
click_link('Milestone')
......@@ -49,7 +49,7 @@ describe 'User sorts merge requests' do
it 'separates remember sorting with issues' do
create(:issue, project: project)
find('button.dropdown-toggle').click
find('.filter-dropdown-container button.dropdown-menu-toggle').click
page.within('.content ul.dropdown-menu.dropdown-menu-right li') do
click_link('Milestone')
......@@ -70,7 +70,7 @@ describe 'User sorts merge requests' do
end
it 'sorts by popularity' do
find('button.dropdown-toggle').click
find('.filter-dropdown-container button.dropdown-menu-toggle').click
page.within('.content ul.dropdown-menu.dropdown-menu-right li') do
click_link('Popularity')
......
......@@ -32,7 +32,7 @@ describe 'Issue prioritization' do
visit project_issues_path(project, sort: 'label_priority')
# Ensure we are indicating that issues are sorted by priority
expect(page).to have_selector('.dropdown-toggle', text: 'Label priority')
expect(page).to have_selector('.dropdown-menu-toggle', text: 'Label priority')
page.within('.issues-holder') do
issue_titles = all('.issues-list .issue-title-text').map(&:text)
......@@ -70,7 +70,7 @@ describe 'Issue prioritization' do
sign_in user
visit project_issues_path(project, sort: 'label_priority')
expect(page).to have_selector('.dropdown-toggle', text: 'Label priority')
expect(page).to have_selector('.dropdown-menu-toggle', text: 'Label priority')
page.within('.issues-holder') do
issue_titles = all('.issues-list .issue-title-text').map(&:text)
......
......@@ -133,19 +133,50 @@ describe 'Projects > Settings > Repository settings' do
expect(page).to have_selector('#mirror_direction')
end
it 'generates an SSH public key on submission', :js do
it 'creates a push mirror that mirrors all branches', :js do
expect(find('.js-mirror-protected-hidden', visible: false).value).to eq('0')
fill_in 'url', with: 'ssh://user@localhost/project.git'
select 'SSH public key', from: 'Authentication method'
direction_select = find('#mirror_direction')
select_direction
# In CE, this select box is disabled, but in EE, it is enabled
if direction_select.disabled?
expect(direction_select.value).to eq('push')
else
direction_select.select('Push')
Sidekiq::Testing.fake! do
click_button 'Mirror repository'
end
project.reload
expect(page).to have_content('Mirroring settings were successfully updated')
expect(project.remote_mirrors.first.only_protected_branches).to eq(false)
end
it 'creates a push mirror that only mirrors protected branches', :js do
find('#only_protected_branches').click
expect(find('.js-mirror-protected-hidden', visible: false).value).to eq('1')
fill_in 'url', with: 'ssh://user@localhost/project.git'
select 'SSH public key', from: 'Authentication method'
select_direction
Sidekiq::Testing.fake! do
click_button 'Mirror repository'
end
project.reload
expect(page).to have_content('Mirroring settings were successfully updated')
expect(project.remote_mirrors.first.only_protected_branches).to eq(true)
end
it 'generates an SSH public key on submission', :js do
fill_in 'url', with: 'ssh://user@localhost/project.git'
select 'SSH public key', from: 'Authentication method'
select_direction
Sidekiq::Testing.fake! do
click_button 'Mirror repository'
end
......@@ -153,6 +184,17 @@ describe 'Projects > Settings > Repository settings' do
expect(page).to have_content('Mirroring settings were successfully updated')
expect(page).to have_selector('[title="Copy SSH public key"]')
end
def select_direction(direction = 'push')
direction_select = find('#mirror_direction')
# In CE, this select box is disabled, but in EE, it is enabled
if direction_select.disabled?
expect(direction_select.value).to eq(direction)
else
direction_select.select(direction.capitalize)
end
end
end
end
end
[
{
"priority": "Unknown",
"file": "pom.xml",
"cve": "CVE-2012-4387",
"url": "http://struts.apache.org/docs/s2-011.html",
"message": "Long parameter name DoS for org.apache.struts/struts2-core",
"tools": [
"gemnasium"
"category": "dependency_scanning",
"name": "io.netty/netty - CVE-2014-3488",
"message": "DoS by CPU exhaustion when using malicious SSL packets",
"cve": "app/pom.xml:io.netty/netty@3.9.1.Final:CVE-2014-3488",
"severity": "Unknown",
"solution": "Upgrade to the latest version",
"scanner": {
"id": "gemnasium",
"name": "Gemnasium"
},
"location": {
"file": "app/pom.xml"
},
"identifiers": [
{
"type": "gemnasium",
"name": "Gemnasium-d1bf36d9-9f07-46cd-9cfc-8675338ada8f",
"value": "d1bf36d9-9f07-46cd-9cfc-8675338ada8f",
"url": "https://deps.sec.gitlab.com/packages/maven/io.netty/netty/versions/3.9.1.Final/advisories"
},
{
"type": "cve",
"name": "CVE-2014-3488",
"value": "CVE-2014-3488",
"url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-3488"
}
],
"tool": "gemnasium"
"links": [
{
"url": "https://bugzilla.redhat.com/CVE-2014-3488"
},
{
"priority": "Unknown",
"file": "pom.xml",
"cve": "CVE-2013-1966",
"url": "http://struts.apache.org/docs/s2-014.html",
"message": "Remote command execution due to flaw in the includeParams attribute of URL and Anchor tags for org.apache.struts/struts2-core",
"tools": [
"gemnasium"
"url": "http://netty.io/news/2014/06/11/3.html"
},
{
"url": "https://github.com/netty/netty/issues/2562"
}
],
"priority": "Unknown",
"file": "app/pom.xml",
"url": "https://bugzilla.redhat.com/CVE-2014-3488",
"tool": "gemnasium"
},
{
"priority": "Unknown",
"file": "pom.xml",
"cve": "CVE-2013-2115",
"url": "http://struts.apache.org/docs/s2-014.html",
"message": "Remote command execution due to flaw in the includeParams attribute of URL and Anchor tags for org.apache.struts/struts2-core",
"tools": [
"gemnasium"
"category": "dependency_scanning",
"name": "Django - CVE-2017-12794",
"message": "Possible XSS in traceback section of technical 500 debug page",
"cve": "app/requirements.txt:Django@1.11.3:CVE-2017-12794",
"severity": "Unknown",
"solution": "Upgrade to latest version or apply patch.",
"scanner": {
"id": "gemnasium",
"name": "Gemnasium"
},
"location": {
"file": "app/requirements.txt"
},
"identifiers": [
{
"type": "gemnasium",
"name": "Gemnasium-6162a015-8635-4a15-8d7c-dc9321db366f",
"value": "6162a015-8635-4a15-8d7c-dc9321db366f",
"url": "https://deps.sec.gitlab.com/packages/pypi/Django/versions/1.11.3/advisories"
},
{
"type": "cve",
"name": "CVE-2017-12794",
"value": "CVE-2017-12794",
"url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-12794"
}
],
"links": [
{
"url": "https://www.djangoproject.com/weblog/2017/sep/05/security-releases/"
}
],
"priority": "Unknown",
"file": "app/requirements.txt",
"url": "https://www.djangoproject.com/weblog/2017/sep/05/security-releases/",
"tool": "gemnasium"
},
{
"priority": "Unknown",
"file": "pom.xml",
"cve": "CVE-2013-2134",
"url": "http://struts.apache.org/docs/s2-015.html",
"message": "Arbitrary OGNL code execution via unsanitized wildcard matching for org.apache.struts/struts2-core",
"tools": [
"gemnasium"
"category": "dependency_scanning",
"name": "nokogiri - USN-3424-1",
"message": "Vulnerabilities in libxml2",
"cve": "rails/Gemfile.lock:nokogiri@1.8.0:USN-3424-1",
"severity": "Unknown",
"solution": "Upgrade to latest version.",
"scanner": {
"id": "gemnasium",
"name": "Gemnasium"
},
"location": {
"file": "rails/Gemfile.lock"
},
"identifiers": [
{
"type": "gemnasium",
"name": "Gemnasium-06565b64-486d-4326-b906-890d9915804d",
"value": "06565b64-486d-4326-b906-890d9915804d",
"url": "https://deps.sec.gitlab.com/packages/gem/nokogiri/versions/1.8.0/advisories"
},
{
"type": "usn",
"name": "USN-3424-1",
"value": "USN-3424-1",
"url": "https://usn.ubuntu.com/3424-1/"
}
],
"links": [
{
"url": "https://github.com/sparklemotion/nokogiri/issues/1673"
}
],
"priority": "Unknown",
"file": "rails/Gemfile.lock",
"url": "https://github.com/sparklemotion/nokogiri/issues/1673",
"tool": "gemnasium"
},
{
"category": "dependency_scanning",
"name": "ffi - CVE-2018-1000201",
"message": "ruby-ffi DDL loading issue on Windows OS",
"cve": "ffi:1.9.18:CVE-2018-1000201",
"severity": "High",
"solution": "upgrade to \u003e= 1.9.24",
"scanner": {
"id": "bundler_audit",
"name": "bundler-audit"
},
"location": {
"file": "sast-sample-rails/Gemfile.lock"
},
"identifiers": [
{
"type": "cve",
"name": "CVE-2018-1000201",
"value": "CVE-2018-1000201",
"url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-1000201"
}
],
"links": [
{
"url": "https://github.com/ffi/ffi/releases/tag/1.9.24"
}
],
"priority": "High",
"file": "sast-sample-rails/Gemfile.lock",
"url": "https://github.com/ffi/ffi/releases/tag/1.9.24",
"tool": "bundler_audit"
}
]
[
{
"priority": "Unknown",
"file": "pom.xml",
"cve": "CVE-2012-4386",
"url": "http://struts.apache.org/docs/s2-010.html",
"message": "CSRF protection bypass for org.apache.struts/struts2-core",
"tools": [
"gemnasium"
"category": "dependency_scanning",
"name": "io.netty/netty - CVE-2014-3488",
"message": "DoS by CPU exhaustion when using malicious SSL packets",
"cve": "app/pom.xml:io.netty/netty@3.9.1.Final:CVE-2014-3488",
"severity": "Unknown",
"solution": "Upgrade to the latest version",
"scanner": {
"id": "gemnasium",
"name": "Gemnasium"
},
"location": {
"file": "app/pom.xml"
},
"identifiers": [
{
"type": "gemnasium",
"name": "Gemnasium-d1bf36d9-9f07-46cd-9cfc-8675338ada8f",
"value": "d1bf36d9-9f07-46cd-9cfc-8675338ada8f",
"url": "https://deps.sec.gitlab.com/packages/maven/io.netty/netty/versions/3.9.1.Final/advisories"
},
{
"type": "cve",
"name": "CVE-2014-3488",
"value": "CVE-2014-3488",
"url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-3488"
}
],
"tool": "gemnasium"
"links": [
{
"url": "https://bugzilla.redhat.com/CVE-2014-3488"
},
{
"priority": "Unknown",
"file": "pom.xml",
"cve": "CVE-2012-4387",
"url": "http://struts.apache.org/docs/s2-011.html",
"message": "Long parameter name DoS for org.apache.struts/struts2-core",
"tools": [
"gemnasium"
"url": "http://netty.io/news/2014/06/11/3.html"
},
{
"url": "https://github.com/netty/netty/issues/2562"
}
],
"priority": "Unknown",
"file": "app/pom.xml",
"url": "https://bugzilla.redhat.com/CVE-2014-3488",
"tool": "gemnasium"
},
{
"category": "dependency_scanning",
"name": "Django - CVE-2017-12794",
"message": "Possible XSS in traceback section of technical 500 debug page",
"cve": "app/requirements.txt:Django@1.11.3:CVE-2017-12794",
"severity": "Unknown",
"solution": "Upgrade to latest version or apply patch.",
"scanner": {
"id": "gemnasium",
"name": "Gemnasium"
},
"location": {
"file": "app/requirements.txt"
},
"identifiers": [
{
"type": "gemnasium",
"name": "Gemnasium-6162a015-8635-4a15-8d7c-dc9321db366f",
"value": "6162a015-8635-4a15-8d7c-dc9321db366f",
"url": "https://deps.sec.gitlab.com/packages/pypi/Django/versions/1.11.3/advisories"
},
{
"type": "cve",
"name": "CVE-2017-12794",
"value": "CVE-2017-12794",
"url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-12794"
}
],
"links": [
{
"url": "https://www.djangoproject.com/weblog/2017/sep/05/security-releases/"
}
],
"priority": "Unknown",
"file": "pom.xml",
"cve": "CVE-2013-1966",
"url": "http://struts.apache.org/docs/s2-014.html",
"message": "Remote command execution due to flaw in the includeParams attribute of URL and Anchor tags for org.apache.struts/struts2-core",
"tools": [
"gemnasium"
"file": "app/requirements.txt",
"url": "https://www.djangoproject.com/weblog/2017/sep/05/security-releases/",
"tool": "gemnasium"
},
{
"category": "dependency_scanning",
"name": "nokogiri - USN-3424-1",
"message": "Vulnerabilities in libxml2",
"cve": "rails/Gemfile.lock:nokogiri@1.8.0:USN-3424-1",
"severity": "Unknown",
"solution": "Upgrade to latest version.",
"scanner": {
"id": "gemnasium",
"name": "Gemnasium"
},
"location": {
"file": "rails/Gemfile.lock"
},
"identifiers": [
{
"type": "gemnasium",
"name": "Gemnasium-06565b64-486d-4326-b906-890d9915804d",
"value": "06565b64-486d-4326-b906-890d9915804d",
"url": "https://deps.sec.gitlab.com/packages/gem/nokogiri/versions/1.8.0/advisories"
},
{
"type": "usn",
"name": "USN-3424-1",
"value": "USN-3424-1",
"url": "https://usn.ubuntu.com/3424-1/"
}
],
"links": [
{
"url": "https://github.com/sparklemotion/nokogiri/issues/1673"
}
],
"priority": "Unknown",
"file": "rails/Gemfile.lock",
"url": "https://github.com/sparklemotion/nokogiri/issues/1673",
"tool": "gemnasium"
},
{
"category": "dependency_scanning",
"name": "ffi - CVE-2018-1000201",
"message": "ruby-ffi DDL loading issue on Windows OS",
"cve": "ffi:1.9.18:CVE-2018-1000201",
"severity": "High",
"solution": "upgrade to \u003e= 1.9.24",
"scanner": {
"id": "bundler_audit",
"name": "bundler-audit"
},
"location": {
"file": "sast-sample-rails/Gemfile.lock"
},
"identifiers": [
{
"type": "cve",
"name": "CVE-2018-1000201",
"value": "CVE-2018-1000201",
"url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-1000201"
}
],
"links": [
{
"url": "https://github.com/ffi/ffi/releases/tag/1.9.24"
}
],
"priority": "High",
"file": "sast-sample-rails/Gemfile.lock",
"url": "https://github.com/ffi/ffi/releases/tag/1.9.24",
"tool": "bundler_audit"
}
]
......@@ -139,57 +139,17 @@ describe('pipeline graph job item', () => {
});
});
describe('tooltip placement', () => {
it('does not set tooltip boundary by default', () => {
component = mountComponent(JobComponent, {
job: mockJob,
});
expect(component.tooltipBoundary).toBeNull();
});
it('sets tooltip boundary to viewport for small dropdowns', () => {
component = mountComponent(JobComponent, {
job: mockJob,
dropdownLength: 1,
});
expect(component.tooltipBoundary).toEqual('viewport');
});
it('does not set tooltip boundary for large lists', () => {
component = mountComponent(JobComponent, {
job: mockJob,
dropdownLength: 7,
});
expect(component.tooltipBoundary).toBeNull();
});
});
describe('for delayed job', () => {
beforeEach(() => {
const fifteenMinutesInMilliseconds = 900000;
spyOn(Date, 'now').and.callFake(
() => new Date(delayedJobFixture.scheduled_at).getTime() - fifteenMinutesInMilliseconds,
);
});
it('displays remaining time in tooltip', done => {
it('displays remaining time in tooltip', () => {
component = mountComponent(JobComponent, {
job: delayedJobFixture,
});
Vue.nextTick()
.then(() => {
expect(
component.$el
.querySelector('.js-pipeline-graph-job-link')
.getAttribute('data-original-title'),
).toEqual('delayed job - delayed manual action (00:15:00)');
})
.then(done)
.catch(done.fail);
).toEqual(`delayed job - delayed manual action (${component.remainingTime})`);
});
});
});
import { shallowMount, createLocalVue } from '@vue/test-utils';
import MrWidgetContainer from '~/vue_merge_request_widget/components/mr_widget_container.vue';
const BODY_HTML = '<div class="test-body">Hello World</div>';
const FOOTER_HTML = '<div class="test-footer">Goodbye!</div>';
describe('MrWidgetContainer', () => {
let wrapper;
const factory = (options = {}) => {
const localVue = createLocalVue();
wrapper = shallowMount(localVue.extend(MrWidgetContainer), {
localVue,
...options,
});
};
afterEach(() => {
wrapper.destroy();
});
it('has layout', () => {
factory();
expect(wrapper.is('.mr-widget-heading')).toBe(true);
expect(wrapper.contains('.mr-widget-content')).toBe(true);
});
it('accepts default slot', () => {
factory({
slots: {
default: BODY_HTML,
},
});
expect(wrapper.contains('.mr-widget-content .test-body')).toBe(true);
});
it('accepts footer slot', () => {
factory({
slots: {
default: BODY_HTML,
footer: FOOTER_HTML,
},
});
expect(wrapper.contains('.mr-widget-content .test-body')).toBe(true);
expect(wrapper.contains('.test-footer')).toBe(true);
});
});
import { shallowMount, createLocalVue } from '@vue/test-utils';
import MrWidgetIcon from '~/vue_merge_request_widget/components/mr_widget_icon.vue';
import Icon from '~/vue_shared/components/icon.vue';
const TEST_ICON = 'commit';
describe('MrWidgetIcon', () => {
let wrapper;
beforeEach(() => {
const localVue = createLocalVue();
wrapper = shallowMount(localVue.extend(MrWidgetIcon), {
propsData: {
name: TEST_ICON,
},
sync: false,
localVue,
});
});
afterEach(() => {
wrapper.destroy();
});
it('renders icon and container', () => {
expect(wrapper.is('.circle-icon-container')).toBe(true);
expect(wrapper.find(Icon).props('name')).toEqual(TEST_ICON);
});
});
import { shallowMount, createLocalVue } from '@vue/test-utils';
import MrWidgetPipelineContainer from '~/vue_merge_request_widget/components/mr_widget_pipeline_container.vue';
import MrWidgetPipeline from '~/vue_merge_request_widget/components/mr_widget_pipeline.vue';
import { mockStore } from '../mock_data';
describe('MrWidgetPipelineContainer', () => {
let wrapper;
const factory = (props = {}) => {
const localVue = createLocalVue();
wrapper = shallowMount(localVue.extend(MrWidgetPipelineContainer), {
propsData: {
mr: Object.assign({}, mockStore),
...props,
},
localVue,
});
};
afterEach(() => {
wrapper.destroy();
});
describe('when pre merge', () => {
beforeEach(() => {
factory();
});
it('renders pipeline', () => {
expect(wrapper.find(MrWidgetPipeline).exists()).toBe(true);
expect(wrapper.find(MrWidgetPipeline).props()).toEqual(
jasmine.objectContaining({
pipeline: mockStore.pipeline,
ciStatus: mockStore.ciStatus,
hasCi: mockStore.hasCI,
sourceBranch: mockStore.sourceBranch,
sourceBranchLink: mockStore.sourceBranchLink,
}),
);
});
it('renders deployments', () => {
const expectedProps = mockStore.deployments.map(dep =>
jasmine.objectContaining({
deployment: dep,
showMetrics: false,
}),
);
const deployments = wrapper.findAll('.mr-widget-extension .js-pre-deployment');
expect(deployments.wrappers.map(x => x.props())).toEqual(expectedProps);
});
});
describe('when post merge', () => {
beforeEach(() => {
factory({
isPostMerge: true,
});
});
it('renders pipeline', () => {
expect(wrapper.find(MrWidgetPipeline).exists()).toBe(true);
expect(wrapper.find(MrWidgetPipeline).props()).toEqual(
jasmine.objectContaining({
pipeline: mockStore.mergePipeline,
ciStatus: mockStore.ciStatus,
hasCi: mockStore.hasCI,
sourceBranch: mockStore.targetBranch,
sourceBranchLink: mockStore.targetBranch,
}),
);
});
it('renders deployments', () => {
const expectedProps = mockStore.postMergeDeployments.map(dep =>
jasmine.objectContaining({
deployment: dep,
showMetrics: true,
}),
);
const deployments = wrapper.findAll('.mr-widget-extension .js-post-deployment');
expect(deployments.wrappers.map(x => x.props())).toEqual(expectedProps);
});
});
});
......@@ -222,3 +222,16 @@ export default {
'http://localhost:3000/root/acets-app/commit/53027d060246c8f47e4a9310fb332aa52f221775',
troubleshooting_docs_path: 'help',
};
export const mockStore = {
pipeline: { id: 0 },
mergePipeline: { id: 1 },
targetBranch: 'target-branch',
sourceBranch: 'source-branch',
sourceBranchLink: 'source-branch-link',
deployments: [{ id: 0, name: 'bogus' }, { id: 1, name: 'bogus-docs' }],
postMergeDeployments: [{ id: 0, name: 'prod' }, { id: 1, name: 'prod-docs' }],
troubleshootingDocsPath: 'troubleshooting-docs-path',
ciStatus: 'ci-status',
hasCI: true,
};
......@@ -121,6 +121,8 @@ pipelines:
- artifacts
- pipeline_schedule
- merge_requests
- deployments
- environments
pipeline_variables:
- pipeline
stages:
......
......@@ -92,16 +92,12 @@ describe EnvironmentStatus do
end
describe '.build_environments_status' do
subject { described_class.send(:build_environments_status, merge_request, user, sha) }
subject { described_class.send(:build_environments_status, merge_request, user, pipeline) }
let!(:build) { create(:ci_build, :deploy_to_production, pipeline: pipeline) }
let(:environment) { build.deployment.environment }
let(:user) { project.owner }
before do
build.deployment&.update!(sha: sha)
end
context 'when environment is created on a forked project' do
let(:project) { create(:project, :repository) }
let(:forked) { fork_project(project, user, repository: true) }
......@@ -160,6 +156,39 @@ describe EnvironmentStatus do
expect(subject.count).to eq(0)
end
end
context 'when multiple deployments with the same SHA in different environments' do
let(:pipeline2) { create(:ci_pipeline, sha: sha, project: project) }
let!(:build2) { create(:ci_build, :start_review_app, pipeline: pipeline2) }
it 'returns deployments related to the head pipeline' do
expect(subject.count).to eq(1)
expect(subject[0].environment).to eq(environment)
expect(subject[0].merge_request).to eq(merge_request)
expect(subject[0].sha).to eq(sha)
end
end
context 'when multiple deployments in the same pipeline for the same environments' do
let!(:build2) { create(:ci_build, :deploy_to_production, pipeline: pipeline) }
it 'returns unique entries' do
expect(subject.count).to eq(1)
expect(subject[0].environment).to eq(environment)
expect(subject[0].merge_request).to eq(merge_request)
expect(subject[0].sha).to eq(sha)
end
end
context 'when environment is stopped' do
before do
environment.stop!
end
it 'does not return environment status' do
expect(subject.count).to eq(0)
end
end
end
end
end
......@@ -13,7 +13,7 @@ module Spec
module Features
module SortingHelpers
def sort_by(value)
find('button.dropdown-toggle').click
find('.filter-dropdown-container button.dropdown-menu-toggle').click
page.within('.content ul.dropdown-menu.dropdown-menu-right li') do
click_link(value)
......
......@@ -10,7 +10,7 @@
#
module SortingHelper
def sorting_by(value)
find('button.dropdown-toggle').click
find('.filter-dropdown-container button.dropdown-menu-toggle').click
page.within('.content ul.dropdown-menu.dropdown-menu-right li') do
click_link value
end
......
......@@ -22,3 +22,4 @@ ingress:
enabled: true
annotations:
kubernetes.io/ingress.class: "nginx"
kubernetes.io/tls-acme: "true"
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