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 @@ ...@@ -2,6 +2,22 @@
documentation](doc/development/changelog.md) for instructions on adding your own documentation](doc/development/changelog.md) for instructions on adding your own
entry. 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) ## 11.5.1 (2018-11-26)
### Security (17 changes) ### Security (17 changes)
...@@ -287,6 +303,14 @@ entry. ...@@ -287,6 +303,14 @@ entry.
- Disables stop environment button while the deploy is in progress. - 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) ## 11.4.8 (2018-11-27)
### Security (24 changes) ### Security (24 changes)
......
...@@ -181,4 +181,4 @@ This [documentation](doc/development/contributing/merge_request_workflow.md) has ...@@ -181,4 +181,4 @@ This [documentation](doc/development/contributing/merge_request_workflow.md) has
## Style guides ## 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' ...@@ -82,7 +82,7 @@ gem 'validates_hostname', '~> 1.0.6'
gem 'browser', '~> 2.5' gem 'browser', '~> 2.5'
# GPG # GPG
gem 'gpgme' gem 'gpgme', '~> 2.0.18'
# LDAP Auth # LDAP Auth
# GitLab fork with several improvements to original library. For full list of changes # GitLab fork with several improvements to original library. For full list of changes
......
...@@ -313,8 +313,8 @@ GEM ...@@ -313,8 +313,8 @@ GEM
multi_json (~> 1.11) multi_json (~> 1.11)
os (>= 0.9, < 2.0) os (>= 0.9, < 2.0)
signet (~> 0.7) signet (~> 0.7)
gpgme (2.0.13) gpgme (2.0.18)
mini_portile2 (~> 2.1) mini_portile2 (~> 2.3)
grape (1.1.0) grape (1.1.0)
activesupport activesupport
builder builder
...@@ -1016,7 +1016,7 @@ DEPENDENCIES ...@@ -1016,7 +1016,7 @@ DEPENDENCIES
gon (~> 6.2) gon (~> 6.2)
google-api-client (~> 0.23) google-api-client (~> 0.23)
google-protobuf (~> 3.6) google-protobuf (~> 3.6)
gpgme gpgme (~> 2.0.18)
grape (~> 1.1.0) grape (~> 1.1.0)
grape-entity (~> 0.7.1) grape-entity (~> 0.7.1)
grape-path-helpers (~> 1.0) grape-path-helpers (~> 1.0)
......
...@@ -310,8 +310,8 @@ GEM ...@@ -310,8 +310,8 @@ GEM
multi_json (~> 1.11) multi_json (~> 1.11)
os (>= 0.9, < 2.0) os (>= 0.9, < 2.0)
signet (~> 0.7) signet (~> 0.7)
gpgme (2.0.13) gpgme (2.0.18)
mini_portile2 (~> 2.1) mini_portile2 (~> 2.3)
grape (1.1.0) grape (1.1.0)
activesupport activesupport
builder builder
...@@ -1007,7 +1007,7 @@ DEPENDENCIES ...@@ -1007,7 +1007,7 @@ DEPENDENCIES
gon (~> 6.2) gon (~> 6.2)
google-api-client (~> 0.23) google-api-client (~> 0.23)
google-protobuf (~> 3.6) google-protobuf (~> 3.6)
gpgme gpgme (~> 2.0.18)
grape (~> 1.1.0) grape (~> 1.1.0)
grape-entity (~> 0.7.1) grape-entity (~> 0.7.1)
grape-path-helpers (~> 1.0) grape-path-helpers (~> 1.0)
......
...@@ -88,10 +88,15 @@ export const conditions = [ ...@@ -88,10 +88,15 @@ export const conditions = [
value: 'started', value: 'started',
}, },
{ {
url: 'label_name[]=No+Label', url: 'label_name[]=None',
tokenKey: 'label', tokenKey: 'label',
value: 'none', value: 'none',
}, },
{
url: 'label_name[]=Any',
tokenKey: 'any',
value: 'any',
},
{ {
url: 'my_reaction_emoji=None', url: 'my_reaction_emoji=None',
tokenKey: 'my-reaction', tokenKey: 'my-reaction',
......
...@@ -105,7 +105,7 @@ export default { ...@@ -105,7 +105,7 @@ export default {
:key="tabView.name" :key="tabView.name"
class="h-100" class="h-100"
> >
<component :is="tabView.name" /> <component :is="tabView.component || tabView.name" />
</div> </div>
</resizable-panel> </resizable-panel>
<nav class="ide-activity-bar"> <nav class="ide-activity-bar">
......
...@@ -30,6 +30,7 @@ export default class MirrorRepos { ...@@ -30,6 +30,7 @@ export default class MirrorRepos {
this.$password.on('input.updateUrl', () => this.debouncedUpdateUrl()); this.$password.on('input.updateUrl', () => this.debouncedUpdateUrl());
this.initMirrorSSH(); this.initMirrorSSH();
this.updateProtectedBranches();
} }
initMirrorSSH() { initMirrorSSH() {
......
...@@ -18,23 +18,19 @@ export default { ...@@ -18,23 +18,19 @@ export default {
required: true, required: true,
}, },
}, },
computed: { computed: {
graph() { graph() {
return this.pipeline.details && this.pipeline.details.stages; return this.pipeline.details && this.pipeline.details.stages;
}, },
}, },
methods: { methods: {
capitalizeStageName(name) { capitalizeStageName(name) {
const escapedName = _.escape(name); const escapedName = _.escape(name);
return escapedName.charAt(0).toUpperCase() + escapedName.slice(1); return escapedName.charAt(0).toUpperCase() + escapedName.slice(1);
}, },
isFirstColumn(index) { isFirstColumn(index) {
return index === 0; return index === 0;
}, },
stageConnectorClass(index, stage) { stageConnectorClass(index, stage) {
let className; let className;
...@@ -48,7 +44,6 @@ export default { ...@@ -48,7 +44,6 @@ export default {
return className; return className;
}, },
refreshPipelineGraph() { refreshPipelineGraph() {
this.$emit('refreshPipelineGraph'); this.$emit('refreshPipelineGraph');
}, },
......
...@@ -84,10 +84,6 @@ export default { ...@@ -84,10 +84,6 @@ export default {
return textBuilder.join(' '); return textBuilder.join(' ');
}, },
tooltipBoundary() {
return this.dropdownLength < 5 ? 'viewport' : null;
},
/** /**
* Verifies if the provided job has an action path * Verifies if the provided job has an action path
* *
...@@ -108,7 +104,7 @@ export default { ...@@ -108,7 +104,7 @@ export default {
<div class="ci-job-component"> <div class="ci-job-component">
<gl-link <gl-link
v-if="status.has_details" v-if="status.has_details"
v-gl-tooltip="{ boundary: tooltipBoundary }" v-gl-tooltip
:href="status.details_path" :href="status.details_path"
:title="tooltipText" :title="tooltipText"
:class="cssClassJobName" :class="cssClassJobName"
......
...@@ -23,11 +23,11 @@ export default class Star { ...@@ -23,11 +23,11 @@ export default class Star {
if (isStarred) { if (isStarred) {
$starSpan.removeClass('starred').text(s__('StarProject|Star')); $starSpan.removeClass('starred').text(s__('StarProject|Star'));
$startIcon.remove(); $startIcon.remove();
$this.prepend(spriteIcon('star-o')); $this.prepend(spriteIcon('star-o', 'icon'));
} else { } else {
$starSpan.addClass('starred').text(__('Unstar')); $starSpan.addClass('starred').text(__('Unstar'));
$startIcon.remove(); $startIcon.remove();
$this.prepend(spriteIcon('star')); $this.prepend(spriteIcon('star', 'icon'));
} }
}) })
.catch(() => Flash('Star toggle failed. Try again later.')); .catch(() => Flash('Star toggle failed. Try again later.'));
......
...@@ -112,7 +112,7 @@ export default { ...@@ -112,7 +112,7 @@ export default {
</script> </script>
<template> <template>
<div class="mr-widget-heading deploy-heading append-bottom-default"> <div class="deploy-heading">
<div class="ci-widget media"> <div class="ci-widget media">
<div class="media-body"> <div class="media-body">
<div class="deploy-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'; ...@@ -6,6 +6,7 @@ import Icon from '~/vue_shared/components/icon.vue';
import clipboardButton from '~/vue_shared/components/clipboard_button.vue'; import clipboardButton from '~/vue_shared/components/clipboard_button.vue';
import tooltip from '~/vue_shared/directives/tooltip'; import tooltip from '~/vue_shared/directives/tooltip';
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue'; import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue';
import MrWidgetIcon from './mr_widget_icon.vue';
export default { export default {
name: 'MRWidgetHeader', name: 'MRWidgetHeader',
...@@ -13,6 +14,7 @@ export default { ...@@ -13,6 +14,7 @@ export default {
Icon, Icon,
clipboardButton, clipboardButton,
TooltipOnTruncate, TooltipOnTruncate,
MrWidgetIcon,
}, },
directives: { directives: {
tooltip, tooltip,
...@@ -76,7 +78,7 @@ export default { ...@@ -76,7 +78,7 @@ export default {
</script> </script>
<template> <template>
<div class="mr-source-target append-bottom-default"> <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="git-merge-container d-flex">
<div class="normal"> <div class="normal">
<strong> <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 { ...@@ -79,8 +79,7 @@ export default {
</script> </script>
<template> <template>
<div v-if="hasPipeline || hasCIError" class="mr-widget-heading append-bottom-default"> <div v-if="hasPipeline || hasCIError" class="ci-widget media">
<div class="ci-widget media">
<template v-if="hasCIError"> <template v-if="hasCIError">
<div <div
class="add-border ci-status-icon ci-status-icon-failed ci-error class="add-border ci-status-icon ci-status-icon-failed ci-error
...@@ -141,5 +140,4 @@ export default { ...@@ -141,5 +140,4 @@ export default {
</div> </div>
</template> </template>
</div> </div>
</div>
</template> </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'; ...@@ -6,7 +6,7 @@ import SmartInterval from '~/smart_interval';
import createFlash from '../flash'; import createFlash from '../flash';
import WidgetHeader from './components/mr_widget_header.vue'; import WidgetHeader from './components/mr_widget_header.vue';
import WidgetMergeHelp from './components/mr_widget_merge_help.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 Deployment from './components/deployment.vue';
import WidgetRelatedLinks from './components/mr_widget_related_links.vue'; import WidgetRelatedLinks from './components/mr_widget_related_links.vue';
import MergedState from './components/states/mr_widget_merged.vue'; import MergedState from './components/states/mr_widget_merged.vue';
...@@ -44,7 +44,7 @@ export default { ...@@ -44,7 +44,7 @@ export default {
components: { components: {
'mr-widget-header': WidgetHeader, 'mr-widget-header': WidgetHeader,
'mr-widget-merge-help': WidgetMergeHelp, 'mr-widget-merge-help': WidgetMergeHelp,
'mr-widget-pipeline': WidgetPipeline, MrWidgetPipelineContainer,
Deployment, Deployment,
'mr-widget-related-links': WidgetRelatedLinks, 'mr-widget-related-links': WidgetRelatedLinks,
'mr-widget-merged': MergedState, 'mr-widget-merged': MergedState,
...@@ -296,23 +296,12 @@ export default { ...@@ -296,23 +296,12 @@ export default {
<template> <template>
<div class="mr-state-widget prepend-top-default"> <div class="mr-state-widget prepend-top-default">
<mr-widget-header :mr="mr" /> <mr-widget-header :mr="mr" />
<mr-widget-pipeline <mr-widget-pipeline-container
v-if="shouldRenderPipelines" v-if="shouldRenderPipelines"
:pipeline="mr.pipeline" class="mr-widget-workflow"
:ci-status="mr.ciStatus" :mr="mr"
:has-ci="mr.hasCI"
:source-branch="mr.sourceBranch"
:source-branch-link="mr.sourceBranchLink"
:troubleshooting-docs-path="mr.troubleshootingDocsPath"
/> />
<deployment <div class="mr-section-container mr-widget-workflow">
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">
<grouped-test-reports-app <grouped-test-reports-app
v-if="mr.testResultsPath" v-if="mr.testResultsPath"
class="js-reports-container" class="js-reports-container"
...@@ -336,24 +325,11 @@ export default { ...@@ -336,24 +325,11 @@ export default {
</div> </div>
<div v-if="shouldRenderMergeHelp" class="mr-widget-footer"><mr-widget-merge-help /></div> <div v-if="shouldRenderMergeHelp" class="mr-widget-footer"><mr-widget-merge-help /></div>
</div> </div>
<mr-widget-pipeline-container
<template v-if="shouldRenderMergedPipeline"> v-if="shouldRenderMergedPipeline"
<mr-widget-pipeline class="js-post-merge-pipeline mr-widget-workflow"
class="js-post-merge-pipeline prepend-top-default" :mr="mr"
:pipeline="mr.mergePipeline" :is-post-merge="true"
: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>
</div> </div>
</template> </template>
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
@import 'bootstrap_migration'; @import 'bootstrap_migration';
@import 'framework/layout'; @import 'framework/layout';
@import 'framework/alerts';
@import 'framework/animations'; @import 'framework/animations';
@import 'framework/vue_transitions'; @import 'framework/vue_transitions';
@import 'framework/avatar'; @import 'framework/avatar';
......
.alert-tip {
background-color: $theme-gray-100;
color: $theme-gray-900;
}
...@@ -363,6 +363,12 @@ ...@@ -363,6 +363,12 @@
background-color: $white-light; background-color: $white-light;
border-top: 0; border-top: 0;
} }
.filter-dropdown-container {
.dropdown {
margin-left: 0;
}
}
} }
@include media-breakpoint-down(sm) { @include media-breakpoint-down(sm) {
...@@ -372,16 +378,6 @@ ...@@ -372,16 +378,6 @@
.dropdown-menu { .dropdown-menu {
width: 100%; width: 100%;
} }
.dropdown {
margin-left: 0;
}
.fa-chevron-down {
position: absolute;
right: 10px;
top: 10px;
}
} }
} }
......
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
padding: 10px; padding: 10px;
text-align: right; text-align: right;
float: left; float: left;
line-height: 1;
a { a {
font-family: $monospace-font; font-family: $monospace-font;
......
...@@ -80,3 +80,15 @@ ...@@ -80,3 +80,15 @@
.user-avatar-link { .user-avatar-link {
text-decoration: none; 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 @@ ...@@ -158,6 +158,10 @@
width: 100%; width: 100%;
} }
.dropdown-menu-toggle {
margin-bottom: 0;
}
form { form {
display: block; display: block;
height: auto; height: auto;
......
...@@ -50,9 +50,19 @@ ...@@ -50,9 +50,19 @@
.mr-widget-heading { .mr-widget-heading {
position: relative; position: relative;
border: 1px solid $border-color; 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: ''; content: '';
border-left: 1px solid $theme-gray-200; border-left: 1px solid $theme-gray-200;
position: absolute; position: absolute;
...@@ -68,8 +78,8 @@ ...@@ -68,8 +78,8 @@
border-top: 0; border-top: 0;
} }
.mr-widget-heading,
.mr-widget-section, .mr-widget-section,
.mr-widget-content,
.mr-widget-footer { .mr-widget-footer {
padding: $gl-padding; padding: $gl-padding;
} }
...@@ -560,19 +570,6 @@ ...@@ -560,19 +570,6 @@
color: $gl-text-color; 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 { .git-merge-container {
justify-content: space-between; justify-content: space-between;
...@@ -854,11 +851,6 @@ ...@@ -854,11 +851,6 @@
} }
.deploy-heading { .deploy-heading {
margin-top: -19px;
border-top-left-radius: 0;
border-top-right-radius: 0;
background-color: $gray-light;
@include media-breakpoint-up(md) { @include media-breakpoint-up(md) {
padding: $gl-padding-8 $gl-padding; padding: $gl-padding-8 $gl-padding;
} }
...@@ -868,6 +860,10 @@ ...@@ -868,6 +860,10 @@
font-size: 12px; font-size: 12px;
margin-left: 48px; margin-left: 48px;
} }
&:not(:last-child) {
border-bottom: 1px solid $border-color;
}
} }
.deploy-body { .deploy-body {
......
...@@ -11,7 +11,7 @@ class Admin::RequestsProfilesController < Admin::ApplicationController ...@@ -11,7 +11,7 @@ class Admin::RequestsProfilesController < Admin::ApplicationController
profile = Gitlab::RequestProfiler::Profile.find(clean_name) profile = Gitlab::RequestProfiler::Profile.find(clean_name)
if profile if profile
render text: profile.content render html: profile.content
else else
redirect_to admin_requests_profiles_path, alert: 'Profile not found' redirect_to admin_requests_profiles_path, alert: 'Profile not found'
end end
......
...@@ -15,7 +15,7 @@ class ChaosController < ActionController::Base ...@@ -15,7 +15,7 @@ class ChaosController < ActionController::Base
duration_taken = (Time.now - start).seconds duration_taken = (Time.now - start).seconds
Kernel.sleep duration_s - duration_taken if duration_s > duration_taken Kernel.sleep duration_s - duration_taken if duration_s > duration_taken
render text: "OK", content_type: 'text/plain' render plain: "OK"
end end
def cpuspin def cpuspin
...@@ -24,14 +24,14 @@ class ChaosController < ActionController::Base ...@@ -24,14 +24,14 @@ class ChaosController < ActionController::Base
rand while Time.now < end_time rand while Time.now < end_time
render text: "OK", content_type: 'text/plain' render plain: "OK"
end end
def sleep def sleep
duration_s = (params[:duration_s]&.to_i || 30).seconds duration_s = (params[:duration_s]&.to_i || 30).seconds
Kernel.sleep duration_s Kernel.sleep duration_s
render text: "OK", content_type: 'text/plain' render plain: "OK"
end end
def kill def kill
...@@ -44,13 +44,13 @@ class ChaosController < ActionController::Base ...@@ -44,13 +44,13 @@ class ChaosController < ActionController::Base
secret = ENV['GITLAB_CHAOS_SECRET'] secret = ENV['GITLAB_CHAOS_SECRET']
# GITLAB_CHAOS_SECRET is required unless you're running in Development mode # GITLAB_CHAOS_SECRET is required unless you're running in Development mode
if !secret && !Rails.env.development? 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 end
return unless secret return unless secret
unless request.headers["HTTP_X_CHAOS_SECRET"] == 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 end
end end
...@@ -15,7 +15,7 @@ class MetricsController < ActionController::Base ...@@ -15,7 +15,7 @@ class MetricsController < ActionController::Base
"# Metrics are disabled, see: #{help_page}\n" "# Metrics are disabled, see: #{help_page}\n"
end end
render text: response, content_type: 'text/plain; version=0.0.4' render plain: response, content_type: 'text/plain; version=0.0.4'
end end
private private
......
...@@ -41,12 +41,12 @@ class Profiles::KeysController < Profiles::ApplicationController ...@@ -41,12 +41,12 @@ class Profiles::KeysController < Profiles::ApplicationController
user = UserFinder.new(params[:username]).find_by_username user = UserFinder.new(params[:username]).find_by_username
if user.present? if user.present?
headers['Content-Disposition'] = 'attachment' 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 else
return render_404 return render_404
end end
rescue => e rescue => e
render text: e.message render html: e.message
end end
else else
return render_404 return render_404
......
...@@ -122,7 +122,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController ...@@ -122,7 +122,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController
set_workhorse_internal_api_content_type set_workhorse_internal_api_content_type
render json: Gitlab::Workhorse.terminal_websocket(terminal) render json: Gitlab::Workhorse.terminal_websocket(terminal)
else else
render text: 'Not found', status: :not_found render html: 'Not found', status: :not_found
end end
end end
......
...@@ -26,6 +26,8 @@ module Ci ...@@ -26,6 +26,8 @@ module Ci
has_many :builds, foreign_key: :commit_id, inverse_of: :pipeline 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 :trigger_requests, dependent: :destroy, foreign_key: :commit_id # rubocop:disable Cop/ActiveRecordDependent
has_many :variables, class_name: 'Ci::PipelineVariable' 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 # Merge requests for which the current pipeline is running against
# the merge request's latest commit. # the merge request's latest commit.
...@@ -523,10 +525,6 @@ module Ci ...@@ -523,10 +525,6 @@ module Ci
yaml_errors.present? yaml_errors.present?
end end
def environments
builds.where.not(environment: nil).success.pluck(:environment).uniq
end
# Manually set the notes for a Ci::Pipeline # Manually set the notes for a Ci::Pipeline
# There is no ActiveRecord relation between Ci::Pipeline and notes # There is no ActiveRecord relation between Ci::Pipeline and notes
# as they are related to a commit sha. This method helps importing # as they are related to a commit sha. This method helps importing
......
...@@ -56,7 +56,11 @@ module Clusters ...@@ -56,7 +56,11 @@ module Clusters
def specification def specification
{ {
"ingress" => { "ingress" => {
"hosts" => [hostname] "hosts" => [hostname],
"tls" => [{
"hosts" => [hostname],
"secretName" => "jupyter-cert"
}]
}, },
"hub" => { "hub" => {
"extraEnv" => { "extraEnv" => {
......
...@@ -12,13 +12,13 @@ class EnvironmentStatus ...@@ -12,13 +12,13 @@ class EnvironmentStatus
delegate :deployed_at, to: :deployment, allow_nil: true delegate :deployed_at, to: :deployment, allow_nil: true
def self.for_merge_request(mr, user) 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 end
def self.after_merge_request(mr, user) def self.after_merge_request(mr, user)
return [] unless mr.merged? return [] unless mr.merged?
build_environments_status(mr, user, mr.merge_commit_sha) build_environments_status(mr, user, mr.merge_pipeline)
end end
def initialize(environment, merge_request, sha) def initialize(environment, merge_request, sha)
...@@ -61,13 +61,13 @@ class EnvironmentStatus ...@@ -61,13 +61,13 @@ class EnvironmentStatus
} }
end end
def self.build_environments_status(mr, user, sha) def self.build_environments_status(mr, user, pipeline)
Environment.where(project_id: [mr.source_project_id, mr.target_project_id]) return [] unless pipeline
.available
.with_deployment(sha).map do |environment| pipeline.environments.available.map do |environment|
next unless Ability.allowed?(user, :read_environment, environment) next unless Ability.allowed?(user, :read_environment, environment)
EnvironmentStatus.new(environment, mr, sha) EnvironmentStatus.new(environment, mr, pipeline.sha)
end.compact end.compact
end end
private_class_method :build_environments_status private_class_method :build_environments_status
......
...@@ -5,7 +5,7 @@ class NotificationSetting < ActiveRecord::Base ...@@ -5,7 +5,7 @@ class NotificationSetting < ActiveRecord::Base
ignore_column :events 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] default_value_for :level, NotificationSetting.levels[:global]
......
- sorted_by = sort_options_hash[@sort] - sorted_by = sort_options_hash[@sort]
.dropdown.inline.prepend-left-10 .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 = sorted_by
= icon('chevron-down') = icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable.dropdown-menu-sort %ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable.dropdown-menu-sort
......
- if current_user - if current_user
.dropdown .dropdown
%button.dropdown-toggle{ href: '#', "data-toggle" => "dropdown", 'data-display' => 'static' } %button.dropdown-menu-toggle{ href: '#', "data-toggle" => "dropdown", 'data-display' => 'static' }
= icon('globe') = icon('globe', class: 'mt-1')
%span.light= _("Visibility:") %span.light.ml-3= _("Visibility:")
- if params[:visibility_level].present? - if params[:visibility_level].present?
= visibility_level_label(params[:visibility_level].to_i) = visibility_level_label(params[:visibility_level].to_i)
- else - else
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
spellcheck: false, data: { 'filter-selector' => 'span.namespace-name' } spellcheck: false, data: { 'filter-selector' => 'span.namespace-name' }
.dropdown .dropdown
%button.dropdown-toggle{ type: 'button', 'data-toggle' => 'dropdown' } %button.dropdown-menu-toggle{ type: 'button', 'data-toggle' => 'dropdown' }
%span.light sort: %span.light sort:
- if @sort.present? - if @sort.present?
= sort_options_hash[@sort] = sort_options_hash[@sort]
......
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
= link_to icon('question-circle'), help_page_path('user/project/protected_branches') = link_to icon('question-circle'), help_page_path('user/project/protected_branches')
.panel-footer .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 .panel.panel-default
.table-responsive .table-responsive
......
...@@ -14,7 +14,7 @@ ...@@ -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 } = 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 .dropdown
%button.dropdown-toggle{ type: 'button', data: { toggle: 'dropdown'} } %button.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown'} }
%span.light %span.light
= tags_sort_options_hash[@sort] = tags_sort_options_hash[@sort]
= icon('chevron-down') = icon('chevron-down')
......
- if show_auto_devops_implicitly_enabled_banner?(project) - if show_auto_devops_implicitly_enabled_banner?(project)
.auto-devops-implicitly-enabled-banner.alert.alert-warning .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 = 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 = auto_devops_message.html_safe
.alert-link-group .alert-link-group
......
.dropdown.inline.prepend-left-10 .dropdown.inline.prepend-left-10
%button.dropdown-toggle{ type: 'button', data: { toggle: 'dropdown' } } %button.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown' } }
%span.light %span.light
- if @sort.present? - if @sort.present?
= milestone_sort_options_hash[@sort] = milestone_sort_options_hash[@sort]
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
- viewing_issues = controller.controller_name == 'issues' || controller.action_name == 'issues' - viewing_issues = controller.controller_name == 'issues' || controller.action_name == 'issues'
.dropdown.inline.prepend-left-10 .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 = sorted_by
= icon('chevron-down') = icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable.dropdown-menu-sort %ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable.dropdown-menu-sort
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
- default_sort_by = sort_value_recently_created - default_sort_by = sort_value_recently_created
.dropdown.inline.js-group-filter-dropdown-wrap.append-right-10 .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 %span.dropdown-label
= options_hash[default_sort_by] = options_hash[default_sort_by]
= icon('chevron-down') = icon('chevron-down')
......
...@@ -95,7 +95,10 @@ ...@@ -95,7 +95,10 @@
%ul{ data: { dropdown: true } } %ul{ data: { dropdown: true } }
%li.filter-dropdown-item{ data: { value: 'none' } } %li.filter-dropdown-item{ data: { value: 'none' } }
%button.btn.btn-link{ type: 'button' } %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 %li.divider.droplab-item-ignore
%ul.filter-dropdown{ data: { dynamic: true, dropdown: true } } %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } }
%li.filter-dropdown-item %li.filter-dropdown-item
......
- sort_title = label_sort_options_hash[@sort] || sort_title_name_desc - sort_title = label_sort_options_hash[@sort] || sort_title_name_desc
.dropdown.inline .dropdown.inline
%button.dropdown-toggle{ type: 'button', data: { toggle: 'dropdown' } } %button.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown' } }
= sort_title = sort_title
= icon('chevron-down') = icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-sort %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 ...@@ -108,14 +108,18 @@ Get an archive of the repository. This endpoint can be accessed without
authentication if the repository is publicly accessible. 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: Parameters:
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user - `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 - `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 ## Compare branches, tags or commits
......
...@@ -273,6 +273,8 @@ The `releases` directory will hold all our deployments: ...@@ -273,6 +273,8 @@ The `releases` directory will hold all our deployments:
echo 'Cloning repository' echo 'Cloning repository'
[ -d {{ $releases_dir }} ] || mkdir {{ $releases_dir }} [ -d {{ $releases_dir }} ] || mkdir {{ $releases_dir }}
git clone --depth 1 {{ $repository }} {{ $new_release_dir }} git clone --depth 1 {{ $repository }} {{ $new_release_dir }}
cd {{ $releases_dir }}
git reset --hard {{ $commit }}
@endtask @endtask
... ...
...@@ -349,6 +351,8 @@ At the end, our `Envoy.blade.php` file will look like this: ...@@ -349,6 +351,8 @@ At the end, our `Envoy.blade.php` file will look like this:
echo 'Cloning repository' echo 'Cloning repository'
[ -d {{ $releases_dir }} ] || mkdir {{ $releases_dir }} [ -d {{ $releases_dir }} ] || mkdir {{ $releases_dir }}
git clone --depth 1 {{ $repository }} {{ $new_release_dir }} git clone --depth 1 {{ $repository }} {{ $new_release_dir }}
cd {{ $releases_dir }}
git reset --hard {{ $commit }}
@endtask @endtask
@task('run_composer') @task('run_composer')
...@@ -519,7 +523,7 @@ deploy_production: ...@@ -519,7 +523,7 @@ deploy_production:
- mkdir -p ~/.ssh - mkdir -p ~/.ssh
- '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config' - '[[ -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: environment:
name: production name: production
url: http://192.168.1.1 url: http://192.168.1.1
......
...@@ -1590,7 +1590,7 @@ Possible values for `when` are: ...@@ -1590,7 +1590,7 @@ Possible values for `when` are:
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22631) in GitLab 11.5. > [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` 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 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`. 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: ...@@ -55,7 +55,7 @@ GitLab can be considered to have two layers from a process perspective:
### gitaly ### 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) - Layer: Core Service (Data)
Gitaly is a service designed by GitLab to remove our need for NFS for Git storage in distributed deployments of GitLab. (Think GitLab.com or High Availablity Deployments) As of 11.3.0, This service handles all Git level access in GitLab. You can read more about the project [in the project's readme](https://gitlab.com/gitlab-org/gitaly). Gitaly is a service designed by GitLab to remove our need for NFS for Git storage in distributed deployments of GitLab. (Think GitLab.com or High 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 ...@@ -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 Kubernetes can run the application, but in case it should be restarted or
executed somewhere else, it cannot be accessed again. executed somewhere else, it cannot be accessed again.
#### Migrations
> [Introduced][ce-21955] in GitLab 11.4 > [Introduced][ce-21955] in GitLab 11.4
Database initialization and migrations for PostgreSQL can be configured to run Database initialization and migrations for PostgreSQL can be configured to run
within the application pod by setting the project variables `DB_INITIALIZE` and within the application pod by setting the project variables `DB_INITIALIZE` and
`DB_MIGRATE` respectively. `DB_MIGRATE` respectively.
If present, `DB_INITIALIZE` will be run as a shell command within an application pod as a helm If present, `DB_INITIALIZE` will be run as a shell command within an
post-install hook. Note that this means that if any deploy succeeds, 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. `DB_INITIALIZE` will not be processed thereafter.
If present, `DB_MIGRATE` will be run as a shell command within an application pod as 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. ...@@ -1022,7 +1022,7 @@ A link starting with a `/` is relative to the wiki root.
[rouge]: http://rouge.jneen.net/ "Rouge website" [rouge]: http://rouge.jneen.net/ "Rouge website"
[redcarpet]: https://github.com/vmg/redcarpet "Redcarpet website" [redcarpet]: https://github.com/vmg/redcarpet "Redcarpet website"
[katex]: https://github.com/Khan/KaTeX "KaTeX 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" [asciidoctor-manual]: http://asciidoctor.org/docs/user-manual/#activating-stem-support "Asciidoctor user manual"
[commonmarker]: https://github.com/gjtorikian/commonmarker [commonmarker]: https://github.com/gjtorikian/commonmarker
[commonmark-spec]: https://spec.commonmark.org/current/ [commonmark-spec]: https://spec.commonmark.org/current/
...@@ -658,6 +658,7 @@ rollout 100%: ...@@ -658,6 +658,7 @@ rollout 100%:
fi fi
if [[ -n "$DB_INITIALIZE" && -z "$(helm ls -q "^$name$")" ]]; then if [[ -n "$DB_INITIALIZE" && -z "$(helm ls -q "^$name$")" ]]; then
echo "Deploying first release with database initialization..."
helm upgrade --install \ helm upgrade --install \
--wait \ --wait \
--set service.enabled="$service_enabled" \ --set service.enabled="$service_enabled" \
...@@ -680,6 +681,7 @@ rollout 100%: ...@@ -680,6 +681,7 @@ rollout 100%:
"$name" \ "$name" \
chart/ chart/
echo "Deploying second release..."
helm upgrade --reuse-values \ helm upgrade --reuse-values \
--wait \ --wait \
--set application.initializeCommand="" \ --set application.initializeCommand="" \
...@@ -688,6 +690,7 @@ rollout 100%: ...@@ -688,6 +690,7 @@ rollout 100%:
"$name" \ "$name" \
chart/ chart/
else else
echo "Deploying new release..."
helm upgrade --install \ helm upgrade --install \
--wait \ --wait \
--set service.enabled="$service_enabled" \ --set service.enabled="$service_enabled" \
......
...@@ -4297,9 +4297,6 @@ msgstr "" ...@@ -4297,9 +4297,6 @@ msgstr ""
msgid "No" msgid "No"
msgstr "" msgstr ""
msgid "No Label"
msgstr ""
msgid "No assignee" msgid "No assignee"
msgstr "" msgstr ""
......
...@@ -114,7 +114,7 @@ describe ApplicationController do ...@@ -114,7 +114,7 @@ describe ApplicationController do
skip_before_action :authenticate_user!, only: :index skip_before_action :authenticate_user!, only: :index
def index def index
render text: 'authenticated' render html: 'authenticated'
end end
end end
...@@ -401,7 +401,7 @@ describe ApplicationController do ...@@ -401,7 +401,7 @@ describe ApplicationController do
context 'terms' do context 'terms' do
controller(described_class) do controller(described_class) do
def index def index
render text: 'authenticated' render html: 'authenticated'
end end
end end
...@@ -444,7 +444,7 @@ describe ApplicationController do ...@@ -444,7 +444,7 @@ describe ApplicationController do
attr_reader :last_payload attr_reader :last_payload
def index def index
render text: 'authenticated' render html: 'authenticated'
end end
def append_info_to_payload(payload) def append_info_to_payload(payload)
......
...@@ -176,7 +176,7 @@ describe 'Issue Boards add issue modal filtering', :js do ...@@ -176,7 +176,7 @@ describe 'Issue Boards add issue modal filtering', :js do
it 'filters by no label' do it 'filters by no label' do
set_filter('label') set_filter('label')
click_filter_link('No Label') click_filter_link('None')
submit_filter submit_filter
page.within('.add-issues-modal') do page.within('.add-issues-modal') do
......
...@@ -45,7 +45,8 @@ describe 'Dropdown label', :js do ...@@ -45,7 +45,8 @@ describe 'Dropdown label', :js do
bug_label = create(:label, project: project, title: 'bug-label') bug_label = create(:label, project: project, title: 'bug-label')
init_label_search 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_tokens([label_token(bug_label.title)])
expect_filtered_search_input_empty expect_filtered_search_input_empty
...@@ -234,12 +235,20 @@ describe 'Dropdown label', :js do ...@@ -234,12 +235,20 @@ describe 'Dropdown label', :js do
end end
it 'selects `no label`' do 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(page).not_to have_css(js_dropdown_label)
expect_tokens([label_token('none', false)]) expect_tokens([label_token('none', false)])
expect_filtered_search_input_empty expect_filtered_search_input_empty
end 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 end
describe 'input has existing content' do describe 'input has existing content' do
......
...@@ -430,10 +430,10 @@ describe 'Filter issues', :js do ...@@ -430,10 +430,10 @@ describe 'Filter issues', :js do
expect_issues_list_count(2) 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 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 wait_for_requests
expect(find('.issues-list .issue:first-of-type .issue-title-text a')).to have_content(new_issue.title) 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 ...@@ -20,7 +20,7 @@ describe "User sorts issues" do
end end
it 'keeps the sort option' do 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 page.within('.content ul.dropdown-menu.dropdown-menu-right li') do
click_link('Milestone') click_link('Milestone')
...@@ -40,7 +40,7 @@ describe "User sorts issues" do ...@@ -40,7 +40,7 @@ describe "User sorts issues" do
end end
it "sorts by popularity" do 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 page.within(".content ul.dropdown-menu.dropdown-menu-right li") do
click_link("Popularity") click_link("Popularity")
......
...@@ -29,6 +29,22 @@ describe 'Merge request > User sees deployment widget', :js do ...@@ -29,6 +29,22 @@ describe 'Merge request > User sees deployment widget', :js do
expect(page).to have_content("Deployed to #{environment.name}") 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)) expect(find('.js-deploy-time')['data-original-title']).to eq(deployment.created_at.to_time.in_time_zone.to_s(:medium))
end 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 end
context 'when deployment failed' do context 'when deployment failed' do
......
...@@ -60,7 +60,7 @@ describe 'Merge request > User sees merge widget', :js do ...@@ -60,7 +60,7 @@ describe 'Merge request > User sees merge widget', :js do
it 'shows environments link' do it 'shows environments link' do
wait_for_requests 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(page).to have_content("Deployed to #{environment.name}")
expect(find('.js-deploy-url')[:href]).to include(environment.formatted_external_url) expect(find('.js-deploy-url')[:href]).to include(environment.formatted_external_url)
end end
......
...@@ -19,7 +19,7 @@ describe 'User sorts merge requests' do ...@@ -19,7 +19,7 @@ describe 'User sorts merge requests' do
end end
it 'keeps the sort option' do 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 page.within('.content ul.dropdown-menu.dropdown-menu-right li') do
click_link('Milestone') click_link('Milestone')
...@@ -49,7 +49,7 @@ describe 'User sorts merge requests' do ...@@ -49,7 +49,7 @@ describe 'User sorts merge requests' do
it 'separates remember sorting with issues' do it 'separates remember sorting with issues' do
create(:issue, project: project) 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 page.within('.content ul.dropdown-menu.dropdown-menu-right li') do
click_link('Milestone') click_link('Milestone')
...@@ -70,7 +70,7 @@ describe 'User sorts merge requests' do ...@@ -70,7 +70,7 @@ describe 'User sorts merge requests' do
end end
it 'sorts by popularity' do 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 page.within('.content ul.dropdown-menu.dropdown-menu-right li') do
click_link('Popularity') click_link('Popularity')
......
...@@ -32,7 +32,7 @@ describe 'Issue prioritization' do ...@@ -32,7 +32,7 @@ describe 'Issue prioritization' do
visit project_issues_path(project, sort: 'label_priority') visit project_issues_path(project, sort: 'label_priority')
# Ensure we are indicating that issues are sorted by 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 page.within('.issues-holder') do
issue_titles = all('.issues-list .issue-title-text').map(&:text) issue_titles = all('.issues-list .issue-title-text').map(&:text)
...@@ -70,7 +70,7 @@ describe 'Issue prioritization' do ...@@ -70,7 +70,7 @@ describe 'Issue prioritization' do
sign_in user sign_in user
visit project_issues_path(project, sort: 'label_priority') 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 page.within('.issues-holder') do
issue_titles = all('.issues-list .issue-title-text').map(&:text) issue_titles = all('.issues-list .issue-title-text').map(&:text)
......
...@@ -133,19 +133,50 @@ describe 'Projects > Settings > Repository settings' do ...@@ -133,19 +133,50 @@ describe 'Projects > Settings > Repository settings' do
expect(page).to have_selector('#mirror_direction') expect(page).to have_selector('#mirror_direction')
end 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' fill_in 'url', with: 'ssh://user@localhost/project.git'
select 'SSH public key', from: 'Authentication method' 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 Sidekiq::Testing.fake! do
if direction_select.disabled? click_button 'Mirror repository'
expect(direction_select.value).to eq('push') end
else
direction_select.select('Push') project.reload
expect(page).to have_content('Mirroring settings were successfully updated')
expect(project.remote_mirrors.first.only_protected_branches).to eq(false)
end 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 Sidekiq::Testing.fake! do
click_button 'Mirror repository' click_button 'Mirror repository'
end end
...@@ -153,6 +184,17 @@ describe 'Projects > Settings > Repository settings' do ...@@ -153,6 +184,17 @@ describe 'Projects > Settings > Repository settings' do
expect(page).to have_content('Mirroring settings were successfully updated') expect(page).to have_content('Mirroring settings were successfully updated')
expect(page).to have_selector('[title="Copy SSH public key"]') expect(page).to have_selector('[title="Copy SSH public key"]')
end 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 end
end end
[ [
{ {
"priority": "Unknown", "category": "dependency_scanning",
"file": "pom.xml", "name": "io.netty/netty - CVE-2014-3488",
"cve": "CVE-2012-4387", "message": "DoS by CPU exhaustion when using malicious SSL packets",
"url": "http://struts.apache.org/docs/s2-011.html", "cve": "app/pom.xml:io.netty/netty@3.9.1.Final:CVE-2014-3488",
"message": "Long parameter name DoS for org.apache.struts/struts2-core", "severity": "Unknown",
"tools": [ "solution": "Upgrade to the latest version",
"gemnasium" "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", "url": "http://netty.io/news/2014/06/11/3.html"
"file": "pom.xml", },
"cve": "CVE-2013-1966", {
"url": "http://struts.apache.org/docs/s2-014.html", "url": "https://github.com/netty/netty/issues/2562"
"message": "Remote command execution due to flaw in the includeParams attribute of URL and Anchor tags for org.apache.struts/struts2-core", }
"tools": [
"gemnasium"
], ],
"priority": "Unknown",
"file": "app/pom.xml",
"url": "https://bugzilla.redhat.com/CVE-2014-3488",
"tool": "gemnasium" "tool": "gemnasium"
}, },
{ {
"priority": "Unknown", "category": "dependency_scanning",
"file": "pom.xml", "name": "Django - CVE-2017-12794",
"cve": "CVE-2013-2115", "message": "Possible XSS in traceback section of technical 500 debug page",
"url": "http://struts.apache.org/docs/s2-014.html", "cve": "app/requirements.txt:Django@1.11.3:CVE-2017-12794",
"message": "Remote command execution due to flaw in the includeParams attribute of URL and Anchor tags for org.apache.struts/struts2-core", "severity": "Unknown",
"tools": [ "solution": "Upgrade to latest version or apply patch.",
"gemnasium" "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" "tool": "gemnasium"
}, },
{ {
"priority": "Unknown", "category": "dependency_scanning",
"file": "pom.xml", "name": "nokogiri - USN-3424-1",
"cve": "CVE-2013-2134", "message": "Vulnerabilities in libxml2",
"url": "http://struts.apache.org/docs/s2-015.html", "cve": "rails/Gemfile.lock:nokogiri@1.8.0:USN-3424-1",
"message": "Arbitrary OGNL code execution via unsanitized wildcard matching for org.apache.struts/struts2-core", "severity": "Unknown",
"tools": [ "solution": "Upgrade to latest version.",
"gemnasium" "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" "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", "category": "dependency_scanning",
"file": "pom.xml", "name": "io.netty/netty - CVE-2014-3488",
"cve": "CVE-2012-4386", "message": "DoS by CPU exhaustion when using malicious SSL packets",
"url": "http://struts.apache.org/docs/s2-010.html", "cve": "app/pom.xml:io.netty/netty@3.9.1.Final:CVE-2014-3488",
"message": "CSRF protection bypass for org.apache.struts/struts2-core", "severity": "Unknown",
"tools": [ "solution": "Upgrade to the latest version",
"gemnasium" "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", "url": "http://netty.io/news/2014/06/11/3.html"
"file": "pom.xml", },
"cve": "CVE-2012-4387", {
"url": "http://struts.apache.org/docs/s2-011.html", "url": "https://github.com/netty/netty/issues/2562"
"message": "Long parameter name DoS for org.apache.struts/struts2-core", }
"tools": [
"gemnasium"
], ],
"priority": "Unknown",
"file": "app/pom.xml",
"url": "https://bugzilla.redhat.com/CVE-2014-3488",
"tool": "gemnasium" "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", "priority": "Unknown",
"file": "pom.xml", "file": "app/requirements.txt",
"cve": "CVE-2013-1966", "url": "https://www.djangoproject.com/weblog/2017/sep/05/security-releases/",
"url": "http://struts.apache.org/docs/s2-014.html", "tool": "gemnasium"
"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" "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', () => { ...@@ -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', () => { describe('for delayed job', () => {
beforeEach(() => { it('displays remaining time in tooltip', () => {
const fifteenMinutesInMilliseconds = 900000;
spyOn(Date, 'now').and.callFake(
() => new Date(delayedJobFixture.scheduled_at).getTime() - fifteenMinutesInMilliseconds,
);
});
it('displays remaining time in tooltip', done => {
component = mountComponent(JobComponent, { component = mountComponent(JobComponent, {
job: delayedJobFixture, job: delayedJobFixture,
}); });
Vue.nextTick()
.then(() => {
expect( expect(
component.$el component.$el
.querySelector('.js-pipeline-graph-job-link') .querySelector('.js-pipeline-graph-job-link')
.getAttribute('data-original-title'), .getAttribute('data-original-title'),
).toEqual('delayed job - delayed manual action (00:15:00)'); ).toEqual(`delayed job - delayed manual action (${component.remainingTime})`);
})
.then(done)
.catch(done.fail);
}); });
}); });
}); });
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 { ...@@ -222,3 +222,16 @@ export default {
'http://localhost:3000/root/acets-app/commit/53027d060246c8f47e4a9310fb332aa52f221775', 'http://localhost:3000/root/acets-app/commit/53027d060246c8f47e4a9310fb332aa52f221775',
troubleshooting_docs_path: 'help', 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: ...@@ -121,6 +121,8 @@ pipelines:
- artifacts - artifacts
- pipeline_schedule - pipeline_schedule
- merge_requests - merge_requests
- deployments
- environments
pipeline_variables: pipeline_variables:
- pipeline - pipeline
stages: stages:
......
...@@ -92,16 +92,12 @@ describe EnvironmentStatus do ...@@ -92,16 +92,12 @@ describe EnvironmentStatus do
end end
describe '.build_environments_status' do 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!(:build) { create(:ci_build, :deploy_to_production, pipeline: pipeline) }
let(:environment) { build.deployment.environment } let(:environment) { build.deployment.environment }
let(:user) { project.owner } let(:user) { project.owner }
before do
build.deployment&.update!(sha: sha)
end
context 'when environment is created on a forked project' do context 'when environment is created on a forked project' do
let(:project) { create(:project, :repository) } let(:project) { create(:project, :repository) }
let(:forked) { fork_project(project, user, repository: true) } let(:forked) { fork_project(project, user, repository: true) }
...@@ -160,6 +156,39 @@ describe EnvironmentStatus do ...@@ -160,6 +156,39 @@ describe EnvironmentStatus do
expect(subject.count).to eq(0) expect(subject.count).to eq(0)
end end
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 end
end end
...@@ -13,7 +13,7 @@ module Spec ...@@ -13,7 +13,7 @@ module Spec
module Features module Features
module SortingHelpers module SortingHelpers
def sort_by(value) 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 page.within('.content ul.dropdown-menu.dropdown-menu-right li') do
click_link(value) click_link(value)
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
# #
module SortingHelper module SortingHelper
def sorting_by(value) 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 page.within('.content ul.dropdown-menu.dropdown-menu-right li') do
click_link value click_link value
end end
......
...@@ -22,3 +22,4 @@ ingress: ...@@ -22,3 +22,4 @@ ingress:
enabled: true enabled: true
annotations: annotations:
kubernetes.io/ingress.class: "nginx" 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