summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouwe Maan <douwe@gitlab.com>2015-10-14 12:44:28 +0200
committerDouwe Maan <douwe@gitlab.com>2015-10-14 13:23:01 +0200
commitd10e3c4498e11e97548b0212887a146d19d19ffd (patch)
tree40160307c143e1796e1dad5e55caf1b9d4a7cdfb
parent3a69dd20a1d8e54c25f871f35f09a1b8b5d4b2a4 (diff)
parent82da19cecd6390d2372bad36da0a9f89b21abb38 (diff)
downloadgitlab-ce-filter-label.tar.gz
Merge branch 'master' into filter-labelfilter-label
-rw-r--r--CHANGELOG7
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock4
-rw-r--r--app/assets/javascripts/line_highlighter.js.coffee6
-rw-r--r--app/assets/stylesheets/application.scss50
-rw-r--r--app/assets/stylesheets/framework.scss33
-rw-r--r--app/assets/stylesheets/framework/avatar.scss (renamed from app/assets/stylesheets/generic/avatar.scss)0
-rw-r--r--app/assets/stylesheets/framework/blocks.scss (renamed from app/assets/stylesheets/generic/blocks.scss)0
-rw-r--r--app/assets/stylesheets/framework/buttons.scss (renamed from app/assets/stylesheets/generic/buttons.scss)10
-rw-r--r--app/assets/stylesheets/framework/calendar.scss (renamed from app/assets/stylesheets/generic/calendar.scss)0
-rw-r--r--app/assets/stylesheets/framework/callout.scss (renamed from app/assets/stylesheets/generic/callout.scss)0
-rw-r--r--app/assets/stylesheets/framework/common.scss (renamed from app/assets/stylesheets/generic/common.scss)4
-rw-r--r--app/assets/stylesheets/framework/files.scss (renamed from app/assets/stylesheets/generic/files.scss)0
-rw-r--r--app/assets/stylesheets/framework/filters.scss (renamed from app/assets/stylesheets/generic/filters.scss)0
-rw-r--r--app/assets/stylesheets/framework/flash.scss (renamed from app/assets/stylesheets/generic/flash.scss)0
-rw-r--r--app/assets/stylesheets/framework/fonts.scss (renamed from app/assets/stylesheets/base/fonts.scss)0
-rw-r--r--app/assets/stylesheets/framework/forms.scss (renamed from app/assets/stylesheets/generic/forms.scss)0
-rw-r--r--app/assets/stylesheets/framework/gfm.scss (renamed from app/assets/stylesheets/generic/gfm.scss)1
-rw-r--r--app/assets/stylesheets/framework/gitlab-theme.scss (renamed from app/assets/stylesheets/themes/gitlab-theme.scss)0
-rw-r--r--app/assets/stylesheets/framework/header.scss (renamed from app/assets/stylesheets/generic/header.scss)0
-rw-r--r--app/assets/stylesheets/framework/highlight.scss (renamed from app/assets/stylesheets/generic/highlight.scss)0
-rw-r--r--app/assets/stylesheets/framework/issue_box.scss (renamed from app/assets/stylesheets/generic/issue_box.scss)0
-rw-r--r--app/assets/stylesheets/framework/jquery.scss (renamed from app/assets/stylesheets/generic/jquery.scss)0
-rw-r--r--app/assets/stylesheets/framework/layout.scss (renamed from app/assets/stylesheets/base/layout.scss)0
-rw-r--r--app/assets/stylesheets/framework/lists.scss (renamed from app/assets/stylesheets/generic/lists.scss)6
-rw-r--r--app/assets/stylesheets/framework/markdown_area.scss (renamed from app/assets/stylesheets/generic/markdown_area.scss)0
-rw-r--r--app/assets/stylesheets/framework/mixins.scss (renamed from app/assets/stylesheets/base/mixins.scss)141
-rw-r--r--app/assets/stylesheets/framework/mobile.scss (renamed from app/assets/stylesheets/generic/mobile.scss)14
-rw-r--r--app/assets/stylesheets/framework/pagination.scss (renamed from app/assets/stylesheets/generic/pagination.scss)0
-rw-r--r--app/assets/stylesheets/framework/selects.scss (renamed from app/assets/stylesheets/generic/selects.scss)0
-rw-r--r--app/assets/stylesheets/framework/sidebar.scss (renamed from app/assets/stylesheets/generic/sidebar.scss)0
-rw-r--r--app/assets/stylesheets/framework/tables.scss (renamed from app/assets/stylesheets/generic/tables.scss)18
-rw-r--r--app/assets/stylesheets/framework/timeline.scss (renamed from app/assets/stylesheets/generic/timeline.scss)0
-rw-r--r--app/assets/stylesheets/framework/tw_bootstrap.scss (renamed from app/assets/stylesheets/base/gl_bootstrap.scss)22
-rw-r--r--app/assets/stylesheets/framework/tw_bootstrap_variables.scss (renamed from app/assets/stylesheets/base/gl_variables.scss)2
-rw-r--r--app/assets/stylesheets/framework/typography.scss271
-rw-r--r--app/assets/stylesheets/framework/variables.scss (renamed from app/assets/stylesheets/base/variables.scss)1
-rw-r--r--app/assets/stylesheets/framework/zen.scss (renamed from app/assets/stylesheets/generic/zen.scss)0
-rw-r--r--app/assets/stylesheets/generic/typography.scss130
-rw-r--r--app/assets/stylesheets/pages/builds.scss (renamed from app/assets/stylesheets/ci/builds.scss)0
-rw-r--r--app/assets/stylesheets/pages/ci_projects.scss (renamed from app/assets/stylesheets/ci/projects.scss)0
-rw-r--r--app/assets/stylesheets/pages/lint.scss (renamed from app/assets/stylesheets/ci/lint.scss)0
-rw-r--r--app/assets/stylesheets/pages/note_form.scss1
-rw-r--r--app/assets/stylesheets/pages/projects.scss6
-rw-r--r--app/assets/stylesheets/pages/runners.scss (renamed from app/assets/stylesheets/ci/runners.scss)0
-rw-r--r--app/assets/stylesheets/pages/status.scss (renamed from app/assets/stylesheets/ci/status.scss)0
-rw-r--r--app/assets/stylesheets/pages/tree.scss34
-rw-r--r--app/assets/stylesheets/pages/xterm.scss (renamed from app/assets/stylesheets/ci/xterm.scss)0
-rw-r--r--app/controllers/groups_controller.rb2
-rw-r--r--app/models/ability.rb2
-rw-r--r--app/models/ci/build.rb97
-rw-r--r--app/models/ci/commit.rb94
-rw-r--r--app/models/commit.rb8
-rw-r--r--app/models/commit_status.rb91
-rw-r--r--app/models/generic_commit_status.rb15
-rw-r--r--app/models/merge_request.rb5
-rw-r--r--app/models/project_services/bamboo_service.rb7
-rw-r--r--app/models/project_services/ci/hip_chat_service.rb2
-rw-r--r--app/models/project_services/ci/mail_service.rb2
-rw-r--r--app/models/project_services/ci/slack_message.rb2
-rw-r--r--app/models/project_services/ci/slack_service.rb2
-rw-r--r--app/models/project_services/teamcity_service.rb7
-rw-r--r--app/models/service.rb9
-rw-r--r--app/services/ci/create_commit_service.rb6
-rw-r--r--app/views/admin/users/index.html.haml2
-rw-r--r--app/views/ci/admin/runners/index.html.haml2
-rw-r--r--app/views/ci/admin/runners/show.html.haml2
-rw-r--r--app/views/dashboard/_activities.html.haml7
-rw-r--r--app/views/dashboard/projects/_projects.html.haml2
-rw-r--r--app/views/explore/groups/index.html.haml2
-rw-r--r--app/views/explore/projects/_filter.html.haml2
-rw-r--r--app/views/groups/_projects.html.haml4
-rw-r--r--app/views/groups/group_members/index.html.haml2
-rw-r--r--app/views/groups/show.html.haml8
-rw-r--r--app/views/layouts/_page.html.haml3
-rw-r--r--app/views/layouts/_search.html.haml2
-rw-r--r--app/views/projects/_activity.html.haml7
-rw-r--r--app/views/projects/blob/_blob.html.haml2
-rw-r--r--app/views/projects/builds/_build.html.haml50
-rw-r--r--app/views/projects/builds/show.html.haml4
-rw-r--r--app/views/projects/buttons/_notifications.html.haml2
-rw-r--r--app/views/projects/commit/ci.html.haml49
-rw-r--r--app/views/projects/commit_statuses/_commit_status.html.haml51
-rw-r--r--app/views/projects/new.html.haml4
-rw-r--r--app/views/projects/project_members/index.html.haml2
-rw-r--r--app/views/projects/repositories/_download_archive.html.haml4
-rw-r--r--app/views/projects/show.html.haml7
-rw-r--r--app/views/projects/tree/_readme.html.haml13
-rw-r--r--app/views/projects/tree/_tree.html.haml106
-rw-r--r--app/views/projects/wikis/pages.html.haml1
-rw-r--r--app/views/search/_form.html.haml2
-rw-r--r--app/views/shared/issuable/_search_form.html.haml2
-rw-r--r--app/views/shared/projects/_list.html.haml3
-rw-r--r--app/views/shared/projects/_project.html.haml3
-rw-r--r--config/mail_room.yml.example12
-rw-r--r--db/migrate/20151008123042_add_type_and_description_to_builds.rb9
-rw-r--r--db/migrate/20151008130321_migrate_name_to_description_for_builds.rb5
-rw-r--r--db/schema.rb7
-rw-r--r--doc/api/commits.md84
-rw-r--r--doc/api/merge_requests.md2
-rw-r--r--doc/customization/welcome_message.md30
-rw-r--r--doc/incoming_email/README.md34
-rw-r--r--doc/update/patch_versions.md6
-rw-r--r--doc/web_hooks/web_hooks.md6
-rw-r--r--features/steps/project/commits/commits.rb2
-rw-r--r--lib/api/api.rb1
-rw-r--r--lib/api/commit_statuses.rb80
-rw-r--r--lib/api/entities.rb7
-rw-r--r--lib/api/merge_requests.rb2
-rw-r--r--lib/ci/api/entities.rb4
-rw-r--r--lib/gitlab/ldap/user.rb2
-rw-r--r--spec/factories/ci/builds.rb1
-rw-r--r--spec/factories/commit_statuses.rb15
-rw-r--r--spec/features/commits_spec.rb1
-rw-r--r--spec/helpers/labels_helper_spec.rb5
-rw-r--r--spec/javascripts/fixtures/line_highlighter.html.haml2
-rw-r--r--spec/javascripts/line_highlighter_spec.js.coffee2
-rw-r--r--spec/lib/gitlab/ldap/user_spec.rb21
-rw-r--r--spec/models/build_spec.rb (renamed from spec/models/ci/build_spec.rb)111
-rw-r--r--spec/models/ci/commit_spec.rb64
-rw-r--r--spec/models/ci/project_services/mail_service_spec.rb12
-rw-r--r--spec/models/commit_status_spec.rb164
-rw-r--r--spec/models/generic_commit_status_spec.rb39
-rw-r--r--spec/models/merge_request_spec.rb11
-rw-r--r--spec/models/project_services/bamboo_service_spec.rb56
-rw-r--r--spec/models/project_services/teamcity_service_spec.rb56
-rw-r--r--spec/models/service_spec.rb23
-rw-r--r--spec/requests/api/commit_status_spec.rb135
-rw-r--r--spec/requests/api/commits_spec.rb13
129 files changed, 1630 insertions, 858 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 0a0afb4a053..b59d4b39e5a 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -16,6 +16,7 @@ v 8.1.0 (unreleased)
- Move CI charts to project graphs area
- Fix cases where Markdown did not render links in activity feed (Stan Hu)
- Add first and last to pagination (Zeger-Jan van de Weg)
+ - Added Commit Status API
- Show CI status on commit page
- Show CI status on Your projects page and Starred projects page
- Remove "Continuous Integration" page from dashboard
@@ -46,6 +47,11 @@ v 8.1.0 (unreleased)
- Fix bug where Emojis in Markdown would truncate remaining text (Sakata Sinji)
- Persist filters when sorting on admin user page (Jerry Lukins)
- Allow dashboard and group issues/MRs to be filtered by label
+ - Add spellcheck=false to certain input fields
+ - Invalidate stored service password if the endpoint URL is changed
+ - Project names are not fully shown if group name is too big, even on group page view
+ - Apply new design for Files page
+ - Add "New Page" button to Wiki Pages tab (Stan Hu)
v 8.0.4
- Fix Message-ID header to be RFC 2111-compliant to prevent e-mails being dropped (Stan Hu)
@@ -60,6 +66,7 @@ v 8.0.3
- Fix URL shown in Slack notifications
- Fix bug where projects would appear to be stuck in the forked import state (Stan Hu)
- Fix Error 500 in creating merge requests with > 1000 diffs (Stan Hu)
+ - Add work_in_progress key to MR web hooks (Ben Boeckel)
v 8.0.2
- Fix default avatar not rendering in network graph (Stan Hu)
diff --git a/Gemfile b/Gemfile
index e8cdb7a4a97..967092994a6 100644
--- a/Gemfile
+++ b/Gemfile
@@ -290,7 +290,7 @@ gem 'newrelic-grape'
gem 'octokit', '~> 3.7.0'
-gem "mail_room", "~> 0.5.2"
+gem "mail_room", "~> 0.6.0"
gem 'email_reply_parser', '~> 0.5.8'
diff --git a/Gemfile.lock b/Gemfile.lock
index 9911904d304..58426a60683 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -392,7 +392,7 @@ GEM
systemu (~> 2.6.2)
mail (2.6.3)
mime-types (>= 1.16, < 3)
- mail_room (0.5.2)
+ mail_room (0.6.0)
method_source (0.8.2)
mime-types (1.25.1)
mimemagic (0.3.0)
@@ -854,7 +854,7 @@ DEPENDENCIES
jquery-ui-rails (~> 4.2.1)
kaminari (~> 0.16.3)
letter_opener (~> 1.1.2)
- mail_room (~> 0.5.2)
+ mail_room (~> 0.6.0)
minitest (~> 5.7.0)
mousetrap-rails (~> 1.4.6)
mysql2 (~> 0.3.16)
diff --git a/app/assets/javascripts/line_highlighter.js.coffee b/app/assets/javascripts/line_highlighter.js.coffee
index e604e6025c2..2254a3f91ae 100644
--- a/app/assets/javascripts/line_highlighter.js.coffee
+++ b/app/assets/javascripts/line_highlighter.js.coffee
@@ -6,7 +6,7 @@
#
# ### Example Markup
#
-# <div id="tree-content-holder">
+# <div id="blob-content-holder">
# <div class="file-content">
# <div class="line-numbers">
# <a href="#L1" id="L1" data-line-number="1">1</a>
@@ -53,7 +53,7 @@ class @LineHighlighter
$.scrollTo("#L#{range[0]}", offset: -150)
bindEvents: ->
- $('#tree-content-holder').on 'mousedown', 'a[data-line-number]', @clickHandler
+ $('#blob-content-holder').on 'mousedown', 'a[data-line-number]', @clickHandler
# While it may seem odd to bind to the mousedown event and then throw away
# the click event, there is a method to our madness.
@@ -62,7 +62,7 @@ class @LineHighlighter
# active state even when the event is cancelled, resulting in an ugly border
# around the link and/or a persisted underline text decoration.
- $('#tree-content-holder').on 'click', 'a[data-line-number]', (event) ->
+ $('#blob-content-holder').on 'click', 'a[data-line-number]', (event) ->
event.preventDefault()
clickHandler: (event) =>
diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss
index d9ede637944..7b060ce4853 100644
--- a/app/assets/stylesheets/application.scss
+++ b/app/assets/stylesheets/application.scss
@@ -11,59 +11,41 @@
*= require cal-heatmap
*/
+/*
+ * Welcome to GitLab css!
+ * If you need to add or modify UI component that is common for many pages
+ * like a table or typography then make changes in the framework/ directory.
+ * If you need to add unique style that should affect only one page - use pages/
+ * directory.
+ */
-@import "base/fonts";
-@import "base/variables";
-@import "base/mixins";
-@import "base/layout";
-
-
-/**
- * Customized Twitter bootstrap
+/*
+ * GitLab UI framework
*/
-@import 'base/gl_variables';
-@import 'base/gl_bootstrap';
+@import "framework";
-/**
+/*
* NProgress load bar css
*/
@import 'nprogress';
@import 'nprogress-bootstrap';
-/**
+/*
* Font icons
- *
*/
@import "font-awesome";
-/**
- * UI themes:
- */
-@import "themes/**/*";
-
-/**
- * Generic css (forms, nav etc):
- */
-@import "generic/**/*";
-
-/**
+/*
* Page specific styles (issues, projects etc):
*/
-
@import "pages/**/*";
-/**
+/*
* Code highlight
*/
@import "highlight/**/*";
-/**
+/*
* Styles for JS behaviors.
*/
-@import "behaviors.scss";
-
-/**
- * CI specific styles:
- */
-@import "ci/**/*";
-
+@import "behaviors.scss"; \ No newline at end of file
diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss
new file mode 100644
index 00000000000..1ec9d2fd84f
--- /dev/null
+++ b/app/assets/stylesheets/framework.scss
@@ -0,0 +1,33 @@
+@import "framework/fonts";
+@import "framework/variables";
+@import "framework/mixins";
+@import "framework/layout";
+@import 'framework/tw_bootstrap_variables';
+@import 'framework/tw_bootstrap';
+
+@import "framework/avatar.scss";
+@import "framework/blocks.scss";
+@import "framework/buttons.scss";
+@import "framework/calendar.scss";
+@import "framework/callout.scss";
+@import "framework/common.scss";
+@import "framework/files.scss";
+@import "framework/filters.scss";
+@import "framework/flash.scss";
+@import "framework/forms.scss";
+@import "framework/gfm.scss";
+@import "framework/gitlab-theme.scss";
+@import "framework/header.scss";
+@import "framework/highlight.scss";
+@import "framework/issue_box.scss";
+@import "framework/jquery.scss";
+@import "framework/lists.scss";
+@import "framework/markdown_area.scss";
+@import "framework/mobile.scss";
+@import "framework/pagination.scss";
+@import "framework/selects.scss";
+@import "framework/sidebar.scss";
+@import "framework/tables.scss";
+@import "framework/timeline.scss";
+@import "framework/typography.scss";
+@import "framework/zen.scss";
diff --git a/app/assets/stylesheets/generic/avatar.scss b/app/assets/stylesheets/framework/avatar.scss
index 36e582d4854..36e582d4854 100644
--- a/app/assets/stylesheets/generic/avatar.scss
+++ b/app/assets/stylesheets/framework/avatar.scss
diff --git a/app/assets/stylesheets/generic/blocks.scss b/app/assets/stylesheets/framework/blocks.scss
index 6ce34b5c3e8..6ce34b5c3e8 100644
--- a/app/assets/stylesheets/generic/blocks.scss
+++ b/app/assets/stylesheets/framework/blocks.scss
diff --git a/app/assets/stylesheets/generic/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index 11acbe3adfa..e5f0c0ad9ef 100644
--- a/app/assets/stylesheets/generic/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -6,7 +6,7 @@
font-size: 13px;
font-weight: 600;
line-height: 18px;
- padding: 11px 16px;
+ padding: 11px $gl-padding;
letter-spacing: .4px;
&:focus,
@@ -71,6 +71,14 @@
@include btn-default;
@include btn-white;
+ &.btn-sm {
+ padding: 5px 10px;
+ }
+
+ &.btn-xs {
+ padding: 1px 5px;
+ }
+
&.btn-success,
&.btn-new,
&.btn-create,
diff --git a/app/assets/stylesheets/generic/calendar.scss b/app/assets/stylesheets/framework/calendar.scss
index a36fefe22c5..a36fefe22c5 100644
--- a/app/assets/stylesheets/generic/calendar.scss
+++ b/app/assets/stylesheets/framework/calendar.scss
diff --git a/app/assets/stylesheets/generic/callout.scss b/app/assets/stylesheets/framework/callout.scss
index f1699d21c9b..f1699d21c9b 100644
--- a/app/assets/stylesheets/generic/callout.scss
+++ b/app/assets/stylesheets/framework/callout.scss
diff --git a/app/assets/stylesheets/generic/common.scss b/app/assets/stylesheets/framework/common.scss
index 03919f15f1f..e1a1793be9c 100644
--- a/app/assets/stylesheets/generic/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -398,7 +398,3 @@ table {
.space-right {
margin-right: 10px;
}
-
-.in-line {
- display: inline-block;
-}
diff --git a/app/assets/stylesheets/generic/files.scss b/app/assets/stylesheets/framework/files.scss
index 9dd77747884..9dd77747884 100644
--- a/app/assets/stylesheets/generic/files.scss
+++ b/app/assets/stylesheets/framework/files.scss
diff --git a/app/assets/stylesheets/generic/filters.scss b/app/assets/stylesheets/framework/filters.scss
index 8e6922c9231..8e6922c9231 100644
--- a/app/assets/stylesheets/generic/filters.scss
+++ b/app/assets/stylesheets/framework/filters.scss
diff --git a/app/assets/stylesheets/generic/flash.scss b/app/assets/stylesheets/framework/flash.scss
index 82eb50ad4be..82eb50ad4be 100644
--- a/app/assets/stylesheets/generic/flash.scss
+++ b/app/assets/stylesheets/framework/flash.scss
diff --git a/app/assets/stylesheets/base/fonts.scss b/app/assets/stylesheets/framework/fonts.scss
index e214567eca1..e214567eca1 100644
--- a/app/assets/stylesheets/base/fonts.scss
+++ b/app/assets/stylesheets/framework/fonts.scss
diff --git a/app/assets/stylesheets/generic/forms.scss b/app/assets/stylesheets/framework/forms.scss
index 0edfe24f195..0edfe24f195 100644
--- a/app/assets/stylesheets/generic/forms.scss
+++ b/app/assets/stylesheets/framework/forms.scss
diff --git a/app/assets/stylesheets/generic/gfm.scss b/app/assets/stylesheets/framework/gfm.scss
index bd9200ace23..5ae0520fd7b 100644
--- a/app/assets/stylesheets/generic/gfm.scss
+++ b/app/assets/stylesheets/framework/gfm.scss
@@ -22,4 +22,5 @@
.gfm-commit, .gfm-commit_range {
font-family: $monospace_font;
+ font-size: 90%;
}
diff --git a/app/assets/stylesheets/themes/gitlab-theme.scss b/app/assets/stylesheets/framework/gitlab-theme.scss
index 8d9a0aae568..8d9a0aae568 100644
--- a/app/assets/stylesheets/themes/gitlab-theme.scss
+++ b/app/assets/stylesheets/framework/gitlab-theme.scss
diff --git a/app/assets/stylesheets/generic/header.scss b/app/assets/stylesheets/framework/header.scss
index 91e6975e269..91e6975e269 100644
--- a/app/assets/stylesheets/generic/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
diff --git a/app/assets/stylesheets/generic/highlight.scss b/app/assets/stylesheets/framework/highlight.scss
index 2e13ee842e0..2e13ee842e0 100644
--- a/app/assets/stylesheets/generic/highlight.scss
+++ b/app/assets/stylesheets/framework/highlight.scss
diff --git a/app/assets/stylesheets/generic/issue_box.scss b/app/assets/stylesheets/framework/issue_box.scss
index 93377e45e70..93377e45e70 100644
--- a/app/assets/stylesheets/generic/issue_box.scss
+++ b/app/assets/stylesheets/framework/issue_box.scss
diff --git a/app/assets/stylesheets/generic/jquery.scss b/app/assets/stylesheets/framework/jquery.scss
index 871b808bad4..871b808bad4 100644
--- a/app/assets/stylesheets/generic/jquery.scss
+++ b/app/assets/stylesheets/framework/jquery.scss
diff --git a/app/assets/stylesheets/base/layout.scss b/app/assets/stylesheets/framework/layout.scss
index c7b3b60e769..c7b3b60e769 100644
--- a/app/assets/stylesheets/base/layout.scss
+++ b/app/assets/stylesheets/framework/layout.scss
diff --git a/app/assets/stylesheets/generic/lists.scss b/app/assets/stylesheets/framework/lists.scss
index 3bfed8de772..c5764c36597 100644
--- a/app/assets/stylesheets/generic/lists.scss
+++ b/app/assets/stylesheets/framework/lists.scss
@@ -117,8 +117,12 @@ ul.content-list {
}
.controls {
- padding-top: 10px;
+ padding-top: 4px;
float: right;
+
+ .btn {
+ padding: 10px 14px;
+ }
}
}
}
diff --git a/app/assets/stylesheets/generic/markdown_area.scss b/app/assets/stylesheets/framework/markdown_area.scss
index ed0333d2336..ed0333d2336 100644
--- a/app/assets/stylesheets/generic/markdown_area.scss
+++ b/app/assets/stylesheets/framework/markdown_area.scss
diff --git a/app/assets/stylesheets/base/mixins.scss b/app/assets/stylesheets/framework/mixins.scss
index c74a6d39824..089e6958eeb 100644
--- a/app/assets/stylesheets/base/mixins.scss
+++ b/app/assets/stylesheets/framework/mixins.scss
@@ -54,147 +54,6 @@
@include box-shadow(0 0 0 3px #f1f1f1);
}
-@mixin md-typography {
- color: $md-text-color;
-
- a {
- color: $md-link-color;
- }
-
- img {
- max-width: 100%;
- }
-
- *:first-child {
- margin-top: 0;
- }
-
- code {
- font-family: $monospace_font;
- white-space: pre;
- word-wrap: normal;
- padding: 1px 2px;
- }
-
- kbd {
- display: inline-block;
- padding: 3px 5px;
- font-size: 11px;
- line-height: 10px;
- color: #555;
- vertical-align: middle;
- background-color: #FCFCFC;
- border-width: 1px;
- border-style: solid;
- border-color: #CCC #CCC #BBB;
- border-image: none;
- border-radius: 3px;
- box-shadow: 0px -1px 0px #BBB inset;
- }
-
- h1 {
- font-size: 1.3em;
- font-weight: 600;
- margin: 24px 0 12px 0;
- padding: 0 0 10px 0;
- border-bottom: 1px solid #e7e9ed;
- color: #313236;
- }
-
- h2 {
- font-size: 1.2em;
- font-weight: 600;
- margin: 24px 0 12px 0;
- color: #313236;
- }
-
- h3 {
- margin: 24px 0 12px 0;
- font-size: 1.25em;
- }
-
- h4 {
- margin: 24px 0 12px 0;
- font-size: 1.1em;
- }
-
- h5 {
- margin: 24px 0 12px 0;
- font-size: 1em;
- }
-
- h6 {
- margin: 24px 0 12px 0;
- font-size: 0.90em;
- }
-
- blockquote {
- padding: 8px 21px;
- margin: 12px 0 12px;
- border-left: 3px solid #e7e9ed;
- }
-
- blockquote p {
- color: #7f8fa4 !important;
- font-size: 15px;
- line-height: 1.5;
- }
-
- p {
- color:#5c5d5e;
- margin:6px 0 0 0;
- }
-
- table {
- @extend .table;
- @extend .table-bordered;
- margin: 12px 0 12px 0;
- color: #5c5d5e;
- th {
- background: #f8fafc;
- }
- }
-
- pre {
- margin: 12px 0 12px 0 !important;
- background-color: #f8fafc !important;
- font-size: 13px !important;
- color: #5b6169 !important;
- line-height: 1.6em !important;
- @include border-radius(2px);
- }
-
- p > code {
- font-weight: inherit;
- }
-
-
- ul {
- color: #5c5d5e;
- }
-
- li {
- line-height: 1.6em;
- }
-
- a[href*="/uploads/"], a[href*="storage.googleapis.com/google-code-attachments/"] {
- &:before {
- margin-right: 4px;
-
- font: normal normal normal 14px/1 FontAwesome;
- font-size: inherit;
- text-rendering: auto;
- -webkit-font-smoothing: antialiased;
- content: "\f0c6";
- }
-
- &:hover:before {
- text-decoration: none;
- }
- }
-}
-
-
@mixin str-truncated($max_width: 82%) {
display: inline-block;
overflow: hidden;
diff --git a/app/assets/stylesheets/generic/mobile.scss b/app/assets/stylesheets/framework/mobile.scss
index 36ae126f865..cea47fba192 100644
--- a/app/assets/stylesheets/generic/mobile.scss
+++ b/app/assets/stylesheets/framework/mobile.scss
@@ -23,7 +23,7 @@
margin-right: 0;
}
- .issues-filters,
+ .issues-details-filters,
.dash-projects-filters,
.check-all-holder {
display: none;
@@ -83,6 +83,7 @@
.center-top-menu {
height: 45px;
+ margin-bottom: 30px;
li a {
font-size: 14px;
@@ -90,9 +91,11 @@
}
}
- .projects-search-form {
- margin: 0 -5px !important;
+ .activity-filter-block {
+ display: none;
+ }
+ .projects-search-form {
.btn {
display: none;
}
@@ -100,6 +103,11 @@
}
@media (max-width: $screen-sm-max) {
+ .page-with-sidebar .content-wrapper {
+ padding: 0;
+ padding-top: 1px;
+ }
+
.issues-filters {
.milestone-filter, .labels-filter {
display: none;
diff --git a/app/assets/stylesheets/generic/pagination.scss b/app/assets/stylesheets/framework/pagination.scss
index 6677f94dafd..6677f94dafd 100644
--- a/app/assets/stylesheets/generic/pagination.scss
+++ b/app/assets/stylesheets/framework/pagination.scss
diff --git a/app/assets/stylesheets/generic/selects.scss b/app/assets/stylesheets/framework/selects.scss
index cba621635b6..cba621635b6 100644
--- a/app/assets/stylesheets/generic/selects.scss
+++ b/app/assets/stylesheets/framework/selects.scss
diff --git a/app/assets/stylesheets/generic/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index c5ea3aca7ca..c5ea3aca7ca 100644
--- a/app/assets/stylesheets/generic/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
diff --git a/app/assets/stylesheets/generic/tables.scss b/app/assets/stylesheets/framework/tables.scss
index a66e45577de..789b34020c1 100644
--- a/app/assets/stylesheets/generic/tables.scss
+++ b/app/assets/stylesheets/framework/tables.scss
@@ -1,5 +1,21 @@
table {
&.table {
+ .dropdown-menu a {
+ text-decoration: none;
+ }
+
+ .success,
+ .warning,
+ .danger,
+ .info {
+ color: #fff;
+
+ a:not(.btn) {
+ text-decoration: underline;
+ color: #fff;
+ }
+ }
+
tr {
td, th {
padding: 8px 10px;
@@ -12,7 +28,7 @@ table {
border-bottom: 1px solid $border-color !important;
}
td {
- border-color: #F1F1F1 !important;
+ border-color: $table-border-color !important;
border-bottom: 1px solid;
}
}
diff --git a/app/assets/stylesheets/generic/timeline.scss b/app/assets/stylesheets/framework/timeline.scss
index bf21d7fce76..bf21d7fce76 100644
--- a/app/assets/stylesheets/generic/timeline.scss
+++ b/app/assets/stylesheets/framework/timeline.scss
diff --git a/app/assets/stylesheets/base/gl_bootstrap.scss b/app/assets/stylesheets/framework/tw_bootstrap.scss
index eb8d23d6453..99d028d1228 100644
--- a/app/assets/stylesheets/base/gl_bootstrap.scss
+++ b/app/assets/stylesheets/framework/tw_bootstrap.scss
@@ -32,8 +32,6 @@
@import "bootstrap/pager";
@import "bootstrap/labels";
@import "bootstrap/badges";
-@import "bootstrap/jumbotron";
-@import "bootstrap/thumbnails";
@import "bootstrap/alerts";
@import "bootstrap/progress-bars";
@import "bootstrap/list-group";
@@ -251,23 +249,3 @@
.text-info:hover {
color: $brand-info;
}
-
-// Tables =====================================================================
-
-table.table {
- .dropdown-menu a {
- text-decoration: none;
- }
-
- .success,
- .warning,
- .danger,
- .info {
- color: #fff;
-
- a:not(.btn) {
- text-decoration: underline;
- color: #fff;
- }
- }
-}
diff --git a/app/assets/stylesheets/base/gl_variables.scss b/app/assets/stylesheets/framework/tw_bootstrap_variables.scss
index 18632da4f2a..63868a34e2a 100644
--- a/app/assets/stylesheets/base/gl_variables.scss
+++ b/app/assets/stylesheets/framework/tw_bootstrap_variables.scss
@@ -156,3 +156,5 @@ $nav-link-padding: 13px $gl-padding;
$pre-bg: #f8fafc !default;
$pre-color: $gl-gray !default;
$pre-border-color: #e7e9ed;
+
+$table-bg-accent: $background-color;
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
new file mode 100644
index 00000000000..bf36f96cc97
--- /dev/null
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -0,0 +1,271 @@
+@mixin md-typography {
+ color: $md-text-color;
+
+ a {
+ color: $md-link-color;
+ }
+
+ img {
+ max-width: 100%;
+ }
+
+ *:first-child {
+ margin-top: 0;
+ }
+
+ code {
+ font-family: $monospace_font;
+ white-space: pre;
+ word-wrap: normal;
+ padding: 1px 2px;
+ }
+
+ kbd {
+ display: inline-block;
+ padding: 3px 5px;
+ font-size: 11px;
+ line-height: 10px;
+ color: #555;
+ vertical-align: middle;
+ background-color: #FCFCFC;
+ border-width: 1px;
+ border-style: solid;
+ border-color: #CCC #CCC #BBB;
+ border-image: none;
+ border-radius: 3px;
+ box-shadow: 0px -1px 0px #BBB inset;
+ }
+
+ h1 {
+ font-size: 1.3em;
+ font-weight: 600;
+ margin: 24px 0 12px 0;
+ padding: 0 0 10px 0;
+ border-bottom: 1px solid #e7e9ed;
+ color: #313236;
+ }
+
+ h2 {
+ font-size: 1.2em;
+ font-weight: 600;
+ margin: 24px 0 12px 0;
+ color: #313236;
+ }
+
+ h3 {
+ margin: 24px 0 12px 0;
+ font-size: 1.25em;
+ }
+
+ h4 {
+ margin: 24px 0 12px 0;
+ font-size: 1.1em;
+ }
+
+ h5 {
+ margin: 24px 0 12px 0;
+ font-size: 1em;
+ }
+
+ h6 {
+ margin: 24px 0 12px 0;
+ font-size: 0.90em;
+ }
+
+ blockquote {
+ padding: 8px 21px;
+ margin: 12px 0 12px;
+ border-left: 3px solid #e7e9ed;
+ }
+
+ blockquote p {
+ color: #7f8fa4 !important;
+ font-size: 15px;
+ line-height: 1.5;
+ }
+
+ p {
+ color:#5c5d5e;
+ margin:6px 0 0 0;
+ }
+
+ table {
+ @extend .table;
+ @extend .table-bordered;
+ margin: 12px 0 12px 0;
+ color: #5c5d5e;
+ th {
+ background: #f8fafc;
+ }
+ }
+
+ pre {
+ margin: 12px 0 12px 0 !important;
+ background-color: #f8fafc !important;
+ font-size: 13px !important;
+ color: #5b6169 !important;
+ line-height: 1.6em !important;
+ @include border-radius(2px);
+ }
+
+ p > code {
+ font-weight: inherit;
+ }
+
+
+ ul {
+ color: #5c5d5e;
+ }
+
+ li {
+ line-height: 1.6em;
+ }
+
+ a[href*="/uploads/"], a[href*="storage.googleapis.com/google-code-attachments/"] {
+ &:before {
+ margin-right: 4px;
+
+ font: normal normal normal 14px/1 FontAwesome;
+ font-size: inherit;
+ text-rendering: auto;
+ -webkit-font-smoothing: antialiased;
+ content: "\f0c6";
+ }
+
+ &:hover:before {
+ text-decoration: none;
+ }
+ }
+}
+
+
+/**
+ * Headers
+ *
+ */
+body {
+ text-rendering:optimizeLegibility;
+ -webkit-text-shadow: rgba(255,255,255,0.01) 0 0 1px;
+}
+
+.page-title {
+ margin-top: 0px;
+ line-height: 1.3;
+ font-size: 1.25em;
+ font-weight: 600;
+}
+
+.page-title-empty {
+ margin-top: 0px;
+ line-height: 1.3;
+ font-size: 1.25em;
+ font-weight: 600;
+ margin: 12px 7px 12px 7px;
+}
+
+h1, h2, h3, h4, h5, h6 {
+ color: $gl-header-color;
+ font-weight: 500;
+}
+
+/** CODE **/
+pre {
+ font-family: $monospace_font;
+
+ &.dark {
+ background: #333;
+ color: $background-color;
+ }
+
+ &.plain-readme {
+ background: none;
+ border: none;
+ padding: 0;
+ margin: 0;
+ font-size: 14px;
+ }
+}
+
+.monospace {
+ font-family: $monospace_font;
+}
+
+code {
+ &.key-fingerprint {
+ background: $body-bg;
+ color: $text-color;
+ }
+}
+
+a > code {
+ color: $link-color;
+}
+
+/**
+ * Wiki typography
+ *
+ */
+.wiki {
+ @include md-typography;
+
+ word-wrap: break-word;
+ padding: 7px;
+
+ /* Link to current header. */
+ h1, h2, h3, h4, h5, h6 {
+ position: relative;
+
+ a.anchor {
+ // Setting `display: none` would prevent the anchor being scrolled to, so
+ // instead we set the height to 0 and it gets updated on hover.
+ height: 0;
+ }
+
+ &:hover > a.anchor {
+ $size: 16px;
+ position: absolute;
+ right: 100%;
+ top: 50%;
+ margin-top: -$size/2;
+ margin-right: 0px;
+ padding-right: 20px;
+ display: inline-block;
+ width: $size;
+ height: $size;
+ background-image: image-url("icon-link.png");
+ background-size: contain;
+ background-repeat: no-repeat;
+ }
+ }
+
+ ul,ol {
+ padding: 0;
+ margin: 6px 0 6px 18px !important;
+ }
+ ol {
+ color: #5c5d5e;
+ }
+}
+
+.md-area {
+ @include md-typography;
+}
+
+.md {
+ @include md-typography;
+}
+
+/**
+ * Textareas intended for GFM
+ *
+ */
+textarea.js-gfm-input {
+ font-family: $monospace_font;
+}
+
+.md-preview {
+}
+
+.strikethrough {
+ text-decoration: line-through;
+}
diff --git a/app/assets/stylesheets/base/variables.scss b/app/assets/stylesheets/framework/variables.scss
index eb9a2966389..91954683c3e 100644
--- a/app/assets/stylesheets/base/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -16,6 +16,7 @@ $avatar_radius: 50%;
$code_font_size: 13px;
$code_line_height: 1.5;
$border-color: #dce0e6;
+$table-border-color: #eef0f2;
$background-color: #F7F8FA;
$header-height: 58px;
$fixed-layout-width: 1200px;
diff --git a/app/assets/stylesheets/generic/zen.scss b/app/assets/stylesheets/framework/zen.scss
index 32e2c020e06..32e2c020e06 100644
--- a/app/assets/stylesheets/generic/zen.scss
+++ b/app/assets/stylesheets/framework/zen.scss
diff --git a/app/assets/stylesheets/generic/typography.scss b/app/assets/stylesheets/generic/typography.scss
deleted file mode 100644
index 6a3cb49baae..00000000000
--- a/app/assets/stylesheets/generic/typography.scss
+++ /dev/null
@@ -1,130 +0,0 @@
-/**
- * Headers
- *
- */
-body {
- text-rendering:optimizeLegibility;
- -webkit-text-shadow: rgba(255,255,255,0.01) 0 0 1px;
-}
-
-.page-title {
- margin-top: 0px;
- line-height: 1.3;
- font-size: 1.25em;
- font-weight: 600;
-}
-
-.page-title-empty {
- margin-top: 0px;
- line-height: 1.3;
- font-size: 1.25em;
- font-weight: 600;
- margin: 12px 7px 12px 7px;
-}
-
-h1, h2, h3, h4, h5, h6 {
- color: $gl-header-color;
- font-weight: 500;
-}
-
-/** CODE **/
-pre {
- font-family: $monospace_font;
-
- &.dark {
- background: #333;
- color: $background-color;
- }
-
- &.plain-readme {
- background: none;
- border: none;
- padding: 0;
- margin: 0;
- font-size: 14px;
- }
-}
-
-.monospace {
- font-family: $monospace_font;
-}
-
-code {
- &.key-fingerprint {
- background: $body-bg;
- color: $text-color;
- }
-}
-
-a > code {
- color: $link-color;
-}
-
-/**
- * Wiki typography
- *
- */
-.wiki {
- @include md-typography;
-
- word-wrap: break-word;
- padding: 7px;
-
- /* Link to current header. */
- h1, h2, h3, h4, h5, h6 {
- position: relative;
-
- a.anchor {
- // Setting `display: none` would prevent the anchor being scrolled to, so
- // instead we set the height to 0 and it gets updated on hover.
- height: 0;
- }
-
- &:hover > a.anchor {
- $size: 16px;
- position: absolute;
- right: 100%;
- top: 50%;
- margin-top: -$size/2;
- margin-right: 0px;
- padding-right: 20px;
- display: inline-block;
- width: $size;
- height: $size;
- background-image: image-url("icon-link.png");
- background-size: contain;
- background-repeat: no-repeat;
- }
- }
-
- ul,ol {
- padding: 0;
- margin: 6px 0 6px 18px !important;
- }
- ol {
- color: #5c5d5e;
- }
-}
-
-.md-area {
- @include md-typography;
-}
-
-.md {
- @include md-typography;
-}
-
-/**
- * Textareas intended for GFM
- *
- */
-textarea.js-gfm-input {
- font-family: $monospace_font;
-}
-
-.md-preview {
-}
-
-.strikethrough {
- text-decoration: line-through;
-} \ No newline at end of file
diff --git a/app/assets/stylesheets/ci/builds.scss b/app/assets/stylesheets/pages/builds.scss
index 74dc3e321c1..74dc3e321c1 100644
--- a/app/assets/stylesheets/ci/builds.scss
+++ b/app/assets/stylesheets/pages/builds.scss
diff --git a/app/assets/stylesheets/ci/projects.scss b/app/assets/stylesheets/pages/ci_projects.scss
index 8c5273abcda..8c5273abcda 100644
--- a/app/assets/stylesheets/ci/projects.scss
+++ b/app/assets/stylesheets/pages/ci_projects.scss
diff --git a/app/assets/stylesheets/ci/lint.scss b/app/assets/stylesheets/pages/lint.scss
index 6d2bd33b28b..6d2bd33b28b 100644
--- a/app/assets/stylesheets/ci/lint.scss
+++ b/app/assets/stylesheets/pages/lint.scss
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index dcd1aed7196..4392f08942b 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -65,7 +65,6 @@
.note-image-attach {
@extend .col-md-4;
- @extend .thumbnail;
margin-left: 45px;
float: none;
}
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index 0031ab5151b..f7a22849003 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -63,6 +63,7 @@
}
p {
+ padding: 0 $gl-padding;
color: #5c5d5e;
}
}
@@ -510,8 +511,3 @@ pre.light-well {
margin-top: -1px;
}
}
-
-.inline-form {
- display: inline-block;
-}
-
diff --git a/app/assets/stylesheets/ci/runners.scss b/app/assets/stylesheets/pages/runners.scss
index 2b15ab83129..2b15ab83129 100644
--- a/app/assets/stylesheets/ci/runners.scss
+++ b/app/assets/stylesheets/pages/runners.scss
diff --git a/app/assets/stylesheets/ci/status.scss b/app/assets/stylesheets/pages/status.scss
index a7d3b2197f1..a7d3b2197f1 100644
--- a/app/assets/stylesheets/ci/status.scss
+++ b/app/assets/stylesheets/pages/status.scss
diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss
index 271cc547e2b..dadd86e88cc 100644
--- a/app/assets/stylesheets/pages/tree.scss
+++ b/app/assets/stylesheets/pages/tree.scss
@@ -1,7 +1,7 @@
.tree-holder {
- .tree-content-holder {
- float: left;
- width: 100%;
+ .tree-table-holder {
+ margin-left: -$gl-padding;
+ margin-right: -$gl-padding;
}
.tree_progress {
@@ -13,10 +13,15 @@
}
.tree-table {
- @extend .table;
- @include border-radius(0);
+ margin-bottom: 0;
tr {
+ > td, > th {
+ padding: 10px $gl-padding;
+ line-height: 32px;
+ border-color: $table-border-color !important;
+ }
+
&:hover {
td {
background: $hover;
@@ -27,9 +32,9 @@
}
&.selected {
td {
- background: $background-color;
- border-top: 1px solid #EEE;
- border-bottom: 1px solid #EEE;
+ background: $gray-dark;
+ border-top: 1px solid $border-gray-dark;
+ border-bottom: 1px solid $border-gray-dark;
}
}
}
@@ -85,19 +90,6 @@
margin-right: 15px;
}
-.readme-holder {
- margin: 0 auto;
-
- .readme-file-title {
- font-size: 14px;
- font-weight: bold;
- margin-bottom: 20px;
- color: #777;
- border-bottom: 1px solid #DDD;
- padding: 10px 0;
- }
-}
-
.blob-commit-info {
list-style: none;
margin: 0;
diff --git a/app/assets/stylesheets/ci/xterm.scss b/app/assets/stylesheets/pages/xterm.scss
index 9a50096c0d0..9a50096c0d0 100644
--- a/app/assets/stylesheets/ci/xterm.scss
+++ b/app/assets/stylesheets/pages/xterm.scss
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index 524218290c6..40fb15a5b36 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -88,7 +88,7 @@ class GroupsController < Groups::ApplicationController
def destroy
DestroyGroupService.new(@group, current_user).execute
- redirect_to root_path, alert: "Group '#{@group.name} was deleted."
+ redirect_to root_path, alert: "Group '#{@group.name}' was successfully deleted."
end
protected
diff --git a/app/models/ability.rb b/app/models/ability.rb
index a020b24a550..77c121ca5e8 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -135,6 +135,8 @@ class Ability
def project_report_rules
project_guest_rules + [
+ :create_commit_status,
+ :read_commit_statuses,
:download_code,
:fork_project,
:create_project_snippet,
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 5d17f4418ed..f8c731a7bf7 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -24,32 +24,19 @@
#
module Ci
- class Build < ActiveRecord::Base
- extend Ci::Model
-
+ class Build < CommitStatus
LAZY_ATTRIBUTES = ['trace']
- belongs_to :commit, class_name: 'Ci::Commit'
belongs_to :runner, class_name: 'Ci::Runner'
belongs_to :trigger_request, class_name: 'Ci::TriggerRequest'
- belongs_to :user
serialize :options
- validates :commit, presence: true
- validates :status, presence: true
validates :coverage, numericality: true, allow_blank: true
validates_presence_of :ref
- scope :running, ->() { where(status: "running") }
- scope :pending, ->() { where(status: "pending") }
- scope :success, ->() { where(status: "success") }
- scope :failed, ->() { where(status: "failed") }
scope :unstarted, ->() { where(runner_id: nil) }
- scope :running_or_pending, ->() { where(status:[:running, :pending]) }
- scope :latest, ->() { where(id: unscope(:select).select('max(id)').group(:name, :ref)).order(stage_idx: :asc) }
scope :ignore_failures, ->() { where(allow_failure: false) }
- scope :for_ref, ->(ref) { where(ref: ref) }
scope :similar, ->(build) { where(ref: build.ref, tag: build.tag, trigger_request_id: build.trigger_request_id) }
acts_as_taggable
@@ -74,13 +61,14 @@ module Ci
def create_from(build)
new_build = build.dup
- new_build.status = :pending
+ new_build.status = 'pending'
new_build.runner_id = nil
+ new_build.trigger_request_id = nil
new_build.save
end
def retry(build)
- new_build = Ci::Build.new(status: :pending)
+ new_build = Ci::Build.new(status: 'pending')
new_build.ref = build.ref
new_build.tag = build.tag
new_build.options = build.options
@@ -98,28 +86,7 @@ module Ci
end
state_machine :status, initial: :pending do
- event :run do
- transition pending: :running
- end
-
- event :drop do
- transition running: :failed
- end
-
- event :success do
- transition running: :success
- end
-
- event :cancel do
- transition [:pending, :running] => :canceled
- end
-
- after_transition pending: :running do |build, transition|
- build.update_attributes started_at: Time.now
- end
-
after_transition any => [:success, :failed, :canceled] do |build, transition|
- build.update_attributes finished_at: Time.now
project = build.project
if project.web_hooks?
@@ -136,19 +103,10 @@ module Ci
build.update_coverage
end
end
-
- state :pending, value: 'pending'
- state :running, value: 'running'
- state :failed, value: 'failed'
- state :success, value: 'success'
- state :canceled, value: 'canceled'
end
- delegate :sha, :short_sha, :project, :gl_project,
- to: :commit, prefix: false
-
- def before_sha
- Gitlab::Git::BLANK_SHA
+ def ignored?
+ failed? && allow_failure?
end
def trace_html
@@ -156,22 +114,6 @@ module Ci
html || ''
end
- def started?
- !pending? && !canceled? && started_at
- end
-
- def active?
- running? || pending?
- end
-
- def complete?
- canceled? || success? || failed?
- end
-
- def ignored?
- failed? && allow_failure?
- end
-
def timeout
project.timeout
end
@@ -180,14 +122,6 @@ module Ci
yaml_variables + project_variables + trigger_variables
end
- def duration
- if started_at && finished_at
- finished_at - started_at
- elsif started_at
- Time.now - started_at
- end
- end
-
def project
commit.project
end
@@ -278,6 +212,25 @@ module Ci
"#{dir_to_trace}/#{id}.log"
end
+ def target_url
+ Gitlab::Application.routes.url_helpers.
+ namespace_project_build_url(gl_project.namespace, gl_project, self)
+ end
+
+ def cancel_url
+ if active?
+ Gitlab::Application.routes.url_helpers.
+ cancel_namespace_project_build_path(gl_project.namespace, gl_project, self, return_to: request.original_url)
+ end
+ end
+
+ def retry_url
+ if commands.present?
+ Gitlab::Application.routes.url_helpers.
+ cancel_namespace_project_build_path(gl_project.namespace, gl_project, self, return_to: request.original_url)
+ end
+ end
+
private
def yaml_variables
diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb
index fde754a92a1..68864edfbbf 100644
--- a/app/models/ci/commit.rb
+++ b/app/models/ci/commit.rb
@@ -20,7 +20,8 @@ module Ci
extend Ci::Model
belongs_to :gl_project, class_name: '::Project', foreign_key: :gl_project_id
- has_many :builds, dependent: :destroy, class_name: 'Ci::Build'
+ has_many :statuses, dependent: :destroy, class_name: 'CommitStatus'
+ has_many :builds, class_name: 'Ci::Build'
has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest'
validates_presence_of :sha
@@ -47,7 +48,7 @@ module Ci
end
def retry
- builds_without_retry.each do |build|
+ latest_builds.each do |build|
Ci::Build.retry(build)
end
end
@@ -81,12 +82,11 @@ module Ci
end
def stage
- running_or_pending = builds_without_retry.running_or_pending
- running_or_pending.limit(1).pluck(:stage).first
+ running_or_pending = statuses.latest.running_or_pending.ordered
+ running_or_pending.first.try(:stage)
end
def create_builds(ref, tag, user, trigger_request = nil)
- return if skip_ci? && trigger_request.blank?
return unless config_processor
config_processor.stages.any? do |stage|
CreateBuildsService.new.execute(self, stage, ref, tag, user, trigger_request).present?
@@ -94,7 +94,6 @@ module Ci
end
def create_next_builds(ref, tag, user, trigger_request)
- return if skip_ci? && trigger_request.blank?
return unless config_processor
stages = builds.where(ref: ref, tag: tag, trigger_request: trigger_request).group_by(&:stage)
@@ -107,61 +106,60 @@ module Ci
end
def refs
- builds.group(:ref).pluck(:ref)
+ statuses.order(:ref).pluck(:ref).uniq
end
- def last_ref
- builds.latest.first.try(:ref)
+ def latest_statuses
+ @latest_statuses ||= statuses.latest.to_a
end
- def builds_without_retry
- builds.latest
+ def latest_builds
+ @latest_builds ||= builds.latest.to_a
end
- def builds_without_retry_for_ref(ref)
- builds.for_ref(ref).latest
+ def latest_builds_for_ref(ref)
+ latest_builds.select { |build| build.ref == ref }
end
- def retried_builds
- @retried_builds ||= (builds.order(id: :desc) - builds_without_retry)
+ def retried
+ @retried ||= (statuses.order(id: :desc) - statuses.latest)
end
def status
- if skip_ci?
- return 'skipped'
- elsif yaml_errors.present?
+ if yaml_errors.present?
return 'failed'
- elsif builds.none?
- return 'skipped'
- elsif success?
- 'success'
- elsif pending?
- 'pending'
- elsif running?
- 'running'
- elsif canceled?
- 'canceled'
- else
- 'failed'
+ end
+
+ @status ||= begin
+ latest = latest_statuses
+ latest.reject! { |status| status.try(&:allow_failure?) }
+
+ if latest.none?
+ 'skipped'
+ elsif latest.all?(&:success?)
+ 'success'
+ elsif latest.all?(&:pending?)
+ 'pending'
+ elsif latest.any?(&:running?) || latest.any?(&:pending?)
+ 'running'
+ elsif latest.all?(&:canceled?)
+ 'canceled'
+ else
+ 'failed'
+ end
end
end
def pending?
- builds_without_retry.all? do |build|
- build.pending?
- end
+ status == 'pending'
end
def running?
- builds_without_retry.any? do |build|
- build.running? || build.pending?
- end
+ status == 'running'
end
def success?
- builds_without_retry.all? do |build|
- build.success? || build.ignored?
- end
+ status == 'success'
end
def failed?
@@ -169,26 +167,21 @@ module Ci
end
def canceled?
- builds_without_retry.all? do |build|
- build.canceled?
- end
+ status == 'canceled'
end
def duration
- @duration ||= builds_without_retry.select(&:duration).sum(&:duration).to_i
- end
-
- def duration_for_ref(ref)
- builds_without_retry_for_ref(ref).select(&:duration).sum(&:duration).to_i
+ duration_array = latest_statuses.map(&:duration).compact
+ duration_array.reduce(:+).to_i
end
def finished_at
- @finished_at ||= builds.order('finished_at DESC').first.try(:finished_at)
+ @finished_at ||= statuses.order('finished_at DESC').first.try(:finished_at)
end
def coverage
if project.coverage_enabled?
- coverage_array = builds_without_retry.map(&:coverage).compact
+ coverage_array = latest_builds.map(&:coverage).compact
if coverage_array.size >= 1
'%.2f' % (coverage_array.reduce(:+) / coverage_array.size)
end
@@ -196,7 +189,7 @@ module Ci
end
def matrix_for_ref?(ref)
- builds_without_retry_for_ref(ref).pluck(:id).size > 1
+ latest_builds_for_ref(ref).size > 1
end
def config_processor
@@ -217,7 +210,6 @@ module Ci
end
def skip_ci?
- return false if builds.any?
git_commit_message =~ /(\[ci skip\])/ if git_commit_message
end
diff --git a/app/models/commit.rb b/app/models/commit.rb
index aff329d71fa..d5c50013525 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -184,4 +184,12 @@ class Commit
def parents
@parents ||= Commit.decorate(super, project)
end
+
+ def ci_commit
+ project.ci_commit(sha)
+ end
+
+ def status
+ ci_commit.try(:status) || :not_found
+ end
end
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
new file mode 100644
index 00000000000..b4d91b1b0c3
--- /dev/null
+++ b/app/models/commit_status.rb
@@ -0,0 +1,91 @@
+class CommitStatus < ActiveRecord::Base
+ self.table_name = 'ci_builds'
+
+ belongs_to :commit, class_name: 'Ci::Commit'
+ belongs_to :user
+
+ validates :commit, presence: true
+ validates :status, inclusion: { in: %w(pending running failed success canceled) }
+
+ validates_presence_of :name
+
+ alias_attribute :author, :user
+
+ scope :running, -> { where(status: 'running') }
+ scope :pending, -> { where(status: 'pending') }
+ scope :success, -> { where(status: 'success') }
+ scope :failed, -> { where(status: 'failed') }
+ scope :running_or_pending, -> { where(status:[:running, :pending]) }
+ scope :latest, -> { where(id: unscope(:select).select('max(id)').group(:name, :ref)) }
+ scope :ordered, -> { order(:ref, :stage_idx, :name) }
+ scope :for_ref, ->(ref) { where(ref: ref) }
+ scope :running_or_pending, -> { where(status: [:running, :pending]) }
+
+ state_machine :status, initial: :pending do
+ event :run do
+ transition pending: :running
+ end
+
+ event :drop do
+ transition running: :failed
+ end
+
+ event :success do
+ transition [:pending, :running] => :success
+ end
+
+ event :cancel do
+ transition [:pending, :running] => :canceled
+ end
+
+ after_transition pending: :running do |build, transition|
+ build.update_attributes started_at: Time.now
+ end
+
+ after_transition any => [:success, :failed, :canceled] do |build, transition|
+ build.update_attributes finished_at: Time.now
+ end
+
+ state :pending, value: 'pending'
+ state :running, value: 'running'
+ state :failed, value: 'failed'
+ state :success, value: 'success'
+ state :canceled, value: 'canceled'
+ end
+
+ delegate :sha, :short_sha, :gl_project,
+ to: :commit, prefix: false
+
+ # TODO: this should be removed with all references
+ def before_sha
+ Gitlab::Git::BLANK_SHA
+ end
+
+ def started?
+ !pending? && !canceled? && started_at
+ end
+
+ def active?
+ running? || pending?
+ end
+
+ def complete?
+ canceled? || success? || failed?
+ end
+
+ def duration
+ if started_at && finished_at
+ finished_at - started_at
+ elsif started_at
+ Time.now - started_at
+ end
+ end
+
+ def cancel_url
+ nil
+ end
+
+ def retry_url
+ nil
+ end
+end
diff --git a/app/models/generic_commit_status.rb b/app/models/generic_commit_status.rb
new file mode 100644
index 00000000000..fa54e3540d0
--- /dev/null
+++ b/app/models/generic_commit_status.rb
@@ -0,0 +1,15 @@
+class GenericCommitStatus < CommitStatus
+ before_validation :set_default_values
+
+ # GitHub compatible API
+ alias_attribute :context, :name
+
+ def set_default_values
+ self.context ||= 'default'
+ self.stage ||= 'external'
+ end
+
+ def tags
+ [:external]
+ end
+end
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index eb468c6cd53..c83b15c7d39 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -227,7 +227,7 @@ class MergeRequest < ActiveRecord::Base
end
def work_in_progress?
- title =~ /\A\[?WIP\]?:? /i
+ !!(title =~ /\A\[?WIP\]?:? /i)
end
def mergeable?
@@ -275,7 +275,8 @@ class MergeRequest < ActiveRecord::Base
attrs = {
source: source_project.hook_attrs,
target: target_project.hook_attrs,
- last_commit: nil
+ last_commit: nil,
+ work_in_progress: work_in_progress?
}
unless last_commit.nil?
diff --git a/app/models/project_services/bamboo_service.rb b/app/models/project_services/bamboo_service.rb
index d8aedbd2ab4..5f5255ab487 100644
--- a/app/models/project_services/bamboo_service.rb
+++ b/app/models/project_services/bamboo_service.rb
@@ -40,12 +40,19 @@ class BambooService < CiService
attr_accessor :response
after_save :compose_service_hook, if: :activated?
+ before_update :reset_password
def compose_service_hook
hook = service_hook || build_service_hook
hook.save
end
+ def reset_password
+ if prop_updated?(:bamboo_url)
+ self.password = nil
+ end
+ end
+
def title
'Atlassian Bamboo CI'
end
diff --git a/app/models/project_services/ci/hip_chat_service.rb b/app/models/project_services/ci/hip_chat_service.rb
index 0e6e97394bc..f17993d9f3b 100644
--- a/app/models/project_services/ci/hip_chat_service.rb
+++ b/app/models/project_services/ci/hip_chat_service.rb
@@ -49,7 +49,7 @@ module Ci
commit = build.commit
return unless commit
- return unless commit.builds_without_retry.include? build
+ return unless commit.latest_builds.include? build
case commit.status.to_sym
when :failed
diff --git a/app/models/project_services/ci/mail_service.rb b/app/models/project_services/ci/mail_service.rb
index 11a2743f969..fd193301001 100644
--- a/app/models/project_services/ci/mail_service.rb
+++ b/app/models/project_services/ci/mail_service.rb
@@ -48,7 +48,7 @@ module Ci
# it doesn't make sense to send emails for retried builds
commit = build.commit
return unless commit
- return unless commit.builds_without_retry.include?(build)
+ return unless commit.latest_builds.include?(build)
case build.status.to_sym
when :failed
diff --git a/app/models/project_services/ci/slack_message.rb b/app/models/project_services/ci/slack_message.rb
index 5ac8907ecd0..dc050a3fc59 100644
--- a/app/models/project_services/ci/slack_message.rb
+++ b/app/models/project_services/ci/slack_message.rb
@@ -23,7 +23,7 @@ module Ci
def attachments
fields = []
- commit.builds_without_retry.each do |build|
+ commit.latest_builds.each do |build|
next if build.allow_failure?
next unless build.failed?
fields << {
diff --git a/app/models/project_services/ci/slack_service.rb b/app/models/project_services/ci/slack_service.rb
index 76db573dc17..ee8e4988826 100644
--- a/app/models/project_services/ci/slack_service.rb
+++ b/app/models/project_services/ci/slack_service.rb
@@ -48,7 +48,7 @@ module Ci
commit = build.commit
return unless commit
- return unless commit.builds_without_retry.include?(build)
+ return unless commit.latest_builds.include?(build)
case commit.status.to_sym
when :failed
diff --git a/app/models/project_services/teamcity_service.rb b/app/models/project_services/teamcity_service.rb
index 3c002a1634b..fb11cad352e 100644
--- a/app/models/project_services/teamcity_service.rb
+++ b/app/models/project_services/teamcity_service.rb
@@ -37,12 +37,19 @@ class TeamcityService < CiService
attr_accessor :response
after_save :compose_service_hook, if: :activated?
+ before_update :reset_password
def compose_service_hook
hook = service_hook || build_service_hook
hook.save
end
+ def reset_password
+ if prop_updated?(:teamcity_url)
+ self.password = nil
+ end
+ end
+
def title
'JetBrains TeamCity CI'
end
diff --git a/app/models/service.rb b/app/models/service.rb
index 60fcc9d2857..7e845d565b1 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -117,6 +117,15 @@ class Service < ActiveRecord::Base
end
end
+ # ActiveRecord does not provide a mechanism to track changes in serialized keys.
+ # This is why we need to perform extra query to do it mannually.
+ def prop_updated?(prop_name)
+ relation_name = self.type.underscore
+ previous_value = project.send(relation_name).send(prop_name)
+ return false if previous_value.nil?
+ previous_value != send(prop_name)
+ end
+
def async_execute(data)
return unless supported_events.include?(data[:object_kind])
diff --git a/app/services/ci/create_commit_service.rb b/app/services/ci/create_commit_service.rb
index fc1ae5774d5..479a2d6defc 100644
--- a/app/services/ci/create_commit_service.rb
+++ b/app/services/ci/create_commit_service.rb
@@ -17,8 +17,10 @@ module Ci
tag = origin_ref.start_with?('refs/tags/')
commit = project.gl_project.ensure_ci_commit(sha)
- commit.update_committed!
- commit.create_builds(ref, tag, user)
+ unless commit.skip_ci?
+ commit.update_committed!
+ commit.create_builds(ref, tag, user)
+ end
commit
end
diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml
index 97e4bb2fd73..bc08458312c 100644
--- a/app/views/admin/users/index.html.haml
+++ b/app/views/admin/users/index.html.haml
@@ -32,7 +32,7 @@
%hr
= form_tag admin_users_path, method: :get, class: 'form-inline' do
.form-group
- = search_field_tag :name, params[:name], placeholder: 'Name, email or username', class: 'form-control'
+ = search_field_tag :name, params[:name], placeholder: 'Name, email or username', class: 'form-control', spellcheck: false
= hidden_field_tag "filter", params[:filter]
= button_tag class: 'btn btn-primary' do
%i.fa.fa-search
diff --git a/app/views/ci/admin/runners/index.html.haml b/app/views/ci/admin/runners/index.html.haml
index b9d6703ff41..01ce81b4476 100644
--- a/app/views/ci/admin/runners/index.html.haml
+++ b/app/views/ci/admin/runners/index.html.haml
@@ -27,7 +27,7 @@
.pull-left
= form_tag ci_admin_runners_path, id: 'runners-search', class: 'form-inline', method: :get do
.form-group
- = search_field_tag :search, params[:search], class: 'form-control', placeholder: 'Runner description or token'
+ = search_field_tag :search, params[:search], class: 'form-control', placeholder: 'Runner description or token', spellcheck: false
= submit_tag 'Search', class: 'btn'
.pull-right.light
diff --git a/app/views/ci/admin/runners/show.html.haml b/app/views/ci/admin/runners/show.html.haml
index 5bb442cbf92..92787b2e6ac 100644
--- a/app/views/ci/admin/runners/show.html.haml
+++ b/app/views/ci/admin/runners/show.html.haml
@@ -76,7 +76,7 @@
%td
= form_tag ci_admin_runner_path(@runner), id: 'runner-projects-search', class: 'form-inline', method: :get do
.form-group
- = search_field_tag :search, params[:search], class: 'form-control'
+ = search_field_tag :search, params[:search], class: 'form-control', spellcheck: false
= submit_tag 'Search', class: 'btn'
%td
diff --git a/app/views/dashboard/_activities.html.haml b/app/views/dashboard/_activities.html.haml
index 19d919f9b6a..f98fd9f06ba 100644
--- a/app/views/dashboard/_activities.html.haml
+++ b/app/views/dashboard/_activities.html.haml
@@ -3,10 +3,9 @@
.gray-content-block
- if current_user
- %ul.nav.nav-pills.event_filter.pull-right
- %li.pull-right
- = link_to dashboard_projects_path(:atom, { private_token: current_user.private_token }), class: 'rss-btn' do
- %i.fa.fa-rss
+ .pull-right
+ = link_to dashboard_projects_path(:atom, { private_token: current_user.private_token }), class: 'btn rss-btn' do
+ %i.fa.fa-rss
= render 'shared/event_filter'
.content_list
diff --git a/app/views/dashboard/projects/_projects.html.haml b/app/views/dashboard/projects/_projects.html.haml
index e09e032a7f1..d0194a17b01 100644
--- a/app/views/dashboard/projects/_projects.html.haml
+++ b/app/views/dashboard/projects/_projects.html.haml
@@ -1,7 +1,7 @@
.projects-list-holder
.projects-search-form
.input-group
- = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control'
+ = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control', spellcheck: false
- if current_user.can_create_project?
%span.input-group-btn
= link_to new_project_path, class: 'btn btn-green' do
diff --git a/app/views/explore/groups/index.html.haml b/app/views/explore/groups/index.html.haml
index 83d4d321c83..fcb07b04083 100644
--- a/app/views/explore/groups/index.html.haml
+++ b/app/views/explore/groups/index.html.haml
@@ -11,7 +11,7 @@
= form_tag explore_groups_path, method: :get, class: 'form-inline form-tiny' do |f|
= hidden_field_tag :sort, @sort
.form-group
- = search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input", id: "groups_search"
+ = search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input", id: "groups_search", spellcheck: false
.form-group
= button_tag 'Search', class: "btn btn-default"
diff --git a/app/views/explore/projects/_filter.html.haml b/app/views/explore/projects/_filter.html.haml
index 5a3d689d1e5..2761272aa8a 100644
--- a/app/views/explore/projects/_filter.html.haml
+++ b/app/views/explore/projects/_filter.html.haml
@@ -1,7 +1,7 @@
.pull-left
= form_tag explore_projects_filter_path, method: :get, class: 'form-inline form-tiny' do |f|
.form-group
- = search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input", id: "projects_search"
+ = search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input", id: "projects_search", spellcheck: false
.form-group
= button_tag 'Search', class: "btn btn-success"
diff --git a/app/views/groups/_projects.html.haml b/app/views/groups/_projects.html.haml
index 2b27a88794d..133f3e2d5a8 100644
--- a/app/views/groups/_projects.html.haml
+++ b/app/views/groups/_projects.html.haml
@@ -1,10 +1,10 @@
.panel.panel-default.projects-list-holder
.panel-heading.clearfix
.input-group
- = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control'
+ = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control', spellcheck: false
- if can? current_user, :create_projects, @group
%span.input-group-btn
= link_to new_project_path(namespace_id: @group.id), class: 'btn btn-green' do
New project
- = render 'shared/projects/list', projects: @projects, projects_limit: 20, stars: false
+ = render 'shared/projects/list', projects: @projects, projects_limit: 20, stars: false, skip_namespace: true
diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml
index 3a6d07ebddf..fee4b0052b5 100644
--- a/app/views/groups/group_members/index.html.haml
+++ b/app/views/groups/group_members/index.html.haml
@@ -12,7 +12,7 @@
.clearfix.js-toggle-container
= form_tag group_group_members_path(@group), method: :get, class: 'form-inline member-search-form' do
.form-group
- = search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control search-text-input' }
+ = search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control search-text-input', spellcheck: false }
= button_tag 'Search', class: 'btn'
- if current_user && current_user.can?(:admin_group_member, @group)
diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml
index a9ba9d2ba10..dc8e81323a6 100644
--- a/app/views/groups/show.html.haml
+++ b/app/views/groups/show.html.haml
@@ -25,11 +25,9 @@
.hidden-xs
- if current_user
= render "events/event_last_push", event: @last_push
-
- %ul.nav.nav-pills.event_filter.pull-right
- %li
- = link_to group_path(@group, { format: :atom, private_token: current_user.private_token }), title: "Feed", class: 'rss-btn' do
- %i.fa.fa-rss
+ .pull-right
+ = link_to group_path(@group, { format: :atom, private_token: current_user.private_token }), title: "Feed", class: 'btn rss-btn' do
+ %i.fa.fa-rss
= render 'shared/event_filter'
%hr
diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml
index 2468687b56d..1a883e20e89 100644
--- a/app/views/layouts/_page.html.haml
+++ b/app/views/layouts/_page.html.haml
@@ -6,7 +6,7 @@
= brand_header_logo
.gitlab-text-container
%h3 GitLab
-
+
- if defined?(sidebar) && sidebar
= render "layouts/nav/#{sidebar}"
- elsif current_user
@@ -23,6 +23,7 @@
= current_user.username
.content-wrapper
= render "layouts/flash"
+ = yield :flash_message
%div{ class: container_class }
.content
.clearfix
diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml
index e2d2dec7ab8..ceb64ce3157 100644
--- a/app/views/layouts/_search.html.haml
+++ b/app/views/layouts/_search.html.haml
@@ -1,6 +1,6 @@
.search
= form_tag search_path, method: :get, class: 'navbar-form pull-left' do |f|
- = search_field_tag "search", nil, placeholder: search_placeholder, class: "search-input form-control"
+ = search_field_tag "search", nil, placeholder: search_placeholder, class: "search-input form-control", spellcheck: false
= hidden_field_tag :group_id, @group.try(:id)
- if @project && @project.persisted?
= hidden_field_tag :project_id, @project.id
diff --git a/app/views/projects/_activity.html.haml b/app/views/projects/_activity.html.haml
index 1261f6254d7..c2683bc6219 100644
--- a/app/views/projects/_activity.html.haml
+++ b/app/views/projects/_activity.html.haml
@@ -1,10 +1,9 @@
= render 'projects/last_push'
.gray-content-block.activity-filter-block
- if current_user
- %ul.nav.nav-pills.event_filter.pull-right
- %li
- = link_to namespace_project_path(@project.namespace, @project, format: :atom, private_token: current_user.private_token), title: "Feed", class: 'rss-btn' do
- %i.fa.fa-rss
+ .pull-right
+ = link_to namespace_project_path(@project.namespace, @project, format: :atom, private_token: current_user.private_token), title: "Feed", class: 'btn rss-btn' do
+ %i.fa.fa-rss
= render 'shared/event_filter'
.content_list{:"data-href" => activity_project_path(@project)}
diff --git a/app/views/projects/blob/_blob.html.haml b/app/views/projects/blob/_blob.html.haml
index b4c7d8b9b71..a1ae1397584 100644
--- a/app/views/projects/blob/_blob.html.haml
+++ b/app/views/projects/blob/_blob.html.haml
@@ -19,7 +19,7 @@
- blob_commit = @repository.last_commit_for_path(@commit.id, blob.path)
= render blob_commit, project: @project
-%div#tree-content-holder.tree-content-holder
+%div#blob-content-holder.blob-content-holder
%article.file-holder
.file-title
= blob_icon blob.mode, blob.name
diff --git a/app/views/projects/builds/_build.html.haml b/app/views/projects/builds/_build.html.haml
deleted file mode 100644
index 65fd9413b60..00000000000
--- a/app/views/projects/builds/_build.html.haml
+++ /dev/null
@@ -1,50 +0,0 @@
-- gl_project = build.project.gl_project
-%tr.build
- %td.status
- = ci_status_with_icon(build.status)
-
- %td.build-link
- = link_to namespace_project_build_path(gl_project.namespace, gl_project, build) do
- %strong Build ##{build.id}
-
- - if defined?(ref)
- %td
- = build.ref
-
- %td
- = build.stage
-
- %td
- = build.name
- .pull-right
- - if build.tags.any?
- - build.tag_list.each do |tag|
- %span.label.label-primary
- = tag
- - if build.trigger_request
- %span.label.label-info triggered
- - if build.allow_failure
- %span.label.label-danger allowed to fail
-
- %td.duration
- - if build.duration
- #{duration_in_words(build.finished_at, build.started_at)}
-
- %td.timestamp
- - if build.finished_at
- %span #{time_ago_in_words build.finished_at} ago
-
- - if build.project.coverage_enabled?
- %td.coverage
- - if build.coverage
- #{build.coverage}%
-
- %td
- - if defined?(controls) && current_user && can?(current_user, :manage_builds, gl_project)
- .pull-right
- - if build.active?
- = link_to cancel_namespace_project_build_path(gl_project.namespace, gl_project, build, return_to: request.original_url), title: 'Cancel build' do
- %i.fa.fa-remove.cred
- - elsif build.commands.present?
- = link_to retry_namespace_project_build_path(gl_project.namespace, gl_project, build, return_to: request.original_url), method: :post, title: 'Retry build' do
- %i.fa.fa-repeat
diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml
index b561078e8c7..9c3ae622b72 100644
--- a/app/views/projects/builds/show.html.haml
+++ b/app/views/projects/builds/show.html.haml
@@ -9,7 +9,7 @@
#up-build-trace
- if @commit.matrix_for_ref?(@build.ref)
%ul.center-top-menu.build-top-menu
- - @commit.builds_without_retry_for_ref(@build.ref).each do |build|
+ - @commit.latest_builds_for_ref(@build.ref).each do |build|
%li{class: ('active' if build == @build) }
= link_to namespace_project_build_path(@project.namespace, @project, build) do
= ci_icon_for_status(build.status)
@@ -20,7 +20,7 @@
= build.id
- - unless @commit.builds_without_retry_for_ref(@build.ref).include?(@build)
+ - unless @commit.latest_builds_for_ref(@build.ref).include?(@build)
%li.active
%a
Build ##{@build.id}
diff --git a/app/views/projects/buttons/_notifications.html.haml b/app/views/projects/buttons/_notifications.html.haml
index 4b69a6d7a6f..3bc2daeec4e 100644
--- a/app/views/projects/buttons/_notifications.html.haml
+++ b/app/views/projects/buttons/_notifications.html.haml
@@ -1,6 +1,6 @@
- return unless @membership
-= form_tag profile_notifications_path, method: :put, remote: true, class: 'inline-form', id: 'notification-form' do
+= form_tag profile_notifications_path, method: :put, remote: true, class: 'inline', id: 'notification-form' do
= hidden_field_tag :notification_type, 'project'
= hidden_field_tag :notification_id, @membership.id
= hidden_field_tag :notification_level
diff --git a/app/views/projects/commit/ci.html.haml b/app/views/projects/commit/ci.html.haml
index 26ab38445c2..4a1ef378a30 100644
--- a/app/views/projects/commit/ci.html.haml
+++ b/app/views/projects/commit/ci.html.haml
@@ -20,30 +20,31 @@
.bs-callout.bs-callout-warning
\.gitlab-ci.yml not found in this commit
-- @ci_commit.refs.each do |ref|
+.gray-content-block.second-block
+ Latest builds
+ - if @ci_commit.duration > 0
+ %small.pull-right
+ %i.fa.fa-time
+ #{time_interval_in_words @ci_commit.duration}
+
+%table.table.builds
+ %thead
+ %tr
+ %th Status
+ %th Build ID
+ %th Ref
+ %th Stage
+ %th Name
+ %th Duration
+ %th Finished at
+ - if @ci_project && @ci_project.coverage_enabled?
+ %th Coverage
+ %th
+ - @ci_commit.refs.each do |ref|
+ = render partial: "projects/commit_statuses/commit_status", collection: @ci_commit.statuses.for_ref(ref).latest.ordered, coverage: @ci_project.try(:coverage_enabled?), controls: true
+
+- if @ci_commit.retried.any?
.gray-content-block.second-block
- Builds for #{ref}
- - if @ci_commit.duration_for_ref(ref) > 0
- %small.pull-right
- %i.fa.fa-time
- #{time_interval_in_words @ci_commit.duration_for_ref(ref)}
-
- %table.table.builds
- %thead
- %tr
- %th Status
- %th Build ID
- %th Stage
- %th Name
- %th Duration
- %th Finished at
- - if @ci_project && @ci_project.coverage_enabled?
- %th Coverage
- %th
- = render partial: "projects/builds/build", collection: @ci_commit.builds_without_retry.for_ref(ref), controls: true
-
-- if @ci_commit.retried_builds.any?
- %h3
Retried builds
%table.table.builds
@@ -59,4 +60,4 @@
- if @ci_project && @ci_project.coverage_enabled?
%th Coverage
%th
- = render partial: "projects/builds/build", collection: @ci_commit.retried_builds, ref: true
+ = render partial: "projects/commit_statuses/commit_status", collection: @ci_commit.retried, coverage: @ci_project.try(:coverage_enabled?)
diff --git a/app/views/projects/commit_statuses/_commit_status.html.haml b/app/views/projects/commit_statuses/_commit_status.html.haml
new file mode 100644
index 00000000000..e3a17faf0bd
--- /dev/null
+++ b/app/views/projects/commit_statuses/_commit_status.html.haml
@@ -0,0 +1,51 @@
+%tr.commit_status
+ %td.status
+ = ci_status_with_icon(commit_status.status)
+
+ %td.commit_status-link
+ - if commit_status.target_url
+ = link_to commit_status.target_url do
+ %strong Build ##{commit_status.id}
+ - else
+ %strong Build ##{commit_status.id}
+
+ %td
+ = commit_status.ref
+
+ %td
+ = commit_status.stage
+
+ %td
+ = commit_status.name
+ .pull-right
+ - if commit_status.tags.any?
+ - commit_status.tags.each do |tag|
+ %span.label.label-primary
+ = tag
+ - if commit_status.try(:trigger_request)
+ %span.label.label-info triggered
+ - if commit_status.try(:allow_failure)
+ %span.label.label-danger allowed to fail
+
+ %td.duration
+ - if commit_status.duration
+ #{duration_in_words(commit_status.finished_at, commit_status.started_at)}
+
+ %td.timestamp
+ - if commit_status.finished_at
+ %span #{time_ago_in_words commit_status.finished_at} ago
+
+ - if defined?(coverage) && coverage
+ %td.coverage
+ - if commit_status.try(:coverage)
+ #{commit_status.coverage}%
+
+ %td
+ - if defined?(controls) && controls && current_user && can?(current_user, :manage_builds, gl_project)
+ .pull-right
+ - if commit_status.cancel_url
+ = link_to commit_status.cancel_url, title: 'Cancel' do
+ %i.fa.fa-remove.cred
+ - elsif commit_status.retry_url
+ = link_to commit_status.retry_url, method: :post, title: 'Retry' do
+ %i.fa.fa-repeat
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index 1b093c8f514..daab2326bc7 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -111,10 +111,10 @@
- if current_user.can_create_group?
.pull-right
- .light.in-line
+ .light.inline
.space-right
Need a group for several dependent projects?
- = link_to new_group_path, class: "btn btn-xs" do
+ = link_to new_group_path, class: "btn" do
Create a group
.save-project-loader.hide
diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml
index 9a0a824b811..82809bec5b8 100644
--- a/app/views/projects/project_members/index.html.haml
+++ b/app/views/projects/project_members/index.html.haml
@@ -5,7 +5,7 @@
.clearfix.js-toggle-container
= form_tag namespace_project_project_members_path(@project.namespace, @project), method: :get, class: 'form-inline member-search-form' do
.form-group
- = search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control search-text-input' }
+ = search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control search-text-input', spellcheck: false }
= button_tag 'Search', class: 'btn'
- if can?(current_user, :admin_project_member, @project)
diff --git a/app/views/projects/repositories/_download_archive.html.haml b/app/views/projects/repositories/_download_archive.html.haml
index b9486a9b492..07c24950ee2 100644
--- a/app/views/projects/repositories/_download_archive.html.haml
+++ b/app/views/projects/repositories/_download_archive.html.haml
@@ -3,10 +3,10 @@
- split_button = split_button || false
- if split_button == true
%span.btn-group{class: btn_class}
- = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'zip'), class: 'btn col-xs-10', rel: 'nofollow' do
+ = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'zip'), class: 'btn btn-success col-xs-10', rel: 'nofollow' do
%i.fa.fa-download
%span Download zip
- %a.col-xs-2.btn.dropdown-toggle{ 'data-toggle' => 'dropdown' }
+ %a.col-xs-2.btn.btn-success.dropdown-toggle{ 'data-toggle' => 'dropdown' }
%span.caret
%span.sr-only
Select Archive Format
diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml
index efa119edd5a..e95d987d74c 100644
--- a/app/views/projects/show.html.haml
+++ b/app/views/projects/show.html.haml
@@ -2,9 +2,10 @@
- if current_user
= auto_discovery_link_tag(:atom, namespace_project_path(@project.namespace, @project, format: :atom, private_token: current_user.private_token), title: "#{@project.name} activity")
-- if current_user && can?(current_user, :download_code, @project)
- = render 'shared/no_ssh'
- = render 'shared/no_password'
+= content_for :flash_message do
+ - if current_user && can?(current_user, :download_code, @project)
+ = render 'shared/no_ssh'
+ = render 'shared/no_password'
- if prefer_readme?
= render 'projects/last_push'
diff --git a/app/views/projects/tree/_readme.html.haml b/app/views/projects/tree/_readme.html.haml
index f082d711865..7e9af19c8ba 100644
--- a/app/views/projects/tree/_readme.html.haml
+++ b/app/views/projects/tree/_readme.html.haml
@@ -1,7 +1,8 @@
-%article.readme-holder#README
- = link_to '#README' do
- %h4.readme-file-title
- %i.fa.fa-file
- = readme.name
- .wiki
+%article.file-holder.readme-holder#README
+ .file-title
+ = link_to '#README' do
+ %strong
+ %i.fa.fa-file
+ = readme.name
+ .file-content.wiki
= render_readme(readme)
diff --git a/app/views/projects/tree/_tree.html.haml b/app/views/projects/tree/_tree.html.haml
index 457f8a4a585..7ff48e32e60 100644
--- a/app/views/projects/tree/_tree.html.haml
+++ b/app/views/projects/tree/_tree.html.haml
@@ -1,59 +1,61 @@
-%ul.breadcrumb.repo-breadcrumb
- %li
- = link_to namespace_project_tree_path(@project.namespace, @project, @ref) do
- = @project.path
- - tree_breadcrumbs(tree, 6) do |title, path|
+.gray-content-block
+ %ul.breadcrumb.repo-breadcrumb
%li
- - if path
- = link_to truncate(title, length: 40), namespace_project_tree_path(@project.namespace, @project, path)
- - else
- = link_to title, '#'
- - if allowed_tree_edit?
- %li
- %span.dropdown
- %a.dropdown-toggle.btn.btn-xs.add-to-tree{href: '#', "data-toggle" => "dropdown"}
- = icon('plus')
- %ul.dropdown-menu
- %li
- = link_to namespace_project_new_blob_path(@project.namespace, @project, @id), title: 'Create file', id: 'new-file-link' do
- = icon('pencil fw')
- Create file
- %li
- = link_to '#modal-upload-blob', { 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal'} do
- = icon('file fw')
- Upload file
- %li.divider
- %li
- = link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal'} do
- = icon('folder fw')
- New directory
+ = link_to namespace_project_tree_path(@project.namespace, @project, @ref) do
+ = @project.path
+ - tree_breadcrumbs(tree, 6) do |title, path|
+ %li
+ - if path
+ = link_to truncate(title, length: 40), namespace_project_tree_path(@project.namespace, @project, path)
+ - else
+ = link_to title, '#'
+ - if allowed_tree_edit?
+ %li
+ %span.dropdown
+ %a.dropdown-toggle.btn.add-to-tree{href: '#', "data-toggle" => "dropdown"}
+ = icon('plus')
+ %ul.dropdown-menu
+ %li
+ = link_to namespace_project_new_blob_path(@project.namespace, @project, @id), title: 'Create file', id: 'new-file-link' do
+ = icon('pencil fw')
+ Create file
+ %li
+ = link_to '#modal-upload-blob', { 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal'} do
+ = icon('file fw')
+ Upload file
+ %li.divider
+ %li
+ = link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal'} do
+ = icon('folder fw')
+ New directory
-%div#tree-content-holder.tree-content-holder.prepend-top-20
- %table#tree-slider{class: "table_#{@hex_path} tree-table" }
- %thead
- %tr
- %th Name
- %th Last Update
- %th.hidden-xs
- .pull-left Last Commit
- .last-commit.hidden-sm.pull-left
- &nbsp;
- %i.fa.fa-angle-right
- &nbsp;
- %small.light
- = link_to @commit.short_id, namespace_project_commit_path(@project.namespace, @project, @commit)
- &ndash;
- = truncate(@commit.title, length: 50)
- = link_to 'History', namespace_project_commits_path(@project.namespace, @project, @id), class: 'pull-right'
+%div#tree-content-holder.tree-content-holder
+ .tree-table-holder
+ %table.table#tree-slider{class: "table_#{@hex_path} tree-table table-striped" }
+ %thead
+ %tr
+ %th Name
+ %th Last Update
+ %th.hidden-xs
+ .pull-left Last Commit
+ .last-commit.hidden-sm.pull-left
+ &nbsp;
+ %i.fa.fa-angle-right
+ &nbsp;
+ %small.light
+ = link_to @commit.short_id, namespace_project_commit_path(@project.namespace, @project, @commit)
+ &ndash;
+ = truncate(@commit.title, length: 50)
+ = link_to 'History', namespace_project_commits_path(@project.namespace, @project, @id), class: 'pull-right'
- - if @path.present?
- %tr.tree-item
- %td.tree-item-file-name
- = link_to "..", namespace_project_tree_path(@project.namespace, @project, up_dir_path), class: 'prepend-left-10'
- %td
- %td.hidden-xs
+ - if @path.present?
+ %tr.tree-item
+ %td.tree-item-file-name
+ = link_to "..", namespace_project_tree_path(@project.namespace, @project, up_dir_path), class: 'prepend-left-10'
+ %td
+ %td.hidden-xs
- = render_tree(tree)
+ = render_tree(tree)
- if tree.readme
= render "projects/tree/readme", readme: tree.readme
diff --git a/app/views/projects/wikis/pages.html.haml b/app/views/projects/wikis/pages.html.haml
index 03e6a522b25..d179a1abec1 100644
--- a/app/views/projects/wikis/pages.html.haml
+++ b/app/views/projects/wikis/pages.html.haml
@@ -3,6 +3,7 @@
= render 'nav'
.gray-content-block
+ = render 'main_links'
%h3.page-title
All Pages
%ul.content-list
diff --git a/app/views/search/_form.html.haml b/app/views/search/_form.html.haml
index 3938c545cad..17b0981f073 100644
--- a/app/views/search/_form.html.haml
+++ b/app/views/search/_form.html.haml
@@ -6,7 +6,7 @@
.search-holder.clearfix
.input-group
- = search_field_tag :search, params[:search], placeholder: "Search for projects, issues etc", class: "form-control search-text-input", id: "dashboard_search", autofocus: true
+ = search_field_tag :search, params[:search], placeholder: "Search for projects, issues etc", class: "form-control search-text-input", id: "dashboard_search", autofocus: true, spellcheck: false
%span.input-group-btn
= button_tag 'Search', class: "btn btn-primary"
- unless params[:snippets].eql? 'true'
diff --git a/app/views/shared/issuable/_search_form.html.haml b/app/views/shared/issuable/_search_form.html.haml
index 58c3de64b77..3a5ad00aa91 100644
--- a/app/views/shared/issuable/_search_form.html.haml
+++ b/app/views/shared/issuable/_search_form.html.haml
@@ -1,6 +1,6 @@
= form_tag(path, method: :get, id: "issue_search_form", class: 'pull-left issue-search-form') do
.append-right-10.hidden-xs.hidden-sm
- = search_field_tag :issue_search, params[:issue_search], { placeholder: 'Filter by title or description', class: 'form-control issue_search search-text-input' }
+ = search_field_tag :issue_search, params[:issue_search], { placeholder: 'Filter by title or description', class: 'form-control issue_search search-text-input', spellcheck: false }
= hidden_field_tag :state, params['state']
= hidden_field_tag :scope, params['scope']
= hidden_field_tag :assignee_id, params['assignee_id']
diff --git a/app/views/shared/projects/_list.html.haml b/app/views/shared/projects/_list.html.haml
index 16e1d8421de..357cfd6a370 100644
--- a/app/views/shared/projects/_list.html.haml
+++ b/app/views/shared/projects/_list.html.haml
@@ -2,11 +2,12 @@
- avatar = true unless local_assigns[:avatar] == false
- stars = true unless local_assigns[:stars] == false
- ci = false unless local_assigns[:ci] == true
+- skip_namespace = false unless local_assigns[:skip_namespace] == true
%ul.projects-list
- projects.each_with_index do |project, i|
- css_class = (i >= projects_limit) ? 'hide' : nil
- = render "shared/projects/project", project: project,
+ = render "shared/projects/project", project: project, skip_namespace: skip_namespace,
avatar: avatar, stars: stars, css_class: css_class, ci: ci
- if projects.size > projects_limit
diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml
index e67e5a8a638..aee839b44e7 100644
--- a/app/views/shared/projects/_project.html.haml
+++ b/app/views/shared/projects/_project.html.haml
@@ -1,6 +1,7 @@
- avatar = true unless local_assigns[:avatar] == false
- stars = true unless local_assigns[:stars] == false
- ci = false unless local_assigns[:ci] == true
+- skip_namespace = false unless local_assigns[:skip_namespace] == true
- css_class = '' unless local_assigns[:css_class]
- css_class += " no-description" unless project.description.present?
%li.project-row{ class: css_class }
@@ -11,7 +12,7 @@
= project_icon(project, alt: '', class: 'avatar project-avatar s46')
%span.project-full-name
%span.namespace-name
- - if project.namespace
+ - if project.namespace && !skip_namespace
= project.namespace.human_name
\/
%span.project-name.filter-title
diff --git a/config/mail_room.yml.example b/config/mail_room.yml.example
index ed4a5193a6a..bb624e8a187 100644
--- a/config/mail_room.yml.example
+++ b/config/mail_room.yml.example
@@ -12,8 +12,10 @@
# :email: "gitlab-incoming@gmail.com"
# # Email account password
# :password: "password"
+
# # The name of the mailbox where incoming mail will end up. Usually "inbox".
# :name: "inbox"
+
# # Always "sidekiq".
# :delivery_method: sidekiq
# # Always true.
@@ -25,5 +27,13 @@
# :namespace: resque:gitlab
# # Always "incoming_email".
# :queue: incoming_email
- # # Always "EmailReceiverWorker"
+ # # Always "EmailReceiverWorker".
# :worker: EmailReceiverWorker
+
+ # # Always "redis".
+ # :arbitration_method: redis
+ # :arbitration_options:
+ # # The URL to the Redis server. Should match the URL in config/resque.yml.
+ # :redis_url: redis://localhost:6379
+ # # Always "mail_room:gitlab".
+ # :namespace: mail_room:gitlab
diff --git a/db/migrate/20151008123042_add_type_and_description_to_builds.rb b/db/migrate/20151008123042_add_type_and_description_to_builds.rb
new file mode 100644
index 00000000000..c72b1c611c6
--- /dev/null
+++ b/db/migrate/20151008123042_add_type_and_description_to_builds.rb
@@ -0,0 +1,9 @@
+class AddTypeAndDescriptionToBuilds < ActiveRecord::Migration
+ def change
+ add_column :ci_builds, :type, :string
+ add_column :ci_builds, :target_url, :string
+ add_column :ci_builds, :description, :string
+ add_index :ci_builds, [:commit_id, :type, :ref]
+ add_index :ci_builds, [:commit_id, :type, :name, :ref]
+ end
+end
diff --git a/db/migrate/20151008130321_migrate_name_to_description_for_builds.rb b/db/migrate/20151008130321_migrate_name_to_description_for_builds.rb
new file mode 100644
index 00000000000..f5c44babd84
--- /dev/null
+++ b/db/migrate/20151008130321_migrate_name_to_description_for_builds.rb
@@ -0,0 +1,5 @@
+class MigrateNameToDescriptionForBuilds < ActiveRecord::Migration
+ def change
+ execute("UPDATE ci_builds SET type='Ci::Build' WHERE type IS NULL")
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index c5c462c2e57..7a11dfca034 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20151007120511) do
+ActiveRecord::Schema.define(version: 20151008130321) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -103,9 +103,14 @@ ActiveRecord::Schema.define(version: 20151007120511) do
t.boolean "tag"
t.string "ref"
t.integer "user_id"
+ t.string "type"
+ t.string "target_url"
+ t.string "description"
end
add_index "ci_builds", ["commit_id", "stage_idx", "created_at"], name: "index_ci_builds_on_commit_id_and_stage_idx_and_created_at", using: :btree
+ add_index "ci_builds", ["commit_id", "type", "name", "ref"], name: "index_ci_builds_on_commit_id_and_type_and_name_and_ref", using: :btree
+ add_index "ci_builds", ["commit_id", "type", "ref"], name: "index_ci_builds_on_commit_id_and_type_and_ref", using: :btree
add_index "ci_builds", ["commit_id"], name: "index_ci_builds_on_commit_id", using: :btree
add_index "ci_builds", ["project_id", "commit_id"], name: "index_ci_builds_on_project_id_and_commit_id", using: :btree
add_index "ci_builds", ["project_id"], name: "index_ci_builds_on_project_id", using: :btree
diff --git a/doc/api/commits.md b/doc/api/commits.md
index eb8d6a43592..9f72adc6ed9 100644
--- a/doc/api/commits.md
+++ b/doc/api/commits.md
@@ -62,7 +62,8 @@ Parameters:
"authored_date": "2012-09-20T09:06:12+03:00",
"parent_ids": [
"ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba"
- ]
+ ],
+ "status": "running"
}
```
@@ -156,3 +157,84 @@ Parameters:
"line_type": "new"
}
```
+
+## Get the status of a commit
+
+Get the statuses of a commit in a project.
+
+```
+GET /projects/:id/repository/commits/:sha/statuses
+```
+
+Parameters:
+
+- `id` (required) - The ID of a project
+- `sha` (required) - The commit SHA
+- `ref` (optional) - Filter by ref name, it can be branch or tag
+- `stage` (optional) - Filter by stage
+- `name` (optional) - Filer by status name, eg. jenkins
+- `all` (optional) - The flag to return all statuses, not only latest ones
+
+```json
+[
+ {
+ "id": 13,
+ "sha": "b0b3a907f41409829b307a28b82fdbd552ee5a27",
+ "ref": "test",
+ "status": "success",
+ "name": "ci/jenkins",
+ "target_url": "http://jenkins/project/url",
+ "description": "Jenkins success",
+ "created_at": "2015-10-12T09:47:16.250Z",
+ "started_at": "2015-10-12T09:47:16.250Z"",
+ "finished_at": "2015-10-12T09:47:16.262Z",
+ "author": {
+ "id": 1,
+ "username": "admin",
+ "email": "admin@local.host",
+ "name": "Administrator",
+ "blocked": false,
+ "created_at": "2012-04-29T08:46:00Z"
+ }
+ }
+]
+```
+
+## Post the status to commit
+
+Adds or updates a status of a commit.
+
+```
+POST /projects/:id/statuses/:sha
+```
+
+- `id` (required) - The ID of a project
+- `sha` (required) - The commit SHA
+- `state` (required) - The state of the status. Can be: pending, running, success, failed, canceled
+- `ref` (optional) - The ref (branch or tag) to which the status refers
+- `name` or `context` (optional) - The label to differentiate this status from the status of other systems. Default: "default"
+- `target_url` (optional) - The target URL to associate with this status
+- `description` (optional) - The short description of the status
+
+```json
+{
+ "id": 13,
+ "sha": "b0b3a907f41409829b307a28b82fdbd552ee5a27",
+ "ref": "test",
+ "status": "success",
+ "name": "ci/jenkins",
+ "target_url": "http://jenkins/project/url",
+ "description": "Jenkins success",
+ "created_at": "2015-10-12T09:47:16.250Z",
+ "started_at": "2015-10-12T09:47:16.250Z"",
+ "finished_at": "2015-10-12T09:47:16.262Z",
+ "author": {
+ "id": 1,
+ "username": "admin",
+ "email": "admin@local.host",
+ "name": "Administrator",
+ "blocked": false,
+ "created_at": "2012-04-29T08:46:00Z"
+ }
+}
+```
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index bb551fc67f7..ffa7f2cdf14 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -188,6 +188,7 @@ Parameters:
- `title` (required) - Title of MR
- `description` (optional) - Description of MR
- `target_project_id` (optional) - The target project (numeric id)
+- `labels` (optional) - Labels for MR as a comma-separated list
```json
{
@@ -239,6 +240,7 @@ Parameters:
- `title` - Title of MR
- `description` - Description of MR
- `state_event` - New state (close|reopen|merge)
+- `labels` (optional) - Labels for MR as a comma-separated list
```json
{
diff --git a/doc/customization/welcome_message.md b/doc/customization/welcome_message.md
index 6c141d1fb7a..e993230bb88 100644
--- a/doc/customization/welcome_message.md
+++ b/doc/customization/welcome_message.md
@@ -8,31 +8,5 @@ It is possible to add a markdown-formatted welcome message to your GitLab
sign-in page. Users of GitLab Enterprise Edition should use the [branded login
page feature](/ee/customization/branded_login_page.html) instead.
-## Omnibus-gitlab example
-
-In `/etc/gitlab/gitlab.rb`:
-
-```ruby
-gitlab_rails['extra_sign_in_text'] = <<'EOS'
-# ACME GitLab
-Welcome to the [ACME](http://www.example.com) GitLab server!
-EOS
-```
-
-Run `sudo gitlab-ctl reconfigure` for changes to take effect.
-
-## Installation from source
-
-In `/home/git/gitlab/config/gitlab.yml`:
-
-```yaml
-# snip
-production:
- # snip
- extra:
- sign_in_text: |
- # ACME GitLab
- Welcome to the [ACME](http://www.example.com) GitLab server!
-```
-
-Run `sudo service gitlab reload` for the change to take effect.
+The welcome message (extra_sign_in_text) can now be set/changed in the Admin UI.
+Admin area > Settings \ No newline at end of file
diff --git a/doc/incoming_email/README.md b/doc/incoming_email/README.md
index 87267423471..aafa2345fab 100644
--- a/doc/incoming_email/README.md
+++ b/doc/incoming_email/README.md
@@ -2,10 +2,6 @@
GitLab can be set up to allow users to comment on issues and merge requests by replying to notification emails.
-**Warning**: Do not enable Reply by email if you have **multiple GitLab application servers**.
-Due to an issue with the way incoming emails are read from the mail server, every incoming reply-by-email email will result in as many comments being created as you have application servers.
-[A fix is being worked on.](https://github.com/tpitale/mail_room/issues/46)
-
## Get a mailbox
Reply by email requires an IMAP-enabled email account, with a provider or server that supports [email sub-addressing](https://en.wikipedia.org/wiki/Email_address#Sub-addressing). Sub-addressing is a feature where any email to `user+some_arbitrary_tag@example.com` will end up in the mailbox for `user@example.com`, and is supported by providers such as Gmail, Yahoo! Mail, Outlook.com and iCloud, as well as the Postfix mail server which you can run on-premises.
@@ -118,8 +114,10 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow [these
:email: "incoming"
# Email account password
:password: "[REDACTED]"
+
# The name of the mailbox where incoming mail will end up. Usually "inbox".
:name: "inbox"
+
# Always "sidekiq".
:delivery_method: sidekiq
# Always true.
@@ -133,6 +131,14 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow [these
:queue: incoming_email
# Always "EmailReceiverWorker"
:worker: EmailReceiverWorker
+
+ # Always "redis".
+ :arbitration_method: redis
+ :arbitration_options:
+ # The URL to the Redis server. Should match the URL in config/resque.yml.
+ :redis_url: redis://localhost:6379
+ # Always "mail_room:gitlab".
+ :namespace: mail_room:gitlab
```
```yaml
@@ -151,8 +157,10 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow [these
:email: "gitlab-incoming@gmail.com"
# Email account password
:password: "[REDACTED]"
+
# The name of the mailbox where incoming mail will end up. Usually "inbox".
:name: "inbox"
+
# Always "sidekiq".
:delivery_method: sidekiq
# Always true.
@@ -166,6 +174,14 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow [these
:queue: incoming_email
# Always "EmailReceiverWorker"
:worker: EmailReceiverWorker
+
+ # Always "redis".
+ :arbitration_method: redis
+ :arbitration_options:
+ # The URL to the Redis server. Should match the URL in config/resque.yml.
+ :redis_url: redis://localhost:6379
+ # Always "mail_room:gitlab".
+ :namespace: mail_room:gitlab
```
5. Edit the init script configuration at `/etc/default/gitlab` to enable `mail_room`:
@@ -228,8 +244,10 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow [these
:email: "gitlab-incoming@gmail.com"
# Email account password
:password: "[REDACTED]"
+
# The name of the mailbox where incoming mail will end up. Usually "inbox".
:name: "inbox"
+
# Always "sidekiq".
:delivery_method: sidekiq
# Always true.
@@ -243,6 +261,14 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow [these
:queue: incoming_email
# Always "EmailReceiverWorker"
:worker: EmailReceiverWorker
+
+ # Always "redis".
+ :arbitration_method: redis
+ :arbitration_options:
+ # The URL to the Redis server. Should match the URL in config/resque.yml.
+ :redis_url: redis://localhost:6379
+ # Always "mail_room:gitlab".
+ :namespace: mail_room:gitlab
```
4. Uncomment the `mail_room` line in your `Procfile`:
diff --git a/doc/update/patch_versions.md b/doc/update/patch_versions.md
index a66a863f6c4..da719229ab6 100644
--- a/doc/update/patch_versions.md
+++ b/doc/update/patch_versions.md
@@ -23,9 +23,11 @@ sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
cd /home/git/gitlab
sudo -u git -H git fetch --all
sudo -u git -H git checkout -- Gemfile.lock db/schema.rb
-LATEST_TAG=$(git describe --tags `git rev-list --tags --max-count=1`)
-sudo -u git -H git checkout $LATEST_TAG -b $LATEST_TAG
+sudo -u git -H git checkout LATEST_TAG -b LATEST_TAG
```
+Replace `LATEST_TAG` with the latest GitLab tag you want to update to, for example `v8.0.3`.
+Use `git tag -l 'v*.[0-9]' --sort='v:refname'` to see a list of all tags.
+Make sure to update patch versions only (check your current version with `cat VERSION`)
### 3. Update gitlab-shell to the corresponding version
diff --git a/doc/web_hooks/web_hooks.md b/doc/web_hooks/web_hooks.md
index c185ccfcac3..ef99a69f60a 100644
--- a/doc/web_hooks/web_hooks.md
+++ b/doc/web_hooks/web_hooks.md
@@ -314,7 +314,8 @@ X-Gitlab-Event: Note Hook
"name": "John Smith",
"email": "john@example.com"
}
- }
+ },
+ "work_in_progress": false
}
}
```
@@ -500,6 +501,7 @@ X-Gitlab-Event: Merge Request Hook
"email": "gitlabdev@dv6700.(none)"
}
},
+ "work_in_progress": false,
"url": "http://example.com/diaspora/merge_requests/1",
"action": "open"
}
@@ -537,4 +539,4 @@ When you press 'Test Hook' in GitLab, you should see something like this in the
{"before":"077a85dd266e6f3573ef7e9ef8ce3343ad659c4e","after":"95cd4a99e93bc4bbabacfa2cd10e6725b1403c60",<SNIP>}
example.com - - [14/May/2014:07:45:26 EDT] "POST / HTTP/1.1" 200 0
- -> /
-``` \ No newline at end of file
+```
diff --git a/features/steps/project/commits/commits.rb b/features/steps/project/commits/commits.rb
index ae5f90004e6..a3cb83880e3 100644
--- a/features/steps/project/commits/commits.rb
+++ b/features/steps/project/commits/commits.rb
@@ -118,6 +118,6 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps
step 'I see builds list' do
expect(page).to have_content "build: pending"
- expect(page).to have_content "Builds for master"
+ expect(page).to have_content "Latest builds"
end
end
diff --git a/lib/api/api.rb b/lib/api/api.rb
index c09488d3547..afc0402f9e1 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -46,6 +46,7 @@ module API
mount Services
mount Files
mount Commits
+ mount CommitStatus
mount Namespaces
mount Branches
mount Labels
diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb
new file mode 100644
index 00000000000..2c0596c9dfb
--- /dev/null
+++ b/lib/api/commit_statuses.rb
@@ -0,0 +1,80 @@
+require 'mime/types'
+
+module API
+ # Project commit statuses API
+ class CommitStatus < Grape::API
+ resource :projects do
+ before { authenticate! }
+
+ # Get a commit's statuses
+ #
+ # Parameters:
+ # id (required) - The ID of a project
+ # sha (required) - The commit hash
+ # ref (optional) - The ref
+ # stage (optional) - The stage
+ # name (optional) - The name
+ # all (optional) - Show all statuses, default: false
+ # Examples:
+ # GET /projects/:id/repository/commits/:sha/statuses
+ get ':id/repository/commits/:sha/statuses' do
+ authorize! :read_commit_statuses, user_project
+ sha = params[:sha]
+ ci_commit = user_project.ci_commit(sha)
+ not_found! 'Commit' unless ci_commit
+ statuses = ci_commit.statuses
+ statuses = statuses.latest unless parse_boolean(params[:all])
+ statuses = statuses.where(ref: params[:ref]) if params[:ref].present?
+ statuses = statuses.where(stage: params[:stage]) if params[:stage].present?
+ statuses = statuses.where(name: params[:name]) if params[:name].present?
+ present paginate(statuses), with: Entities::CommitStatus
+ end
+
+ # Post status to commit
+ #
+ # Parameters:
+ # id (required) - The ID of a project
+ # sha (required) - The commit hash
+ # ref (optional) - The ref
+ # state (required) - The state of the status. Can be: pending, running, success, error or failure
+ # target_url (optional) - The target URL to associate with this status
+ # description (optional) - A short description of the status
+ # name or context (optional) - A string label to differentiate this status from the status of other systems. Default: "default"
+ # Examples:
+ # POST /projects/:id/statuses/:sha
+ post ':id/statuses/:sha' do
+ authorize! :create_commit_status, user_project
+ required_attributes! [:state]
+ attrs = attributes_for_keys [:ref, :target_url, :description, :context, :name]
+ commit = @project.commit(params[:sha])
+ not_found! 'Commit' unless commit
+
+ ci_commit = @project.ensure_ci_commit(commit.sha)
+
+ name = params[:name] || params[:context]
+ status = GenericCommitStatus.running_or_pending.find_by(commit: ci_commit, name: name, ref: params[:ref])
+ status ||= GenericCommitStatus.new(commit: ci_commit, user: current_user)
+ status.update(attrs)
+
+ case params[:state].to_s
+ when 'running'
+ status.run
+ when 'success'
+ status.success
+ when 'failed'
+ status.drop
+ when 'canceled'
+ status.cancel
+ else
+ status.status = params[:state].to_s
+ end
+
+ if status.save
+ present status, with: Entities::CommitStatus
+ else
+ render_validation_error!(status)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 9620d36ac41..519072d0157 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -149,6 +149,7 @@ module API
class RepoCommitDetail < RepoCommit
expose :parent_ids, :committed_date, :authored_date
+ expose :status
end
class ProjectSnippet < Grape::Entity
@@ -228,6 +229,12 @@ module API
expose :created_at
end
+ class CommitStatus < Grape::Entity
+ expose :id, :sha, :ref, :status, :name, :target_url, :description,
+ :created_at, :started_at, :finished_at
+ expose :author, using: Entities::UserBasic
+ end
+
class Event < Grape::Entity
expose :title, :project_id, :action_name
expose :target_id, :target_type, :author_id
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index 63ea2f05438..f3a59fadf24 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -99,7 +99,7 @@ module API
# id (required) - The ID of a project - this will be the source of the merge request
# source_branch (required) - The source branch
# target_branch (required) - The target branch
- # target_project - The target project of the merge request defaults to the :id of the project
+ # target_project_id - The target project of the merge request defaults to the :id of the project
# assignee_id - Assignee user ID
# title (required) - Title of MR
# description - Description of MR
diff --git a/lib/ci/api/entities.rb b/lib/ci/api/entities.rb
index f47bc1236b8..b80c0b8b273 100644
--- a/lib/ci/api/entities.rb
+++ b/lib/ci/api/entities.rb
@@ -2,7 +2,7 @@ module Ci
module API
module Entities
class Commit < Grape::Entity
- expose :id, :ref, :sha, :project_id, :before_sha, :created_at
+ expose :id, :sha, :project_id, :created_at
expose :status, :finished_at, :duration
expose :git_commit_message, :git_author_name, :git_author_email
end
@@ -12,7 +12,7 @@ module Ci
end
class Build < Grape::Entity
- expose :id, :commands, :ref, :sha, :project_id, :repo_url,
+ expose :id, :commands, :ref, :sha, :status, :project_id, :repo_url,
:before_sha, :allow_git_fetch, :project_name
expose :options do |model|
diff --git a/lib/gitlab/ldap/user.rb b/lib/gitlab/ldap/user.rb
index 1ea7751e27d..4be99dd88c2 100644
--- a/lib/gitlab/ldap/user.rb
+++ b/lib/gitlab/ldap/user.rb
@@ -35,7 +35,7 @@ module Gitlab
end
def find_by_email
- ::User.find_by(email: auth_hash.email)
+ ::User.find_by(email: auth_hash.email.downcase)
end
def update_user_attributes
diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb
index 21b582afba4..2fcd70182b9 100644
--- a/spec/factories/ci/builds.rb
+++ b/spec/factories/ci/builds.rb
@@ -27,6 +27,7 @@
FactoryGirl.define do
factory :ci_build, class: Ci::Build do
+ name 'test'
ref 'master'
tag false
started_at 'Di 29. Okt 09:51:28 CET 2013'
diff --git a/spec/factories/commit_statuses.rb b/spec/factories/commit_statuses.rb
new file mode 100644
index 00000000000..52de437052d
--- /dev/null
+++ b/spec/factories/commit_statuses.rb
@@ -0,0 +1,15 @@
+FactoryGirl.define do
+ factory :commit_status, class: CommitStatus do
+ started_at 'Di 29. Okt 09:51:28 CET 2013'
+ finished_at 'Di 29. Okt 09:53:28 CET 2013'
+ name 'default'
+ status 'success'
+ description 'commit status'
+ commit factory: :ci_commit
+
+ factory :generic_commit_status, class: GenericCommitStatus do
+ name 'generic'
+ description 'external commit status'
+ end
+ end
+end
diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb
index 5da220859e3..cbb6360069b 100644
--- a/spec/features/commits_spec.rb
+++ b/spec/features/commits_spec.rb
@@ -12,6 +12,7 @@ describe "Commits" do
@ci_project = project.ensure_gitlab_ci_project
@commit = FactoryGirl.create :ci_commit, gl_project: project, sha: project.commit.sha
@build = FactoryGirl.create :ci_build, commit: @commit
+ @generic_status = FactoryGirl.create :generic_commit_status, commit: @commit
end
before do
diff --git a/spec/helpers/labels_helper_spec.rb b/spec/helpers/labels_helper_spec.rb
index fb70a36dc02..0c8d06b7059 100644
--- a/spec/helpers/labels_helper_spec.rb
+++ b/spec/helpers/labels_helper_spec.rb
@@ -14,11 +14,6 @@ describe LabelsHelper do
expect(label).not_to receive(:project)
link_to_label(label)
end
-
- it 'includes option for "No Label"' do
- result = project_labels_options(project)
- expect(result).to include('No Label')
- end
end
context 'without @project set' do
diff --git a/spec/javascripts/fixtures/line_highlighter.html.haml b/spec/javascripts/fixtures/line_highlighter.html.haml
index da1ebcdb23c..514877340e4 100644
--- a/spec/javascripts/fixtures/line_highlighter.html.haml
+++ b/spec/javascripts/fixtures/line_highlighter.html.haml
@@ -1,4 +1,4 @@
-#tree-content-holder
+#blob-content-holder
.file-content
.line-numbers
- 1.upto(25) do |i|
diff --git a/spec/javascripts/line_highlighter_spec.js.coffee b/spec/javascripts/line_highlighter_spec.js.coffee
index 57453c716a5..a073f21e7bc 100644
--- a/spec/javascripts/line_highlighter_spec.js.coffee
+++ b/spec/javascripts/line_highlighter_spec.js.coffee
@@ -39,7 +39,7 @@ describe 'LineHighlighter', ->
expect(spy).toHaveBeenPrevented()
it 'handles garbage input from the hash', ->
- func = -> new LineHighlighter('#tree-content-holder')
+ func = -> new LineHighlighter('#blob-content-holder')
expect(func).not.toThrow()
describe '#clickHandler', ->
diff --git a/spec/lib/gitlab/ldap/user_spec.rb b/spec/lib/gitlab/ldap/user_spec.rb
index fd2e5f6d0e1..b5b56a34952 100644
--- a/spec/lib/gitlab/ldap/user_spec.rb
+++ b/spec/lib/gitlab/ldap/user_spec.rb
@@ -13,6 +13,17 @@ describe Gitlab::LDAP::User do
let(:auth_hash) do
OmniAuth::AuthHash.new(uid: 'my-uid', provider: 'ldapmain', info: info)
end
+ let(:ldap_user_upper_case) { Gitlab::LDAP::User.new(auth_hash_upper_case) }
+ let(:info_upper_case) do
+ {
+ name: 'John',
+ email: 'John@Example.com', # Email address has upper case chars
+ nickname: 'john'
+ }
+ end
+ let(:auth_hash_upper_case) do
+ OmniAuth::AuthHash.new(uid: 'my-uid', provider: 'ldapmain', info: info_upper_case)
+ end
describe :changed? do
it "marks existing ldap user as changed" do
@@ -57,6 +68,16 @@ describe Gitlab::LDAP::User do
expect(existing_user.id).to eql ldap_user.gl_user.id
end
+ it 'connects to existing ldap user if the extern_uid changes and email address has upper case characters' do
+ existing_user = create(:omniauth_user, email: 'john@example.com', extern_uid: 'old-uid', provider: 'ldapmain')
+ expect{ ldap_user_upper_case.save }.not_to change{ User.count }
+
+ existing_user.reload
+ expect(existing_user.ldap_identity.extern_uid).to eql 'my-uid'
+ expect(existing_user.ldap_identity.provider).to eql 'ldapmain'
+ expect(existing_user.id).to eql ldap_user.gl_user.id
+ end
+
it 'maintains an identity per provider' do
existing_user = create(:omniauth_user, email: 'john@example.com', provider: 'twitter')
expect(existing_user.identities.count).to eql(1)
diff --git a/spec/models/ci/build_spec.rb b/spec/models/build_spec.rb
index da56f6e31ae..d875015b991 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/build_spec.rb
@@ -30,17 +30,9 @@ describe Ci::Build do
let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project }
let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project }
let(:build) { FactoryGirl.create :ci_build, commit: commit }
- subject { build }
- it { is_expected.to belong_to(:commit) }
- it { is_expected.to belong_to(:user) }
- it { is_expected.to validate_presence_of :status }
it { is_expected.to validate_presence_of :ref }
- it { is_expected.to respond_to :success? }
- it { is_expected.to respond_to :failed? }
- it { is_expected.to respond_to :running? }
- it { is_expected.to respond_to :pending? }
it { is_expected.to respond_to :trace_html }
describe :first_pending do
@@ -67,72 +59,6 @@ describe Ci::Build do
end
end
- describe :started? do
- subject { build.started? }
-
- context 'without started_at' do
- before { build.started_at = nil }
-
- it { is_expected.to be_falsey }
- end
-
- %w(running success failed).each do |status|
- context "if build status is #{status}" do
- before { build.status = status }
-
- it { is_expected.to be_truthy }
- end
- end
-
- %w(pending canceled).each do |status|
- context "if build status is #{status}" do
- before { build.status = status }
-
- it { is_expected.to be_falsey }
- end
- end
- end
-
- describe :active? do
- subject { build.active? }
-
- %w(pending running).each do |state|
- context "if build.status is #{state}" do
- before { build.status = state }
-
- it { is_expected.to be_truthy }
- end
- end
-
- %w(success failed canceled).each do |state|
- context "if build.status is #{state}" do
- before { build.status = state }
-
- it { is_expected.to be_falsey }
- end
- end
- end
-
- describe :complete? do
- subject { build.complete? }
-
- %w(success failed canceled).each do |state|
- context "if build.status is #{state}" do
- before { build.status = state }
-
- it { is_expected.to be_truthy }
- end
- end
-
- %w(pending running).each do |state|
- context "if build.status is #{state}" do
- before { build.status = state }
-
- it { is_expected.to be_falsey }
- end
- end
- end
-
describe :ignored? do
subject { build.ignored? }
@@ -200,31 +126,6 @@ describe Ci::Build do
it { is_expected.to eq(commit.project.timeout) }
end
- describe :duration do
- subject { build.duration }
-
- it { is_expected.to eq(120.0) }
-
- context 'if the building process has not started yet' do
- before do
- build.started_at = nil
- build.finished_at = nil
- end
-
- it { is_expected.to be_nil }
- end
-
- context 'if the building process has started' do
- before do
- build.started_at = Time.now - 1.minute
- build.finished_at = nil
- end
-
- it { is_expected.to be_a(Float) }
- it { is_expected.to be > 0.0 }
- end
- end
-
describe :options do
let(:options) do
{
@@ -239,18 +140,6 @@ describe Ci::Build do
it { is_expected.to eq(options) }
end
- describe :sha do
- subject { build.sha }
-
- it { is_expected.to eq(commit.sha) }
- end
-
- describe :short_sha do
- subject { build.short_sha }
-
- it { is_expected.to eq(commit.short_sha) }
- end
-
describe :allow_git_fetch do
subject { build.allow_git_fetch }
diff --git a/spec/models/ci/commit_spec.rb b/spec/models/ci/commit_spec.rb
index acff1ddf0fc..330971174fb 100644
--- a/spec/models/ci/commit_spec.rb
+++ b/spec/models/ci/commit_spec.rb
@@ -23,6 +23,8 @@ describe Ci::Commit do
let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project }
it { is_expected.to belong_to(:gl_project) }
+ it { is_expected.to have_many(:statuses) }
+ it { is_expected.to have_many(:trigger_requests) }
it { is_expected.to have_many(:builds) }
it { is_expected.to validate_presence_of :sha }
@@ -47,10 +49,12 @@ describe Ci::Commit do
@second = FactoryGirl.create :ci_build, commit: commit
end
- it "creates new build" do
+ it "creates only a new build" do
expect(commit.builds.count(:all)).to eq 2
+ expect(commit.statuses.count(:all)).to eq 2
commit.retry
expect(commit.builds.count(:all)).to eq 3
+ expect(commit.statuses.count(:all)).to eq 3
end
end
@@ -78,8 +82,8 @@ describe Ci::Commit do
subject { commit.stage }
before do
- @second = FactoryGirl.create :ci_build, commit: commit, name: 'deploy', stage: 'deploy', stage_idx: 1, status: :pending
- @first = FactoryGirl.create :ci_build, commit: commit, name: 'test', stage: 'test', stage_idx: 0, status: :pending
+ @second = FactoryGirl.create :commit_status, commit: commit, name: 'deploy', stage: 'deploy', stage_idx: 1, status: 'pending'
+ @first = FactoryGirl.create :commit_status, commit: commit, name: 'test', stage: 'test', stage_idx: 0, status: 'pending'
end
it 'returns first running stage' do
@@ -88,7 +92,7 @@ describe Ci::Commit do
context 'first build succeeded' do
before do
- @first.update_attributes(status: :success)
+ @first.success
end
it 'returns last running stage' do
@@ -98,8 +102,8 @@ describe Ci::Commit do
context 'all builds succeeded' do
before do
- @first.update_attributes(status: :success)
- @second.update_attributes(status: :success)
+ @first.success
+ @second.success
end
it 'returns nil' do
@@ -111,6 +115,33 @@ describe Ci::Commit do
describe :create_next_builds do
end
+ describe :refs do
+ subject { commit.refs }
+
+ before do
+ FactoryGirl.create :commit_status, commit: commit, name: 'deploy'
+ FactoryGirl.create :commit_status, commit: commit, name: 'deploy', ref: 'develop'
+ FactoryGirl.create :commit_status, commit: commit, name: 'deploy', ref: 'master'
+ end
+
+ it 'returns all refs' do
+ is_expected.to contain_exactly('master', 'develop', nil)
+ end
+ end
+
+ describe :retried do
+ subject { commit.retried }
+
+ before do
+ @commit1 = FactoryGirl.create :ci_build, commit: commit, name: 'deploy'
+ @commit2 = FactoryGirl.create :ci_build, commit: commit, name: 'deploy'
+ end
+
+ it 'returns old builds' do
+ is_expected.to contain_exactly(@commit1)
+ end
+ end
+
describe :create_builds do
let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project }
@@ -194,9 +225,10 @@ describe Ci::Commit do
it 'rebuilds commit' do
expect(commit.status).to eq('skipped')
expect(create_builds(trigger_request)).to be_truthy
- commit.builds.reload
- expect(commit.builds.size).to eq(2)
- expect(commit.status).to eq('pending')
+
+ # since everything in Ci::Commit is cached we need to fetch a new object
+ new_commit = Ci::Commit.find_by_id(commit.id)
+ expect(new_commit.status).to eq('pending')
end
end
end
@@ -252,10 +284,10 @@ describe Ci::Commit do
describe :should_create_next_builds? do
before do
- @build1 = FactoryGirl.create :ci_build, commit: commit, name: 'build1', ref: 'master', tag: false, status: :success
- @build2 = FactoryGirl.create :ci_build, commit: commit, name: 'build1', ref: 'develop', tag: false, status: :failed
- @build3 = FactoryGirl.create :ci_build, commit: commit, name: 'build1', ref: 'master', tag: true, status: :failed
- @build4 = FactoryGirl.create :ci_build, commit: commit, name: 'build4', ref: 'master', tag: false, status: :success
+ @build1 = FactoryGirl.create :ci_build, commit: commit, name: 'build1', ref: 'master', tag: false, status: 'success'
+ @build2 = FactoryGirl.create :ci_build, commit: commit, name: 'build1', ref: 'develop', tag: false, status: 'failed'
+ @build3 = FactoryGirl.create :ci_build, commit: commit, name: 'build1', ref: 'master', tag: true, status: 'failed'
+ @build4 = FactoryGirl.create :ci_build, commit: commit, name: 'build4', ref: 'master', tag: false, status: 'success'
end
context 'for success' do
@@ -266,7 +298,7 @@ describe Ci::Commit do
context 'for failed' do
before do
- @build4.update_attributes(status: :failed)
+ @build4.update_attributes(status: 'failed')
end
it 'to not create' do
@@ -286,7 +318,7 @@ describe Ci::Commit do
context 'for running' do
before do
- @build4.update_attributes(status: :running)
+ @build4.update_attributes(status: 'running')
end
it 'to not create' do
@@ -296,7 +328,7 @@ describe Ci::Commit do
context 'for retried' do
before do
- @build5 = FactoryGirl.create :ci_build, commit: commit, name: 'build4', ref: 'master', tag: false, status: :failed
+ @build5 = FactoryGirl.create :ci_build, commit: commit, name: 'build4', ref: 'master', tag: false, status: 'failed'
end
it 'to not create' do
diff --git a/spec/models/ci/project_services/mail_service_spec.rb b/spec/models/ci/project_services/mail_service_spec.rb
index 04e870dce7f..d9b3d34ff15 100644
--- a/spec/models/ci/project_services/mail_service_spec.rb
+++ b/spec/models/ci/project_services/mail_service_spec.rb
@@ -35,7 +35,7 @@ describe Ci::MailService do
let(:project) { FactoryGirl.create(:ci_project, email_add_pusher: true) }
let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) }
- let(:build) { FactoryGirl.create(:ci_build, status: :failed, commit: commit, user: user) }
+ let(:build) { FactoryGirl.create(:ci_build, status: 'failed', commit: commit, user: user) }
before do
allow(mail).to receive_messages(
@@ -58,7 +58,7 @@ describe Ci::MailService do
let(:project) { FactoryGirl.create(:ci_project, email_add_pusher: true, email_only_broken_builds: false) }
let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) }
- let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit, user: user) }
+ let(:build) { FactoryGirl.create(:ci_build, status: 'success', commit: commit, user: user) }
before do
allow(mail).to receive_messages(
@@ -86,7 +86,7 @@ describe Ci::MailService do
end
let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) }
- let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit, user: user) }
+ let(:build) { FactoryGirl.create(:ci_build, status: 'success', commit: commit, user: user) }
before do
allow(mail).to receive_messages(
@@ -115,7 +115,7 @@ describe Ci::MailService do
end
let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) }
- let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit, user: user) }
+ let(:build) { FactoryGirl.create(:ci_build, status: 'success', commit: commit, user: user) }
before do
allow(mail).to receive_messages(
@@ -144,7 +144,7 @@ describe Ci::MailService do
end
let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) }
- let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit, user: user) }
+ let(:build) { FactoryGirl.create(:ci_build, status: 'success', commit: commit, user: user) }
before do
allow(mail).to receive_messages(
@@ -167,7 +167,7 @@ describe Ci::MailService do
end
let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) }
- let(:build) { FactoryGirl.create(:ci_build, status: :failed, commit: commit, user: user) }
+ let(:build) { FactoryGirl.create(:ci_build, status: 'failed', commit: commit, user: user) }
before do
allow(mail).to receive_messages(
diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb
new file mode 100644
index 00000000000..c96a606fdaa
--- /dev/null
+++ b/spec/models/commit_status_spec.rb
@@ -0,0 +1,164 @@
+require 'spec_helper'
+
+describe CommitStatus do
+ let(:commit) { FactoryGirl.create :ci_commit }
+ let(:commit_status) { FactoryGirl.create :commit_status, commit: commit }
+
+ it { is_expected.to belong_to(:commit) }
+ it { is_expected.to belong_to(:user) }
+ it { is_expected.to validate_presence_of(:name) }
+ it { is_expected.to validate_inclusion_of(:status).in_array(%w(pending running failed success canceled)) }
+
+ it { is_expected.to delegate_method(:sha).to(:commit) }
+ it { is_expected.to delegate_method(:short_sha).to(:commit) }
+ it { is_expected.to delegate_method(:gl_project).to(:commit) }
+
+ it { is_expected.to respond_to :success? }
+ it { is_expected.to respond_to :failed? }
+ it { is_expected.to respond_to :running? }
+ it { is_expected.to respond_to :pending? }
+
+ describe :author do
+ subject { commit_status.author }
+ before { commit_status.author = User.new }
+
+ it { is_expected.to eq(commit_status.user) }
+ end
+
+ describe :started? do
+ subject { commit_status.started? }
+
+ context 'without started_at' do
+ before { commit_status.started_at = nil }
+
+ it { is_expected.to be_falsey }
+ end
+
+ %w(running success failed).each do |status|
+ context "if commit status is #{status}" do
+ before { commit_status.status = status }
+
+ it { is_expected.to be_truthy }
+ end
+ end
+
+ %w(pending canceled).each do |status|
+ context "if commit status is #{status}" do
+ before { commit_status.status = status }
+
+ it { is_expected.to be_falsey }
+ end
+ end
+ end
+
+ describe :active? do
+ subject { commit_status.active? }
+
+ %w(pending running).each do |state|
+ context "if commit_status.status is #{state}" do
+ before { commit_status.status = state }
+
+ it { is_expected.to be_truthy }
+ end
+ end
+
+ %w(success failed canceled).each do |state|
+ context "if commit_status.status is #{state}" do
+ before { commit_status.status = state }
+
+ it { is_expected.to be_falsey }
+ end
+ end
+ end
+
+ describe :complete? do
+ subject { commit_status.complete? }
+
+ %w(success failed canceled).each do |state|
+ context "if commit_status.status is #{state}" do
+ before { commit_status.status = state }
+
+ it { is_expected.to be_truthy }
+ end
+ end
+
+ %w(pending running).each do |state|
+ context "if commit_status.status is #{state}" do
+ before { commit_status.status = state }
+
+ it { is_expected.to be_falsey }
+ end
+ end
+ end
+
+ describe :duration do
+ subject { commit_status.duration }
+
+ it { is_expected.to eq(120.0) }
+
+ context 'if the building process has not started yet' do
+ before do
+ commit_status.started_at = nil
+ commit_status.finished_at = nil
+ end
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'if the building process has started' do
+ before do
+ commit_status.started_at = Time.now - 1.minute
+ commit_status.finished_at = nil
+ end
+
+ it { is_expected.to be_a(Float) }
+ it { is_expected.to be > 0.0 }
+ end
+ end
+
+ describe :latest do
+ subject { CommitStatus.latest.order(:id) }
+
+ before do
+ @commit1 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: 'bb', status: 'running'
+ @commit2 = FactoryGirl.create :commit_status, commit: commit, name: 'cc', ref: 'cc', status: 'pending'
+ @commit3 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: 'cc', status: 'success'
+ @commit4 = FactoryGirl.create :commit_status, commit: commit, name: 'cc', ref: 'bb', status: 'success'
+ @commit5 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: 'bb', status: 'success'
+ end
+
+ it 'return unique statuses' do
+ is_expected.to eq([@commit2, @commit3, @commit4, @commit5])
+ end
+ end
+
+ describe :for_ref do
+ subject { CommitStatus.for_ref('bb').order(:id) }
+
+ before do
+ @commit1 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: 'bb', status: 'running'
+ @commit2 = FactoryGirl.create :commit_status, commit: commit, name: 'cc', ref: 'cc', status: 'pending'
+ @commit3 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: nil, status: 'success'
+ end
+
+ it 'return statuses with equal and nil ref set' do
+ is_expected.to eq([@commit1])
+ end
+ end
+
+ describe :running_or_pending do
+ subject { CommitStatus.running_or_pending.order(:id) }
+
+ before do
+ @commit1 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: 'bb', status: 'running'
+ @commit2 = FactoryGirl.create :commit_status, commit: commit, name: 'cc', ref: 'cc', status: 'pending'
+ @commit3 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: nil, status: 'success'
+ @commit4 = FactoryGirl.create :commit_status, commit: commit, name: 'dd', ref: nil, status: 'failed'
+ @commit5 = FactoryGirl.create :commit_status, commit: commit, name: 'ee', ref: nil, status: 'canceled'
+ end
+
+ it 'return statuses that are running or pending' do
+ is_expected.to eq([@commit1, @commit2])
+ end
+ end
+end
diff --git a/spec/models/generic_commit_status_spec.rb b/spec/models/generic_commit_status_spec.rb
new file mode 100644
index 00000000000..f442fa5fbe5
--- /dev/null
+++ b/spec/models/generic_commit_status_spec.rb
@@ -0,0 +1,39 @@
+require 'spec_helper'
+
+describe GenericCommitStatus do
+ let(:commit) { FactoryGirl.create :ci_commit }
+ let(:generic_commit_status) { FactoryGirl.create :generic_commit_status, commit: commit }
+
+ describe :context do
+ subject { generic_commit_status.context }
+ before { generic_commit_status.context = 'my_context' }
+
+ it { is_expected.to eq(generic_commit_status.name) }
+ end
+
+ describe :tags do
+ subject { generic_commit_status.tags }
+
+ it { is_expected.to eq([:external]) }
+ end
+
+ describe :set_default_values do
+ before do
+ generic_commit_status.context = nil
+ generic_commit_status.stage = nil
+ generic_commit_status.save
+ end
+
+ describe :context do
+ subject { generic_commit_status.context }
+
+ it { is_expected.to_not be_nil }
+ end
+
+ describe :stage do
+ subject { generic_commit_status.stage }
+
+ it { is_expected.to_not be_nil }
+ end
+ end
+end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 17a49013d25..6aaf1c036b0 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -165,6 +165,17 @@ describe MergeRequest do
end
end
+ describe "#hook_attrs" do
+ it "has all the required keys" do
+ attrs = subject.hook_attrs
+ attrs = attrs.to_h
+ expect(attrs).to include(:source)
+ expect(attrs).to include(:target)
+ expect(attrs).to include(:last_commit)
+ expect(attrs).to include(:work_in_progress)
+ end
+ end
+
it_behaves_like 'an editable mentionable' do
subject { create(:merge_request) }
diff --git a/spec/models/project_services/bamboo_service_spec.rb b/spec/models/project_services/bamboo_service_spec.rb
new file mode 100644
index 00000000000..f8a3493f52d
--- /dev/null
+++ b/spec/models/project_services/bamboo_service_spec.rb
@@ -0,0 +1,56 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# active :boolean default(FALSE), not null
+# properties :text
+# template :boolean default(FALSE)
+# push_events :boolean default(TRUE)
+# issues_events :boolean default(TRUE)
+# merge_requests_events :boolean default(TRUE)
+# tag_push_events :boolean default(TRUE)
+# note_events :boolean default(TRUE), not null
+#
+
+require 'spec_helper'
+
+describe BambooService, models: true do
+ describe "Associations" do
+ it { is_expected.to belong_to :project }
+ it { is_expected.to have_one :service_hook }
+ end
+
+ describe "Execute" do
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+
+ before do
+ @bamboo_service = BambooService.create(
+ project: create(:project),
+ properties: {
+ bamboo_url: 'http://gitlab.com',
+ username: 'mic',
+ password: "password"
+ }
+ )
+ end
+
+ it "reset password if url changed" do
+ @bamboo_service.bamboo_url = 'http://gitlab1.com'
+ @bamboo_service.save
+ expect(@bamboo_service.password).to be_nil
+ end
+
+ it "does not reset password if username changed" do
+ @bamboo_service.username = "some_name"
+ @bamboo_service.save
+ expect(@bamboo_service.password).to eq("password")
+ end
+ end
+end
diff --git a/spec/models/project_services/teamcity_service_spec.rb b/spec/models/project_services/teamcity_service_spec.rb
new file mode 100644
index 00000000000..3dbd2346bcc
--- /dev/null
+++ b/spec/models/project_services/teamcity_service_spec.rb
@@ -0,0 +1,56 @@
+# == Schema Information
+#
+# Table name: services
+#
+# id :integer not null, primary key
+# type :string(255)
+# title :string(255)
+# project_id :integer
+# created_at :datetime
+# updated_at :datetime
+# active :boolean default(FALSE), not null
+# properties :text
+# template :boolean default(FALSE)
+# push_events :boolean default(TRUE)
+# issues_events :boolean default(TRUE)
+# merge_requests_events :boolean default(TRUE)
+# tag_push_events :boolean default(TRUE)
+# note_events :boolean default(TRUE), not null
+#
+
+require 'spec_helper'
+
+describe TeamcityService, models: true do
+ describe "Associations" do
+ it { is_expected.to belong_to :project }
+ it { is_expected.to have_one :service_hook }
+ end
+
+ describe "Execute" do
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+
+ before do
+ @teamcity_service = TeamcityService.create(
+ project: create(:project),
+ properties: {
+ teamcity_url: 'http://gitlab.com',
+ username: 'mic',
+ password: "password"
+ }
+ )
+ end
+
+ it "reset password if url changed" do
+ @teamcity_service.teamcity_url = 'http://gitlab1.com'
+ @teamcity_service.save
+ expect(@teamcity_service.password).to be_nil
+ end
+
+ it "does not reset password if username changed" do
+ @teamcity_service.username = "some_name"
+ @teamcity_service.save
+ expect(@teamcity_service.password).to eq("password")
+ end
+ end
+end
diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb
index a213ffe6c4b..da87ea5b84f 100644
--- a/spec/models/service_spec.rb
+++ b/spec/models/service_spec.rb
@@ -103,4 +103,27 @@ describe Service do
end
end
end
+
+ describe "#prop_updated?" do
+ let(:service) do
+ BambooService.create(
+ project: create(:project),
+ properties: {
+ bamboo_url: 'http://gitlab.com',
+ username: 'mic',
+ password: "password"
+ }
+ )
+ end
+
+ it "returns false" do
+ service.username = "key_changed"
+ expect(service.prop_updated?(:bamboo_url)).to be_falsy
+ end
+
+ it "returns true" do
+ service.bamboo_url = "http://other.com"
+ expect(service.prop_updated?(:bamboo_url)).to be_truthy
+ end
+ end
end
diff --git a/spec/requests/api/commit_status_spec.rb b/spec/requests/api/commit_status_spec.rb
new file mode 100644
index 00000000000..b9e6dfc15a7
--- /dev/null
+++ b/spec/requests/api/commit_status_spec.rb
@@ -0,0 +1,135 @@
+require 'spec_helper'
+
+describe API::API, api: true do
+ include ApiHelpers
+ let(:user) { create(:user) }
+ let(:user2) { create(:user) }
+ let!(:project) { create(:project, creator_id: user.id) }
+ let!(:reporter) { create(:project_member, user: user, project: project, access_level: ProjectMember::REPORTER) }
+ let!(:guest) { create(:project_member, user: user2, project: project, access_level: ProjectMember::GUEST) }
+ let(:commit) { project.repository.commit }
+ let!(:ci_commit) { project.ensure_ci_commit(commit.id) }
+ let(:commit_status) { create(:commit_status, commit: ci_commit) }
+
+ describe "GET /projects/:id/repository/commits/:sha/statuses" do
+ context "reporter user" do
+ let(:statuses_id) { json_response.map { |status| status['id'] } }
+
+ before do
+ @status1 = create(:commit_status, commit: ci_commit, status: 'running')
+ @status2 = create(:commit_status, commit: ci_commit, name: 'coverage', status: 'pending')
+ @status3 = create(:commit_status, commit: ci_commit, name: 'coverage', ref: 'develop', status: 'running')
+ @status4 = create(:commit_status, commit: ci_commit, name: 'coverage', status: 'success')
+ @status5 = create(:commit_status, commit: ci_commit, ref: 'develop', status: 'success')
+ @status6 = create(:commit_status, commit: ci_commit, status: 'success')
+ end
+
+ it "should return latest commit statuses" do
+ get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses", user)
+ expect(response.status).to eq(200)
+
+ expect(json_response).to be_an Array
+ expect(statuses_id).to contain_exactly(@status3.id, @status4.id, @status5.id, @status6.id)
+ end
+
+ it "should return all commit statuses" do
+ get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses?all=1", user)
+ expect(response.status).to eq(200)
+
+ expect(json_response).to be_an Array
+ expect(statuses_id).to contain_exactly(@status1.id, @status2.id, @status3.id, @status4.id, @status5.id, @status6.id)
+ end
+
+ it "should return latest commit statuses for specific ref" do
+ get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses?ref=develop", user)
+ expect(response.status).to eq(200)
+
+ expect(json_response).to be_an Array
+ expect(statuses_id).to contain_exactly(@status3.id, @status5.id)
+ end
+
+ it "should return latest commit statuses for specific name" do
+ get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses?name=coverage", user)
+ expect(response.status).to eq(200)
+
+ expect(json_response).to be_an Array
+ expect(statuses_id).to contain_exactly(@status3.id, @status4.id)
+ end
+ end
+
+ context "guest user" do
+ it "should not return project commits" do
+ get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses", user2)
+ expect(response.status).to eq(403)
+ end
+ end
+
+ context "unauthorized user" do
+ it "should not return project commits" do
+ get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses")
+ expect(response.status).to eq(401)
+ end
+ end
+ end
+
+ describe 'POST /projects/:id/statuses/:sha' do
+ let(:post_url) { "/projects/#{project.id}/statuses/#{commit.id}" }
+
+ context 'reporter user' do
+ context 'should create commit status' do
+ it 'with only required parameters' do
+ post api(post_url, user), state: 'success'
+ expect(response.status).to eq(201)
+ expect(json_response['sha']).to eq(commit.id)
+ expect(json_response['status']).to eq('success')
+ expect(json_response['name']).to eq('default')
+ expect(json_response['ref']).to be_nil
+ expect(json_response['target_url']).to be_nil
+ expect(json_response['description']).to be_nil
+ end
+
+ it 'with all optional parameters' do
+ post api(post_url, user), state: 'success', context: 'coverage', ref: 'develop', target_url: 'url', description: 'test'
+ expect(response.status).to eq(201)
+ expect(json_response['sha']).to eq(commit.id)
+ expect(json_response['status']).to eq('success')
+ expect(json_response['name']).to eq('coverage')
+ expect(json_response['ref']).to eq('develop')
+ expect(json_response['target_url']).to eq('url')
+ expect(json_response['description']).to eq('test')
+ end
+ end
+
+ context 'should not create commit status' do
+ it 'with invalid state' do
+ post api(post_url, user), state: 'invalid'
+ expect(response.status).to eq(400)
+ end
+
+ it 'without state' do
+ post api(post_url, user)
+ expect(response.status).to eq(400)
+ end
+
+ it 'invalid commit' do
+ post api("/projects/#{project.id}/statuses/invalid_sha", user), state: 'running'
+ expect(response.status).to eq(404)
+ end
+ end
+ end
+
+ context 'guest user' do
+ it 'should not create commit status' do
+ post api(post_url, user2)
+ expect(response.status).to eq(403)
+ end
+ end
+
+ context 'unauthorized user' do
+ it 'should not create commit status' do
+ post api(post_url)
+ expect(response.status).to eq(401)
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb
index a1c248c636e..49acc3368f4 100644
--- a/spec/requests/api/commits_spec.rb
+++ b/spec/requests/api/commits_spec.rb
@@ -47,6 +47,19 @@ describe API::API, api: true do
get api("/projects/#{project.id}/repository/commits/invalid_sha", user)
expect(response.status).to eq(404)
end
+
+ it "should return not_found for CI status" do
+ get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user)
+ expect(response.status).to eq(200)
+ expect(json_response['status']).to eq('not_found')
+ end
+
+ it "should return status for CI" do
+ ci_commit = project.ensure_ci_commit(project.repository.commit.sha)
+ get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user)
+ expect(response.status).to eq(200)
+ expect(json_response['status']).to eq(ci_commit.status)
+ end
end
context "unauthorized user" do