summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock4
-rw-r--r--app/assets/javascripts/gfm_auto_complete.js3
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/deployment.vue144
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_deployment.js113
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/dependencies.js2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js16
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss35
-rw-r--r--app/models/project.rb2
-rw-r--r--app/models/project_wiki.rb2
-rw-r--r--app/views/admin/runners/show.html.haml4
-rw-r--r--changelogs/unreleased/43717-breadcrumb-on-admin-runner-page.yml5
-rw-r--r--changelogs/unreleased/44160-update-foreman-to-0-84-0.yml5
-rw-r--r--changelogs/unreleased/fix-emoji-popup.yml5
-rw-r--r--doc/administration/incoming_email.md1
-rw-r--r--features/project/graph.feature33
-rw-r--r--features/project/redirects.feature38
-rw-r--r--features/steps/project/graph.rb50
-rw-r--r--features/steps/project/redirects.rb67
-rw-r--r--lib/gitlab/shell.rb13
-rw-r--r--lib/tasks/gitlab/shell.rake2
-rw-r--r--spec/features/merge_request/user_sees_deployment_widget_spec.rb2
-rw-r--r--spec/features/projects/graph_spec.rb75
-rw-r--r--spec/features/projects/redirects_spec.rb74
-rw-r--r--spec/javascripts/vue_mr_widget/components/deployment_spec.js172
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_deployment_spec.js179
-rw-r--r--spec/javascripts/vue_mr_widget/mr_widget_options_spec.js75
-rw-r--r--spec/lib/gitlab/bare_repository_import/repository_spec.rb2
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb2
-rw-r--r--spec/lib/gitlab/shell_spec.rb16
-rw-r--r--spec/models/ci/build_spec.rb123
-rw-r--r--spec/models/ci/pipeline_spec.rb6
-rw-r--r--spec/models/project_spec.rb10
-rw-r--r--spec/models/project_wiki_spec.rb2
-rw-r--r--spec/services/projects/create_service_spec.rb4
-rw-r--r--spec/services/projects/fork_service_spec.rb2
-rw-r--r--spec/services/projects/transfer_service_spec.rb2
-rw-r--r--spec/services/projects/update_service_spec.rb2
38 files changed, 687 insertions, 607 deletions
diff --git a/Gemfile b/Gemfile
index 2793463fd81..66112e23a68 100644
--- a/Gemfile
+++ b/Gemfile
@@ -300,7 +300,7 @@ group :metrics do
end
group :development do
- gem 'foreman', '~> 0.78.0'
+ gem 'foreman', '~> 0.84.0'
gem 'brakeman', '~> 3.6.0', require: false
gem 'letter_opener_web', '~> 1.3.0'
diff --git a/Gemfile.lock b/Gemfile.lock
index b85c7085d07..86b4e5301d8 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -265,7 +265,7 @@ GEM
nokogiri (>= 1.5.11, < 2.0.0)
font-awesome-rails (4.7.0.1)
railties (>= 3.2, < 5.1)
- foreman (0.78.0)
+ foreman (0.84.0)
thor (~> 0.19.1)
formatador (0.2.5)
fuubar (2.2.0)
@@ -1055,7 +1055,7 @@ DEPENDENCIES
fog-openstack (~> 0.1)
fog-rackspace (~> 0.1.1)
font-awesome-rails (~> 4.7)
- foreman (~> 0.78.0)
+ foreman (~> 0.84.0)
fuubar (~> 2.2.0)
gemnasium-gitlab-service (~> 0.2)
gemojione (~> 3.3)
diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js
index 43a5325cf71..8259133c95b 100644
--- a/app/assets/javascripts/gfm_auto_complete.js
+++ b/app/assets/javascripts/gfm_auto_complete.js
@@ -132,9 +132,8 @@ class GfmAutoComplete {
callbacks: {
...this.getDefaultCallbacks(),
matcher(flag, subtext) {
- const relevantText = subtext.trim().split(/\s/).pop();
const regexp = new RegExp(`(?:[^${glRegexp.unicodeLetters}0-9:]|\n|^):([^:]*)$`, 'gi');
- const match = regexp.exec(relevantText);
+ const match = regexp.exec(subtext);
return match && match.length ? match[1] : null;
},
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue b/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue
new file mode 100644
index 00000000000..7bef2e97349
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue
@@ -0,0 +1,144 @@
+<script>
+import timeagoMixin from '../../vue_shared/mixins/timeago';
+import tooltip from '../../vue_shared/directives/tooltip';
+import LoadingButton from '../../vue_shared/components/loading_button.vue';
+import { visitUrl } from '../../lib/utils/url_utility';
+import createFlash from '../../flash';
+import MemoryUsage from './memory_usage.vue';
+import StatusIcon from './mr_widget_status_icon.vue';
+import MRWidgetService from '../services/mr_widget_service';
+
+export default {
+ name: 'Deployment',
+ components: {
+ LoadingButton,
+ MemoryUsage,
+ StatusIcon,
+ },
+ directives: {
+ tooltip,
+ },
+ mixins: [
+ timeagoMixin,
+ ],
+ props: {
+ deployment: {
+ type: Object,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ isStopping: false,
+ };
+ },
+ computed: {
+ deployTimeago() {
+ return this.timeFormated(this.deployment.deployed_at);
+ },
+ hasExternalUrls() {
+ return !!(this.deployment.external_url && this.deployment.external_url_formatted);
+ },
+ hasDeploymentTime() {
+ return !!(this.deployment.deployed_at && this.deployment.deployed_at_formatted);
+ },
+ hasDeploymentMeta() {
+ return !!(this.deployment.url && this.deployment.name);
+ },
+ hasMetrics() {
+ return !!(this.deployment.metrics_url);
+ },
+ },
+ methods: {
+ stopEnvironment() {
+ const msg = 'Are you sure you want to stop this environment?';
+ const isConfirmed = confirm(msg); // eslint-disable-line
+
+ if (isConfirmed) {
+ this.isStopping = true;
+
+ MRWidgetService.stopEnvironment(this.deployment.stop_url)
+ .then(res => res.data)
+ .then((data) => {
+ if (data.redirect_url) {
+ visitUrl(data.redirect_url);
+ }
+
+ this.isStopping = false;
+ })
+ .catch(() => {
+ createFlash('Something went wrong while stopping this environment. Please try again.');
+ this.isStopping = false;
+ });
+ }
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="mr-widget-heading deploy-heading">
+ <div class="ci-widget media">
+ <div class="ci-status-icon ci-status-icon-success">
+ <span class="js-icon-link icon-link">
+ <status-icon status="success" />
+ </span>
+ </div>
+ <div class="media-body">
+ <div class="deploy-body">
+ <template v-if="hasDeploymentMeta">
+ <span>
+ Deployed to
+ </span>
+ <a
+ :href="deployment.url"
+ target="_blank"
+ rel="noopener noreferrer nofollow"
+ class="deploy-link js-deploy-meta"
+ >
+ {{ deployment.name }}
+ </a>
+ </template>
+ <template v-if="hasExternalUrls">
+ <span>
+ on
+ </span>
+ <a
+ :href="deployment.external_url"
+ target="_blank"
+ rel="noopener noreferrer nofollow"
+ class="deploy-link js-deploy-url"
+ >
+ <i
+ class="fa fa-external-link"
+ aria-hidden="true"
+ >
+ </i>
+ {{ deployment.external_url_formatted }}
+ </a>
+ </template>
+ <span
+ v-if="hasDeploymentTime"
+ v-tooltip
+ :title="deployment.deployed_at_formatted"
+ class="js-deploy-time"
+ >
+ {{ deployTimeago }}
+ </span>
+ <loading-button
+ v-if="deployment.stop_url"
+ container-class="btn btn-default btn-xs prepend-left-default"
+ label="Stop environment"
+ :loading="isStopping"
+ @click="stopEnvironment"
+ />
+ </div>
+ <memory-usage
+ v-if="hasMetrics"
+ :metrics-url="deployment.metrics_url"
+ :metrics-monitoring-url="deployment.metrics_monitoring_url"
+ />
+ </div>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_deployment.js b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_deployment.js
deleted file mode 100644
index c7f992384c8..00000000000
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_deployment.js
+++ /dev/null
@@ -1,113 +0,0 @@
-import { getTimeago } from '~/lib/utils/datetime_utility';
-import { visitUrl } from '../../lib/utils/url_utility';
-import Flash from '../../flash';
-import MemoryUsage from './memory_usage.vue';
-import StatusIcon from './mr_widget_status_icon.vue';
-import MRWidgetService from '../services/mr_widget_service';
-
-export default {
- name: 'MRWidgetDeployment',
- props: {
- mr: { type: Object, required: true },
- service: { type: Object, required: true },
- },
- components: {
- MemoryUsage,
- StatusIcon,
- },
- methods: {
- formatDate(date) {
- return getTimeago().format(date);
- },
- hasExternalUrls(deployment = {}) {
- return deployment.external_url && deployment.external_url_formatted;
- },
- hasDeploymentTime(deployment = {}) {
- return deployment.deployed_at && deployment.deployed_at_formatted;
- },
- hasDeploymentMeta(deployment = {}) {
- return deployment.url && deployment.name;
- },
- stopEnvironment(deployment) {
- const msg = 'Are you sure you want to stop this environment?';
- const isConfirmed = confirm(msg); // eslint-disable-line
-
- if (isConfirmed) {
- MRWidgetService.stopEnvironment(deployment.stop_url)
- .then(res => res.data)
- .then((data) => {
- if (data.redirect_url) {
- visitUrl(data.redirect_url);
- }
- })
- .catch(() => {
- new Flash('Something went wrong while stopping this environment. Please try again.'); // eslint-disable-line
- });
- }
- },
- },
- template: `
- <div class="mr-widget-heading deploy-heading">
- <div v-for="deployment in mr.deployments">
- <div class="ci-widget media">
- <div class="ci-status-icon ci-status-icon-success">
- <span class="js-icon-link icon-link">
- <status-icon status="success" />
- </span>
- </div>
- <div class="media-body space-children">
- <span>
- <span
- v-if="hasDeploymentMeta(deployment)">
- Deployed to
- </span>
- <a
- v-if="hasDeploymentMeta(deployment)"
- :href="deployment.url"
- target="_blank"
- rel="noopener noreferrer nofollow"
- class="js-deploy-meta inline">
- {{deployment.name}}
- </a>
- <span
- v-if="hasExternalUrls(deployment)">
- on
- </span>
- <a
- v-if="hasExternalUrls(deployment)"
- :href="deployment.external_url"
- target="_blank"
- rel="noopener noreferrer nofollow"
- class="js-deploy-url inline">
- <i
- class="fa fa-external-link"
- aria-hidden="true" />
- {{deployment.external_url_formatted}}
- </a>
- <span
- v-if="hasDeploymentTime(deployment)"
- :data-title="deployment.deployed_at_formatted"
- class="js-deploy-time"
- data-toggle="tooltip"
- data-placement="top">
- {{formatDate(deployment.deployed_at)}}
- </span>
- </span>
- <button
- type="button"
- v-if="deployment.stop_url"
- @click="stopEnvironment(deployment)"
- class="btn btn-default btn-xs">
- Stop environment
- </button>
- <memory-usage
- v-if="deployment.metrics_url"
- :metrics-url="deployment.metrics_url"
- :metrics-monitoring-url="deployment.metrics_monitoring_url"
- />
- </div>
- </div>
- </div>
- </div>
- `,
-};
diff --git a/app/assets/javascripts/vue_merge_request_widget/dependencies.js b/app/assets/javascripts/vue_merge_request_widget/dependencies.js
index 20624aad0ad..efbe1c96d1c 100644
--- a/app/assets/javascripts/vue_merge_request_widget/dependencies.js
+++ b/app/assets/javascripts/vue_merge_request_widget/dependencies.js
@@ -14,7 +14,7 @@ export { default as SmartInterval } from '~/smart_interval';
export { default as WidgetHeader } from './components/mr_widget_header.vue';
export { default as WidgetMergeHelp } from './components/mr_widget_merge_help.vue';
export { default as WidgetPipeline } from './components/mr_widget_pipeline.vue';
-export { default as WidgetDeployment } from './components/mr_widget_deployment';
+export { default as Deployment } from './components/deployment.vue';
export { default as WidgetMaintainerEdit } from './components/mr_widget_maintainer_edit.vue';
export { default as WidgetRelatedLinks } from './components/mr_widget_related_links.vue';
export { default as MergedState } from './components/states/mr_widget_merged.vue';
diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js
index cc8bc6af1e1..169adfe0a1d 100644
--- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js
+++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js
@@ -5,7 +5,7 @@ import {
WidgetHeader,
WidgetMergeHelp,
WidgetPipeline,
- WidgetDeployment,
+ Deployment,
WidgetMaintainerEdit,
WidgetRelatedLinks,
MergedState,
@@ -67,9 +67,6 @@ export default {
shouldRenderRelatedLinks() {
return !!this.mr.relatedLinks && !this.mr.isNothingToMergeState;
},
- shouldRenderDeployments() {
- return this.mr.deployments.length;
- },
shouldRenderSourceBranchRemovalStatus() {
return !this.mr.canRemoveSourceBranch && this.mr.shouldRemoveSourceBranch &&
(!this.mr.isNothingToMergeState && !this.mr.isMergedState);
@@ -216,7 +213,7 @@ export default {
'mr-widget-header': WidgetHeader,
'mr-widget-merge-help': WidgetMergeHelp,
'mr-widget-pipeline': WidgetPipeline,
- 'mr-widget-deployment': WidgetDeployment,
+ Deployment,
'mr-widget-maintainer-edit': WidgetMaintainerEdit,
'mr-widget-related-links': WidgetRelatedLinks,
'mr-widget-merged': MergedState,
@@ -250,10 +247,11 @@ export default {
:ci-status="mr.ciStatus"
:has-ci="mr.hasCI"
/>
- <mr-widget-deployment
- v-if="shouldRenderDeployments"
- :mr="mr"
- :service="service" />
+ <deployment
+ v-for="deployment in mr.deployments"
+ :key="deployment.id"
+ :deployment="deployment"
+ />
<div class="mr-widget-section">
<component
:is="componentName"
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index f887a11004f..4692d0fb873 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -718,6 +718,8 @@
}
.mr-memory-usage {
+ width: 100%;
+
p.usage-info-loading .usage-info-load-spinner {
margin-right: 10px;
font-size: 16px;
@@ -727,3 +729,36 @@
.fork-sprite {
margin-right: -5px;
}
+
+.deploy-heading {
+ .media-body {
+ min-width: 0;
+ }
+}
+
+.deploy-body {
+ display: flex;
+ flex-wrap: wrap;
+
+ @media (min-width: $screen-xs) {
+ flex-wrap: nowrap;
+ white-space: nowrap;
+ }
+
+ > *:not(:last-child) {
+ margin-right: .3em;
+ }
+}
+
+.deploy-link {
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ min-width: 100px;
+ max-width: 150px;
+
+ @media (min-width: $screen-xs) {
+ min-width: 0;
+ max-width: 100%;
+ }
+}
diff --git a/app/models/project.rb b/app/models/project.rb
index 5f9d9785d64..0183e3d0a38 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -1083,7 +1083,7 @@ class Project < ActiveRecord::Base
# Forked import is handled asynchronously
return if forked? && !force
- if gitlab_shell.add_repository(repository_storage, disk_path)
+ if gitlab_shell.create_repository(repository_storage, disk_path)
repository.after_create
true
else
diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb
index f6041da986c..52e067cb44c 100644
--- a/app/models/project_wiki.rb
+++ b/app/models/project_wiki.rb
@@ -169,7 +169,7 @@ class ProjectWiki
private
def create_repo!(raw_repository)
- gitlab_shell.add_repository(project.repository_storage, disk_path)
+ gitlab_shell.create_repository(project.repository_storage, disk_path)
raise CouldNotCreateWikiError unless raw_repository.exists?
diff --git a/app/views/admin/runners/show.html.haml b/app/views/admin/runners/show.html.haml
index 185e9d7b35d..37269862de6 100644
--- a/app/views/admin/runners/show.html.haml
+++ b/app/views/admin/runners/show.html.haml
@@ -9,6 +9,10 @@
%span.runner-state.runner-state-specific
Specific
+- add_to_breadcrumbs _("Runners"), admin_runners_path
+- breadcrumb_title "##{@runner.id}"
+- @no_container = true
+
- if @runner.shared?
.bs-callout.bs-callout-success
%h4 This Runner will process jobs from ALL UNASSIGNED projects
diff --git a/changelogs/unreleased/43717-breadcrumb-on-admin-runner-page.yml b/changelogs/unreleased/43717-breadcrumb-on-admin-runner-page.yml
new file mode 100644
index 00000000000..3aec71d5ac4
--- /dev/null
+++ b/changelogs/unreleased/43717-breadcrumb-on-admin-runner-page.yml
@@ -0,0 +1,5 @@
+---
+title: Set breadcrumb for admin/runners/show
+merge_request: 17431
+author: Takuya Noguchi
+type: fixed
diff --git a/changelogs/unreleased/44160-update-foreman-to-0-84-0.yml b/changelogs/unreleased/44160-update-foreman-to-0-84-0.yml
new file mode 100644
index 00000000000..990d188eb78
--- /dev/null
+++ b/changelogs/unreleased/44160-update-foreman-to-0-84-0.yml
@@ -0,0 +1,5 @@
+---
+title: Update foreman from 0.78.0 to 0.84.0
+merge_request: 17690
+author: Takuya Noguchi
+type: other
diff --git a/changelogs/unreleased/fix-emoji-popup.yml b/changelogs/unreleased/fix-emoji-popup.yml
new file mode 100644
index 00000000000..c81d061a5d7
--- /dev/null
+++ b/changelogs/unreleased/fix-emoji-popup.yml
@@ -0,0 +1,5 @@
+---
+title: Hide emoji popup after multiple spaces
+merge_request:
+author: Jan Beckmann
+type: fixed
diff --git a/doc/administration/incoming_email.md b/doc/administration/incoming_email.md
index 6c5a466ced5..27a3710632d 100644
--- a/doc/administration/incoming_email.md
+++ b/doc/administration/incoming_email.md
@@ -187,6 +187,7 @@ for a real-world example of this exploit.
```sh
sudo gitlab-ctl reconfigure
+ sudo gitlab-ctl restart
```
1. Verify that everything is configured correctly:
diff --git a/features/project/graph.feature b/features/project/graph.feature
deleted file mode 100644
index b25c73ad870..00000000000
--- a/features/project/graph.feature
+++ /dev/null
@@ -1,33 +0,0 @@
-Feature: Project Graph
- Background:
- Given I sign in as a user
- And I own project "Shop"
-
- @javascript
- Scenario: I should see project graphs
- When I visit project "Shop" graph page
- Then page should have graphs
-
- @javascript
- Scenario: I should see project languages & commits graphs on commits graph url
- When I visit project "Shop" commits graph page
- Then page should have commits graphs
- Then page should have languages graphs
-
- @javascript
- Scenario: I should see project ci graphs
- Given project "Shop" has CI enabled
- When I visit project "Shop" CI graph page
- Then page should have CI graphs
-
- @javascript
- Scenario: I should see project languages & commits graphs on language graph url
- When I visit project "Shop" languages graph page
- Then page should have languages graphs
- Then page should have commits graphs
-
- @javascript
- Scenario: I should see project languages & commits graphs on charts url
- When I visit project "Shop" chart page
- Then page should have languages graphs
- Then page should have commits graphs
diff --git a/features/project/redirects.feature b/features/project/redirects.feature
deleted file mode 100644
index a2e77e7bf30..00000000000
--- a/features/project/redirects.feature
+++ /dev/null
@@ -1,38 +0,0 @@
-Feature: Project Redirects
- Background:
- Given public project "Community"
- And private project "Enterprise"
-
- Scenario: I visit public project page
- When I visit project "Community" page
- Then I should see project "Community" home page
-
- Scenario: I visit private project page
- When I visit project "Enterprise" page
- Then I should be redirected to sign in page
-
- Scenario: I visit a non-existent project page
- When I visit project "CommunityDoesNotExist" page
- Then I should be redirected to sign in page
-
- Scenario: I visit a non-existent project page as user
- Given I sign in as a user
- When I visit project "CommunityDoesNotExist" page
- Then page status code should be 404
-
- Scenario: I visit unauthorized project page as user
- Given I sign in as a user
- When I visit project "Enterprise" page
- Then page status code should be 404
-
- Scenario: I visit a public project without signing in
- When I visit project "Community" page
- And I should see project "Community" home page
- And I click on "Sign In"
- And Authenticate
- Then I should be redirected to "Community" page
-
- Scenario: I visit private project page without signing in
- When I visit project "Enterprise" page
- And I get redirected to signin page where I sign in
- Then I should be redirected to "Enterprise" page
diff --git a/features/steps/project/graph.rb b/features/steps/project/graph.rb
deleted file mode 100644
index b9cddf4041d..00000000000
--- a/features/steps/project/graph.rb
+++ /dev/null
@@ -1,50 +0,0 @@
-class Spinach::Features::ProjectGraph < Spinach::FeatureSteps
- include SharedAuthentication
- include SharedProject
-
- step 'page should have graphs' do
- expect(page).to have_selector ".stat-graph"
- end
-
- When 'I visit project "Shop" graph page' do
- visit project_graph_path(project, "master")
- end
-
- step 'I visit project "Shop" commits graph page' do
- visit commits_project_graph_path(project, "master")
- end
-
- step 'I visit project "Shop" languages graph page' do
- visit languages_project_graph_path(project, "master")
- end
-
- step 'I visit project "Shop" chart page' do
- visit charts_project_graph_path(project, "master")
- end
-
- step 'page should have languages graphs' do
- expect(page).to have_content /Ruby 66.* %/
- expect(page).to have_content /JavaScript 22.* %/
- end
-
- step 'page should have commits graphs' do
- expect(page).to have_content "Commit statistics for master"
- expect(page).to have_content "Commits per day of month"
- end
-
- step 'I visit project "Shop" CI graph page' do
- visit ci_project_graph_path(project, 'master')
- end
-
- step 'page should have CI graphs' do
- expect(page).to have_content 'Overall'
- expect(page).to have_content 'Pipelines for last week'
- expect(page).to have_content 'Pipelines for last month'
- expect(page).to have_content 'Pipelines for last year'
- expect(page).to have_content 'Commit duration in minutes for last 30 commits'
- end
-
- def project
- @project ||= Project.find_by(name: "Shop")
- end
-end
diff --git a/features/steps/project/redirects.rb b/features/steps/project/redirects.rb
deleted file mode 100644
index 9ce86ca45d0..00000000000
--- a/features/steps/project/redirects.rb
+++ /dev/null
@@ -1,67 +0,0 @@
-class Spinach::Features::ProjectRedirects < Spinach::FeatureSteps
- include SharedAuthentication
- include SharedPaths
- include SharedProject
-
- step 'public project "Community"' do
- create(:project, :public, name: 'Community')
- end
-
- step 'private project "Enterprise"' do
- create(:project, :private, name: 'Enterprise')
- end
-
- step 'I visit project "Community" page' do
- project = Project.find_by(name: 'Community')
- visit project_path(project)
- end
-
- step 'I should see project "Community" home page' do
- Gitlab.config.gitlab.should_receive(:host).and_return("www.example.com")
- page.within '.breadcrumbs .breadcrumb-item-text' do
- expect(page).to have_content 'Community'
- end
- end
-
- step 'I visit project "Enterprise" page' do
- project = Project.find_by(name: 'Enterprise')
- visit project_path(project)
- end
-
- step 'I visit project "CommunityDoesNotExist" page' do
- project = Project.find_by(name: 'Community')
- visit project_path(project) + 'DoesNotExist'
- end
-
- step 'I click on "Sign In"' do
- first(:link, "Sign in").click
- end
-
- step 'Authenticate' do
- admin = create(:admin)
- fill_in "user_login", with: admin.email
- fill_in "user_password", with: admin.password
- click_button "Sign in"
- Thread.current[:current_user] = admin
- end
-
- step 'I should be redirected to "Community" page' do
- project = Project.find_by(name: 'Community')
- expect(current_path).to eq "/#{project.full_path}"
- expect(status_code).to eq 200
- end
-
- step 'I get redirected to signin page where I sign in' do
- admin = create(:admin)
- fill_in "user_login", with: admin.email
- fill_in "user_password", with: admin.password
- click_button "Sign in"
- Thread.current[:current_user] = admin
- end
-
- step 'I should be redirected to "Enterprise" page' do
- project = Project.find_by(name: 'Enterprise')
- expect(current_path).to eq "/#{project.full_path}"
- expect(status_code).to eq 200
- end
-end
diff --git a/lib/gitlab/shell.rb b/lib/gitlab/shell.rb
index dda7afc0999..3a8f5826818 100644
--- a/lib/gitlab/shell.rb
+++ b/lib/gitlab/shell.rb
@@ -69,13 +69,14 @@ module Gitlab
# name - project disk path
#
# Ex.
- # add_repository("/path/to/storage", "gitlab/gitlab-ci")
+ # create_repository("/path/to/storage", "gitlab/gitlab-ci")
#
- def add_repository(storage, name)
+ def create_repository(storage, name)
relative_path = name.dup
relative_path << '.git' unless relative_path.end_with?('.git')
- gitaly_migrate(:create_repository) do |is_enabled|
+ gitaly_migrate(:create_repository,
+ status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
if is_enabled
repository = Gitlab::Git::Repository.new(storage, relative_path, '')
repository.gitaly_repository_client.create_repository
@@ -85,7 +86,7 @@ module Gitlab
Gitlab::Git::Repository.create(repo_path, bare: true, symlink_hooks_to: gitlab_shell_hooks_path)
end
end
- rescue => err
+ rescue => err # Once the Rugged codes gets removes this can be improved
Rails.logger.error("Failed to add repository #{storage}/#{name}: #{err}")
false
end
@@ -487,8 +488,8 @@ module Gitlab
Gitlab.config.gitlab_shell.git_timeout
end
- def gitaly_migrate(method, &block)
- Gitlab::GitalyClient.migrate(method, &block)
+ def gitaly_migrate(method, status: Gitlab::GitalyClient::MigrationStatus::OPT_IN, &block)
+ Gitlab::GitalyClient.migrate(method, status: status, &block)
rescue GRPC::NotFound, GRPC::BadStatus => e
# Old Popen code returns [Error, output] to the caller, so we
# need to do the same here...
diff --git a/lib/tasks/gitlab/shell.rake b/lib/tasks/gitlab/shell.rake
index 844664b12d4..4fcbbbf8c9d 100644
--- a/lib/tasks/gitlab/shell.rake
+++ b/lib/tasks/gitlab/shell.rake
@@ -69,7 +69,7 @@ namespace :gitlab do
if File.exist?(path_to_repo)
print '-'
else
- if Gitlab::Shell.new.add_repository(project.repository_storage,
+ if Gitlab::Shell.new.create_repository(project.repository_storage,
project.disk_path)
print '.'
else
diff --git a/spec/features/merge_request/user_sees_deployment_widget_spec.rb b/spec/features/merge_request/user_sees_deployment_widget_spec.rb
index 3abe363d523..f744d7941f5 100644
--- a/spec/features/merge_request/user_sees_deployment_widget_spec.rb
+++ b/spec/features/merge_request/user_sees_deployment_widget_spec.rb
@@ -22,7 +22,7 @@ describe 'Merge request > User sees deployment widget', :js do
wait_for_requests
expect(page).to have_content("Deployed to #{environment.name}")
- expect(find('.js-deploy-time')['data-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
context 'with stop action' do
diff --git a/spec/features/projects/graph_spec.rb b/spec/features/projects/graph_spec.rb
new file mode 100644
index 00000000000..57172610aed
--- /dev/null
+++ b/spec/features/projects/graph_spec.rb
@@ -0,0 +1,75 @@
+require 'spec_helper'
+
+describe 'Project Graph', :js do
+ let(:user) { create :user }
+ let(:project) { create(:project, :repository, namespace: user.namespace) }
+
+ before do
+ project.add_master(user)
+
+ sign_in(user)
+ end
+
+ shared_examples 'page should have commits graphs' do
+ it 'renders commits' do
+ expect(page).to have_content('Commit statistics for master')
+ expect(page).to have_content('Commits per day of month')
+ end
+ end
+
+ shared_examples 'page should have languages graphs' do
+ it 'renders languages' do
+ expect(page).to have_content(/Ruby 66.* %/)
+ expect(page).to have_content(/JavaScript 22.* %/)
+ end
+ end
+
+ it 'renders graphs' do
+ visit project_graph_path(project, 'master')
+
+ expect(page).to have_selector('.stat-graph', visible: false)
+ end
+
+ context 'commits graph' do
+ before do
+ visit commits_project_graph_path(project, 'master')
+ end
+
+ it_behaves_like 'page should have commits graphs'
+ it_behaves_like 'page should have languages graphs'
+ end
+
+ context 'languages graph' do
+ before do
+ visit languages_project_graph_path(project, 'master')
+ end
+
+ it_behaves_like 'page should have commits graphs'
+ it_behaves_like 'page should have languages graphs'
+ end
+
+ context 'charts graph' do
+ before do
+ visit charts_project_graph_path(project, 'master')
+ end
+
+ it_behaves_like 'page should have commits graphs'
+ it_behaves_like 'page should have languages graphs'
+ end
+
+ context 'when CI enabled' do
+ before do
+ project.enable_ci
+
+ visit ci_project_graph_path(project, 'master')
+ end
+
+ it 'renders CI graphs' do
+ expect(page).to have_content 'Overall'
+ expect(page).to have_content 'Pipelines for last week'
+ expect(page).to have_content 'Pipelines for last month'
+ expect(page).to have_content 'Pipelines for last year'
+ expect(page).to have_content 'Commit duration in minutes for last 30 commits'
+ end
+ end
+end
diff --git a/spec/features/projects/redirects_spec.rb b/spec/features/projects/redirects_spec.rb
new file mode 100644
index 00000000000..d1d8ca07035
--- /dev/null
+++ b/spec/features/projects/redirects_spec.rb
@@ -0,0 +1,74 @@
+require 'spec_helper'
+
+describe 'Project redirects' do
+ let(:user) { create :user }
+ let(:public_project) { create :project, :public }
+ let(:private_project) { create :project, :private }
+
+ before do
+ allow(Gitlab.config.gitlab).to receive(:host).and_return('www.example.com')
+ end
+
+ it 'shows public project page' do
+ visit project_path(public_project)
+
+ page.within '.breadcrumbs .breadcrumb-item-text' do
+ expect(page).to have_content(public_project.name)
+ end
+ end
+
+ it 'redirects to sign in page when project is private' do
+ visit project_path(private_project)
+
+ expect(current_path).to eq(new_user_session_path)
+ end
+
+ it 'redirects to sign in page when project does not exist' do
+ visit project_path(build(:project, :public))
+
+ expect(current_path).to eq(new_user_session_path)
+ end
+
+ it 'redirects to public project page after signing in' do
+ visit project_path(public_project)
+
+ first(:link, 'Sign in').click
+
+ fill_in 'user_login', with: user.email
+ fill_in 'user_password', with: user.password
+ click_button 'Sign in'
+
+ expect(status_code).to eq(200)
+ expect(current_path).to eq("/#{public_project.full_path}")
+ end
+
+ it 'redirects to private project page after sign in' do
+ visit project_path(private_project)
+
+ owner = private_project.owner
+ fill_in 'user_login', with: owner.email
+ fill_in 'user_password', with: owner.password
+ click_button 'Sign in'
+
+ expect(status_code).to eq(200)
+ expect(current_path).to eq("/#{private_project.full_path}")
+ end
+
+ context 'when signed in' do
+ before do
+ sign_in(user)
+ end
+
+ it 'returns 404 status when project does not exist' do
+ visit project_path(build(:project, :public))
+
+ expect(status_code).to eq(404)
+ end
+
+ it 'returns 404 when project is private' do
+ visit project_path(private_project)
+
+ expect(status_code).to eq(404)
+ end
+ end
+end
diff --git a/spec/javascripts/vue_mr_widget/components/deployment_spec.js b/spec/javascripts/vue_mr_widget/components/deployment_spec.js
new file mode 100644
index 00000000000..ff8d54c029f
--- /dev/null
+++ b/spec/javascripts/vue_mr_widget/components/deployment_spec.js
@@ -0,0 +1,172 @@
+import Vue from 'vue';
+import * as urlUtils from '~/lib/utils/url_utility';
+import deploymentComponent from '~/vue_merge_request_widget/components/deployment.vue';
+import MRWidgetService from '~/vue_merge_request_widget/services/mr_widget_service';
+import { getTimeago } from '~/lib/utils/datetime_utility';
+
+const deploymentMockData = {
+ id: 15,
+ name: 'review/diplo',
+ url: '/root/acets-review-apps/environments/15',
+ stop_url: '/root/acets-review-apps/environments/15/stop',
+ metrics_url: '/root/acets-review-apps/environments/15/deployments/1/metrics',
+ metrics_monitoring_url: '/root/acets-review-apps/environments/15/metrics',
+ external_url: 'http://diplo.',
+ external_url_formatted: 'diplo.',
+ deployed_at: '2017-03-22T22:44:42.258Z',
+ deployed_at_formatted: 'Mar 22, 2017 10:44pm',
+};
+const createComponent = () => {
+ const Component = Vue.extend(deploymentComponent);
+
+ return new Component({
+ el: document.createElement('div'),
+ propsData: { deployment: { ...deploymentMockData } },
+ });
+};
+
+describe('Deployment component', () => {
+ let vm;
+
+ beforeEach(() => {
+ vm = createComponent();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('computed', () => {
+ describe('deployTimeago', () => {
+ it('return formatted date', () => {
+ const readable = getTimeago().format(deploymentMockData.deployed_at);
+ expect(vm.deployTimeago).toEqual(readable);
+ });
+ });
+
+ describe('hasExternalUrls', () => {
+ it('should return true', () => {
+ expect(vm.hasExternalUrls).toEqual(true);
+ });
+
+ it('should return false when deployment has no external_url_formatted', () => {
+ vm.deployment.external_url_formatted = null;
+
+ expect(vm.hasExternalUrls).toEqual(false);
+ });
+
+ it('should return false when deployment has no external_url', () => {
+ vm.deployment.external_url = null;
+
+ expect(vm.hasExternalUrls).toEqual(false);
+ });
+ });
+
+ describe('hasDeploymentTime', () => {
+ it('should return true', () => {
+ expect(vm.hasDeploymentTime).toEqual(true);
+ });
+
+ it('should return false when deployment has no deployed_at', () => {
+ vm.deployment.deployed_at = null;
+
+ expect(vm.hasDeploymentTime).toEqual(false);
+ });
+
+ it('should return false when deployment has no deployed_at_formatted', () => {
+ vm.deployment.deployed_at_formatted = null;
+
+ expect(vm.hasDeploymentTime).toEqual(false);
+ });
+ });
+
+ describe('hasDeploymentMeta', () => {
+ it('should return true', () => {
+ expect(vm.hasDeploymentMeta).toEqual(true);
+ });
+
+ it('should return false when deployment has no url', () => {
+ vm.deployment.url = null;
+
+ expect(vm.hasDeploymentMeta).toEqual(false);
+ });
+
+ it('should return false when deployment has no name', () => {
+ vm.deployment.name = null;
+
+ expect(vm.hasDeploymentMeta).toEqual(false);
+ });
+ });
+ });
+
+ describe('methods', () => {
+ describe('stopEnvironment', () => {
+ const url = '/foo/bar';
+ const returnPromise = () => new Promise((resolve) => {
+ resolve({
+ data: {
+ redirect_url: url,
+ },
+ });
+ });
+ const mockStopEnvironment = () => {
+ vm.stopEnvironment(deploymentMockData);
+ return vm;
+ };
+
+ it('should show a confirm dialog and call service.stopEnvironment when confirmed', (done) => {
+ spyOn(window, 'confirm').and.returnValue(true);
+ spyOn(MRWidgetService, 'stopEnvironment').and.returnValue(returnPromise(true));
+ spyOn(urlUtils, 'visitUrl').and.returnValue(true);
+ vm = mockStopEnvironment();
+
+ expect(window.confirm).toHaveBeenCalled();
+ expect(MRWidgetService.stopEnvironment).toHaveBeenCalledWith(deploymentMockData.stop_url);
+ setTimeout(() => {
+ expect(urlUtils.visitUrl).toHaveBeenCalledWith(url);
+ done();
+ }, 333);
+ });
+
+ it('should show a confirm dialog but should not work if the dialog is rejected', () => {
+ spyOn(window, 'confirm').and.returnValue(false);
+ spyOn(MRWidgetService, 'stopEnvironment').and.returnValue(returnPromise(false));
+ vm = mockStopEnvironment();
+
+ expect(window.confirm).toHaveBeenCalled();
+ expect(MRWidgetService.stopEnvironment).not.toHaveBeenCalled();
+ });
+ });
+ });
+
+ describe('template', () => {
+ let el;
+
+ beforeEach(() => {
+ vm = createComponent(deploymentMockData);
+ el = vm.$el;
+ });
+
+ it('renders deployment name', () => {
+ expect(el.querySelector('.js-deploy-meta').getAttribute('href')).toEqual(deploymentMockData.url);
+ expect(el.querySelector('.js-deploy-meta').innerText).toContain(deploymentMockData.name);
+ });
+
+ it('renders external URL', () => {
+ expect(el.querySelector('.js-deploy-url').getAttribute('href')).toEqual(deploymentMockData.external_url);
+ expect(el.querySelector('.js-deploy-url').innerText).toContain(deploymentMockData.external_url_formatted);
+ });
+
+ it('renders stop button', () => {
+ expect(el.querySelector('.btn')).not.toBeNull();
+ });
+
+ it('renders deployment time', () => {
+ expect(el.querySelector('.js-deploy-time').innerText).toContain(vm.deployTimeago);
+ });
+
+ it('renders metrics component', () => {
+ expect(el.querySelector('.js-mr-memory-usage')).not.toBeNull();
+ });
+ });
+});
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_deployment_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_deployment_spec.js
deleted file mode 100644
index 6a59dc3c87e..00000000000
--- a/spec/javascripts/vue_mr_widget/components/mr_widget_deployment_spec.js
+++ /dev/null
@@ -1,179 +0,0 @@
-import Vue from 'vue';
-import * as urlUtils from '~/lib/utils/url_utility';
-import deploymentComponent from '~/vue_merge_request_widget/components/mr_widget_deployment';
-import MRWidgetService from '~/vue_merge_request_widget/services/mr_widget_service';
-import { getTimeago } from '~/lib/utils/datetime_utility';
-
-const deploymentMockData = [
- {
- id: 15,
- name: 'review/diplo',
- url: '/root/acets-review-apps/environments/15',
- stop_url: '/root/acets-review-apps/environments/15/stop',
- metrics_url: '/root/acets-review-apps/environments/15/deployments/1/metrics',
- metrics_monitoring_url: '/root/acets-review-apps/environments/15/metrics',
- external_url: 'http://diplo.',
- external_url_formatted: 'diplo.',
- deployed_at: '2017-03-22T22:44:42.258Z',
- deployed_at_formatted: 'Mar 22, 2017 10:44pm',
- },
-];
-const createComponent = () => {
- const Component = Vue.extend(deploymentComponent);
- const mr = {
- deployments: deploymentMockData,
- };
- const service = {};
-
- return new Component({
- el: document.createElement('div'),
- propsData: { mr, service },
- });
-};
-
-describe('MRWidgetDeployment', () => {
- describe('props', () => {
- it('should have props', () => {
- const { mr, service } = deploymentComponent.props;
-
- expect(mr.type instanceof Object).toBeTruthy();
- expect(mr.required).toBeTruthy();
-
- expect(service.type instanceof Object).toBeTruthy();
- expect(service.required).toBeTruthy();
- });
- });
-
- describe('methods', () => {
- let vm = createComponent();
- const deployment = deploymentMockData[0];
-
- describe('formatDate', () => {
- it('should work', () => {
- const readable = getTimeago().format(deployment.deployed_at);
- expect(vm.formatDate(deployment.deployed_at)).toEqual(readable);
- });
- });
-
- describe('hasExternalUrls', () => {
- it('should return true', () => {
- expect(vm.hasExternalUrls(deployment)).toBeTruthy();
- });
-
- it('should return false when there is not enough information', () => {
- expect(vm.hasExternalUrls()).toBeFalsy();
- expect(vm.hasExternalUrls({ external_url: 'Diplo' })).toBeFalsy();
- expect(vm.hasExternalUrls({ external_url_formatted: 'Diplo' })).toBeFalsy();
- });
- });
-
- describe('hasDeploymentTime', () => {
- it('should return true', () => {
- expect(vm.hasDeploymentTime(deployment)).toBeTruthy();
- });
-
- it('should return false when there is not enough information', () => {
- expect(vm.hasDeploymentTime()).toBeFalsy();
- expect(vm.hasDeploymentTime({ deployed_at: 'Diplo' })).toBeFalsy();
- expect(vm.hasDeploymentTime({ deployed_at_formatted: 'Diplo' })).toBeFalsy();
- });
- });
-
- describe('hasDeploymentMeta', () => {
- it('should return true', () => {
- expect(vm.hasDeploymentMeta(deployment)).toBeTruthy();
- });
-
- it('should return false when there is not enough information', () => {
- expect(vm.hasDeploymentMeta()).toBeFalsy();
- expect(vm.hasDeploymentMeta({ url: 'Diplo' })).toBeFalsy();
- expect(vm.hasDeploymentMeta({ name: 'Diplo' })).toBeFalsy();
- });
- });
-
- describe('stopEnvironment', () => {
- const url = '/foo/bar';
- const returnPromise = () => new Promise((resolve) => {
- resolve({
- data: {
- redirect_url: url,
- },
- });
- });
- const mockStopEnvironment = () => {
- vm.stopEnvironment(deploymentMockData);
- return vm;
- };
-
- it('should show a confirm dialog and call service.stopEnvironment when confirmed', (done) => {
- spyOn(window, 'confirm').and.returnValue(true);
- spyOn(MRWidgetService, 'stopEnvironment').and.returnValue(returnPromise(true));
- spyOn(urlUtils, 'visitUrl').and.returnValue(true);
- vm = mockStopEnvironment();
-
- expect(window.confirm).toHaveBeenCalled();
- expect(MRWidgetService.stopEnvironment).toHaveBeenCalledWith(deploymentMockData.stop_url);
- setTimeout(() => {
- expect(urlUtils.visitUrl).toHaveBeenCalledWith(url);
- done();
- }, 333);
- });
-
- it('should show a confirm dialog but should not work if the dialog is rejected', () => {
- spyOn(window, 'confirm').and.returnValue(false);
- spyOn(MRWidgetService, 'stopEnvironment').and.returnValue(returnPromise(false));
- vm = mockStopEnvironment();
-
- expect(window.confirm).toHaveBeenCalled();
- expect(MRWidgetService.stopEnvironment).not.toHaveBeenCalled();
- });
- });
- });
-
- describe('template', () => {
- let vm;
- let el;
- const [deployment] = deploymentMockData;
-
- beforeEach(() => {
- vm = createComponent(deploymentMockData);
- el = vm.$el;
- });
-
- it('should render template elements correctly', () => {
- expect(el.classList.contains('mr-widget-heading')).toBeTruthy();
- expect(el.querySelector('.js-icon-link')).toBeDefined();
- expect(el.querySelector('.js-deploy-meta').getAttribute('href')).toEqual(deployment.url);
- expect(el.querySelector('.js-deploy-meta').innerText).toContain(deployment.name);
- expect(el.querySelector('.js-deploy-url').getAttribute('href')).toEqual(deployment.external_url);
- expect(el.querySelector('.js-deploy-url').innerText).toContain(deployment.external_url_formatted);
- expect(el.querySelector('.js-deploy-time').innerText).toContain(vm.formatDate(deployment.deployed_at));
- expect(el.querySelector('.js-mr-memory-usage')).toBeDefined();
- expect(el.querySelector('button')).toBeDefined();
- });
-
- it('should list multiple deployments', (done) => {
- vm.mr.deployments.push(deployment);
- vm.mr.deployments.push(deployment);
-
- Vue.nextTick(() => {
- expect(el.querySelectorAll('.ci-widget').length).toEqual(3);
- expect(el.querySelectorAll('.js-mr-memory-usage').length).toEqual(3);
- done();
- });
- });
-
- it('should not have some elements when there is not enough data', (done) => {
- vm.mr.deployments = [{}];
-
- Vue.nextTick(() => {
- expect(el.querySelectorAll('.js-deploy-meta').length).toEqual(0);
- expect(el.querySelectorAll('.js-deploy-url').length).toEqual(0);
- expect(el.querySelectorAll('.js-deploy-time').length).toEqual(0);
- expect(el.querySelectorAll('.js-mr-memory-usage').length).toEqual(0);
- expect(el.querySelectorAll('.button').length).toEqual(0);
- done();
- });
- });
- });
-});
diff --git a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js
index 32876ca66ac..e55c7649d40 100644
--- a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js
+++ b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js
@@ -123,17 +123,6 @@ describe('mrWidgetOptions', () => {
expect(vm.shouldRenderSourceBranchRemovalStatus).toEqual(false);
});
});
-
- describe('shouldRenderDeployments', () => {
- it('should return false for the initial data', () => {
- expect(vm.shouldRenderDeployments).toBeFalsy();
- });
-
- it('should return true if there is deployments', () => {
- vm.mr.deployments.push({}, {});
- expect(vm.shouldRenderDeployments).toBeTruthy();
- });
- });
});
describe('methods', () => {
@@ -189,16 +178,16 @@ describe('mrWidgetOptions', () => {
describe('fetchDeployments', () => {
it('should fetch deployments', (done) => {
- spyOn(vm.service, 'fetchDeployments').and.returnValue(returnPromise([{ deployment: 1 }]));
+ spyOn(vm.service, 'fetchDeployments').and.returnValue(returnPromise([{ id: 1 }]));
vm.fetchDeployments();
setTimeout(() => {
expect(vm.service.fetchDeployments).toHaveBeenCalled();
expect(vm.mr.deployments.length).toEqual(1);
- expect(vm.mr.deployments[0].deployment).toEqual(1);
+ expect(vm.mr.deployments[0].id).toBe(1);
done();
- }, 333);
+ });
});
});
@@ -368,34 +357,6 @@ describe('mrWidgetOptions', () => {
});
});
- describe('components', () => {
- it('should register all components', () => {
- const comps = mrWidgetOptions.components;
- expect(comps['mr-widget-header']).toBeDefined();
- expect(comps['mr-widget-merge-help']).toBeDefined();
- expect(comps['mr-widget-pipeline']).toBeDefined();
- expect(comps['mr-widget-deployment']).toBeDefined();
- expect(comps['mr-widget-related-links']).toBeDefined();
- expect(comps['mr-widget-merged']).toBeDefined();
- expect(comps['mr-widget-closed']).toBeDefined();
- expect(comps['mr-widget-merging']).toBeDefined();
- expect(comps['mr-widget-failed-to-merge']).toBeDefined();
- expect(comps['mr-widget-wip']).toBeDefined();
- expect(comps['mr-widget-archived']).toBeDefined();
- expect(comps['mr-widget-conflicts']).toBeDefined();
- expect(comps['mr-widget-nothing-to-merge']).toBeDefined();
- expect(comps['mr-widget-not-allowed']).toBeDefined();
- expect(comps['mr-widget-missing-branch']).toBeDefined();
- expect(comps['mr-widget-ready-to-merge']).toBeDefined();
- expect(comps['mr-widget-checking']).toBeDefined();
- expect(comps['mr-widget-unresolved-discussions']).toBeDefined();
- expect(comps['mr-widget-pipeline-blocked']).toBeDefined();
- expect(comps['mr-widget-pipeline-failed']).toBeDefined();
- expect(comps['mr-widget-merge-when-pipeline-succeeds']).toBeDefined();
- expect(comps['mr-widget-maintainer-edit']).toBeDefined();
- });
- });
-
describe('rendering relatedLinks', () => {
beforeEach((done) => {
vm.mr.relatedLinks = {
@@ -454,4 +415,34 @@ describe('mrWidgetOptions', () => {
});
});
});
+
+ describe('rendering deployments', () => {
+ const deploymentMockData = {
+ id: 15,
+ name: 'review/diplo',
+ url: '/root/acets-review-apps/environments/15',
+ stop_url: '/root/acets-review-apps/environments/15/stop',
+ metrics_url: '/root/acets-review-apps/environments/15/deployments/1/metrics',
+ metrics_monitoring_url: '/root/acets-review-apps/environments/15/metrics',
+ external_url: 'http://diplo.',
+ external_url_formatted: 'diplo.',
+ deployed_at: '2017-03-22T22:44:42.258Z',
+ deployed_at_formatted: 'Mar 22, 2017 10:44pm',
+ };
+
+ beforeEach((done) => {
+ vm.mr.deployments.push({
+ ...deploymentMockData,
+ }, {
+ ...deploymentMockData,
+ id: deploymentMockData.id + 1,
+ });
+
+ vm.$nextTick(done);
+ });
+
+ it('renders multiple deployments', () => {
+ expect(vm.$el.querySelectorAll('.deploy-heading').length).toBe(2);
+ });
+ });
});
diff --git a/spec/lib/gitlab/bare_repository_import/repository_spec.rb b/spec/lib/gitlab/bare_repository_import/repository_spec.rb
index 9f42cf1dfca..5cb1f4deb5f 100644
--- a/spec/lib/gitlab/bare_repository_import/repository_spec.rb
+++ b/spec/lib/gitlab/bare_repository_import/repository_spec.rb
@@ -61,7 +61,7 @@ describe ::Gitlab::BareRepositoryImport::Repository do
let(:wiki_path) { File.join(root_path, "#{hashed_path}.wiki.git") }
before do
- gitlab_shell.add_repository(repository_storage, hashed_path)
+ gitlab_shell.create_repository(repository_storage, hashed_path)
repository = Rugged::Repository.new(repo_path)
repository.config['gitlab.fullpath'] = 'to/repo'
end
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index 52c9876cbb6..54ada3e423f 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -681,7 +681,7 @@ describe Gitlab::Git::Repository, seed_helper: true do
subject { new_repository.fetch_repository_as_mirror(repository) }
before do
- Gitlab::Shell.new.add_repository('default', 'my_project')
+ Gitlab::Shell.new.create_repository('default', 'my_project')
end
after do
diff --git a/spec/lib/gitlab/shell_spec.rb b/spec/lib/gitlab/shell_spec.rb
index 56b45d8da3c..14b59c5e945 100644
--- a/spec/lib/gitlab/shell_spec.rb
+++ b/spec/lib/gitlab/shell_spec.rb
@@ -20,7 +20,7 @@ describe Gitlab::Shell do
it { is_expected.to respond_to :add_key }
it { is_expected.to respond_to :remove_key }
- it { is_expected.to respond_to :add_repository }
+ it { is_expected.to respond_to :create_repository }
it { is_expected.to respond_to :remove_repository }
it { is_expected.to respond_to :fork_repository }
@@ -402,8 +402,8 @@ describe Gitlab::Shell do
allow(Gitlab.config.gitlab_shell).to receive(:git_timeout).and_return(800)
end
- describe '#add_repository' do
- shared_examples '#add_repository' do
+ describe '#create_repository' do
+ shared_examples '#create_repository' do
let(:repository_storage) { 'default' }
let(:repository_storage_path) { Gitlab.config.repositories.storages[repository_storage]['path'] }
let(:repo_name) { 'project/path' }
@@ -414,7 +414,7 @@ describe Gitlab::Shell do
end
it 'creates a repository' do
- expect(gitlab_shell.add_repository(repository_storage, repo_name)).to be_truthy
+ expect(gitlab_shell.create_repository(repository_storage, repo_name)).to be_truthy
expect(File.stat(created_path).mode & 0o777).to eq(0o770)
@@ -426,19 +426,19 @@ describe Gitlab::Shell do
it 'returns false when the command fails' do
FileUtils.mkdir_p(File.dirname(created_path))
# This file will block the creation of the repo's .git directory. That
- # should cause #add_repository to fail.
+ # should cause #create_repository to fail.
FileUtils.touch(created_path)
- expect(gitlab_shell.add_repository(repository_storage, repo_name)).to be_falsy
+ expect(gitlab_shell.create_repository(repository_storage, repo_name)).to be_falsy
end
end
context 'with gitaly' do
- it_behaves_like '#add_repository'
+ it_behaves_like '#create_repository'
end
context 'without gitaly', :skip_gitaly_mock do
- it_behaves_like '#add_repository'
+ it_behaves_like '#create_repository'
end
end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index c27313ed88b..6e202de0db9 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -1424,6 +1424,17 @@ describe Ci::Build do
{ key: 'CI_COMMIT_SHA', value: build.sha, public: true },
{ key: 'CI_COMMIT_REF_NAME', value: build.ref, public: true },
{ key: 'CI_COMMIT_REF_SLUG', value: build.ref_slug, public: true },
+ { key: 'CI_REGISTRY_USER', value: 'gitlab-ci-token', public: true },
+ { key: 'CI_REGISTRY_PASSWORD', value: build.token, public: false },
+ { key: 'CI_REPOSITORY_URL', value: build.repo_url, public: false },
+ { key: 'CI_BUILD_ID', value: build.id.to_s, public: true },
+ { key: 'CI_BUILD_TOKEN', value: build.token, public: false },
+ { key: 'CI_BUILD_REF', value: build.sha, public: true },
+ { key: 'CI_BUILD_BEFORE_SHA', value: build.before_sha, public: true },
+ { key: 'CI_BUILD_REF_NAME', value: build.ref, public: true },
+ { key: 'CI_BUILD_REF_SLUG', value: build.ref_slug, public: true },
+ { key: 'CI_BUILD_NAME', value: 'test', public: true },
+ { key: 'CI_BUILD_STAGE', value: 'test', public: true },
{ key: 'CI_PROJECT_ID', value: project.id.to_s, public: true },
{ key: 'CI_PROJECT_NAME', value: project.path, public: true },
{ key: 'CI_PROJECT_PATH', value: project.full_path, public: true },
@@ -1433,9 +1444,7 @@ describe Ci::Build do
{ key: 'CI_PROJECT_VISIBILITY', value: 'private', public: true },
{ key: 'CI_PIPELINE_ID', value: pipeline.id.to_s, public: true },
{ key: 'CI_CONFIG_PATH', value: pipeline.ci_yaml_file_path, public: true },
- { key: 'CI_REGISTRY_USER', value: 'gitlab-ci-token', public: true },
- { key: 'CI_REGISTRY_PASSWORD', value: build.token, public: false },
- { key: 'CI_REPOSITORY_URL', value: build.repo_url, public: false }
+ { key: 'CI_PIPELINE_SOURCE', value: pipeline.source, public: true }
]
end
@@ -1834,39 +1843,6 @@ describe Ci::Build do
it { is_expected.to include(ci_config_path) }
end
- context 'returns variables in valid order' do
- let(:build_pre_var) { { key: 'build', value: 'value' } }
- let(:project_pre_var) { { key: 'project', value: 'value' } }
- let(:pipeline_pre_var) { { key: 'pipeline', value: 'value' } }
- let(:build_yaml_var) { { key: 'yaml', value: 'value' } }
-
- before do
- allow(build).to receive(:predefined_variables) { [build_pre_var] }
- allow(build).to receive(:yaml_variables) { [build_yaml_var] }
-
- allow_any_instance_of(Project)
- .to receive(:predefined_variables) { [project_pre_var] }
-
- allow_any_instance_of(Project)
- .to receive(:secret_variables_for)
- .with(ref: 'master', environment: nil) do
- [create(:ci_variable, key: 'secret', value: 'value')]
- end
-
- allow_any_instance_of(Ci::Pipeline)
- .to receive(:predefined_variables) { [pipeline_pre_var] }
- end
-
- it do
- is_expected.to eq(
- [build_pre_var,
- project_pre_var,
- pipeline_pre_var,
- build_yaml_var,
- { key: 'secret', value: 'value', public: false }])
- end
- end
-
context 'when using auto devops' do
context 'and is enabled' do
before do
@@ -1890,6 +1866,81 @@ describe Ci::Build do
end
end
end
+
+ context 'when pipeline variable overrides build variable' do
+ before do
+ build.yaml_variables = [{ key: 'MYVAR', value: 'myvar', public: true }]
+ pipeline.variables.build(key: 'MYVAR', value: 'pipeline value')
+ end
+
+ it 'overrides YAML variable using a pipeline variable' do
+ variables = subject.reverse.uniq { |variable| variable[:key] }.reverse
+
+ expect(variables)
+ .not_to include(key: 'MYVAR', value: 'myvar', public: true)
+ expect(variables)
+ .to include(key: 'MYVAR', value: 'pipeline value', public: false)
+ end
+ end
+
+ describe 'variables ordering' do
+ context 'when variables hierarchy is stubbed' do
+ let(:build_pre_var) { { key: 'build', value: 'value' } }
+ let(:project_pre_var) { { key: 'project', value: 'value' } }
+ let(:pipeline_pre_var) { { key: 'pipeline', value: 'value' } }
+ let(:build_yaml_var) { { key: 'yaml', value: 'value' } }
+
+ before do
+ allow(build).to receive(:predefined_variables) { [build_pre_var] }
+ allow(build).to receive(:yaml_variables) { [build_yaml_var] }
+
+ allow_any_instance_of(Project)
+ .to receive(:predefined_variables) { [project_pre_var] }
+
+ allow_any_instance_of(Project)
+ .to receive(:secret_variables_for)
+ .with(ref: 'master', environment: nil) do
+ [create(:ci_variable, key: 'secret', value: 'value')]
+ end
+
+ allow_any_instance_of(Ci::Pipeline)
+ .to receive(:predefined_variables) { [pipeline_pre_var] }
+ end
+
+ it 'returns variables in order depending on resource hierarchy' do
+ is_expected.to eq(
+ [build_pre_var,
+ project_pre_var,
+ pipeline_pre_var,
+ build_yaml_var,
+ { key: 'secret', value: 'value', public: false }])
+ end
+ end
+
+ context 'when build has environment and user-provided variables' do
+ let(:expected_variables) do
+ predefined_variables.map { |variable| variable.fetch(:key) } +
+ %w[YAML_VARIABLE CI_ENVIRONMENT_NAME CI_ENVIRONMENT_SLUG
+ CI_ENVIRONMENT_URL]
+ end
+
+ before do
+ create(:environment, project: build.project,
+ name: 'staging')
+
+ build.yaml_variables = [{ key: 'YAML_VARIABLE',
+ value: 'var',
+ public: true }]
+ build.environment = 'staging'
+ end
+
+ it 'matches explicit variables ordering' do
+ received_variables = subject.map { |variable| variable.fetch(:key) }
+
+ expect(received_variables).to eq expected_variables
+ end
+ end
+ end
end
describe 'state transition: any => [:pending]' do
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 14d234f6aab..86bb2fefae1 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -172,10 +172,10 @@ describe Ci::Pipeline, :mailer do
it { is_expected.to be_an(Array) }
- it 'includes the defined keys' do
- keys = subject.map { |v| v[:key] }
+ it 'includes all predefined variables in a valid order' do
+ keys = subject.map { |variable| variable.fetch(:key) }
- expect(keys).to include('CI_PIPELINE_ID', 'CI_CONFIG_PATH', 'CI_PIPELINE_SOURCE')
+ expect(keys).to eq %w[CI_PIPELINE_ID CI_CONFIG_PATH CI_PIPELINE_SOURCE]
end
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index e970cd7dfdb..4cf8d861595 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -1378,7 +1378,7 @@ describe Project do
context 'using a regular repository' do
it 'creates the repository' do
- expect(shell).to receive(:add_repository)
+ expect(shell).to receive(:create_repository)
.with(project.repository_storage, project.disk_path)
.and_return(true)
@@ -1388,7 +1388,7 @@ describe Project do
end
it 'adds an error if the repository could not be created' do
- expect(shell).to receive(:add_repository)
+ expect(shell).to receive(:create_repository)
.with(project.repository_storage, project.disk_path)
.and_return(false)
@@ -1402,7 +1402,7 @@ describe Project do
context 'using a forked repository' do
it 'does nothing' do
expect(project).to receive(:forked?).and_return(true)
- expect(shell).not_to receive(:add_repository)
+ expect(shell).not_to receive(:create_repository)
project.create_repository
end
@@ -1421,7 +1421,7 @@ describe Project do
allow(project).to receive(:repository_exists?)
.and_return(false)
- allow(shell).to receive(:add_repository)
+ allow(shell).to receive(:create_repository)
.with(project.repository_storage_path, project.disk_path)
.and_return(true)
@@ -1445,7 +1445,7 @@ describe Project do
allow(project).to receive(:repository_exists?)
.and_return(false)
- expect(shell).to receive(:add_repository)
+ expect(shell).to receive(:create_repository)
.with(project.repository_storage, project.disk_path)
.and_return(true)
diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb
index 8b4b5873704..d87c1ca14f0 100644
--- a/spec/models/project_wiki_spec.rb
+++ b/spec/models/project_wiki_spec.rb
@@ -74,7 +74,7 @@ describe ProjectWiki do
# Create a fresh project which will not have a wiki
project_wiki = described_class.new(create(:project), user)
gitlab_shell = double(:gitlab_shell)
- allow(gitlab_shell).to receive(:add_repository)
+ allow(gitlab_shell).to receive(:create_repository)
allow(project_wiki).to receive(:gitlab_shell).and_return(gitlab_shell)
expect { project_wiki.send(:wiki) }.to raise_exception(ProjectWiki::CouldNotCreateWikiError)
diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb
index 9a44dfde41b..8471467d2fa 100644
--- a/spec/services/projects/create_service_spec.rb
+++ b/spec/services/projects/create_service_spec.rb
@@ -164,7 +164,7 @@ describe Projects::CreateService, '#execute' do
context 'with legacy storage' do
before do
- gitlab_shell.add_repository(repository_storage, "#{user.namespace.full_path}/existing")
+ gitlab_shell.create_repository(repository_storage, "#{user.namespace.full_path}/existing")
end
after do
@@ -200,7 +200,7 @@ describe Projects::CreateService, '#execute' do
end
before do
- gitlab_shell.add_repository(repository_storage, hashed_path)
+ gitlab_shell.create_repository(repository_storage, hashed_path)
end
after do
diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb
index 409d5de8d43..d1011b07db6 100644
--- a/spec/services/projects/fork_service_spec.rb
+++ b/spec/services/projects/fork_service_spec.rb
@@ -108,7 +108,7 @@ describe Projects::ForkService do
let(:repository_storage_path) { Gitlab.config.repositories.storages[repository_storage]['path'] }
before do
- gitlab_shell.add_repository(repository_storage, "#{@to_user.namespace.full_path}/#{@from_project.path}")
+ gitlab_shell.create_repository(repository_storage, "#{@to_user.namespace.full_path}/#{@from_project.path}")
end
after do
diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb
index ae0e22e3dc0..ce567fe3879 100644
--- a/spec/services/projects/transfer_service_spec.rb
+++ b/spec/services/projects/transfer_service_spec.rb
@@ -151,7 +151,7 @@ describe Projects::TransferService do
before do
group.add_owner(user)
- unless gitlab_shell.add_repository(repository_storage, "#{group.full_path}/#{project.path}")
+ unless gitlab_shell.create_repository(repository_storage, "#{group.full_path}/#{project.path}")
raise 'failed to add repository'
end
diff --git a/spec/services/projects/update_service_spec.rb b/spec/services/projects/update_service_spec.rb
index d454ac0bda5..f3f97b6b921 100644
--- a/spec/services/projects/update_service_spec.rb
+++ b/spec/services/projects/update_service_spec.rb
@@ -196,7 +196,7 @@ describe Projects::UpdateService do
let(:project) { create(:project, :legacy_storage, :repository, creator: user, namespace: user.namespace) }
before do
- gitlab_shell.add_repository(repository_storage, "#{user.namespace.full_path}/existing")
+ gitlab_shell.create_repository(repository_storage, "#{user.namespace.full_path}/existing")
end
after do