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

This commit is contained in:
Shinya Maeda 2018-12-04 10:20:45 +09:00
commit 5b24739eb3
94 changed files with 985 additions and 358 deletions

View File

@ -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)

View File

@ -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.

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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',

View File

@ -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">

View File

@ -30,6 +30,7 @@ export default class MirrorRepos {
this.$password.on('input.updateUrl', () => this.debouncedUpdateUrl());
this.initMirrorSSH();
this.updateProtectedBranches();
}
initMirrorSSH() {

View File

@ -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');
},

View File

@ -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"

View File

@ -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.'));

View File

@ -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">

View File

@ -0,0 +1,6 @@
<template>
<div class="mr-widget-heading">
<div class="mr-widget-content"><slot name="default"></slot></div>
<slot name="footer"></slot>
</div>
</template>

View File

@ -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>

View File

@ -0,0 +1,17 @@
<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>

View File

@ -79,67 +79,65 @@ export default {
</script>
<template>
<div v-if="hasPipeline || hasCIError" class="mr-widget-heading append-bottom-default">
<div class="ci-widget media">
<template v-if="hasCIError">
<div
class="add-border ci-status-icon ci-status-icon-failed ci-error
js-ci-error append-right-default"
>
<icon :size="32" name="status_failed_borderless" />
</div>
<div class="media-body" v-html="errorText"></div>
</template>
<template v-else-if="hasPipeline">
<a :href="status.details_path" class="align-self-start append-right-default">
<ci-icon :status="status" :size="32" :borderless="true" class="add-border" />
</a>
<div class="ci-widget-container d-flex">
<div class="ci-widget-content">
<div class="media-body">
<div class="font-weight-bold">
Pipeline
<a :href="pipeline.path" class="pipeline-id font-weight-normal pipeline-number"
>#{{ pipeline.id }}</a
<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
js-ci-error append-right-default"
>
<icon :size="32" name="status_failed_borderless" />
</div>
<div class="media-body" v-html="errorText"></div>
</template>
<template v-else-if="hasPipeline">
<a :href="status.details_path" class="align-self-start append-right-default">
<ci-icon :status="status" :size="32" :borderless="true" class="add-border" />
</a>
<div class="ci-widget-container d-flex">
<div class="ci-widget-content">
<div class="media-body">
<div class="font-weight-bold">
Pipeline
<a :href="pipeline.path" class="pipeline-id font-weight-normal pipeline-number"
>#{{ pipeline.id }}</a
>
{{ pipeline.details.status.label }}
<template v-if="hasCommitInfo">
for
<a
:href="pipeline.commit.commit_path"
class="commit-sha js-commit-link font-weight-normal"
>
{{ pipeline.details.status.label }}
<template v-if="hasCommitInfo">
for
<a
:href="pipeline.commit.commit_path"
class="commit-sha js-commit-link font-weight-normal"
>
{{ pipeline.commit.short_id }}</a
>
on
<tooltip-on-truncate
:title="sourceBranch"
truncate-target="child"
class="label-branch label-truncate"
v-html="sourceBranchLink"
/>
</template>
</div>
<div v-if="pipeline.coverage" class="coverage">Coverage {{ pipeline.coverage }}%</div>
{{ pipeline.commit.short_id }}</a
>
on
<tooltip-on-truncate
:title="sourceBranch"
truncate-target="child"
class="label-branch label-truncate"
v-html="sourceBranchLink"
/>
</template>
</div>
</div>
<div>
<span class="mr-widget-pipeline-graph">
<span v-if="hasStages" class="stage-cell">
<div
v-for="(stage, i) in pipeline.details.stages"
:key="i"
class="stage-container dropdown js-mini-pipeline-graph mr-widget-pipeline-stages"
>
<pipeline-stage :stage="stage" />
</div>
</span>
</span>
<div v-if="pipeline.coverage" class="coverage">Coverage {{ pipeline.coverage }}%</div>
</div>
</div>
</template>
</div>
<div>
<span class="mr-widget-pipeline-graph">
<span v-if="hasStages" class="stage-cell">
<div
v-for="(stage, i) in pipeline.details.stages"
:key="i"
class="stage-container dropdown js-mini-pipeline-graph mr-widget-pipeline-stages"
>
<pipeline-stage :stage="stage" />
</div>
</span>
</span>
</div>
</div>
</template>
</div>
</template>

View File

@ -0,0 +1,74 @@
<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>

View File

@ -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"
/>
</template>
<mr-widget-pipeline-container
v-if="shouldRenderMergedPipeline"
class="js-post-merge-pipeline mr-widget-workflow"
:mr="mr"
:is-post-merge="true"
/>
</div>
</template>

View File

@ -6,6 +6,7 @@
@import 'bootstrap_migration';
@import 'framework/layout';
@import 'framework/alerts';
@import 'framework/animations';
@import 'framework/vue_transitions';
@import 'framework/avatar';

View File

@ -0,0 +1,4 @@
.alert-tip {
background-color: $theme-gray-100;
color: $theme-gray-900;
}

View File

@ -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;
}
}
}

View File

@ -42,6 +42,7 @@
padding: 10px;
text-align: right;
float: left;
line-height: 1;
a {
font-family: $monospace-font;

View File

@ -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;
}

View File

@ -158,6 +158,10 @@
width: 100%;
}
.dropdown-menu-toggle {
margin-bottom: 0;
}
form {
display: block;
height: auto;

View File

@ -50,9 +50,19 @@
.mr-widget-heading {
position: relative;
border: 1px solid $border-color;
border-radius: 4px;
border-radius: $border-radius-default;
}
&:not(.deploy-heading)::before {
.mr-widget-extension {
border-top: 1px solid $border-color;
background-color: $gray-light;
}
.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 {

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -56,7 +56,11 @@ module Clusters
def specification
{
"ingress" => {
"hosts" => [hostname]
"hosts" => [hostname],
"tls" => [{
"hosts" => [hostname],
"secretName" => "jupyter-cert"
}]
},
"hub" => {
"extraEnv" => {

View File

@ -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

View File

@ -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]

View File

@ -1,7 +1,7 @@
- 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

View File

@ -1,8 +1,8 @@
- 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

View File

@ -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]

View File

@ -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

View File

@ -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')

View File

@ -1,6 +1,6 @@
- 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

View File

@ -1,5 +1,5 @@
.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]

View File

@ -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

View File

@ -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')

View File

@ -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

View File

@ -1,6 +1,6 @@
- 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

View File

@ -0,0 +1,5 @@
---
title: Redesign of MR header sections (CE)
merge_request: 23465
author:
type: changed

View File

@ -0,0 +1,5 @@
---
title: Adds Any option to label filters
merge_request: 23111
author: Jacopo Beschi @jacopo-beschi
type: added

View File

@ -0,0 +1,5 @@
---
title: 'Fix: Unstar icon button is misaligned'
merge_request: 23444
author:
type: fixed

View File

@ -1,5 +0,0 @@
---
title: Correctly handle data-loss scenarios when encrypting columns
merge_request: 23306
author:
type: fixed

View File

@ -1,5 +0,0 @@
---
title: Removed Site Statistics optimization as it was causing problems
merge_request: 23314
author:
type: removed

View File

@ -0,0 +1,5 @@
---
title: Fix line height of numbers in file blame view
merge_request: 23090
author: Johann Hubert Sonntagbauer
type: fixed

View File

@ -1,5 +0,0 @@
---
title: Fixes stuck tooltip on stop env button
merge_request: 23244
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Fix Order By dropdown menu styling in tablet and mobile screens
merge_request: 23446
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Bump gpgme gem version from 2.0.13 to 2.0.18
merge_request:
author: asaparov
type: other

View File

@ -1,5 +0,0 @@
---
title: Display impersonation token value only after creation
merge_request: 22916
author:
type: fixed

View File

@ -1,5 +0,0 @@
---
title: Clear BatchLoader context between Sidekiq jobs
merge_request: 23308
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Fix unrelated deployment status in MR widget
merge_request: 23175
author:
type: fixed

View File

@ -1,5 +0,0 @@
---
title: Fix not render emoji in filter dropdown
merge_request: 23112
author: Hiroyuki Sato
type: fixed

View File

@ -0,0 +1,6 @@
---
title: 'Auto DevOps: Add echo for each branch of the deploy() function where we run
helm upgrade'
merge_request: 23499
author:
type: changed

View File

@ -0,0 +1,5 @@
---
title: "#52753: HTTPS for JupyterHub installation"
merge_request: 23479
author: Amit Rathi
type: added

View File

@ -0,0 +1,5 @@
---
title: reorder notification settings by noisy-ness
merge_request:
author: C.J. Jameson
type: changed

View File

@ -0,0 +1,6 @@
---
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

View File

@ -1,5 +0,0 @@
---
title: Fix handling of filenames with hash characters in tree view
merge_request: 23368
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Fix "protected branches only" checkbox not set properly at init
merge_request: 23409
author:
type: fixed

View File

@ -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

View File

@ -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

View File

@ -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`.

View File

@ -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).

View File

@ -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

View File

@ -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/

View File

@ -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" \

View File

@ -4297,9 +4297,6 @@ msgstr ""
msgid "No"
msgstr ""
msgid "No Label"
msgstr ""
msgid "No assignee"
msgstr ""

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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")

View File

@ -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

View File

@ -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

View File

@ -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')

View File

@ -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)

View File

@ -133,18 +133,49 @@ describe 'Projects > Settings > Repository settings' do
expect(page).to have_selector('#mirror_direction')
end
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'
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(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'
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('push')
else
direction_select.select('Push')
end
select_direction
Sidekiq::Testing.fake! do
click_button 'Mirror repository'
@ -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

View File

@ -1,46 +1,154 @@
[
{
"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"
}
],
"links": [
{
"url": "https://bugzilla.redhat.com/CVE-2014-3488"
},
{
"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-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"
"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-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": "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"
},
{
"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": "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"
}
],
"tool": "gemnasium"
"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"
}
]

View File

@ -1,35 +1,154 @@
[
{
"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"
}
],
"links": [
{
"url": "https://bugzilla.redhat.com/CVE-2014-3488"
},
{
"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-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": "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-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"
"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"
}
]

View File

@ -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);
expect(
component.$el
.querySelector('.js-pipeline-graph-job-link')
.getAttribute('data-original-title'),
).toEqual(`delayed job - delayed manual action (${component.remainingTime})`);
});
});
});

View File

@ -0,0 +1,51 @@
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);
});
});

View File

@ -0,0 +1,30 @@
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);
});
});

View File

@ -0,0 +1,90 @@
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);
});
});
});

View File

@ -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,
};

View File

@ -121,6 +121,8 @@ pipelines:
- artifacts
- pipeline_schedule
- merge_requests
- deployments
- environments
pipeline_variables:
- pipeline
stages:

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -22,3 +22,4 @@ ingress:
enabled: true
annotations:
kubernetes.io/ingress.class: "nginx"
kubernetes.io/tls-acme: "true"