summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG22
-rw-r--r--Gemfile40
-rw-r--r--Gemfile.lock60
-rw-r--r--README.md2
-rw-r--r--app/assets/javascripts/behaviors/quick_submit.js.coffee29
-rw-r--r--app/assets/javascripts/behaviors/requires_input.js.coffee3
-rw-r--r--app/assets/javascripts/blob/blob_file_dropzone.js.coffee1
-rw-r--r--app/assets/javascripts/ci/Chart.min.js39
-rw-r--r--app/assets/javascripts/ci/projects.js.coffee3
-rw-r--r--app/assets/javascripts/notes.js.coffee7
-rw-r--r--app/assets/javascripts/tree.js.coffee5
-rw-r--r--app/assets/stylesheets/base/gl_variables.scss4
-rw-r--r--app/assets/stylesheets/base/layout.scss1
-rw-r--r--app/assets/stylesheets/base/variables.scss62
-rw-r--r--app/assets/stylesheets/ci/builds.scss7
-rw-r--r--app/assets/stylesheets/ci/xterm.scss2
-rw-r--r--app/assets/stylesheets/generic/buttons.scss243
-rw-r--r--app/assets/stylesheets/generic/common.scss12
-rw-r--r--app/assets/stylesheets/generic/forms.scss20
-rw-r--r--app/assets/stylesheets/generic/issue_box.scss2
-rw-r--r--app/assets/stylesheets/generic/selects.scss42
-rw-r--r--app/assets/stylesheets/generic/timeline.scss4
-rw-r--r--app/assets/stylesheets/pages/commit.scss13
-rw-r--r--app/assets/stylesheets/pages/commits.scss3
-rw-r--r--app/assets/stylesheets/pages/issuable.scss9
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss23
-rw-r--r--app/assets/stylesheets/pages/note_form.scss8
-rw-r--r--app/assets/stylesheets/pages/notes.scss2
-rw-r--r--app/assets/stylesheets/pages/projects.scss30
-rw-r--r--app/controllers/admin/application_settings_controller.rb1
-rw-r--r--app/controllers/ci/application_controller.rb9
-rw-r--r--app/controllers/ci/builds_controller.rb78
-rw-r--r--app/controllers/ci/commits_controller.rb38
-rw-r--r--app/controllers/ci/projects_controller.rb24
-rw-r--r--app/controllers/ci/services_controller.rb59
-rw-r--r--app/controllers/profiles/preferences_controller.rb1
-rw-r--r--app/controllers/projects/blob_controller.rb8
-rw-r--r--app/controllers/projects/builds_controller.rb55
-rw-r--r--app/controllers/projects/ci_services_controller.rb49
-rw-r--r--app/controllers/projects/commit_controller.rb15
-rw-r--r--app/controllers/projects/tree_controller.rb37
-rw-r--r--app/finders/issuable_finder.rb28
-rw-r--r--app/finders/trending_projects_finder.rb11
-rw-r--r--app/helpers/application_helper.rb4
-rw-r--r--app/helpers/builds_helper.rb6
-rw-r--r--app/helpers/ci/commits_helper.rb24
-rw-r--r--app/helpers/ci/gitlab_helper.rb2
-rw-r--r--app/helpers/ci_status_helper.rb3
-rw-r--r--app/helpers/labels_helper.rb4
-rw-r--r--app/helpers/page_layout_helper.rb2
-rw-r--r--app/helpers/preferences_helper.rb7
-rw-r--r--app/models/application_setting.rb3
-rw-r--r--app/models/ci/build.rb29
-rw-r--r--app/models/ci/commit.rb122
-rw-r--r--app/models/ci/project_status.rb12
-rw-r--r--app/models/concerns/case_sensitivity.rb28
-rw-r--r--app/models/label.rb3
-rw-r--r--app/models/project.rb40
-rw-r--r--app/models/project_services/ci/hip_chat_message.rb9
-rw-r--r--app/models/project_services/ci/mail_service.rb2
-rw-r--r--app/models/project_services/ci/slack_message.rb23
-rw-r--r--app/models/project_services/gitlab_ci_service.rb29
-rw-r--r--app/models/repository.rb29
-rw-r--r--app/models/user.rb5
-rw-r--r--app/services/ci/create_builds_service.rb27
-rw-r--r--app/services/ci/create_commit_service.rb32
-rw-r--r--app/services/ci/create_trigger_request_service.rb13
-rw-r--r--app/services/ci/web_hook_service.rb3
-rw-r--r--app/services/files/create_dir_service.rb9
-rw-r--r--app/services/files/create_service.rb2
-rw-r--r--app/services/files/update_service.rb2
-rw-r--r--app/services/merge_requests/merge_service.rb2
-rw-r--r--app/services/system_hooks_service.rb1
-rw-r--r--app/views/admin/application_settings/_form.html.haml9
-rw-r--r--app/views/ci/admin/builds/_build.html.haml6
-rw-r--r--app/views/ci/admin/runners/show.html.haml10
-rw-r--r--app/views/ci/builds/show.html.haml170
-rw-r--r--app/views/ci/commits/_commit.html.haml5
-rw-r--r--app/views/ci/commits/show.html.haml87
-rw-r--r--app/views/ci/notify/build_fail_email.html.haml4
-rw-r--r--app/views/ci/notify/build_fail_email.text.erb4
-rw-r--r--app/views/ci/notify/build_success_email.html.haml4
-rw-r--r--app/views/ci/notify/build_success_email.text.erb4
-rw-r--r--app/views/ci/projects/_info.html.haml2
-rw-r--r--app/views/ci/projects/disabled.html.haml1
-rw-r--r--app/views/ci/projects/show.html.haml60
-rw-r--r--app/views/help/ui.html.haml52
-rw-r--r--app/views/layouts/ci/_nav_project.html.haml11
-rw-r--r--app/views/layouts/ci/build.html.haml11
-rw-r--r--app/views/layouts/ci/commit.html.haml11
-rw-r--r--app/views/layouts/nav/_project.html.haml9
-rw-r--r--app/views/layouts/nav/_project_settings.html.haml10
-rw-r--r--app/views/profiles/preferences/show.html.haml7
-rw-r--r--app/views/profiles/preferences/update.js.erb7
-rw-r--r--app/views/projects/blob/_editor.html.haml2
-rw-r--r--app/views/projects/blob/_new_dir.html.haml25
-rw-r--r--app/views/projects/blob/_upload.html.haml9
-rw-r--r--app/views/projects/blob/new.html.haml9
-rw-r--r--app/views/projects/builds/_build.html.haml (renamed from app/views/ci/builds/_build.html.haml)11
-rw-r--r--app/views/projects/builds/show.html.haml159
-rw-r--r--app/views/projects/ci_services/_form.html.haml (renamed from app/views/ci/services/_form.html.haml)7
-rw-r--r--app/views/projects/ci_services/edit.html.haml (renamed from app/views/ci/services/edit.html.haml)0
-rw-r--r--app/views/projects/ci_services/index.html.haml (renamed from app/views/ci/services/index.html.haml)2
-rw-r--r--app/views/projects/ci_settings/_form.html.haml16
-rw-r--r--app/views/projects/ci_settings/_no_runners.html.haml (renamed from app/views/ci/projects/_no_runners.html.haml)2
-rw-r--r--app/views/projects/ci_settings/edit.html.haml3
-rw-r--r--app/views/projects/commit/_ci_menu.html.haml7
-rw-r--r--app/views/projects/commit/ci.html.haml62
-rw-r--r--app/views/projects/commit/show.html.haml1
-rw-r--r--app/views/projects/edit.html.haml4
-rw-r--r--app/views/projects/labels/_form.html.haml2
-rw-r--r--app/views/projects/merge_requests/_show.html.haml4
-rw-r--r--app/views/projects/merge_requests/widget/_heading.html.haml58
-rw-r--r--app/views/projects/milestones/_form.html.haml4
-rw-r--r--app/views/projects/new.html.haml20
-rw-r--r--app/views/projects/notes/_edit_form.html.haml2
-rw-r--r--app/views/projects/notes/_form.html.haml4
-rw-r--r--app/views/projects/tree/_tree.html.haml26
-rw-r--r--app/views/projects/wikis/_form.html.haml2
-rw-r--r--app/views/shared/_commit_message_container.html.haml2
-rw-r--r--app/views/shared/issuable/_filter.html.haml4
-rw-r--r--app/views/shared/issuable/_form.html.haml4
-rw-r--r--config/application.rb2
-rw-r--r--config/initializers/1_settings.rb1
-rw-r--r--config/mail_room.yml.example2
-rw-r--r--config/routes.rb54
-rw-r--r--db/migrate/20151002112914_add_stage_idx_to_builds.rb5
-rw-r--r--db/migrate/20151002121400_add_index_for_builds.rb5
-rw-r--r--db/migrate/20151002122929_add_ref_and_tag_to_builds.rb6
-rw-r--r--db/migrate/20151002122943_migrate_ref_and_tag_to_build.rb6
-rw-r--r--db/migrate/20151005075649_add_user_id_to_build.rb5
-rw-r--r--db/migrate/20151005150751_add_layout_option_for_users.rb5
-rw-r--r--db/migrate/20151005162154_remove_ci_enabled_from_application_settings.rb5
-rw-r--r--db/migrate/20151007120511_namespaces_projects_path_lower_indexes.rb17
-rw-r--r--db/schema.rb9
-rw-r--r--doc/incoming_email/README.md67
-rw-r--r--doc/system_hooks/system_hooks.md42
-rw-r--r--doc/update/7.14-to-8.0.md13
-rw-r--r--features/project/commits/commits.feature2
-rw-r--r--features/project/source/browse_files.feature33
-rw-r--r--features/steps/project/commits/commits.rb14
-rw-r--r--features/steps/project/source/browse_files.rb61
-rw-r--r--lib/ci/api/api.rb4
-rw-r--r--lib/ci/api/commits.rb2
-rw-r--r--lib/ci/api/helpers.rb6
-rw-r--r--lib/ci/gitlab_ci_yaml_processor.rb5
-rw-r--r--lib/gitlab/database.rb11
-rw-r--r--lib/tasks/gitlab/setup.rake1
-rw-r--r--lib/tasks/migrate/setup_postgresql.rake6
-rw-r--r--spec/benchmarks/finders/trending_projects_finder_spec.rb14
-rw-r--r--spec/benchmarks/models/project_spec.rb50
-rw-r--r--spec/controllers/ci/commits_controller_spec.rb23
-rw-r--r--spec/controllers/projects/tree_controller_spec.rb36
-rw-r--r--spec/factories/ci/builds.rb6
-rw-r--r--spec/factories/ci/commits.rb54
-rw-r--r--spec/features/builds_spec.rb41
-rw-r--r--spec/features/ci/builds_spec.rb61
-rw-r--r--spec/features/ci/projects_spec.rb20
-rw-r--r--spec/features/commits_spec.rb (renamed from spec/features/ci/commits_spec.rb)52
-rw-r--r--spec/features/projects_spec.rb2
-rw-r--r--spec/finders/issues_finder_spec.rb20
-rw-r--r--spec/finders/trending_projects_finder_spec.rb39
-rw-r--r--spec/helpers/labels_helper_spec.rb5
-rw-r--r--spec/javascripts/behaviors/quick_submit_spec.js.coffee70
-rw-r--r--spec/javascripts/fixtures/behaviors/quick_submit.html.haml6
-rw-r--r--spec/javascripts/spec_helper.coffee1
-rw-r--r--spec/lib/ci/gitlab_ci_yaml_processor_spec.rb15
-rw-r--r--spec/lib/gitlab/database_spec.rb17
-rw-r--r--spec/models/ci/build_spec.rb49
-rw-r--r--spec/models/ci/commit_spec.rb216
-rw-r--r--spec/models/ci/project_services/hip_chat_message_spec.rb83
-rw-r--r--spec/models/ci/project_services/mail_service_spec.rb (renamed from spec/models/ci/mail_service_spec.rb)13
-rw-r--r--spec/models/ci/project_services/slack_message_spec.rb97
-rw-r--r--spec/models/concerns/case_sensitivity_spec.rb189
-rw-r--r--spec/models/project_services/gitlab_ci_service_spec.rb7
-rw-r--r--spec/models/project_spec.rb38
-rw-r--r--spec/requests/ci/api/builds_spec.rb12
-rw-r--r--spec/requests/ci/api/commits_spec.rb3
-rw-r--r--spec/requests/ci/api/triggers_spec.rb20
-rw-r--r--spec/requests/ci/builds_spec.rb17
-rw-r--r--spec/requests/ci/commits_spec.rb16
-rw-r--r--spec/services/ci/create_commit_service_spec.rb67
-rw-r--r--spec/services/ci/create_trigger_request_service_spec.rb35
-rw-r--r--spec/services/system_hooks_service_spec.rb4
-rw-r--r--spec/spec_helper.rb4
-rw-r--r--spec/support/stub_gitlab_calls.rb8
-rw-r--r--tmp/.gitkeep0
187 files changed, 2476 insertions, 1846 deletions
diff --git a/CHANGELOG b/CHANGELOG
index a63aea7ca28..b1a35c3c2ab 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,9 +1,13 @@
Please view this file on the master branch, on stable branches it's out of date.
v 8.1.0 (unreleased)
+ - Add support for creating directories from Files page (Stan Hu)
+ - Allow removing of project without confirmation when JavaScript is disabled (Stan Hu)
+ - Support filtering by "Any" milestone or issue and fix "No Milestone" and "No Label" filters (Stan Hu)
+ - Improved performance of the trending projects page
+ - Improved performance of finding projects by their namespace
- Fix bug where transferring a project would result in stale commit links (Stan Hu)
- Include full path of source and target branch names in New Merge Request page (Stan Hu)
- - Fix Message-ID header to be RFC 2111-compliant to prevent e-mails being dropped (Stan Hu)
- Add user preference to view activities as default dashboard (Stan Hu)
- Add option to admin area to sign in as a specific user (Pavel Forkert)
- Show CI status on all pages where commits list is rendered
@@ -29,9 +33,23 @@ v 8.1.0 (unreleased)
- Ensure code blocks are properly highlighted after a note is updated
- Fix wrong access level badge on MR comments
- Hide password in the service settings form
- - Fix anchors to comments in diffs
- Move CI web hooks page to project settings area
- Fix User Identities API. It now allows you to properly create or update user's identities.
+ - Add user preference to change layout width (Peter Göbel)
+ - Use commit status in merge request widget as preffered source of CI status
+ - Integrate CI commit and build pages into project pages
+ - Move CI services page to project settings area
+ - Add "Quick Submit" behavior to input fields throughout the application. Use
+ Cmd+Enter on Mac and Ctrl+Enter on Windows/Linux.
+
+v 8.0.4
+ - Fix Message-ID header to be RFC 2111-compliant to prevent e-mails being dropped (Stan Hu)
+ - Fix referrals for :back and relative URL installs
+ - Fix anchors to comments in diffs
+ - Remove CI token from build traces
+ - Fix "Assign All" button on Runner admin page
+ - Fix search in Files
+ - Add full project namespace to payload of system webhooks (Ricardo Band)
v 8.0.3
- Fix URL shown in Slack notifications
diff --git a/Gemfile b/Gemfile
index 4938cbf8b80..044dc30ecd4 100644
--- a/Gemfile
+++ b/Gemfile
@@ -22,20 +22,20 @@ gem "mysql2", '~> 0.3.16', group: :mysql
gem "pg", '~> 0.18.2', group: :postgres
# Authentication libraries
-gem "devise", '~> 3.5.2'
-gem "devise-async", '~> 0.9.0'
-gem 'omniauth', "~> 1.2.2"
-gem 'omniauth-google-oauth2', '~> 0.2.5'
-gem 'omniauth-twitter', '~> 1.0.1'
-gem 'omniauth-github', '~> 1.1.1'
-gem 'omniauth-shibboleth', '~> 1.1.1'
-gem 'omniauth-kerberos', '~> 0.2.0', group: :kerberos
-gem 'omniauth-gitlab', '~> 1.0.0'
-gem 'omniauth-bitbucket', '~> 0.0.2'
-gem 'omniauth-saml', '~> 1.4.0'
-gem 'doorkeeper', '~> 2.1.3'
+gem 'devise', '~> 3.5.2'
+gem 'devise-async', '~> 0.9.0'
+gem 'doorkeeper', '~> 2.1.3'
+gem 'omniauth', '~> 1.2.2'
+gem 'omniauth-bitbucket', '~> 0.0.2'
+gem 'omniauth-github', '~> 1.1.1'
+gem 'omniauth-gitlab', '~> 1.0.0'
+gem 'omniauth-google-oauth2', '~> 0.2.0'
+gem 'omniauth-kerberos', '~> 0.3.0', group: :kerberos
+gem 'omniauth-saml', '~> 1.4.0'
+gem 'omniauth-shibboleth', '~> 1.2.0'
+gem 'omniauth-twitter', '~> 1.2.0'
gem 'omniauth_crowd'
-gem "rack-oauth2", "~> 1.0.5"
+gem 'rack-oauth2', '~> 1.0.5'
# Two-factor authentication
gem 'devise-two-factor', '~> 2.0.0'
@@ -47,7 +47,7 @@ gem "browser", '~> 1.0.0'
# Extracting information from a git repository
# Provide access to Gitlab::Git library
-gem "gitlab_git", '~> 7.2.17'
+gem "gitlab_git", '~> 7.2.18'
# LDAP Auth
# GitLab fork with several improvements to original library. For full list of changes
@@ -65,9 +65,9 @@ gem 'gollum-lib', '~> 4.0.2'
gem "gitlab-linguist", "~> 3.0.1", require: "linguist"
# API
-gem "grape", "~> 0.6.1"
-gem "grape-entity", "~> 0.4.2"
-gem 'rack-cors', '~> 0.2.9', require: 'rack/cors'
+gem 'grape', '~> 0.6.1'
+gem 'grape-entity', '~> 0.4.2'
+gem 'rack-cors', '~> 0.4.0', require: 'rack/cors'
# Format dates and times
# based on human-friendly examples
@@ -80,7 +80,7 @@ gem 'enumerize', '~> 0.7.0'
gem "kaminari", "~> 0.16.3"
# HAML
-gem "haml-rails", '~> 0.5.3'
+gem "haml-rails", '~> 0.9.0'
# Files attachments
gem "carrierwave", '~> 0.9.0'
@@ -151,7 +151,7 @@ gem 'version_sorter', '~> 2.0.0'
gem "redis-rails", '~> 4.0.0'
# Campfire integration
-gem 'tinder', '~> 1.9.2'
+gem 'tinder', '~> 1.10.0'
# HipChat integration
gem 'hipchat', '~> 1.5.0'
@@ -163,7 +163,7 @@ gem "gitlab-flowdock-git-hook", "~> 1.0.1"
gem "gemnasium-gitlab-service", "~> 0.2"
# Slack integration
-gem "slack-notifier", "~> 1.0.0"
+gem "slack-notifier", "~> 1.2.0"
# Asana integration
gem 'asana', '~> 0.0.6'
diff --git a/Gemfile.lock b/Gemfile.lock
index 1dd56cd9c8c..f716c0254ec 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -182,8 +182,8 @@ GEM
factory_girl_rails (4.3.0)
factory_girl (~> 4.3.0)
railties (>= 3.0.0)
- faraday (0.8.10)
- multipart-post (~> 1.2.0)
+ faraday (0.9.2)
+ multipart-post (>= 1.2, < 3)
faraday_middleware (0.10.0)
faraday (>= 0.7.4, < 0.10)
fastercsv (1.5.5)
@@ -279,7 +279,7 @@ GEM
mime-types (~> 1.19)
gitlab_emoji (0.1.1)
gemojione (~> 2.0)
- gitlab_git (7.2.17)
+ gitlab_git (7.2.18)
activesupport (~> 4.0)
charlock_holmes (~> 0.6)
gitlab-linguist (~> 3.0)
@@ -330,12 +330,13 @@ GEM
rspec (>= 2.14, < 4.0)
haml (4.0.7)
tilt
- haml-rails (0.5.3)
+ haml-rails (0.9.0)
actionpack (>= 4.0.1)
activesupport (>= 4.0.1)
- haml (>= 3.1, < 5.0)
+ haml (>= 4.0.6, < 5.0)
+ html2haml (>= 1.0.1)
railties (>= 4.0.1)
- hashie (2.1.2)
+ hashie (3.4.2)
highline (1.6.21)
hike (1.2.3)
hipchat (1.5.2)
@@ -345,6 +346,11 @@ GEM
html-pipeline (1.11.0)
activesupport (>= 2)
nokogiri (~> 1.4)
+ html2haml (2.0.0)
+ erubis (~> 2.7.0)
+ haml (~> 4.0.0)
+ nokogiri (~> 1.6.0)
+ ruby_parser (~> 3.5)
http-cookie (1.0.2)
domain_name (~> 0.5)
http_parser.rb (0.5.3)
@@ -396,7 +402,7 @@ GEM
mousetrap-rails (1.4.6)
multi_json (1.11.2)
multi_xml (0.5.5)
- multipart-post (1.2.0)
+ multipart-post (2.0.0)
mysql2 (0.3.20)
nenv (0.2.0)
nested_form (0.3.2)
@@ -440,7 +446,7 @@ GEM
omniauth-google-oauth2 (0.2.6)
omniauth (> 1.0)
omniauth-oauth2 (~> 1.1)
- omniauth-kerberos (0.2.0)
+ omniauth-kerberos (0.3.0)
omniauth-multipassword
timfel-krb5-auth (~> 0.8)
omniauth-multipassword (0.4.2)
@@ -454,11 +460,11 @@ GEM
omniauth-saml (1.4.1)
omniauth (~> 1.1)
ruby-saml (~> 1.0.0)
- omniauth-shibboleth (1.1.2)
+ omniauth-shibboleth (1.2.1)
omniauth (>= 1.0.0)
- omniauth-twitter (1.0.1)
- multi_json (~> 1.3)
- omniauth-oauth (~> 1.0)
+ omniauth-twitter (1.2.1)
+ json (~> 1.3)
+ omniauth-oauth (~> 1.1)
omniauth_crowd (2.2.3)
activesupport
nokogiri (>= 1.4.4)
@@ -496,7 +502,7 @@ GEM
rack (>= 0.4)
rack-attack (4.3.0)
rack
- rack-cors (0.2.9)
+ rack-cors (0.4.0)
rack-mini-profiler (0.9.7)
rack (>= 1.1.3)
rack-mount (0.8.3)
@@ -666,7 +672,7 @@ GEM
rack-protection (~> 1.4)
tilt (>= 1.3, < 3)
six (0.2.0)
- slack-notifier (1.0.0)
+ slack-notifier (1.2.1)
slim (2.0.3)
temple (~> 0.6.6)
tilt (>= 1.3.3, < 2.1)
@@ -721,13 +727,13 @@ GEM
timers (4.0.4)
hitimes
timfel-krb5-auth (0.8.3)
- tinder (1.9.4)
+ tinder (1.10.1)
eventmachine (~> 1.0)
- faraday (~> 0.8.9)
+ faraday (~> 0.9.0)
faraday_middleware (~> 0.9)
- hashie (>= 1.0, < 3)
+ hashie (>= 1.0)
json (~> 1.8.0)
- mime-types (~> 1.19)
+ mime-types
multi_json (~> 1.7)
twitter-stream (~> 0.1)
tins (1.6.0)
@@ -836,7 +842,7 @@ DEPENDENCIES
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-linguist (~> 3.0.1)
gitlab_emoji (~> 0.1)
- gitlab_git (~> 7.2.17)
+ gitlab_git (~> 7.2.18)
gitlab_meta (= 7.0)
gitlab_omniauth-ldap (~> 1.2.1)
gollum-lib (~> 4.0.2)
@@ -845,7 +851,7 @@ DEPENDENCIES
grape-entity (~> 0.4.2)
growl
guard-rspec (~> 4.2.0)
- haml-rails (~> 0.5.3)
+ haml-rails (~> 0.9.0)
hipchat (~> 1.5.0)
html-pipeline (~> 1.11.0)
httparty (~> 0.13.3)
@@ -870,11 +876,11 @@ DEPENDENCIES
omniauth-bitbucket (~> 0.0.2)
omniauth-github (~> 1.1.1)
omniauth-gitlab (~> 1.0.0)
- omniauth-google-oauth2 (~> 0.2.5)
- omniauth-kerberos (~> 0.2.0)
+ omniauth-google-oauth2 (~> 0.2.0)
+ omniauth-kerberos (~> 0.3.0)
omniauth-saml (~> 1.4.0)
- omniauth-shibboleth (~> 1.1.1)
- omniauth-twitter (~> 1.0.1)
+ omniauth-shibboleth (~> 1.2.0)
+ omniauth-twitter (~> 1.2.0)
omniauth_crowd
org-ruby (~> 0.9.12)
paranoia (~> 2.0)
@@ -883,7 +889,7 @@ DEPENDENCIES
pry-rails
quiet_assets (~> 1.0.2)
rack-attack (~> 4.3.0)
- rack-cors (~> 0.2.9)
+ rack-cors (~> 0.4.0)
rack-mini-profiler (~> 0.9.0)
rack-oauth2 (~> 1.0.5)
rails (= 4.1.12)
@@ -912,7 +918,7 @@ DEPENDENCIES
simplecov (~> 0.10.0)
sinatra (~> 1.4.4)
six (~> 0.2.0)
- slack-notifier (~> 1.0.0)
+ slack-notifier (~> 1.2.0)
slim (~> 2.0.2)
spinach-rails (~> 0.2.1)
spring (~> 1.3.6)
@@ -927,7 +933,7 @@ DEPENDENCIES
teaspoon-jasmine (~> 2.2.0)
test_after_commit (~> 0.2.2)
thin (~> 1.6.1)
- tinder (~> 1.9.2)
+ tinder (~> 1.10.0)
turbolinks (~> 2.5.0)
uglifier (~> 2.3.2)
underscore-rails (~> 1.4.4)
diff --git a/README.md b/README.md
index 91855b42d29..52e2d977620 100644
--- a/README.md
+++ b/README.md
@@ -71,7 +71,7 @@ GitLab is a Ruby on Rails application that runs on the following software:
- Ubuntu/Debian/CentOS/RHEL
- Ruby (MRI) 2.1
- Git 1.7.10+
-- Redis 2.0+
+- Redis 2.4+
- MySQL or PostgreSQL
For more information please see the [architecture documentation](http://doc.gitlab.com/ce/development/architecture.html).
diff --git a/app/assets/javascripts/behaviors/quick_submit.js.coffee b/app/assets/javascripts/behaviors/quick_submit.js.coffee
new file mode 100644
index 00000000000..4ec8531d580
--- /dev/null
+++ b/app/assets/javascripts/behaviors/quick_submit.js.coffee
@@ -0,0 +1,29 @@
+# Quick Submit behavior
+#
+# When an input field with the `js-quick-submit` class receives a "Meta+Enter"
+# (Mac) or "Ctrl+Enter" (Linux/Windows) key combination, its parent form is
+# submitted.
+#
+#= require extensions/jquery
+#
+# ### Example Markup
+#
+# <form action="/foo">
+# <input type="text" class="js-quick-submit" />
+# <textarea class="js-quick-submit"></textarea>
+# </form>
+#
+$(document).on 'keydown.quick_submit', '.js-quick-submit', (e) ->
+ return if (e.originalEvent && e.originalEvent.repeat) || e.repeat
+ return unless e.keyCode == 13 # Enter
+
+ if navigator.userAgent.match(/Macintosh/)
+ return unless (e.metaKey && !e.altKey && !e.ctrlKey && !e.shiftKey)
+ else
+ return unless (e.ctrlKey && !e.altKey && !e.metaKey && !e.shiftKey)
+
+ e.preventDefault()
+
+ $form = $(e.target).closest('form')
+ $form.find('input[type=submit], button[type=submit]').disable()
+ $form.submit()
diff --git a/app/assets/javascripts/behaviors/requires_input.js.coffee b/app/assets/javascripts/behaviors/requires_input.js.coffee
index 8318fe435b3..79d750d1847 100644
--- a/app/assets/javascripts/behaviors/requires_input.js.coffee
+++ b/app/assets/javascripts/behaviors/requires_input.js.coffee
@@ -34,6 +34,5 @@ $.fn.requiresInput = ->
$form.on 'change input', fieldSelector, requireInput
-# Triggered on standard document `ready` and on Turbolinks `page:load` events
-$(document).on 'ready page:load', ->
+$ ->
$('form.js-requires-input').requiresInput()
diff --git a/app/assets/javascripts/blob/blob_file_dropzone.js.coffee b/app/assets/javascripts/blob/blob_file_dropzone.js.coffee
index 3ab3ba66754..5b604adbbb1 100644
--- a/app/assets/javascripts/blob/blob_file_dropzone.js.coffee
+++ b/app/assets/javascripts/blob/blob_file_dropzone.js.coffee
@@ -47,6 +47,7 @@ class @BlobFileDropzone
return
this.on 'sending', (file, xhr, formData) ->
+ formData.append('new_branch', form.find('#new_branch').val())
formData.append('commit_message', form.find('#commit_message').val())
return
diff --git a/app/assets/javascripts/ci/Chart.min.js b/app/assets/javascripts/ci/Chart.min.js
deleted file mode 100644
index ab635881087..00000000000
--- a/app/assets/javascripts/ci/Chart.min.js
+++ /dev/null
@@ -1,39 +0,0 @@
-var Chart=function(s){function v(a,c,b){a=A((a-c.graphMin)/(c.steps*c.stepValue),1,0);return b*c.steps*a}function x(a,c,b,e){function h(){g+=f;var k=a.animation?A(d(g),null,0):1;e.clearRect(0,0,q,u);a.scaleOverlay?(b(k),c()):(c(),b(k));if(1>=g)D(h);else if("function"==typeof a.onAnimationComplete)a.onAnimationComplete()}var f=a.animation?1/A(a.animationSteps,Number.MAX_VALUE,1):1,d=B[a.animationEasing],g=a.animation?0:1;"function"!==typeof c&&(c=function(){});D(h)}function C(a,c,b,e,h,f){var d;a=
-Math.floor(Math.log(e-h)/Math.LN10);h=Math.floor(h/(1*Math.pow(10,a)))*Math.pow(10,a);e=Math.ceil(e/(1*Math.pow(10,a)))*Math.pow(10,a)-h;a=Math.pow(10,a);for(d=Math.round(e/a);d<b||d>c;)a=d<b?a/2:2*a,d=Math.round(e/a);c=[];z(f,c,d,h,a);return{steps:d,stepValue:a,graphMin:h,labels:c}}function z(a,c,b,e,h){if(a)for(var f=1;f<b+1;f++)c.push(E(a,{value:(e+h*f).toFixed(0!=h%1?h.toString().split(".")[1].length:0)}))}function A(a,c,b){return!isNaN(parseFloat(c))&&isFinite(c)&&a>c?c:!isNaN(parseFloat(b))&&
-isFinite(b)&&a<b?b:a}function y(a,c){var b={},e;for(e in a)b[e]=a[e];for(e in c)b[e]=c[e];return b}function E(a,c){var b=!/\W/.test(a)?F[a]=F[a]||E(document.getElementById(a).innerHTML):new Function("obj","var p=[],print=function(){p.push.apply(p,arguments);};with(obj){p.push('"+a.replace(/[\r\t\n]/g," ").split("<%").join("\t").replace(/((^|%>)[^\t]*)'/g,"$1\r").replace(/\t=(.*?)%>/g,"',$1,'").split("\t").join("');").split("%>").join("p.push('").split("\r").join("\\'")+"');}return p.join('');");return c?
-b(c):b}var r=this,B={linear:function(a){return a},easeInQuad:function(a){return a*a},easeOutQuad:function(a){return-1*a*(a-2)},easeInOutQuad:function(a){return 1>(a/=0.5)?0.5*a*a:-0.5*(--a*(a-2)-1)},easeInCubic:function(a){return a*a*a},easeOutCubic:function(a){return 1*((a=a/1-1)*a*a+1)},easeInOutCubic:function(a){return 1>(a/=0.5)?0.5*a*a*a:0.5*((a-=2)*a*a+2)},easeInQuart:function(a){return a*a*a*a},easeOutQuart:function(a){return-1*((a=a/1-1)*a*a*a-1)},easeInOutQuart:function(a){return 1>(a/=0.5)?
-0.5*a*a*a*a:-0.5*((a-=2)*a*a*a-2)},easeInQuint:function(a){return 1*(a/=1)*a*a*a*a},easeOutQuint:function(a){return 1*((a=a/1-1)*a*a*a*a+1)},easeInOutQuint:function(a){return 1>(a/=0.5)?0.5*a*a*a*a*a:0.5*((a-=2)*a*a*a*a+2)},easeInSine:function(a){return-1*Math.cos(a/1*(Math.PI/2))+1},easeOutSine:function(a){return 1*Math.sin(a/1*(Math.PI/2))},easeInOutSine:function(a){return-0.5*(Math.cos(Math.PI*a/1)-1)},easeInExpo:function(a){return 0==a?1:1*Math.pow(2,10*(a/1-1))},easeOutExpo:function(a){return 1==
-a?1:1*(-Math.pow(2,-10*a/1)+1)},easeInOutExpo:function(a){return 0==a?0:1==a?1:1>(a/=0.5)?0.5*Math.pow(2,10*(a-1)):0.5*(-Math.pow(2,-10*--a)+2)},easeInCirc:function(a){return 1<=a?a:-1*(Math.sqrt(1-(a/=1)*a)-1)},easeOutCirc:function(a){return 1*Math.sqrt(1-(a=a/1-1)*a)},easeInOutCirc:function(a){return 1>(a/=0.5)?-0.5*(Math.sqrt(1-a*a)-1):0.5*(Math.sqrt(1-(a-=2)*a)+1)},easeInElastic:function(a){var c=1.70158,b=0,e=1;if(0==a)return 0;if(1==(a/=1))return 1;b||(b=0.3);e<Math.abs(1)?(e=1,c=b/4):c=b/(2*
-Math.PI)*Math.asin(1/e);return-(e*Math.pow(2,10*(a-=1))*Math.sin((1*a-c)*2*Math.PI/b))},easeOutElastic:function(a){var c=1.70158,b=0,e=1;if(0==a)return 0;if(1==(a/=1))return 1;b||(b=0.3);e<Math.abs(1)?(e=1,c=b/4):c=b/(2*Math.PI)*Math.asin(1/e);return e*Math.pow(2,-10*a)*Math.sin((1*a-c)*2*Math.PI/b)+1},easeInOutElastic:function(a){var c=1.70158,b=0,e=1;if(0==a)return 0;if(2==(a/=0.5))return 1;b||(b=1*0.3*1.5);e<Math.abs(1)?(e=1,c=b/4):c=b/(2*Math.PI)*Math.asin(1/e);return 1>a?-0.5*e*Math.pow(2,10*
-(a-=1))*Math.sin((1*a-c)*2*Math.PI/b):0.5*e*Math.pow(2,-10*(a-=1))*Math.sin((1*a-c)*2*Math.PI/b)+1},easeInBack:function(a){return 1*(a/=1)*a*(2.70158*a-1.70158)},easeOutBack:function(a){return 1*((a=a/1-1)*a*(2.70158*a+1.70158)+1)},easeInOutBack:function(a){var c=1.70158;return 1>(a/=0.5)?0.5*a*a*(((c*=1.525)+1)*a-c):0.5*((a-=2)*a*(((c*=1.525)+1)*a+c)+2)},easeInBounce:function(a){return 1-B.easeOutBounce(1-a)},easeOutBounce:function(a){return(a/=1)<1/2.75?1*7.5625*a*a:a<2/2.75?1*(7.5625*(a-=1.5/2.75)*
-a+0.75):a<2.5/2.75?1*(7.5625*(a-=2.25/2.75)*a+0.9375):1*(7.5625*(a-=2.625/2.75)*a+0.984375)},easeInOutBounce:function(a){return 0.5>a?0.5*B.easeInBounce(2*a):0.5*B.easeOutBounce(2*a-1)+0.5}},q=s.canvas.width,u=s.canvas.height;window.devicePixelRatio&&(s.canvas.style.width=q+"px",s.canvas.style.height=u+"px",s.canvas.height=u*window.devicePixelRatio,s.canvas.width=q*window.devicePixelRatio,s.scale(window.devicePixelRatio,window.devicePixelRatio));this.PolarArea=function(a,c){r.PolarArea.defaults={scaleOverlay:!0,
-scaleOverride:!1,scaleSteps:null,scaleStepWidth:null,scaleStartValue:null,scaleShowLine:!0,scaleLineColor:"rgba(0,0,0,.1)",scaleLineWidth:1,scaleShowLabels:!0,scaleLabel:"<%=value%>",scaleFontFamily:"'Arial'",scaleFontSize:12,scaleFontStyle:"normal",scaleFontColor:"#666",scaleShowLabelBackdrop:!0,scaleBackdropColor:"rgba(255,255,255,0.75)",scaleBackdropPaddingY:2,scaleBackdropPaddingX:2,segmentShowStroke:!0,segmentStrokeColor:"#fff",segmentStrokeWidth:2,animation:!0,animationSteps:100,animationEasing:"easeOutBounce",
-animateRotate:!0,animateScale:!1,onAnimationComplete:null};var b=c?y(r.PolarArea.defaults,c):r.PolarArea.defaults;return new G(a,b,s)};this.Radar=function(a,c){r.Radar.defaults={scaleOverlay:!1,scaleOverride:!1,scaleSteps:null,scaleStepWidth:null,scaleStartValue:null,scaleShowLine:!0,scaleLineColor:"rgba(0,0,0,.1)",scaleLineWidth:1,scaleShowLabels:!1,scaleLabel:"<%=value%>",scaleFontFamily:"'Arial'",scaleFontSize:12,scaleFontStyle:"normal",scaleFontColor:"#666",scaleShowLabelBackdrop:!0,scaleBackdropColor:"rgba(255,255,255,0.75)",
-scaleBackdropPaddingY:2,scaleBackdropPaddingX:2,angleShowLineOut:!0,angleLineColor:"rgba(0,0,0,.1)",angleLineWidth:1,pointLabelFontFamily:"'Arial'",pointLabelFontStyle:"normal",pointLabelFontSize:12,pointLabelFontColor:"#666",pointDot:!0,pointDotRadius:3,pointDotStrokeWidth:1,datasetStroke:!0,datasetStrokeWidth:2,datasetFill:!0,animation:!0,animationSteps:60,animationEasing:"easeOutQuart",onAnimationComplete:null};var b=c?y(r.Radar.defaults,c):r.Radar.defaults;return new H(a,b,s)};this.Pie=function(a,
-c){r.Pie.defaults={segmentShowStroke:!0,segmentStrokeColor:"#fff",segmentStrokeWidth:2,animation:!0,animationSteps:100,animationEasing:"easeOutBounce",animateRotate:!0,animateScale:!1,onAnimationComplete:null};var b=c?y(r.Pie.defaults,c):r.Pie.defaults;return new I(a,b,s)};this.Doughnut=function(a,c){r.Doughnut.defaults={segmentShowStroke:!0,segmentStrokeColor:"#fff",segmentStrokeWidth:2,percentageInnerCutout:50,animation:!0,animationSteps:100,animationEasing:"easeOutBounce",animateRotate:!0,animateScale:!1,
-onAnimationComplete:null};var b=c?y(r.Doughnut.defaults,c):r.Doughnut.defaults;return new J(a,b,s)};this.Line=function(a,c){r.Line.defaults={scaleOverlay:!1,scaleOverride:!1,scaleSteps:null,scaleStepWidth:null,scaleStartValue:null,scaleLineColor:"rgba(0,0,0,.1)",scaleLineWidth:1,scaleShowLabels:!0,scaleLabel:"<%=value%>",scaleFontFamily:"'Arial'",scaleFontSize:12,scaleFontStyle:"normal",scaleFontColor:"#666",scaleShowGridLines:!0,scaleGridLineColor:"rgba(0,0,0,.05)",scaleGridLineWidth:1,bezierCurve:!0,
-pointDot:!0,pointDotRadius:4,pointDotStrokeWidth:2,datasetStroke:!0,datasetStrokeWidth:2,datasetFill:!0,animation:!0,animationSteps:60,animationEasing:"easeOutQuart",onAnimationComplete:null};var b=c?y(r.Line.defaults,c):r.Line.defaults;return new K(a,b,s)};this.Bar=function(a,c){r.Bar.defaults={scaleOverlay:!1,scaleOverride:!1,scaleSteps:null,scaleStepWidth:null,scaleStartValue:null,scaleLineColor:"rgba(0,0,0,.1)",scaleLineWidth:1,scaleShowLabels:!0,scaleLabel:"<%=value%>",scaleFontFamily:"'Arial'",
-scaleFontSize:12,scaleFontStyle:"normal",scaleFontColor:"#666",scaleShowGridLines:!0,scaleGridLineColor:"rgba(0,0,0,.05)",scaleGridLineWidth:1,barShowStroke:!0,barStrokeWidth:2,barValueSpacing:5,barDatasetSpacing:1,animation:!0,animationSteps:60,animationEasing:"easeOutQuart",onAnimationComplete:null};var b=c?y(r.Bar.defaults,c):r.Bar.defaults;return new L(a,b,s)};var G=function(a,c,b){var e,h,f,d,g,k,j,l,m;g=Math.min.apply(Math,[q,u])/2;g-=Math.max.apply(Math,[0.5*c.scaleFontSize,0.5*c.scaleLineWidth]);
-d=2*c.scaleFontSize;c.scaleShowLabelBackdrop&&(d+=2*c.scaleBackdropPaddingY,g-=1.5*c.scaleBackdropPaddingY);l=g;d=d?d:5;e=Number.MIN_VALUE;h=Number.MAX_VALUE;for(f=0;f<a.length;f++)a[f].value>e&&(e=a[f].value),a[f].value<h&&(h=a[f].value);f=Math.floor(l/(0.66*d));d=Math.floor(0.5*(l/d));m=c.scaleShowLabels?c.scaleLabel:null;c.scaleOverride?(j={steps:c.scaleSteps,stepValue:c.scaleStepWidth,graphMin:c.scaleStartValue,labels:[]},z(m,j.labels,j.steps,c.scaleStartValue,c.scaleStepWidth)):j=C(l,f,d,e,h,
-m);k=g/j.steps;x(c,function(){for(var a=0;a<j.steps;a++)if(c.scaleShowLine&&(b.beginPath(),b.arc(q/2,u/2,k*(a+1),0,2*Math.PI,!0),b.strokeStyle=c.scaleLineColor,b.lineWidth=c.scaleLineWidth,b.stroke()),c.scaleShowLabels){b.textAlign="center";b.font=c.scaleFontStyle+" "+c.scaleFontSize+"px "+c.scaleFontFamily;var e=j.labels[a];if(c.scaleShowLabelBackdrop){var d=b.measureText(e).width;b.fillStyle=c.scaleBackdropColor;b.beginPath();b.rect(Math.round(q/2-d/2-c.scaleBackdropPaddingX),Math.round(u/2-k*(a+
-1)-0.5*c.scaleFontSize-c.scaleBackdropPaddingY),Math.round(d+2*c.scaleBackdropPaddingX),Math.round(c.scaleFontSize+2*c.scaleBackdropPaddingY));b.fill()}b.textBaseline="middle";b.fillStyle=c.scaleFontColor;b.fillText(e,q/2,u/2-k*(a+1))}},function(e){var d=-Math.PI/2,g=2*Math.PI/a.length,f=1,h=1;c.animation&&(c.animateScale&&(f=e),c.animateRotate&&(h=e));for(e=0;e<a.length;e++)b.beginPath(),b.arc(q/2,u/2,f*v(a[e].value,j,k),d,d+h*g,!1),b.lineTo(q/2,u/2),b.closePath(),b.fillStyle=a[e].color,b.fill(),
-c.segmentShowStroke&&(b.strokeStyle=c.segmentStrokeColor,b.lineWidth=c.segmentStrokeWidth,b.stroke()),d+=h*g},b)},H=function(a,c,b){var e,h,f,d,g,k,j,l,m;a.labels||(a.labels=[]);g=Math.min.apply(Math,[q,u])/2;d=2*c.scaleFontSize;for(e=l=0;e<a.labels.length;e++)b.font=c.pointLabelFontStyle+" "+c.pointLabelFontSize+"px "+c.pointLabelFontFamily,h=b.measureText(a.labels[e]).width,h>l&&(l=h);g-=Math.max.apply(Math,[l,1.5*(c.pointLabelFontSize/2)]);g-=c.pointLabelFontSize;l=g=A(g,null,0);d=d?d:5;e=Number.MIN_VALUE;
-h=Number.MAX_VALUE;for(f=0;f<a.datasets.length;f++)for(m=0;m<a.datasets[f].data.length;m++)a.datasets[f].data[m]>e&&(e=a.datasets[f].data[m]),a.datasets[f].data[m]<h&&(h=a.datasets[f].data[m]);f=Math.floor(l/(0.66*d));d=Math.floor(0.5*(l/d));m=c.scaleShowLabels?c.scaleLabel:null;c.scaleOverride?(j={steps:c.scaleSteps,stepValue:c.scaleStepWidth,graphMin:c.scaleStartValue,labels:[]},z(m,j.labels,j.steps,c.scaleStartValue,c.scaleStepWidth)):j=C(l,f,d,e,h,m);k=g/j.steps;x(c,function(){var e=2*Math.PI/
-a.datasets[0].data.length;b.save();b.translate(q/2,u/2);if(c.angleShowLineOut){b.strokeStyle=c.angleLineColor;b.lineWidth=c.angleLineWidth;for(var d=0;d<a.datasets[0].data.length;d++)b.rotate(e),b.beginPath(),b.moveTo(0,0),b.lineTo(0,-g),b.stroke()}for(d=0;d<j.steps;d++){b.beginPath();if(c.scaleShowLine){b.strokeStyle=c.scaleLineColor;b.lineWidth=c.scaleLineWidth;b.moveTo(0,-k*(d+1));for(var f=0;f<a.datasets[0].data.length;f++)b.rotate(e),b.lineTo(0,-k*(d+1));b.closePath();b.stroke()}c.scaleShowLabels&&
-(b.textAlign="center",b.font=c.scaleFontStyle+" "+c.scaleFontSize+"px "+c.scaleFontFamily,b.textBaseline="middle",c.scaleShowLabelBackdrop&&(f=b.measureText(j.labels[d]).width,b.fillStyle=c.scaleBackdropColor,b.beginPath(),b.rect(Math.round(-f/2-c.scaleBackdropPaddingX),Math.round(-k*(d+1)-0.5*c.scaleFontSize-c.scaleBackdropPaddingY),Math.round(f+2*c.scaleBackdropPaddingX),Math.round(c.scaleFontSize+2*c.scaleBackdropPaddingY)),b.fill()),b.fillStyle=c.scaleFontColor,b.fillText(j.labels[d],0,-k*(d+
-1)))}for(d=0;d<a.labels.length;d++){b.font=c.pointLabelFontStyle+" "+c.pointLabelFontSize+"px "+c.pointLabelFontFamily;b.fillStyle=c.pointLabelFontColor;var f=Math.sin(e*d)*(g+c.pointLabelFontSize),h=Math.cos(e*d)*(g+c.pointLabelFontSize);b.textAlign=e*d==Math.PI||0==e*d?"center":e*d>Math.PI?"right":"left";b.textBaseline="middle";b.fillText(a.labels[d],f,-h)}b.restore()},function(d){var e=2*Math.PI/a.datasets[0].data.length;b.save();b.translate(q/2,u/2);for(var g=0;g<a.datasets.length;g++){b.beginPath();
-b.moveTo(0,d*-1*v(a.datasets[g].data[0],j,k));for(var f=1;f<a.datasets[g].data.length;f++)b.rotate(e),b.lineTo(0,d*-1*v(a.datasets[g].data[f],j,k));b.closePath();b.fillStyle=a.datasets[g].fillColor;b.strokeStyle=a.datasets[g].strokeColor;b.lineWidth=c.datasetStrokeWidth;b.fill();b.stroke();if(c.pointDot){b.fillStyle=a.datasets[g].pointColor;b.strokeStyle=a.datasets[g].pointStrokeColor;b.lineWidth=c.pointDotStrokeWidth;for(f=0;f<a.datasets[g].data.length;f++)b.rotate(e),b.beginPath(),b.arc(0,d*-1*
-v(a.datasets[g].data[f],j,k),c.pointDotRadius,2*Math.PI,!1),b.fill(),b.stroke()}b.rotate(e)}b.restore()},b)},I=function(a,c,b){for(var e=0,h=Math.min.apply(Math,[u/2,q/2])-5,f=0;f<a.length;f++)e+=a[f].value;x(c,null,function(d){var g=-Math.PI/2,f=1,j=1;c.animation&&(c.animateScale&&(f=d),c.animateRotate&&(j=d));for(d=0;d<a.length;d++){var l=j*a[d].value/e*2*Math.PI;b.beginPath();b.arc(q/2,u/2,f*h,g,g+l);b.lineTo(q/2,u/2);b.closePath();b.fillStyle=a[d].color;b.fill();c.segmentShowStroke&&(b.lineWidth=
-c.segmentStrokeWidth,b.strokeStyle=c.segmentStrokeColor,b.stroke());g+=l}},b)},J=function(a,c,b){for(var e=0,h=Math.min.apply(Math,[u/2,q/2])-5,f=h*(c.percentageInnerCutout/100),d=0;d<a.length;d++)e+=a[d].value;x(c,null,function(d){var k=-Math.PI/2,j=1,l=1;c.animation&&(c.animateScale&&(j=d),c.animateRotate&&(l=d));for(d=0;d<a.length;d++){var m=l*a[d].value/e*2*Math.PI;b.beginPath();b.arc(q/2,u/2,j*h,k,k+m,!1);b.arc(q/2,u/2,j*f,k+m,k,!0);b.closePath();b.fillStyle=a[d].color;b.fill();c.segmentShowStroke&&
-(b.lineWidth=c.segmentStrokeWidth,b.strokeStyle=c.segmentStrokeColor,b.stroke());k+=m}},b)},K=function(a,c,b){var e,h,f,d,g,k,j,l,m,t,r,n,p,s=0;g=u;b.font=c.scaleFontStyle+" "+c.scaleFontSize+"px "+c.scaleFontFamily;t=1;for(d=0;d<a.labels.length;d++)e=b.measureText(a.labels[d]).width,t=e>t?e:t;q/a.labels.length<t?(s=45,q/a.labels.length<Math.cos(s)*t?(s=90,g-=t):g-=Math.sin(s)*t):g-=c.scaleFontSize;d=c.scaleFontSize;g=g-5-d;e=Number.MIN_VALUE;h=Number.MAX_VALUE;for(f=0;f<a.datasets.length;f++)for(l=
-0;l<a.datasets[f].data.length;l++)a.datasets[f].data[l]>e&&(e=a.datasets[f].data[l]),a.datasets[f].data[l]<h&&(h=a.datasets[f].data[l]);f=Math.floor(g/(0.66*d));d=Math.floor(0.5*(g/d));l=c.scaleShowLabels?c.scaleLabel:"";c.scaleOverride?(j={steps:c.scaleSteps,stepValue:c.scaleStepWidth,graphMin:c.scaleStartValue,labels:[]},z(l,j.labels,j.steps,c.scaleStartValue,c.scaleStepWidth)):j=C(g,f,d,e,h,l);k=Math.floor(g/j.steps);d=1;if(c.scaleShowLabels){b.font=c.scaleFontStyle+" "+c.scaleFontSize+"px "+c.scaleFontFamily;
-for(e=0;e<j.labels.length;e++)h=b.measureText(j.labels[e]).width,d=h>d?h:d;d+=10}r=q-d-t;m=Math.floor(r/(a.labels.length-1));n=q-t/2-r;p=g+c.scaleFontSize/2;x(c,function(){b.lineWidth=c.scaleLineWidth;b.strokeStyle=c.scaleLineColor;b.beginPath();b.moveTo(q-t/2+5,p);b.lineTo(q-t/2-r-5,p);b.stroke();0<s?(b.save(),b.textAlign="right"):b.textAlign="center";b.fillStyle=c.scaleFontColor;for(var d=0;d<a.labels.length;d++)b.save(),0<s?(b.translate(n+d*m,p+c.scaleFontSize),b.rotate(-(s*(Math.PI/180))),b.fillText(a.labels[d],
-0,0),b.restore()):b.fillText(a.labels[d],n+d*m,p+c.scaleFontSize+3),b.beginPath(),b.moveTo(n+d*m,p+3),c.scaleShowGridLines&&0<d?(b.lineWidth=c.scaleGridLineWidth,b.strokeStyle=c.scaleGridLineColor,b.lineTo(n+d*m,5)):b.lineTo(n+d*m,p+3),b.stroke();b.lineWidth=c.scaleLineWidth;b.strokeStyle=c.scaleLineColor;b.beginPath();b.moveTo(n,p+5);b.lineTo(n,5);b.stroke();b.textAlign="right";b.textBaseline="middle";for(d=0;d<j.steps;d++)b.beginPath(),b.moveTo(n-3,p-(d+1)*k),c.scaleShowGridLines?(b.lineWidth=c.scaleGridLineWidth,
-b.strokeStyle=c.scaleGridLineColor,b.lineTo(n+r+5,p-(d+1)*k)):b.lineTo(n-0.5,p-(d+1)*k),b.stroke(),c.scaleShowLabels&&b.fillText(j.labels[d],n-8,p-(d+1)*k)},function(d){function e(b,c){return p-d*v(a.datasets[b].data[c],j,k)}for(var f=0;f<a.datasets.length;f++){b.strokeStyle=a.datasets[f].strokeColor;b.lineWidth=c.datasetStrokeWidth;b.beginPath();b.moveTo(n,p-d*v(a.datasets[f].data[0],j,k));for(var g=1;g<a.datasets[f].data.length;g++)c.bezierCurve?b.bezierCurveTo(n+m*(g-0.5),e(f,g-1),n+m*(g-0.5),
-e(f,g),n+m*g,e(f,g)):b.lineTo(n+m*g,e(f,g));b.stroke();c.datasetFill?(b.lineTo(n+m*(a.datasets[f].data.length-1),p),b.lineTo(n,p),b.closePath(),b.fillStyle=a.datasets[f].fillColor,b.fill()):b.closePath();if(c.pointDot){b.fillStyle=a.datasets[f].pointColor;b.strokeStyle=a.datasets[f].pointStrokeColor;b.lineWidth=c.pointDotStrokeWidth;for(g=0;g<a.datasets[f].data.length;g++)b.beginPath(),b.arc(n+m*g,p-d*v(a.datasets[f].data[g],j,k),c.pointDotRadius,0,2*Math.PI,!0),b.fill(),b.stroke()}}},b)},L=function(a,
-c,b){var e,h,f,d,g,k,j,l,m,t,r,n,p,s,w=0;g=u;b.font=c.scaleFontStyle+" "+c.scaleFontSize+"px "+c.scaleFontFamily;t=1;for(d=0;d<a.labels.length;d++)e=b.measureText(a.labels[d]).width,t=e>t?e:t;q/a.labels.length<t?(w=45,q/a.labels.length<Math.cos(w)*t?(w=90,g-=t):g-=Math.sin(w)*t):g-=c.scaleFontSize;d=c.scaleFontSize;g=g-5-d;e=Number.MIN_VALUE;h=Number.MAX_VALUE;for(f=0;f<a.datasets.length;f++)for(l=0;l<a.datasets[f].data.length;l++)a.datasets[f].data[l]>e&&(e=a.datasets[f].data[l]),a.datasets[f].data[l]<
-h&&(h=a.datasets[f].data[l]);f=Math.floor(g/(0.66*d));d=Math.floor(0.5*(g/d));l=c.scaleShowLabels?c.scaleLabel:"";c.scaleOverride?(j={steps:c.scaleSteps,stepValue:c.scaleStepWidth,graphMin:c.scaleStartValue,labels:[]},z(l,j.labels,j.steps,c.scaleStartValue,c.scaleStepWidth)):j=C(g,f,d,e,h,l);k=Math.floor(g/j.steps);d=1;if(c.scaleShowLabels){b.font=c.scaleFontStyle+" "+c.scaleFontSize+"px "+c.scaleFontFamily;for(e=0;e<j.labels.length;e++)h=b.measureText(j.labels[e]).width,d=h>d?h:d;d+=10}r=q-d-t;m=
-Math.floor(r/a.labels.length);s=(m-2*c.scaleGridLineWidth-2*c.barValueSpacing-(c.barDatasetSpacing*a.datasets.length-1)-(c.barStrokeWidth/2*a.datasets.length-1))/a.datasets.length;n=q-t/2-r;p=g+c.scaleFontSize/2;x(c,function(){b.lineWidth=c.scaleLineWidth;b.strokeStyle=c.scaleLineColor;b.beginPath();b.moveTo(q-t/2+5,p);b.lineTo(q-t/2-r-5,p);b.stroke();0<w?(b.save(),b.textAlign="right"):b.textAlign="center";b.fillStyle=c.scaleFontColor;for(var d=0;d<a.labels.length;d++)b.save(),0<w?(b.translate(n+
-d*m,p+c.scaleFontSize),b.rotate(-(w*(Math.PI/180))),b.fillText(a.labels[d],0,0),b.restore()):b.fillText(a.labels[d],n+d*m+m/2,p+c.scaleFontSize+3),b.beginPath(),b.moveTo(n+(d+1)*m,p+3),b.lineWidth=c.scaleGridLineWidth,b.strokeStyle=c.scaleGridLineColor,b.lineTo(n+(d+1)*m,5),b.stroke();b.lineWidth=c.scaleLineWidth;b.strokeStyle=c.scaleLineColor;b.beginPath();b.moveTo(n,p+5);b.lineTo(n,5);b.stroke();b.textAlign="right";b.textBaseline="middle";for(d=0;d<j.steps;d++)b.beginPath(),b.moveTo(n-3,p-(d+1)*
-k),c.scaleShowGridLines?(b.lineWidth=c.scaleGridLineWidth,b.strokeStyle=c.scaleGridLineColor,b.lineTo(n+r+5,p-(d+1)*k)):b.lineTo(n-0.5,p-(d+1)*k),b.stroke(),c.scaleShowLabels&&b.fillText(j.labels[d],n-8,p-(d+1)*k)},function(d){b.lineWidth=c.barStrokeWidth;for(var e=0;e<a.datasets.length;e++){b.fillStyle=a.datasets[e].fillColor;b.strokeStyle=a.datasets[e].strokeColor;for(var f=0;f<a.datasets[e].data.length;f++){var g=n+c.barValueSpacing+m*f+s*e+c.barDatasetSpacing*e+c.barStrokeWidth*e;b.beginPath();
-b.moveTo(g,p);b.lineTo(g,p-d*v(a.datasets[e].data[f],j,k)+c.barStrokeWidth/2);b.lineTo(g+s,p-d*v(a.datasets[e].data[f],j,k)+c.barStrokeWidth/2);b.lineTo(g+s,p);c.barShowStroke&&b.stroke();b.closePath();b.fill()}}},b)},D=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(a){window.setTimeout(a,1E3/60)},F={}}; \ No newline at end of file
diff --git a/app/assets/javascripts/ci/projects.js.coffee b/app/assets/javascripts/ci/projects.js.coffee
index 7e028b4e115..e6406011d11 100644
--- a/app/assets/javascripts/ci/projects.js.coffee
+++ b/app/assets/javascripts/ci/projects.js.coffee
@@ -1,6 +1,3 @@
$(document).on 'click', '.badge-codes-toggle', ->
$('.badge-codes-block').toggleClass("hide")
return false
-
-$(document).on 'click', '.sync-now', ->
- $(this).find('i').addClass('fa-spin')
diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee
index 4b9f0d68912..ea75c656bcc 100644
--- a/app/assets/javascripts/notes.js.coffee
+++ b/app/assets/javascripts/notes.js.coffee
@@ -63,12 +63,6 @@ class @Notes
# fetch notes when tab becomes visible
$(document).on "visibilitychange", @visibilityChange
- # Chrome doesn't fire keypress or keyup for Command+Enter, so we need keydown.
- $(document).on 'keydown', '.js-note-text', (e) ->
- return if e.originalEvent.repeat
- if e.keyCode == 10 || ((e.metaKey || e.ctrlKey) && e.keyCode == 13)
- $(@).closest('form').submit()
-
cleanBinding: ->
$(document).off "ajax:success", ".js-main-target-form"
$(document).off "ajax:success", ".js-discussion-note-form"
@@ -82,7 +76,6 @@ class @Notes
$(document).off "click", ".js-discussion-reply-button"
$(document).off "click", ".js-add-diff-note-button"
$(document).off "visibilitychange"
- $(document).off "keydown", ".js-note-text"
$(document).off "keyup", ".js-note-text"
$(document).off "click", ".js-note-target-reopen"
$(document).off "click", ".js-note-target-close"
diff --git a/app/assets/javascripts/tree.js.coffee b/app/assets/javascripts/tree.js.coffee
index d428db5b422..de8eebcd0b2 100644
--- a/app/assets/javascripts/tree.js.coffee
+++ b/app/assets/javascripts/tree.js.coffee
@@ -16,6 +16,9 @@ class @TreeView
li = $("tr.tree-item")
liSelected = null
$('body').keydown (e) ->
+ if $("input:focus").length > 0 && (e.which == 38 || e.which == 40)
+ return false
+
if e.which is 40
if liSelected
next = liSelected.next()
@@ -38,4 +41,4 @@ class @TreeView
$(liSelected).focus()
else if e.which is 13
path = $('.tree-item.selected .tree-item-file-name a').attr('href')
- Turbolinks.visit(path)
+ if path then Turbolinks.visit(path)
diff --git a/app/assets/stylesheets/base/gl_variables.scss b/app/assets/stylesheets/base/gl_variables.scss
index 7378d404008..18632da4f2a 100644
--- a/app/assets/stylesheets/base/gl_variables.scss
+++ b/app/assets/stylesheets/base/gl_variables.scss
@@ -22,8 +22,8 @@ $brand-info: $gl-info;
$brand-warning: $gl-warning;
$brand-danger: $gl-danger;
-$border-radius-base: 3px !default;
-$border-radius-large: 5px !default;
+$border-radius-base: 2px !default;
+$border-radius-large: 2px !default;
$border-radius-small: 2px !default;
diff --git a/app/assets/stylesheets/base/layout.scss b/app/assets/stylesheets/base/layout.scss
index b91c15d8910..c7b3b60e769 100644
--- a/app/assets/stylesheets/base/layout.scss
+++ b/app/assets/stylesheets/base/layout.scss
@@ -5,6 +5,7 @@ html {
body {
padding-top: $header-height;
+ text-rendering: geometricPrecision;
}
}
diff --git a/app/assets/stylesheets/base/variables.scss b/app/assets/stylesheets/base/variables.scss
index befd63832d5..eb9a2966389 100644
--- a/app/assets/stylesheets/base/variables.scss
+++ b/app/assets/stylesheets/base/variables.scss
@@ -23,15 +23,67 @@ $gl-gray: #7f8fa4;
$gl-padding: 16px;
$gl-avatar-size: 46px;
+/*
+ * Color schema
+ */
+
+$white-light: #FFFFFF;
+$white-normal: #DCE0E5;
+$white-dark: #E4E7ED;
+
+$gray-light: #F0F2F5;
+$gray-normal: #DCE0E5;
+$gray-dark: #E4E7ED;
+
+$green-light: #31AF64;
+$green-normal: #2FAA60;
+$green-dark: #2CA05B;
+
+$blue-light: #2EA8E5;
+$blue-normal: #2D9FD8;
+$blue-dark: #2897CE;
+
+$orange-light: #FC6443;
+$orange-normal: #E75E40;
+$orange-dark: #CE5237;
+
+$red-light: #F43263;
+$red-normal: #E52C5A;
+$red-dark: #D22852;
+
+$border-white-light: #E3E7EC;
+$border-white-normal: #D6DAE2;
+$border-white-dark: #C6CACF;
+
+$border-gray-light: #DCE0E5;
+$border-gray-normal: #D6DAE2;
+$border-gray-dark: #C6CACF;
+
+$border-green-light: #2FAA60;
+$border-green-normal: #2CA05B;
+$border-green-dark: #279654;
+
+$border-blue-light: #2D9FD8;
+$border-blue-normal: #2897CE;
+$border-blue-dark: #258DC1;
+
+$border-orange-light: #ED5C3D;
+$border-orange-normal: #CE5237;
+$border-orange-dark: #C14E35;
+
+$border-red-light: #E52C5A;
+$border-red-normal: #D22852;
+$border-red-dark: #CA264F;
+
/*
* State colors:
*/
-$gl-primary: #446e9b;
-$gl-success: #44c679;
-$gl-info: #00aaff;
-$gl-warning: #EB9532;
-$gl-danger: #d9534f;
+$gl-primary: $blue-normal;
+$gl-success: $green-normal;
+$gl-info: $blue-normal;
+$gl-warning: $orange-normal;
+$gl-danger: $red-normal;
/*
* Commit Diff Colors
diff --git a/app/assets/stylesheets/ci/builds.scss b/app/assets/stylesheets/ci/builds.scss
index a11a935b54d..74dc3e321c1 100644
--- a/app/assets/stylesheets/ci/builds.scss
+++ b/app/assets/stylesheets/ci/builds.scss
@@ -1,4 +1,4 @@
-.ci-body {
+.build-page {
pre.trace {
background: #111111;
color: #fff;
@@ -67,4 +67,9 @@
color: #3084bb !important;
}
}
+
+ .build-top-menu {
+ margin-top: 0;
+ margin-bottom: 2px;
+ }
}
diff --git a/app/assets/stylesheets/ci/xterm.scss b/app/assets/stylesheets/ci/xterm.scss
index 532dede0b23..9a50096c0d0 100644
--- a/app/assets/stylesheets/ci/xterm.scss
+++ b/app/assets/stylesheets/ci/xterm.scss
@@ -1,4 +1,4 @@
-.ci-body {
+.build-page {
// color codes are based on http://en.wikipedia.org/wiki/File:Xterm_256color_chart.svg
// see also: https://gist.github.com/jasonm23/2868981
diff --git a/app/assets/stylesheets/generic/buttons.scss b/app/assets/stylesheets/generic/buttons.scss
index cf76f538e01..11acbe3adfa 100644
--- a/app/assets/stylesheets/generic/buttons.scss
+++ b/app/assets/stylesheets/generic/buttons.scss
@@ -1,23 +1,101 @@
-body {
- text-rendering: geometricPrecision;
+@mixin btn-default {
+ @include border-radius(2px);
+ border-width: 1px;
+ border-style: solid;
+ text-transform: uppercase;
+ font-size: 13px;
+ font-weight: 600;
+ line-height: 18px;
+ padding: 11px 16px;
+ letter-spacing: .4px;
+
+ &:focus,
+ &:active {
+ outline: none;
+ @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12));
+ }
+}
+
+@mixin btn-middle {
+ @include btn-default;
+ @include border-radius(2px);
+ padding: 11px 24px;
+}
+
+@mixin btn-color($light, $border-light, $normal, $border-normal, $dark, $border-dark, $color) {
+ background-color: $light;
+ border-color: $border-light;
+ color: $color;
+
+ &:hover,
+ &:focus {
+ background-color: $normal;
+ border-color: $border-normal;
+ color: $color;
+ }
+
+ &:active {
+ @include box-shadow (inset 0 0 4px rgba(0, 0, 0, 0.12));
+
+ background-color: $dark;
+ border-color: $border-dark;
+ color: $color;
+ }
+}
+
+@mixin btn-green {
+ @include btn-color($green-light, $border-green-light, $green-normal, $border-green-normal, $green-dark, $border-green-dark, #FFFFFF);
+}
+
+@mixin btn-blue {
+ @include btn-color($blue-light, $border-blue-light, $blue-normal, $border-blue-normal, $blue-dark, $border-blue-dark, #FFFFFF);
+}
+
+@mixin btn-orange {
+ @include btn-color($orange-light, $border-orange-light, $orange-normal, $border-orange-normal, $orange-dark, $border-orange-dark, #FFFFFF);
+}
+
+@mixin btn-red {
+ @include btn-color($red-light, $border-red-light, $red-normal, $border-red-normal, $red-dark, $border-red-dark, #FFFFFF);
+}
+
+@mixin btn-gray {
+ @include btn-color($gray-light, $border-gray-light, $gray-normal, $border-gray-normal, $gray-dark, $border-gray-dark, #313236);
}
+
+@mixin btn-white {
+ @include btn-color($white-light, $border-white-light, $white-normal, $border-white-normal, $white-dark, $border-white-dark, #313236);
+}
+
.btn {
- @extend .btn-default;
+ @include btn-default;
+ @include btn-white;
- &.btn-new {
- @extend .btn-success;
+ &.btn-success,
+ &.btn-new,
+ &.btn-create,
+ &.btn-save,
+ &.btn-green {
+ @include btn-green;
}
- &.btn-create {
- @extend .btn-success;
+ &.btn-gray {
+ @include btn-gray;
}
- &.btn-save {
- @extend .btn-success;
+ &.btn-primary,
+ &.btn-info {
+ @include btn-blue;
}
- &.btn-remove {
- @extend .btn-danger;
+ &.btn-warning {
+ @include btn-orange;
+ }
+
+ &.btn-danger,
+ &.btn-remove,
+ &.btn-red {
+ @include btn-red;
}
&.btn-cancel {
@@ -47,14 +125,6 @@ body {
margin-right: 0px;
}
}
-
- &.btn-save {
- @extend .btn-primary;
- }
-
- &.btn-new, &.btn-create {
- @extend .btn-success;
- }
}
.btn-block {
@@ -91,138 +161,3 @@ body {
}
}
}
-
-@mixin btn-info {
- @include border-radius(2px);
-
- border-width: 1px;
- border-style: solid;
- text-transform: uppercase;
- font-size: 13px;
- font-weight: 600;
- line-height: 18px;
- padding: 11px 16px;
- letter-spacing: .4px;
-
- &:hover {
- border-width: 1px;
- border-style: solid;
- }
-
- &:focus {
- border-width: 1px;
- border-style: solid;
- }
-
- &:active {
- @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12));
- border-width: 1px;
- border-style: solid;
- }
-}
-
-@mixin btn-middle {
- @include border-radius(2px);
-
- border-width: 1px;
- border-style: solid;
- text-transform: uppercase;
- font-size: 13px;
- font-weight: 600;
- line-height: 18px;
- padding: 11px 24px;
- letter-spacing: .4px;
-
- &:hover {
- border-width: 1px;
- border-style: solid;
- }
-
- &:focus {
- border-width: 1px;
- border-style: solid;
- }
-
- &:active {
- @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12));
- border-width: 1px;
- border-style: solid;
- }
-}
-
-
-@mixin btn-green {
- background-color: #28b061;
- border: 1px solid #26a65c;
- color: #fff;
-
- &:hover {
- background-color: #26ab5d;
- border: 1px solid #229954;
- color: #fff;
- }
-
- &:focus {
- background-color: #26ab5d;
- border: 1px solid #229954;
- color: #fff;
- }
-
- &:active {
- @include box-shadow (inset 0 0 4px rgba(0, 0, 0, 0.12));
-
- background-color: #23a158 !important;
- border: 1px solid #229954 !important;
- color: #fff !important;
- }
-}
-
-/*Butons*/
-
-@mixin bnt-project {
- background-color: #f0f2f5;
- border-color: #dce0e5;
- color: #313236;
-
- &:hover {
- border-color:#dce0e5;
- background-color: #ebeef2;
- color: #313236;
- }
-
- &:focus {
- border-color: #dce0e5;
- background-color: #ebeef2;
- color: #313236;
- }
-
- &:active {
- @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12));
-
- color: #313236 !important;
- border-color: #c6cacf !important;
- background-color: #e4e7ed !important;
- }
-}
-
-@mixin btn-remove {
- background-color: #f72e60;
- border-color: #ee295a;
-
- &:hover {
- background-color: #e82757;
- border-color: #e32555;
- }
-
- &:focus {
- background-color: #e82757;
- border-color: #e32555;
- }
-
- &:active {
- @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12));
- background-color: #d42450 !important;
- border-color: #e12554 !important;
- }
-
-} \ No newline at end of file
diff --git a/app/assets/stylesheets/generic/common.scss b/app/assets/stylesheets/generic/common.scss
index 016cc015e9c..03919f15f1f 100644
--- a/app/assets/stylesheets/generic/common.scss
+++ b/app/assets/stylesheets/generic/common.scss
@@ -381,6 +381,10 @@ table {
&.no-bottom {
margin-bottom: 0;
}
+
+ &.no-top {
+ margin-top: 0;
+ }
}
.dropzone .dz-preview .dz-progress {
@@ -390,3 +394,11 @@ table {
.dropzone .dz-preview .dz-progress .dz-upload {
background: $gl-success !important;
}
+
+.space-right {
+ margin-right: 10px;
+}
+
+.in-line {
+ display: inline-block;
+}
diff --git a/app/assets/stylesheets/generic/forms.scss b/app/assets/stylesheets/generic/forms.scss
index 4282832e2bf..0edfe24f195 100644
--- a/app/assets/stylesheets/generic/forms.scss
+++ b/app/assets/stylesheets/generic/forms.scss
@@ -29,12 +29,6 @@ input[type='text'].danger {
border-top: 1px solid $border-color;
}
-@media (min-width: $screen-sm-min) {
- .form-actions {
- padding-left: 17%;
- }
-}
-
label {
&.control-label {
@extend .col-sm-2;
@@ -84,3 +78,17 @@ label {
.wiki-content {
margin-top: 35px;
}
+
+.form-group .control-label {
+ font-weight: normal;
+}
+
+.form-control::-webkit-input-placeholder {
+ color: #7f8fa4;
+}
+
+.input-group {
+ .input-group-addon {
+ background-color: #f7f8fa;
+ }
+}
diff --git a/app/assets/stylesheets/generic/issue_box.scss b/app/assets/stylesheets/generic/issue_box.scss
index b1fb87a6830..93377e45e70 100644
--- a/app/assets/stylesheets/generic/issue_box.scss
+++ b/app/assets/stylesheets/generic/issue_box.scss
@@ -5,7 +5,7 @@
*/
.issue-box {
- @include border-radius(3px);
+ @include border-radius(2px);
display: inline-block;
padding: 10px $gl-padding;
diff --git a/app/assets/stylesheets/generic/selects.scss b/app/assets/stylesheets/generic/selects.scss
index f0860de1c49..cba621635b6 100644
--- a/app/assets/stylesheets/generic/selects.scss
+++ b/app/assets/stylesheets/generic/selects.scss
@@ -8,7 +8,7 @@
font-size: $gl-font-size;
line-height: 1.42857143;
- @include border-radius(4px);
+ @include border-radius(2px);
.select2-arrow {
background: #FFF;
@@ -18,8 +18,39 @@
}
}
+.select2-container .select2-choice, .select2-container.select2-drop-above .select2-choice{
+ color: #7f8fa4;
+ border: 1px solid #e7e9ed;
+}
+
+.select2-drop {
+ @include box-shadow(rgba(76, 86, 103, 0.247059) 0px 0px 1px 0px, rgba(31, 37, 50, 0.317647) 0px 2px 18px 0px);
+ @include border-radius (0px);
+
+ padding: 16px;
+ border: none !important;
+}
+
+.select2-results .select2-result-label {
+ padding: 16px;
+}
+
+.select2-drop{
+ color: #7f8fa4;
+}
+
+.select2-highlighted {
+ background: #3084bb !important;
+}
+
+.select2-results li.select2-result-with-children > .select2-result-label {
+ font-weight: 600;
+ color: #313236;
+}
+
+
.select2-container-multi .select2-choices {
- @include border-radius(4px);
+ @include border-radius(2px);
border-color: #CCC;
}
@@ -63,7 +94,7 @@
.ajax-users-dropdown, .ajax-project-users-dropdown {
.select2-search {
- padding-top: 4px;
+ padding-top: 2px;
}
}
@@ -97,9 +128,6 @@
}
.user-name {
}
- .user-username {
- color: #999;
- }
}
.namespace-result {
@@ -114,5 +142,5 @@
}
.ajax-users-dropdown {
- min-width: 225px !important;
+ min-width: 250px !important;
}
diff --git a/app/assets/stylesheets/generic/timeline.scss b/app/assets/stylesheets/generic/timeline.scss
index 74bbaabad39..bf21d7fce76 100644
--- a/app/assets/stylesheets/generic/timeline.scss
+++ b/app/assets/stylesheets/generic/timeline.scss
@@ -10,8 +10,8 @@
margin-left: -$gl-padding;
margin-right: -$gl-padding;
color: $gl-gray;
- border-bottom: 1px solid #f1f2f4;
- border-right: 1px solid #f1f2f4;
+ border-bottom: 1px solid #ECEEF1;
+ border-right: 1px solid #ECEEF1;
&:last-child {
border-bottom: none;
diff --git a/app/assets/stylesheets/pages/commit.scss b/app/assets/stylesheets/pages/commit.scss
index 741ff9051a2..fbd7c363de1 100644
--- a/app/assets/stylesheets/pages/commit.scss
+++ b/app/assets/stylesheets/pages/commit.scss
@@ -107,3 +107,16 @@
z-index: 2;
}
}
+
+.commit-ci-menu {
+ padding: 0;
+ margin: 0;
+ list-style: none;
+ margin-top: 5px;
+ height: 56px;
+ margin: -16px;
+ padding: 16px;
+ text-align: center;
+ margin-top: 0px;
+ margin-bottom: 2px;
+}
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index de2ae93df37..4e121b95d13 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -1,5 +1,6 @@
.commits-compare-switch{
- @extend .btn;
+ @include btn-default;
+ @include btn-white;
background: image-url("switch_icon.png") no-repeat center center;
text-indent: -9999px;
float: left;
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index b5c61f7f91d..9da085a3473 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -54,21 +54,22 @@
margin-top: -15px;
padding: 10px 0;
margin-bottom: 0;
- color: $gl-gray;
+ color: #5c5d5e;
font-size: 16px;
.author {
- color: $gl-gray;
+ color: #5c5d5e;
}
.issue-id {
- font-size: 19px;
- color: $gl-text-color;
+ color: #5c5d5e;
}
}
.issue-title {
margin: 0;
+ font-size: 23px;
+ color: #313236;
}
.description {
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index d8c8e5ad0a4..fe69d16fc4b 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -3,12 +3,11 @@
*
*/
.mr-state-widget {
- background: #f8fafc;
+ background: #F7F8FA;
margin-bottom: 20px;
color: $gl-gray;
- border: 1px solid #eef0f2;
- @include box-shadow(0 1px 1px rgba(0, 0, 0, 0.05));
- @include border-radius(3px);
+ border: 1px solid #dce0e6;
+ @include border-radius(2px);
form {
margin-bottom: 0;
@@ -76,11 +75,17 @@
.mr-widget-footer {
padding: 15px;
}
+
+ .normal {
+ color: #5c5d5e;
+ }
.mr-widget-body {
h4 {
- font-weight: bold;
+ font-weight: 600;
+ font-size: 17px;
margin: 5px 0;
+ color: #313236;
}
p:last-child {
@@ -102,9 +107,7 @@
margin: -$gl-padding;
padding: $gl-padding;
text-align: center;
- border-top: 1px solid #e7e9ed;
- margin-top: 18px;
- margin-bottom: 3px;
+ margin-bottom: 1px;
}
.mr_source_commit,
@@ -120,10 +123,12 @@
}
.label-branch {
- color: #222;
+ color: #313236;
font-family: $monospace_font;
font-weight: bold;
overflow: hidden;
+ font-size: 14px;
+ margin: 0 3px;
}
.mr-list {
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index fdc2c3332df..dcd1aed7196 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -72,12 +72,12 @@
.common-note-form {
margin: 0;
- background: #f8fafc;
+ background: #F7F8FA;
padding: $gl-padding;
margin-left: -$gl-padding;
margin-right: -$gl-padding;
- border-right: 1px solid #f1f2f4;
- border-top: 1px solid #f1f2f4;
+ border-right: 1px solid #ECEEF1;
+ border-top: 1px solid #ECEEF1;
margin-bottom: -$gl-padding;
}
@@ -168,7 +168,7 @@
.comment-hints {
color: #999;
background: #FFF;
- padding: 5px;
+ padding: 7px;
margin-top: -11px;
border: 1px solid $border-color;
font-size: 13px;
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index 2a77f065aed..abb03b07f51 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -18,7 +18,7 @@ ul.notes {
font-size: 14px;
padding-top: 10px;
padding-bottom: 10px;
- background: #f8fafc;
+ background: #FDFDFD;
.timeline-icon {
.avatar {
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index 818aa10aefe..0031ab5151b 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -13,11 +13,15 @@
.edit_project {
fieldset.features {
.control-label {
- font-weight: bold;
+ font-weight: normal;
}
}
}
+.project-edit-content {
+ padding: 7px;
+}
+
.project-name-holder {
.help-inline {
vertical-align: top;
@@ -92,8 +96,7 @@
margin-bottom: 0px;
.btn {
- @include bnt-project;
- @include btn-info;
+ @include btn-gray;
.count {
display: inline-block;
@@ -149,7 +152,7 @@
.input-group-btn {
.btn {
- @include bnt-project;
+ @include btn-gray;
@include btn-middle;
&:hover {
@@ -183,8 +186,8 @@
margin: 0 12px 0 12px;
.btn{
- @include bnt-project;
- @include btn-info;
+ @include btn-gray;
+ @include btn-default;
}
.dropdown-toggle {
@@ -251,18 +254,19 @@
margin-bottom: 10px;
i {
- margin: 0 3px;
+ margin: 2px 0;
font-size: 20px;
}
.option-title {
- font-weight: bold;
+ font-weight: normal;
display: inline-block;
+ color: #313236;
}
.option-descr {
- margin-left: 36px;
- color: $gray;
+ margin-left: 29px;
+ color: #54565b;
}
}
}
@@ -376,8 +380,8 @@ table.table.protected-branches-list tr.no-border {
}
.nav > li > a {
- @include btn-info;
- @include bnt-project;
+ @include btn-default;
+ @include btn-gray;
background-color: transparent;
border: 1px solid #f7f8fa;
@@ -437,7 +441,7 @@ pre.light-well {
.btn-remove {
@include btn-middle;
- @include btn-remove;
+ @include btn-red;
float: left !important;
}
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb
index 5f70582cbb7..7c134d2ec9b 100644
--- a/app/controllers/admin/application_settings_controller.rb
+++ b/app/controllers/admin/application_settings_controller.rb
@@ -56,7 +56,6 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:restricted_signup_domains_raw,
:version_check_enabled,
:user_oauth_applications,
- :ci_enabled,
restricted_visibility_levels: [],
import_sources: []
)
diff --git a/app/controllers/ci/application_controller.rb b/app/controllers/ci/application_controller.rb
index d8227e632e4..9be470660e6 100644
--- a/app/controllers/ci/application_controller.rb
+++ b/app/controllers/ci/application_controller.rb
@@ -1,7 +1,5 @@
module Ci
class ApplicationController < ::ApplicationController
- before_action :check_enable_flag!
-
def self.railtie_helpers_paths
"app/helpers/ci"
end
@@ -10,13 +8,6 @@ module Ci
private
- def check_enable_flag!
- unless current_application_settings.ci_enabled
- redirect_to(disabled_ci_projects_path)
- return
- end
- end
-
def authenticate_public_page!
unless project.public
authenticate_user!
diff --git a/app/controllers/ci/builds_controller.rb b/app/controllers/ci/builds_controller.rb
deleted file mode 100644
index 80ee8666792..00000000000
--- a/app/controllers/ci/builds_controller.rb
+++ /dev/null
@@ -1,78 +0,0 @@
-module Ci
- class BuildsController < Ci::ApplicationController
- before_action :authenticate_user!, except: [:status, :show]
- before_action :authenticate_public_page!, only: :show
- before_action :project
- before_action :authorize_access_project!, except: [:status, :show]
- before_action :authorize_manage_project!, except: [:status, :show, :retry, :cancel]
- before_action :authorize_manage_builds!, only: [:retry, :cancel]
- before_action :build, except: [:show]
- layout 'ci/build'
-
- def show
- if params[:id] =~ /\A\d+\Z/
- @build = build
- else
- # try to find commit by sha
- commit = commit_by_sha
-
- if commit
- # Redirect to commit page
- redirect_to ci_project_ref_commit_path(@project, @build.commit.ref, @build.commit.sha)
- return
- end
- end
-
- raise ActiveRecord::RecordNotFound unless @build
-
- @builds = @project.commits.find_by_sha(@build.sha).builds.order('id DESC')
- @builds = @builds.where("id not in (?)", @build.id).page(params[:page]).per(20)
- @commit = @build.commit
-
- respond_to do |format|
- format.html
- format.json do
- render json: @build.to_json(methods: :trace_html)
- end
- end
- end
-
- def retry
- if @build.commands.blank?
- return page_404
- end
-
- build = Ci::Build.retry(@build)
-
- if params[:return_to]
- redirect_to URI.parse(params[:return_to]).path
- else
- redirect_to ci_project_build_path(project, build)
- end
- end
-
- def status
- render json: @build.to_json(only: [:status, :id, :sha, :coverage], methods: :sha)
- end
-
- def cancel
- @build.cancel
-
- redirect_to ci_project_build_path(@project, @build)
- end
-
- protected
-
- def project
- @project = Ci::Project.find(params[:project_id])
- end
-
- def build
- @build ||= project.builds.unscoped.find_by(id: params[:id])
- end
-
- def commit_by_sha
- @project.commits.find_by(sha: params[:id])
- end
- end
-end
diff --git a/app/controllers/ci/commits_controller.rb b/app/controllers/ci/commits_controller.rb
deleted file mode 100644
index 7a0a500fbe6..00000000000
--- a/app/controllers/ci/commits_controller.rb
+++ /dev/null
@@ -1,38 +0,0 @@
-module Ci
- class CommitsController < Ci::ApplicationController
- before_action :authenticate_user!, except: [:status, :show]
- before_action :authenticate_public_page!, only: :show
- before_action :project
- before_action :authorize_access_project!, except: [:status, :show, :cancel]
- before_action :authorize_manage_builds!, only: [:cancel]
- before_action :commit, only: :show
- layout 'ci/commit'
-
- def show
- @builds = @commit.builds
- end
-
- def status
- commit = Ci::Project.find(params[:project_id]).commits.find_by_sha_and_ref!(params[:id], params[:ref_id])
- render json: commit.to_json(only: [:id, :sha], methods: [:status, :coverage])
- rescue ActiveRecord::RecordNotFound
- render json: { status: "not_found" }
- end
-
- def cancel
- commit.builds.running_or_pending.each(&:cancel)
-
- redirect_to ci_project_ref_commits_path(project, commit.ref, commit.sha)
- end
-
- private
-
- def project
- @project ||= Ci::Project.find(params[:project_id])
- end
-
- def commit
- @commit ||= Ci::Project.find(params[:project_id]).commits.find_by_sha_and_ref!(params[:id], params[:ref_id])
- end
- end
-end
diff --git a/app/controllers/ci/projects_controller.rb b/app/controllers/ci/projects_controller.rb
index e8788955eba..7777aa18031 100644
--- a/app/controllers/ci/projects_controller.rb
+++ b/app/controllers/ci/projects_controller.rb
@@ -1,27 +1,11 @@
module Ci
class ProjectsController < Ci::ApplicationController
- before_action :authenticate_user!, except: [:build, :badge, :show]
- before_action :authenticate_public_page!, only: :show
- before_action :project, only: [:build, :show, :badge, :toggle_shared_runners, :dumped_yaml]
- before_action :authorize_access_project!, except: [:build, :badge, :show, :new, :disabled]
+ before_action :project
+ before_action :authenticate_user!, except: [:build, :badge]
+ before_action :authorize_access_project!, except: [:badge]
before_action :authorize_manage_project!, only: [:toggle_shared_runners, :dumped_yaml]
- before_action :authenticate_token!, only: [:build]
before_action :no_cache, only: [:badge]
- skip_before_action :check_enable_flag!, only: [:disabled]
- protect_from_forgery except: :build
-
- layout 'ci/project', except: [:index, :disabled]
-
- def disabled
- end
-
- def show
- @ref = params[:ref]
-
- @commits = @project.commits.reverse_order
- @commits = @commits.where(ref: @ref) if @ref
- @commits = @commits.page(params[:page]).per(20)
- end
+ protect_from_forgery
# Project status badge
# Image with build status for sha or ref
diff --git a/app/controllers/ci/services_controller.rb b/app/controllers/ci/services_controller.rb
deleted file mode 100644
index 52c96a34ce8..00000000000
--- a/app/controllers/ci/services_controller.rb
+++ /dev/null
@@ -1,59 +0,0 @@
-module Ci
- class ServicesController < Ci::ApplicationController
- before_action :authenticate_user!
- before_action :project
- before_action :authorize_access_project!
- before_action :authorize_manage_project!
- before_action :service, only: [:edit, :update, :test]
-
- respond_to :html
-
- layout 'ci/project'
-
- def index
- @project.build_missing_services
- @services = @project.services.reload
- end
-
- def edit
- end
-
- def update
- if @service.update_attributes(service_params)
- redirect_to edit_ci_project_service_path(@project, @service.to_param)
- else
- render 'edit'
- end
- end
-
- def test
- last_build = @project.builds.last
-
- if @service.execute(last_build)
- message = { notice: 'We successfully tested the service' }
- else
- message = { alert: 'We tried to test the service but error occurred' }
- end
-
- redirect_to :back, message
- end
-
- private
-
- def project
- @project = Ci::Project.find(params[:project_id])
- end
-
- def service
- @service ||= @project.services.find { |service| service.to_param == params[:id] }
- end
-
- def service_params
- params.require(:service).permit(
- :type, :active, :webhook, :notify_only_broken_builds,
- :email_recipients, :email_only_broken_builds, :email_add_pusher,
- :hipchat_token, :hipchat_room, :hipchat_server
- )
- end
- end
-end
diff --git a/app/controllers/profiles/preferences_controller.rb b/app/controllers/profiles/preferences_controller.rb
index f83b4abd1e2..a9a06ecc808 100644
--- a/app/controllers/profiles/preferences_controller.rb
+++ b/app/controllers/profiles/preferences_controller.rb
@@ -31,6 +31,7 @@ class Profiles::PreferencesController < Profiles::ApplicationController
def preferences_params
params.require(:user).permit(
:color_scheme_id,
+ :layout,
:dashboard,
:project_view,
:theme_id
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index 8776721d243..ae9b1384463 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -8,7 +8,7 @@ class Projects::BlobController < Projects::ApplicationController
before_action :require_non_empty_project, except: [:new, :create]
before_action :authorize_download_code!
- before_action :authorize_push_code!, only: [:destroy]
+ before_action :authorize_push_code!, only: [:destroy, :create]
before_action :assign_blob_vars
before_action :commit, except: [:new, :create]
before_action :blob, except: [:new, :create]
@@ -25,7 +25,7 @@ class Projects::BlobController < Projects::ApplicationController
result = Files::CreateService.new(@project, current_user, @commit_params).execute
if result[:status] == :success
- flash[:notice] = "Your changes have been successfully committed"
+ flash[:notice] = "The changes have been successfully committed"
respond_to do |format|
format.html { redirect_to namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @file_path)) }
format.json { render json: { message: "success", filePath: namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @file_path)) } }
@@ -34,7 +34,7 @@ class Projects::BlobController < Projects::ApplicationController
flash[:alert] = result[:message]
respond_to do |format|
format.html { render :new }
- format.json { render json: { message: "failed", filePath: namespace_project_new_blob_path(@project.namespace, @project, @id) } }
+ format.json { render json: { message: "failed", filePath: namespace_project_blob_path(@project.namespace, @project, @id) } }
end
end
end
@@ -154,7 +154,7 @@ class Projects::BlobController < Projects::ApplicationController
def editor_variables
@current_branch = @ref
- @target_branch = (sanitized_new_branch_name || @ref)
+ @target_branch = params[:new_branch].present? ? sanitized_new_branch_name : @ref
@file_path =
if action_name.to_s == 'create'
diff --git a/app/controllers/projects/builds_controller.rb b/app/controllers/projects/builds_controller.rb
new file mode 100644
index 00000000000..4e4ac6689d3
--- /dev/null
+++ b/app/controllers/projects/builds_controller.rb
@@ -0,0 +1,55 @@
+class Projects::BuildsController < Projects::ApplicationController
+ before_action :ci_project
+ before_action :build
+
+ before_action :authorize_admin_project!, except: [:show, :status]
+
+ layout "project"
+
+ def show
+ @builds = @ci_project.commits.find_by_sha(@build.sha).builds.order('id DESC')
+ @builds = @builds.where("id not in (?)", @build.id).page(params[:page]).per(20)
+ @commit = @build.commit
+
+ respond_to do |format|
+ format.html
+ format.json do
+ render json: @build.to_json(methods: :trace_html)
+ end
+ end
+ end
+
+ def retry
+ if @build.commands.blank?
+ return page_404
+ end
+
+ build = Ci::Build.retry(@build)
+
+ if params[:return_to]
+ redirect_to URI.parse(params[:return_to]).path
+ else
+ redirect_to build_path(build)
+ end
+ end
+
+ def status
+ render json: @build.to_json(only: [:status, :id, :sha, :coverage], methods: :sha)
+ end
+
+ def cancel
+ @build.cancel
+
+ redirect_to build_path(@build)
+ end
+
+ private
+
+ def build
+ @build ||= ci_project.builds.unscoped.find_by!(id: params[:id])
+ end
+
+ def build_path(build)
+ namespace_project_build_path(build.gl_project.namespace, build.gl_project, build)
+ end
+end
diff --git a/app/controllers/projects/ci_services_controller.rb b/app/controllers/projects/ci_services_controller.rb
new file mode 100644
index 00000000000..6d2756eba3d
--- /dev/null
+++ b/app/controllers/projects/ci_services_controller.rb
@@ -0,0 +1,49 @@
+class Projects::CiServicesController < Projects::ApplicationController
+ before_action :ci_project
+ before_action :authorize_admin_project!
+
+ layout "project_settings"
+
+ def index
+ @ci_project.build_missing_services
+ @services = @ci_project.services.reload
+ end
+
+ def edit
+ service
+ end
+
+ def update
+ if @service.update_attributes(service_params)
+ redirect_to edit_namespace_project_ci_service_path(@project, @project.namespace, @service.to_param)
+ else
+ render 'edit'
+ end
+ end
+
+ def test
+ last_build = @project.builds.last
+
+ if @service.execute(last_build)
+ message = { notice: 'We successfully tested the service' }
+ else
+ message = { alert: 'We tried to test the service but error occurred' }
+ end
+
+ redirect_to :back, message
+ end
+
+ private
+
+ def service
+ @service ||= @ci_project.services.find { |service| service.to_param == params[:id] }
+ end
+
+ def service_params
+ params.require(:service).permit(
+ :type, :active, :webhook, :notify_only_broken_builds,
+ :email_recipients, :email_only_broken_builds, :email_add_pusher,
+ :hipchat_token, :hipchat_room, :hipchat_server
+ )
+ end
+end
diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb
index 2fae5057138..7886f3c6deb 100644
--- a/app/controllers/projects/commit_controller.rb
+++ b/app/controllers/projects/commit_controller.rb
@@ -31,6 +31,21 @@ class Projects::CommitController < Projects::ApplicationController
end
end
+ def ci
+ @ci_commit = @project.ci_commit(@commit.sha)
+ @builds = @ci_commit.builds if @ci_commit
+ @notes_count = @commit.notes.count
+ @ci_project = @project.gitlab_ci_project
+ end
+
+ def cancel_builds
+ @ci_commit = @project.ci_commit(@commit.sha)
+ @ci_commit.builds.running_or_pending.each(&:cancel)
+
+ redirect_to ci_namespace_project_commit_path(project.namespace, project, commit.sha)
+ end
+
+
def branches
@branches = @project.repository.branch_names_contains(commit.id)
@tags = @project.repository.tag_names_contains(commit.id)
diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb
index 92e4bc16d9d..7eaff1d61ee 100644
--- a/app/controllers/projects/tree_controller.rb
+++ b/app/controllers/projects/tree_controller.rb
@@ -1,10 +1,13 @@
# Controller for viewing a repository's file structure
class Projects::TreeController < Projects::ApplicationController
include ExtractsPath
+ include ActionView::Helpers::SanitizeHelper
before_action :require_non_empty_project, except: [:new, :create]
before_action :assign_ref_vars
+ before_action :assign_dir_vars, only: [:create_dir]
before_action :authorize_download_code!
+ before_action :authorize_push_code!, only: [:create_dir]
def show
return not_found! unless @repository.commit(@ref)
@@ -26,4 +29,38 @@ class Projects::TreeController < Projects::ApplicationController
format.js { no_cache_headers }
end
end
+
+ def create_dir
+ return not_found! unless @commit_params.values.all?
+
+ begin
+ result = Files::CreateDirService.new(@project, current_user, @commit_params).execute
+ message = result[:message]
+ rescue => e
+ message = e.to_s
+ end
+
+ if result && result[:status] == :success
+ flash[:notice] = "The directory has been successfully created"
+ respond_to do |format|
+ format.html { redirect_to namespace_project_blob_path(@project.namespace, @project, File.join(@new_branch, @dir_name)) }
+ end
+ else
+ flash[:alert] = message
+ respond_to do |format|
+ format.html { redirect_to namespace_project_blob_path(@project.namespace, @project, @new_branch) }
+ end
+ end
+ end
+
+ def assign_dir_vars
+ @new_branch = params[:new_branch].present? ? sanitize(strip_tags(params[:new_branch])) : @ref
+ @dir_name = File.join(@path, params[:dir_name])
+ @commit_params = {
+ file_path: @dir_name,
+ current_branch: @ref,
+ target_branch: @new_branch,
+ commit_message: params[:commit_message],
+ }
+ end
end
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index 6aa16673d63..97c7e74c294 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -72,11 +72,15 @@ class IssuableFinder
params[:milestone_title].present?
end
+ def no_milestones?
+ milestones? && params[:milestone_title] == Milestone::None.title
+ end
+
def milestones
return @milestones if defined?(@milestones)
@milestones =
- if milestones? && params[:milestone_title] != Milestone::None.title
+ if milestones?
Milestone.where(title: params[:milestone_title])
else
nil
@@ -183,7 +187,11 @@ class IssuableFinder
def by_milestone(items)
if milestones?
- items = items.where(milestone_id: milestones.try(:pluck, :id))
+ if no_milestones?
+ items = items.where(milestone_id: [-1, nil])
+ else
+ items = items.where(milestone_id: milestones.try(:pluck, :id))
+ end
end
items
@@ -207,13 +215,19 @@ class IssuableFinder
def by_label(items)
if params[:label_name].present?
- label_names = params[:label_name].split(",")
+ if params[:label_name] == Label::None.title
+ item_ids = LabelLink.where(target_type: klass.name).pluck(:target_id)
- item_ids = LabelLink.joins(:label).
- where('labels.title in (?)', label_names).
- where(target_type: klass.name).pluck(:target_id)
+ items = items.where('id NOT IN (?)', item_ids)
+ else
+ label_names = params[:label_name].split(",")
+
+ item_ids = LabelLink.joins(:label).
+ where('labels.title in (?)', label_names).
+ where(target_type: klass.name).pluck(:target_id)
- items = items.where(id: item_ids)
+ items = items.where(id: item_ids)
+ end
end
items
diff --git a/app/finders/trending_projects_finder.rb b/app/finders/trending_projects_finder.rb
index 9ea342cb26d..81a12403801 100644
--- a/app/finders/trending_projects_finder.rb
+++ b/app/finders/trending_projects_finder.rb
@@ -1,13 +1,6 @@
class TrendingProjectsFinder
- def execute(current_user, start_date = nil)
- start_date ||= Date.today - 1.month
-
- projects = projects_for(current_user)
-
- # Determine trending projects based on comments count
- # for period of time - ex. month
- projects.joins(:notes).where('notes.created_at > ?', start_date).
- group("projects.id").reorder("count(notes.id) DESC")
+ def execute(current_user, start_date = 1.month.ago)
+ projects_for(current_user).trending(start_date)
end
private
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 3ab44719d9f..cab2278adb7 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -314,4 +314,8 @@ module ApplicationHelper
html.html_safe
end
+
+ def truncate_first_line(message, length = 50)
+ truncate(message.each_line.first.chomp, length: length) if message
+ end
end
diff --git a/app/helpers/builds_helper.rb b/app/helpers/builds_helper.rb
index b6658e52d09..1b5a2c31d74 100644
--- a/app/helpers/builds_helper.rb
+++ b/app/helpers/builds_helper.rb
@@ -3,15 +3,11 @@ module BuildsHelper
gitlab_ref_link build.project, build.ref
end
- def build_compare_link build
- gitlab_compare_link build.project, build.commit.short_before_sha, build.short_sha
- end
-
def build_commit_link build
gitlab_commit_link build.project, build.short_sha
end
def build_url(build)
- ci_project_build_url(build.project, build)
+ namespace_project_build_path(build.gl_project, build.project, build)
end
end
diff --git a/app/helpers/ci/commits_helper.rb b/app/helpers/ci/commits_helper.rb
deleted file mode 100644
index 9069aed5b4d..00000000000
--- a/app/helpers/ci/commits_helper.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-module Ci
- module CommitsHelper
- def ci_commit_path(commit)
- ci_project_ref_commits_path(commit.project, commit.ref, commit.sha)
- end
-
- def commit_link(commit)
- link_to(commit.short_sha, ci_commit_path(commit))
- end
-
- def truncate_first_line(message, length = 50)
- truncate(message.each_line.first.chomp, length: length) if message
- end
-
- def ci_commit_title(commit)
- content_tag :span do
- link_to(
- simple_sanitize(commit.project.name), ci_project_path(commit.project)
- ) + ' @ ' +
- gitlab_commit_link(@project, @commit.sha)
- end
- end
- end
-end
diff --git a/app/helpers/ci/gitlab_helper.rb b/app/helpers/ci/gitlab_helper.rb
index 13e4d0fd9c3..baddbc806f2 100644
--- a/app/helpers/ci/gitlab_helper.rb
+++ b/app/helpers/ci/gitlab_helper.rb
@@ -26,7 +26,7 @@ module Ci
def yaml_web_editor_link(project)
commits = project.commits
- if commits.any? && commits.last.push_data[:ci_yaml_file]
+ if commits.any? && commits.last.ci_yaml_file
"#{project.gitlab_url}/edit/master/.gitlab-ci.yml"
else
"#{project.gitlab_url}/new/master"
diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb
index 3a88ed7107e..dbd1e26fa79 100644
--- a/app/helpers/ci_status_helper.rb
+++ b/app/helpers/ci_status_helper.rb
@@ -1,6 +1,7 @@
module CiStatusHelper
def ci_status_path(ci_commit)
- ci_project_ref_commits_path(ci_commit.project, ci_commit.ref, ci_commit)
+ project = ci_commit.gl_project
+ ci_namespace_project_commit_path(project.namespace, project, ci_commit.sha)
end
def ci_status_icon(ci_commit)
diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb
index 8036303851b..662ace367b9 100644
--- a/app/helpers/labels_helper.rb
+++ b/app/helpers/labels_helper.rb
@@ -93,7 +93,9 @@ module LabelsHelper
end
def project_labels_options(project)
- options_from_collection_for_select(project.labels, 'name', 'name', params[:label_name])
+ labels = project.labels.to_a
+ labels.unshift(Label::None)
+ options_from_collection_for_select(labels, 'name', 'name', params[:label_name])
end
# Required for Gitlab::Markdown::LabelReferenceFilter
diff --git a/app/helpers/page_layout_helper.rb b/app/helpers/page_layout_helper.rb
index df37be51ce9..775cf5a3dd4 100644
--- a/app/helpers/page_layout_helper.rb
+++ b/app/helpers/page_layout_helper.rb
@@ -26,7 +26,7 @@ module PageLayoutHelper
def fluid_layout(enabled = false)
if @fluid_layout.nil?
- @fluid_layout = enabled
+ @fluid_layout = (current_user && current_user.layout == "fluid") || enabled
else
@fluid_layout
end
diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb
index 1b1f4162df4..4710171ebaa 100644
--- a/app/helpers/preferences_helper.rb
+++ b/app/helpers/preferences_helper.rb
@@ -1,5 +1,12 @@
# Helper methods for per-User preferences
module PreferencesHelper
+ def layout_choices
+ [
+ ['Fixed', :fixed],
+ ['Fluid', :fluid]
+ ]
+ end
+
# Maps `dashboard` values to more user-friendly option text
DASHBOARD_CHOICES = {
projects: 'Your Projects (default)',
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index 784f5c96a0a..c8841178e93 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -83,8 +83,7 @@ class ApplicationSetting < ActiveRecord::Base
default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'],
restricted_signup_domains: Settings.gitlab['restricted_signup_domains'],
- import_sources: ['github','bitbucket','gitlab','gitorious','google_code','fogbugz','git'],
- ci_enabled: Settings.gitlab_ci['enabled']
+ import_sources: ['github','bitbucket','gitlab','gitorious','google_code','fogbugz','git']
)
end
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 645ad68e1b3..5d17f4418ed 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -32,12 +32,14 @@ module Ci
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") }
@@ -45,6 +47,10 @@ module Ci
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
@@ -75,6 +81,8 @@ module Ci
def retry(build)
new_build = Ci::Build.new(status: :pending)
+ new_build.ref = build.ref
+ new_build.tag = build.tag
new_build.options = build.options
new_build.commands = build.commands
new_build.tag_list = build.tag_list
@@ -82,6 +90,7 @@ module Ci
new_build.name = build.name
new_build.allow_failure = build.allow_failure
new_build.stage = build.stage
+ new_build.stage_idx = build.stage_idx
new_build.trigger_request = build.trigger_request
new_build.save
new_build
@@ -117,8 +126,8 @@ module Ci
Ci::WebHookService.new.build_end(build)
end
- if build.commit.success?
- build.commit.create_next_builds(build.trigger_request)
+ if build.commit.should_create_next_builds?(build)
+ build.commit.create_next_builds(build.ref, build.tag, build.user, build.trigger_request)
end
project.execute_services(build)
@@ -135,9 +144,13 @@ module Ci
state :canceled, value: 'canceled'
end
- delegate :sha, :short_sha, :before_sha, :ref, :project,
+ delegate :sha, :short_sha, :project, :gl_project,
to: :commit, prefix: false
+ def before_sha
+ Gitlab::Git::BLANK_SHA
+ end
+
def trace_html
html = Ci::Ansi2html::convert(trace) if trace.present?
html || ''
@@ -187,6 +200,16 @@ module Ci
project.name
end
+ def project_recipients
+ recipients = project.email_recipients.split(' ')
+
+ if project.email_add_pusher? && user.present? && user.notification_email.present?
+ recipients << user.notification_email
+ end
+
+ recipients.uniq
+ end
+
def repo_url
project.repo_url_with_auth
end
diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb
index 6d048779cde..fde754a92a1 100644
--- a/app/models/ci/commit.rb
+++ b/app/models/ci/commit.rb
@@ -23,9 +23,7 @@ module Ci
has_many :builds, dependent: :destroy, class_name: 'Ci::Build'
has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest'
- serialize :push_data
-
- validates_presence_of :ref, :sha, :before_sha, :push_data
+ validates_presence_of :sha
validate :valid_commit_sha
def self.truncate_sha(sha)
@@ -60,28 +58,16 @@ module Ci
end
end
- def new_branch?
- before_sha == Ci::Git::BLANK_SHA
- end
-
- def compare?
- !new_branch?
- end
-
def git_author_name
- commit_data[:author][:name] if commit_data && commit_data[:author]
+ commit_data.author_name if commit_data
end
def git_author_email
- commit_data[:author][:email] if commit_data && commit_data[:author]
+ commit_data.author_email if commit_data
end
def git_commit_message
- commit_data[:message] if commit_data && commit_data[:message]
- end
-
- def short_before_sha
- Ci::Commit.truncate_sha(before_sha)
+ commit_data.message if commit_data
end
def short_sha
@@ -89,84 +75,51 @@ module Ci
end
def commit_data
- push_data[:commits].find do |commit|
- commit[:id] == sha
- end
+ @commit ||= gl_project.commit(sha)
rescue
nil
end
- def project_recipients
- recipients = project.email_recipients.split(' ')
-
- if project.email_add_pusher? && push_data[:user_email].present?
- recipients << push_data[:user_email]
- end
-
- recipients.uniq
- end
-
def stage
- return unless config_processor
- stages = builds_without_retry.select(&:active?).map(&:stage)
- config_processor.stages.find { |stage| stages.include? stage }
+ running_or_pending = builds_without_retry.running_or_pending
+ running_or_pending.limit(1).pluck(:stage).first
end
- def create_builds_for_stage(stage, trigger_request)
+ def create_builds(ref, tag, user, trigger_request = nil)
return if skip_ci? && trigger_request.blank?
return unless config_processor
-
- builds_attrs = config_processor.builds_for_stage_and_ref(stage, ref, tag)
- builds_attrs.map do |build_attrs|
- builds.create!({
- name: build_attrs[:name],
- commands: build_attrs[:script],
- tag_list: build_attrs[:tags],
- options: build_attrs[:options],
- allow_failure: build_attrs[:allow_failure],
- stage: build_attrs[:stage],
- trigger_request: trigger_request,
- })
+ config_processor.stages.any? do |stage|
+ CreateBuildsService.new.execute(self, stage, ref, tag, user, trigger_request).present?
end
end
- def create_next_builds(trigger_request)
+ def create_next_builds(ref, tag, user, trigger_request)
return if skip_ci? && trigger_request.blank?
return unless config_processor
- stages = builds.where(trigger_request: trigger_request).group_by(&:stage)
+ stages = builds.where(ref: ref, tag: tag, trigger_request: trigger_request).group_by(&:stage)
config_processor.stages.any? do |stage|
- !stages.include?(stage) && create_builds_for_stage(stage, trigger_request).present?
+ unless stages.include?(stage)
+ CreateBuildsService.new.execute(self, stage, ref, tag, user, trigger_request).present?
+ end
end
end
- def create_builds(trigger_request = nil)
- return if skip_ci? && trigger_request.blank?
- return unless config_processor
+ def refs
+ builds.group(:ref).pluck(:ref)
+ end
- config_processor.stages.any? do |stage|
- create_builds_for_stage(stage, trigger_request).present?
- end
+ def last_ref
+ builds.latest.first.try(:ref)
end
def builds_without_retry
- @builds_without_retry ||=
- begin
- grouped_builds = builds.group_by(&:name)
- grouped_builds.map do |name, builds|
- builds.sort_by(&:id).last
- end
- end
+ builds.latest
end
- def builds_without_retry_sorted
- return builds_without_retry unless config_processor
-
- stages = config_processor.stages
- builds_without_retry.sort_by do |build|
- [stages.index(build.stage) || -1, build.name || ""]
- end
+ def builds_without_retry_for_ref(ref)
+ builds.for_ref(ref).latest
end
def retried_builds
@@ -225,6 +178,10 @@ module Ci
@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
+ end
+
def finished_at
@finished_at ||= builds.order('finished_at DESC').first.try(:finished_at)
end
@@ -238,12 +195,12 @@ module Ci
end
end
- def matrix?
- builds_without_retry.size > 1
+ def matrix_for_ref?(ref)
+ builds_without_retry_for_ref(ref).pluck(:id).size > 1
end
def config_processor
- @config_processor ||= Ci::GitlabCiYamlProcessor.new(push_data[:ci_yaml_file])
+ @config_processor ||= Ci::GitlabCiYamlProcessor.new(ci_yaml_file)
rescue Ci::GitlabCiYamlProcessor::ValidationError => e
save_yaml_error(e.message)
nil
@@ -253,16 +210,31 @@ module Ci
nil
end
+ def ci_yaml_file
+ gl_project.repository.blob_at(sha, '.gitlab-ci.yml').data
+ rescue
+ nil
+ end
+
def skip_ci?
return false if builds.any?
- commits = push_data[:commits]
- commits.present? && commits.last[:message] =~ /(\[ci skip\])/
+ git_commit_message =~ /(\[ci skip\])/ if git_commit_message
end
def update_committed!
update!(committed_at: DateTime.now)
end
+ def should_create_next_builds?(build)
+ # don't create other builds if this one is retried
+ other_builds = builds.similar(build).latest
+ return false unless other_builds.include?(build)
+
+ other_builds.all? do |build|
+ build.success? || build.ignored?
+ end
+ end
+
private
def save_yaml_error(error)
diff --git a/app/models/ci/project_status.rb b/app/models/ci/project_status.rb
index 6d5cafe81a2..b66f1212f23 100644
--- a/app/models/ci/project_status.rb
+++ b/app/models/ci/project_status.rb
@@ -28,18 +28,6 @@ module Ci
status
end
- # only check for toggling build status within same ref.
- def last_commit_changed_status?
- ref = last_commit.ref
- last_commits = commits.where(ref: ref).last(2)
-
- if last_commits.size < 2
- false
- else
- last_commits[0].status != last_commits[1].status
- end
- end
-
def last_commit_for_ref(ref)
commits.where(ref: ref).last
end
diff --git a/app/models/concerns/case_sensitivity.rb b/app/models/concerns/case_sensitivity.rb
new file mode 100644
index 00000000000..fe0cea8465f
--- /dev/null
+++ b/app/models/concerns/case_sensitivity.rb
@@ -0,0 +1,28 @@
+# Concern for querying columns with specific case sensitivity handling.
+module CaseSensitivity
+ extend ActiveSupport::Concern
+
+ module ClassMethods
+ # Queries the given columns regardless of the casing used.
+ #
+ # Unlike other ActiveRecord methods this method only operates on a Hash.
+ def iwhere(params)
+ criteria = self
+ cast_lower = Gitlab::Database.postgresql?
+
+ params.each do |key, value|
+ column = ActiveRecord::Base.connection.quote_table_name(key)
+
+ if cast_lower
+ condition = "LOWER(#{column}) = LOWER(:value)"
+ else
+ condition = "#{column} = :value"
+ end
+
+ criteria = criteria.where(condition, value: value)
+ end
+
+ criteria
+ end
+ end
+end
diff --git a/app/models/label.rb b/app/models/label.rb
index 4a22bd53400..14b544b3756 100644
--- a/app/models/label.rb
+++ b/app/models/label.rb
@@ -12,6 +12,9 @@
class Label < ActiveRecord::Base
include Referable
+ # Represents a "No Label" state used for filtering Issues and Merge
+ # Requests that have no label assigned.
+ None = Struct.new(:title, :name).new('No Label', 'No Label')
DEFAULT_COLOR = '#428BCA'
diff --git a/app/models/project.rb b/app/models/project.rb
index b90a82da9f2..021920008ad 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -40,6 +40,7 @@ class Project < ActiveRecord::Base
include Referable
include Sortable
include AfterCommitQueue
+ include CaseSensitivity
extend Gitlab::ConfigHelper
extend Enumerize
@@ -235,13 +236,18 @@ class Project < ActiveRecord::Base
end
def find_with_namespace(id)
- return nil unless id.include?('/')
-
- id = id.split('/')
- namespace = Namespace.by_path(id.first)
- return nil unless namespace
-
- where(namespace_id: namespace.id).where("LOWER(projects.path) = :path", path: id.second.downcase).first
+ namespace_path, project_path = id.split('/')
+
+ return nil if !namespace_path || !project_path
+
+ # Use of unscoped ensures we're not secretly adding any ORDER BYs, which
+ # have a negative impact on performance (and aren't needed for this
+ # query).
+ unscoped.
+ joins(:namespace).
+ iwhere('namespaces.path' => namespace_path).
+ iwhere('projects.path' => project_path).
+ take
end
def visibility_levels
@@ -260,6 +266,20 @@ class Project < ActiveRecord::Base
name_pattern = Gitlab::Regex::NAMESPACE_REGEX_STR
%r{(?<project>#{name_pattern}/#{name_pattern})}
end
+
+ def trending(since = 1.month.ago)
+ # By counting in the JOIN we don't expose the GROUP BY to the outer query.
+ # This means that calls such as "any?" and "count" just return a number of
+ # the total count, instead of the counts grouped per project as a Hash.
+ join_body = "INNER JOIN (
+ SELECT project_id, COUNT(*) AS amount
+ FROM notes
+ WHERE created_at >= #{sanitize(since)}
+ GROUP BY project_id
+ ) join_note_counts ON projects.id = join_note_counts.project_id"
+
+ joins(join_body).reorder('join_note_counts.amount DESC')
+ end
end
def team
@@ -744,7 +764,11 @@ class Project < ActiveRecord::Base
end
def ci_commit(sha)
- gitlab_ci_project.commits.find_by(sha: sha) if gitlab_ci?
+ ci_commits.find_by(sha: sha)
+ end
+
+ def ensure_ci_commit(sha)
+ ci_commit(sha) || ci_commits.create(sha: sha)
end
def ensure_gitlab_ci_project
diff --git a/app/models/project_services/ci/hip_chat_message.rb b/app/models/project_services/ci/hip_chat_message.rb
index 25c72033eac..cbf325cc525 100644
--- a/app/models/project_services/ci/hip_chat_message.rb
+++ b/app/models/project_services/ci/hip_chat_message.rb
@@ -11,14 +11,7 @@ module Ci
def to_s
lines = Array.new
lines.push("<a href=\"#{ci_project_url(project)}\">#{project.name}</a> - ")
-
- if commit.matrix?
- lines.push("<a href=\"#{ci_project_ref_commits_url(project, commit.ref, commit.sha)}\">Commit ##{commit.id}</a></br>")
- else
- first_build = commit.builds_without_retry.first
- lines.push("<a href=\"#{ci_project_build_url(project, first_build)}\">Build '#{first_build.name}' ##{first_build.id}</a></br>")
- end
-
+ lines.push("<a href=\"#{ci_namespace_project_commit_url(commit.gl_project.namespace, commit.gl_project, commit.sha)}\">Commit ##{commit.id}</a></br>")
lines.push("#{commit.short_sha} #{commit.git_author_name} - #{commit.git_commit_message}</br>")
lines.push("#{humanized_status(commit_status)} in #{commit.duration} second(s).")
lines.join('')
diff --git a/app/models/project_services/ci/mail_service.rb b/app/models/project_services/ci/mail_service.rb
index 1bd2f33612b..11a2743f969 100644
--- a/app/models/project_services/ci/mail_service.rb
+++ b/app/models/project_services/ci/mail_service.rb
@@ -61,7 +61,7 @@ module Ci
end
def execute(build)
- build.commit.project_recipients.each do |recipient|
+ build.project_recipients.each do |recipient|
case build.status.to_sym
when :success
mailer.build_success_email(build.id, recipient)
diff --git a/app/models/project_services/ci/slack_message.rb b/app/models/project_services/ci/slack_message.rb
index 757b1961143..5ac8907ecd0 100644
--- a/app/models/project_services/ci/slack_message.rb
+++ b/app/models/project_services/ci/slack_message.rb
@@ -23,15 +23,13 @@ module Ci
def attachments
fields = []
- if commit.matrix?
- commit.builds_without_retry.each do |build|
- next if build.allow_failure?
- next unless build.failed?
- fields << {
- title: build.name,
- value: "Build <#{ci_project_build_url(project, build)}|\##{build.id}> failed in #{build.duration.to_i} second(s)."
- }
- end
+ commit.builds_without_retry.each do |build|
+ next if build.allow_failure?
+ next unless build.failed?
+ fields << {
+ title: build.name,
+ value: "Build <#{namespace_project_build_url(build.gl_project.namespace, build.gl_project, build)}|\##{build.id}> failed in #{build.duration.to_i} second(s)."
+ }
end
[{
@@ -47,12 +45,7 @@ module Ci
def attachment_message
out = "<#{ci_project_url(project)}|#{project_name}>: "
- if commit.matrix?
- out << "Commit <#{ci_project_ref_commits_url(project, commit.ref, commit.sha)}|\##{commit.id}> "
- else
- build = commit.builds_without_retry.first
- out << "Build <#{ci_project_build_url(project, build)}|\##{build.id}> "
- end
+ out << "Commit <#{ci_namespace_project_commit_url(commit.gl_project.namespace, commit.gl_project, commit.sha)}|\##{commit.id}> "
out << "(<#{commit_sha_link}|#{commit.short_sha}>) "
out << "of <#{commit_ref_link}|#{commit.ref}> "
out << "by #{commit.git_author_name} " if commit.git_author_name
diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb
index 6d2cf79b691..4dcd16ede3a 100644
--- a/app/models/project_services/gitlab_ci_service.rb
+++ b/app/models/project_services/gitlab_ci_service.rb
@@ -40,19 +40,10 @@ class GitlabCiService < CiService
def execute(data)
return unless supported_events.include?(data[:object_kind])
- sha = data[:checkout_sha]
-
- if sha.present?
- file = ci_yaml_file(sha)
-
- if file && file.data
- data.merge!(ci_yaml_file: file.data)
- end
- end
-
- ci_project = Ci::Project.find_by(gitlab_id: project.id)
+ ci_project = project.gitlab_ci_project
if ci_project
- Ci::CreateCommitService.new.execute(ci_project, data)
+ current_user = User.find_by(id: data[:user_id])
+ Ci::CreateCommitService.new.execute(ci_project, current_user, data)
end
end
@@ -63,7 +54,7 @@ class GitlabCiService < CiService
end
def get_ci_commit(sha, ref)
- Ci::Project.find(project.gitlab_ci_project).commits.find_by_sha_and_ref!(sha, ref)
+ Ci::Project.find(project.gitlab_ci_project).commits.find_by_sha!(sha)
end
def commit_status(sha, ref)
@@ -80,7 +71,7 @@ class GitlabCiService < CiService
def build_page(sha, ref)
if project.gitlab_ci_project.present?
- ci_project_ref_commits_url(project.gitlab_ci_project, ref, sha)
+ ci_namespace_project_commit_url(project.namespace, project, sha)
end
end
@@ -99,14 +90,4 @@ class GitlabCiService < CiService
def fields
[]
end
-
- private
-
- def ci_yaml_file(sha)
- repository.blob_at(sha, '.gitlab-ci.yml')
- end
-
- def repository
- project.repository
- end
end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 2c5ab62d22c..8b51602bc23 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -373,11 +373,25 @@ class Repository
@root_ref ||= raw_repository.root_ref
end
- def commit_file(user, path, content, message, branch)
+ def commit_dir(user, path, message, branch)
commit_with_hooks(user, branch) do |ref|
- path[0] = '' if path[0] == '/'
+ committer = user_to_committer(user)
+ options = {}
+ options[:committer] = committer
+ options[:author] = committer
+
+ options[:commit] = {
+ message: message,
+ branch: ref,
+ }
+
+ raw_repository.mkdir(path, options)
+ end
+ end
- committer = user_to_comitter(user)
+ def commit_file(user, path, content, message, branch, update)
+ commit_with_hooks(user, branch) do |ref|
+ committer = user_to_committer(user)
options = {}
options[:committer] = committer
options[:author] = committer
@@ -388,7 +402,8 @@ class Repository
options[:file] = {
content: content,
- path: path
+ path: path,
+ update: update
}
Gitlab::Git::Blob.commit(raw_repository, options)
@@ -397,9 +412,7 @@ class Repository
def remove_file(user, path, message, branch)
commit_with_hooks(user, branch) do |ref|
- path[0] = '' if path[0] == '/'
-
- committer = user_to_comitter(user)
+ committer = user_to_committer(user)
options = {}
options[:committer] = committer
options[:author] = committer
@@ -416,7 +429,7 @@ class Repository
end
end
- def user_to_comitter(user)
+ def user_to_committer(user)
{
email: user.email,
name: user.name,
diff --git a/app/models/user.rb b/app/models/user.rb
index 1069f8e3664..889d2d3b867 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -54,6 +54,7 @@
# public_email :string(255) default(""), not null
# dashboard :integer default(0)
# project_view :integer default(0)
+# layout :integer default(0)
#
require 'carrierwave/orm/activerecord'
@@ -130,6 +131,7 @@ class User < ActiveRecord::Base
has_many :assigned_merge_requests, dependent: :destroy, foreign_key: :assignee_id, class_name: "MergeRequest"
has_many :oauth_applications, class_name: 'Doorkeeper::Application', as: :owner, dependent: :destroy
has_one :abuse_report, dependent: :destroy
+ has_many :ci_builds, dependent: :nullify, class_name: 'Ci::Build'
#
@@ -171,6 +173,9 @@ class User < ActiveRecord::Base
after_create :post_create_hook
after_destroy :post_destroy_hook
+ # User's Layout preference
+ enum layout: [:fixed, :fluid]
+
# User's Dashboard preference
# Note: When adding an option, it MUST go on the end of the array.
enum dashboard: [:projects, :stars, :project_activity, :starred_project_activity]
diff --git a/app/services/ci/create_builds_service.rb b/app/services/ci/create_builds_service.rb
new file mode 100644
index 00000000000..c420f3268fd
--- /dev/null
+++ b/app/services/ci/create_builds_service.rb
@@ -0,0 +1,27 @@
+module Ci
+ class CreateBuildsService
+ def execute(commit, stage, ref, tag, user, trigger_request)
+ builds_attrs = commit.config_processor.builds_for_stage_and_ref(stage, ref, tag)
+
+ builds_attrs.map do |build_attrs|
+ # don't create the same build twice
+ unless commit.builds.find_by(ref: ref, tag: tag, trigger_request: trigger_request, name: build_attrs[:name])
+ build_attrs.slice!(:name,
+ :commands,
+ :tag_list,
+ :options,
+ :allow_failure,
+ :stage,
+ :stage_idx)
+
+ build_attrs.merge!(ref: ref,
+ tag: tag,
+ trigger_request: trigger_request,
+ user: user)
+
+ commit.builds.create!(build_attrs)
+ end
+ end
+ end
+ end
+end
diff --git a/app/services/ci/create_commit_service.rb b/app/services/ci/create_commit_service.rb
index 0a1abf89a95..fc1ae5774d5 100644
--- a/app/services/ci/create_commit_service.rb
+++ b/app/services/ci/create_commit_service.rb
@@ -1,7 +1,6 @@
module Ci
class CreateCommitService
- def execute(project, params)
- before_sha = params[:before]
+ def execute(project, user, params)
sha = params[:checkout_sha] || params[:after]
origin_ref = params[:ref]
@@ -16,33 +15,10 @@ module Ci
return false
end
- commit = project.commits.find_by_sha_and_ref(sha, ref)
-
- # Create commit if not exists yet
- unless commit
- data = {
- ref: ref,
- sha: sha,
- tag: origin_ref.start_with?('refs/tags/'),
- before_sha: before_sha,
- push_data: {
- before: before_sha,
- after: sha,
- ref: ref,
- user_name: params[:user_name],
- user_email: params[:user_email],
- repository: params[:repository],
- commits: params[:commits],
- total_commits_count: params[:total_commits_count],
- ci_yaml_file: params[:ci_yaml_file]
- }
- }
-
- commit = project.commits.create(data)
- end
-
+ tag = origin_ref.start_with?('refs/tags/')
+ commit = project.gl_project.ensure_ci_commit(sha)
commit.update_committed!
- commit.create_builds unless commit.builds.any?
+ commit.create_builds(ref, tag, user)
commit
end
diff --git a/app/services/ci/create_trigger_request_service.rb b/app/services/ci/create_trigger_request_service.rb
index 9bad09f2f54..4b86cb0a1f5 100644
--- a/app/services/ci/create_trigger_request_service.rb
+++ b/app/services/ci/create_trigger_request_service.rb
@@ -1,15 +1,20 @@
module Ci
class CreateTriggerRequestService
def execute(project, trigger, ref, variables = nil)
- commit = project.commits.where(ref: ref).last
+ commit = project.gl_project.commit(ref)
return unless commit
+ # check if ref is tag
+ tag = project.gl_project.repository.find_tag(ref).present?
+
+ ci_commit = project.gl_project.ensure_ci_commit(commit.sha)
+
trigger_request = trigger.trigger_requests.create!(
- commit: commit,
- variables: variables
+ variables: variables,
+ commit: ci_commit,
)
- if commit.create_builds(trigger_request)
+ if ci_commit.create_builds(ref, tag, nil, trigger_request)
trigger_request
end
end
diff --git a/app/services/ci/web_hook_service.rb b/app/services/ci/web_hook_service.rb
index 87984b20fa1..92e6df442b4 100644
--- a/app/services/ci/web_hook_service.rb
+++ b/app/services/ci/web_hook_service.rb
@@ -27,9 +27,8 @@ module Ci
project_name: project.name,
gitlab_url: project.gitlab_url,
ref: build.ref,
- sha: build.sha,
before_sha: build.before_sha,
- push_data: build.commit.push_data
+ sha: build.sha,
})
end
end
diff --git a/app/services/files/create_dir_service.rb b/app/services/files/create_dir_service.rb
new file mode 100644
index 00000000000..71272fb5707
--- /dev/null
+++ b/app/services/files/create_dir_service.rb
@@ -0,0 +1,9 @@
+require_relative "base_service"
+
+module Files
+ class CreateDirService < Files::BaseService
+ def commit
+ repository.commit_dir(current_user, @file_path, @commit_message, @target_branch)
+ end
+ end
+end
diff --git a/app/services/files/create_service.rb b/app/services/files/create_service.rb
index ffbb5993279..c8e3a910bba 100644
--- a/app/services/files/create_service.rb
+++ b/app/services/files/create_service.rb
@@ -3,7 +3,7 @@ require_relative "base_service"
module Files
class CreateService < Files::BaseService
def commit
- repository.commit_file(current_user, @file_path, @file_content, @commit_message, @target_branch)
+ repository.commit_file(current_user, @file_path, @file_content, @commit_message, @target_branch, false)
end
def validate
diff --git a/app/services/files/update_service.rb b/app/services/files/update_service.rb
index a20903c6f02..1960dc7d949 100644
--- a/app/services/files/update_service.rb
+++ b/app/services/files/update_service.rb
@@ -3,7 +3,7 @@ require_relative "base_service"
module Files
class UpdateService < Files::BaseService
def commit
- repository.commit_file(current_user, @file_path, @file_content, @commit_message, @target_branch)
+ repository.commit_file(current_user, @file_path, @file_content, @commit_message, @target_branch, true)
end
end
end
diff --git a/app/services/merge_requests/merge_service.rb b/app/services/merge_requests/merge_service.rb
index fcc0f2a6a8d..7963af127e1 100644
--- a/app/services/merge_requests/merge_service.rb
+++ b/app/services/merge_requests/merge_service.rb
@@ -29,7 +29,7 @@ module MergeRequests
private
def commit
- committer = repository.user_to_comitter(current_user)
+ committer = repository.user_to_committer(current_user)
options = {
message: commit_message,
diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb
index 60235b6be2a..9a5fe4af9dd 100644
--- a/app/services/system_hooks_service.rb
+++ b/app/services/system_hooks_service.rb
@@ -54,6 +54,7 @@ class SystemHooksService
data.merge!({
project_name: model.project.name,
project_path: model.project.path,
+ project_path_with_namespace: model.project.path_with_namespace,
project_id: model.project.id,
user_name: model.user.name,
user_email: model.user.email,
diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml
index 143cd10c543..a36ae0b766c 100644
--- a/app/views/admin/application_settings/_form.html.haml
+++ b/app/views/admin/application_settings/_form.html.haml
@@ -124,14 +124,5 @@
= f.text_area :help_page_text, class: 'form-control', rows: 4
.help-block Markdown enabled
- %fieldset
- %legend Continuous Integration
- .form-group
- .col-sm-offset-2.col-sm-10
- .checkbox
- = f.label :ci_enabled do
- = f.check_box :ci_enabled
- Disable to prevent CI usage until rake ci:migrate is run (8.0 only)
-
.form-actions
= f.submit 'Save', class: 'btn btn-primary'
diff --git a/app/views/ci/admin/builds/_build.html.haml b/app/views/ci/admin/builds/_build.html.haml
index 778d51d03be..2df58713214 100644
--- a/app/views/ci/admin/builds/_build.html.haml
+++ b/app/views/ci/admin/builds/_build.html.haml
@@ -1,14 +1,16 @@
+- gl_project = build.project.gl_project
- if build.commit && build.project
%tr.build
%td.build-link
- = link_to ci_project_build_url(build.project, build) do
+ = link_to namespace_project_build_path(gl_project.namespace, gl_project, build) do
%strong #{build.id}
%td.status
= ci_status_with_icon(build.status)
%td.commit-link
- = commit_link(build.commit)
+ = link_to ci_status_path(build.commit) do
+ %strong #{build.commit.short_sha}
%td.runner
- if build.runner
diff --git a/app/views/ci/admin/runners/show.html.haml b/app/views/ci/admin/runners/show.html.haml
index 09905e0eb47..5bb442cbf92 100644
--- a/app/views/ci/admin/runners/show.html.haml
+++ b/app/views/ci/admin/runners/show.html.haml
@@ -96,6 +96,7 @@
%table.builds.runner-builds
%thead
%tr
+ %th Build ID
%th Status
%th Project
%th Commit
@@ -103,6 +104,11 @@
- @builds.each do |build|
%tr.build
+ %td.id
+ - gl_project = build.project.gl_project
+ = link_to namespace_project_build_path(gl_project.namespace, gl_project, build) do
+ = build.id
+
%td.status
= ci_status_with_icon(build.status)
@@ -110,8 +116,8 @@
= build.project.name
%td.build-link
- = link_to ci_project_build_path(build.project, build) do
- %strong #{build.short_sha}
+ = link_to ci_status_path(build.commit) do
+ %strong #{build.commit.short_sha}
%td.timestamp
- if build.finished_at
diff --git a/app/views/ci/builds/show.html.haml b/app/views/ci/builds/show.html.haml
deleted file mode 100644
index 839dbf5c554..00000000000
--- a/app/views/ci/builds/show.html.haml
+++ /dev/null
@@ -1,170 +0,0 @@
-#up-build-trace
-- if @commit.matrix?
- %ul.center-top-menu
- - @commit.builds_without_retry_sorted.each do |build|
- %li{class: ('active' if build == @build) }
- = link_to ci_project_build_url(@project, build) do
- = ci_icon_for_status(build.status)
- %span
- - if build.name
- = build.name
- - else
- = build.id
-
-
- - unless @commit.builds_without_retry.include?(@build)
- %li.active
- %a
- Build ##{@build.id}
- &middot;
- %i.fa.fa-warning-sign
- This build was retried.
-
-.gray-content-block
- .build-head
- %h4
- - if @build.commit.tag?
- Build for tag
- %code #{@build.ref}
- - else
- Build for commit
- %strong.monospace= commit_link(@build.commit)
- from
-
- = link_to ci_project_path(@build.project, ref: @build.ref) do
- %strong.monospace= "#{@build.ref}"
-
- - if @build.duration
- .pull-right
- %span
- %i.fa.fa-time
- #{duration_in_words(@build.finished_at, @build.started_at)}
-
- .clearfix
- = ci_status_with_icon(@build.status)
- .pull-right
- = @build.updated_at.stamp('19:00 Aug 27')
-
-.row.prepend-top-default
- .col-md-9
- .clearfix
- - if @build.active?
- .autoscroll-container
- %button.btn.btn-success.btn-sm#autoscroll-button{:type => "button", :data => {:state => 'disabled'}} enable autoscroll
- .clearfix
- .scroll-controls
- = link_to '#up-build-trace', class: 'btn' do
- %i.fa.fa-angle-up
- = link_to '#down-build-trace', class: 'btn' do
- %i.fa.fa-angle-down
-
- %pre.trace#build-trace
- %code.bash
- = preserve do
- = raw @build.trace_html
- %div#down-build-trace
-
- .col-md-3
- - if @build.coverage
- .build-widget
- %h4.title
- Test coverage
- %h1 #{@build.coverage}%
-
-
- .build-widget
- %h4.title
- Build
- - if current_user && can?(current_user, :manage_builds, gl_project)
- .pull-right
- - if @build.active?
- = link_to "Cancel", cancel_ci_project_build_path(@project, @build), class: 'btn btn-sm btn-danger'
- - elsif @build.commands.present?
- = link_to "Retry", retry_ci_project_build_path(@project, @build), class: 'btn btn-sm btn-primary', method: :post
-
- - if @build.duration
- %p
- %span.attr-name Duration:
- #{duration_in_words(@build.finished_at, @build.started_at)}
- %p
- %span.attr-name Created:
- #{time_ago_in_words(@build.created_at)} ago
- - if @build.finished_at
- %p
- %span.attr-name Finished:
- #{time_ago_in_words(@build.finished_at)} ago
- %p
- %span.attr-name Runner:
- - if @build.runner && current_user && current_user.admin
- \#{link_to "##{@build.runner.id}", ci_admin_runner_path(@build.runner.id)}
- - elsif @build.runner
- \##{@build.runner.id}
-
- - if @build.trigger_request
- .build-widget
- %h4.title
- Trigger
-
- %p
- %span.attr-name Token:
- #{@build.trigger_request.trigger.short_token}
-
- - if @build.trigger_request.variables
- %p
- %span.attr-name Variables:
-
- %code
- - @build.trigger_request.variables.each do |key, value|
- #{key}=#{value}
-
- .build-widget
- %h4.title
- Commit
- .pull-right
- %small #{build_commit_link @build}
-
- - if @build.commit.compare?
- %p
- %span.attr-name Compare:
- #{build_compare_link @build}
- %p
- %span.attr-name Branch:
- #{build_ref_link @build}
- %p
- %span.attr-name Author:
- #{@build.commit.git_author_name}
- %p
- %span.attr-name Message:
- #{@build.commit.git_commit_message}
-
- - if @build.tags.any?
- .build-widget
- %h4.title
- Tags
- - @build.tag_list.each do |tag|
- %span.label.label-primary
- = tag
-
- - if @builds.present?
- .build-widget
- %h4.title #{pluralize(@builds.count, "other build")} for #{@build.short_sha}:
- %table.builds
- - @builds.each_with_index do |build, i|
- %tr.build
- %td
- = ci_icon_for_status(build.status)
- %td
- = link_to ci_project_build_url(@project, build) do
- - if build.name
- = build.name
- - else
- %span ##{build.id}
-
- %td.status= build.status
-
-
- = paginate @builds
-
-
-:javascript
- new CiBuild("#{ci_project_build_url(@project, @build)}", "#{@build.status}")
diff --git a/app/views/ci/commits/_commit.html.haml b/app/views/ci/commits/_commit.html.haml
index 1eacfca944f..b24a3b826cf 100644
--- a/app/views/ci/commits/_commit.html.haml
+++ b/app/views/ci/commits/_commit.html.haml
@@ -7,7 +7,7 @@
%td.build-link
- = link_to ci_project_ref_commits_path(commit.project, commit.ref, commit.sha) do
+ = link_to ci_status_path(commit) do
%strong #{commit.short_sha}
%td.build-message
@@ -16,7 +16,8 @@
%td.build-branch
- unless @ref
%span
- = link_to truncate(commit.ref, length: 25), ci_project_path(@project, ref: commit.ref)
+ - commit.refs.each do |ref|
+ = link_to truncate(ref, length: 25), ci_project_path(@project, ref: ref)
%td.duration
- if commit.duration > 0
diff --git a/app/views/ci/commits/show.html.haml b/app/views/ci/commits/show.html.haml
deleted file mode 100644
index 8f38aa84676..00000000000
--- a/app/views/ci/commits/show.html.haml
+++ /dev/null
@@ -1,87 +0,0 @@
-.commit-info
- .append-bottom-20
- = ci_status_with_icon(@commit.status)
-
- .gray-content-block.middle-block
- %pre.commit-message
- #{@commit.git_commit_message}
-
- .gray-content-block.second-block
- .row
- .col-sm-6
- - if @commit.compare?
- %p
- %span.attr-name Compare:
- #{gitlab_compare_link(@project, @commit.short_before_sha, @commit.short_sha)}
- - else
- %p
- %span.attr-name Commit:
- #{gitlab_commit_link(@project, @commit.sha)}
-
- %p
- %span.attr-name Branch:
- #{gitlab_ref_link(@project, @commit.ref)}
- .col-sm-6
- %p
- %span.attr-name Author:
- #{@commit.git_author_name} (#{@commit.git_author_email})
- - if @commit.created_at
- %p
- %span.attr-name Created at:
- #{@commit.created_at.to_s(:short)}
-
-- if current_user && can?(current_user, :manage_builds, gl_project)
- .pull-right
- - if @commit.builds.running_or_pending.any?
- = link_to "Cancel", cancel_ci_project_ref_commits_path(@project, @commit.ref, @commit.sha), class: 'btn btn-sm btn-danger'
-
-
-- if @commit.yaml_errors.present?
- .bs-callout.bs-callout-danger
- %h4 Found errors in your .gitlab-ci.yml:
- %ul
- - @commit.yaml_errors.split(",").each do |error|
- %li= error
-
-- unless @commit.push_data[:ci_yaml_file]
- .bs-callout.bs-callout-warning
- \.gitlab-ci.yml not found in this commit
-
-%h3
- Builds
- - if @commit.duration > 0
- %small.pull-right
- %i.fa.fa-time
- #{time_interval_in_words @commit.duration}
-
-%table.table.builds
- %thead
- %tr
- %th Status
- %th Build ID
- %th Stage
- %th Name
- %th Duration
- %th Finished at
- - if @project.coverage_enabled?
- %th Coverage
- %th
- = render @commit.builds_without_retry_sorted, controls: true
-
-- if @commit.retried_builds.any?
- %h3
- Retried builds
-
- %table.table.builds
- %thead
- %tr
- %th Status
- %th Build ID
- %th Stage
- %th Name
- %th Duration
- %th Finished at
- - if @project.coverage_enabled?
- %th Coverage
- %th
- = render @commit.retried_builds
diff --git a/app/views/ci/notify/build_fail_email.html.haml b/app/views/ci/notify/build_fail_email.html.haml
index d818e8b6756..69689a75022 100644
--- a/app/views/ci/notify/build_fail_email.html.haml
+++ b/app/views/ci/notify/build_fail_email.html.haml
@@ -11,9 +11,9 @@
%p
Author: #{@build.commit.git_author_name}
%p
- Branch: #{@build.commit.ref}
+ Branch: #{@build.ref}
%p
Message: #{@build.commit.git_commit_message}
%p
- Url: #{link_to @build.short_sha, ci_project_build_url(@project, @build)}
+ Url: #{link_to @build.short_sha, namespace_project_build_url(@build.gl_project.namespace, @build.gl_project, @build)}
diff --git a/app/views/ci/notify/build_fail_email.text.erb b/app/views/ci/notify/build_fail_email.text.erb
index 1add215a1c8..6de5dc10f17 100644
--- a/app/views/ci/notify/build_fail_email.text.erb
+++ b/app/views/ci/notify/build_fail_email.text.erb
@@ -3,7 +3,7 @@ Build failed for <%= @project.name %>
Status: <%= @build.status %>
Commit: <%= @build.commit.short_sha %>
Author: <%= @build.commit.git_author_name %>
-Branch: <%= @build.commit.ref %>
+Branch: <%= @build.ref %>
Message: <%= @build.commit.git_commit_message %>
-Url: <%= ci_project_build_url(@build.project, @build) %>
+Url: <%= namespace_project_build_url(@build.gl_project.namespace, @build.gl_project, @build) %>
diff --git a/app/views/ci/notify/build_success_email.html.haml b/app/views/ci/notify/build_success_email.html.haml
index a20dcaee24e..4e3015a356b 100644
--- a/app/views/ci/notify/build_success_email.html.haml
+++ b/app/views/ci/notify/build_success_email.html.haml
@@ -12,9 +12,9 @@
%p
Author: #{@build.commit.git_author_name}
%p
- Branch: #{@build.commit.ref}
+ Branch: #{@build.ref}
%p
Message: #{@build.commit.git_commit_message}
%p
- Url: #{link_to @build.short_sha, ci_project_build_url(@project, @build)}
+ Url: #{link_to @build.short_sha, namespace_project_build_url(@build.gl_project.namespace, @build.gl_project, @build)}
diff --git a/app/views/ci/notify/build_success_email.text.erb b/app/views/ci/notify/build_success_email.text.erb
index 7ebd17e7270..d0a43ae1c12 100644
--- a/app/views/ci/notify/build_success_email.text.erb
+++ b/app/views/ci/notify/build_success_email.text.erb
@@ -3,7 +3,7 @@ Build successful for <%= @project.name %>
Status: <%= @build.status %>
Commit: <%= @build.commit.short_sha %>
Author: <%= @build.commit.git_author_name %>
-Branch: <%= @build.commit.ref %>
+Branch: <%= @build.ref %>
Message: <%= @build.commit.git_commit_message %>
-Url: <%= ci_project_build_url(@build.project, @build) %>
+Url: <%= namespace_project_build_url(@build.gl_project.namespace, @build.gl_project, @build) %>
diff --git a/app/views/ci/projects/_info.html.haml b/app/views/ci/projects/_info.html.haml
deleted file mode 100644
index 1888e1bde93..00000000000
--- a/app/views/ci/projects/_info.html.haml
+++ /dev/null
@@ -1,2 +0,0 @@
-- if no_runners_for_project?(@project)
- = render 'no_runners'
diff --git a/app/views/ci/projects/disabled.html.haml b/app/views/ci/projects/disabled.html.haml
deleted file mode 100644
index 83b0d8329e1..00000000000
--- a/app/views/ci/projects/disabled.html.haml
+++ /dev/null
@@ -1 +0,0 @@
-Continuous Integration has been disabled for time of the migration.
diff --git a/app/views/ci/projects/show.html.haml b/app/views/ci/projects/show.html.haml
deleted file mode 100644
index 888b1ea41d5..00000000000
--- a/app/views/ci/projects/show.html.haml
+++ /dev/null
@@ -1,60 +0,0 @@
-= render 'ci/shared/guide' unless @project.setup_finished?
-
-- if current_user && can?(current_user, :manage_project, gl_project) && !@project.any_runners?
- .alert.alert-danger
- Builds for this project wont be served unless you configure runners on
- = link_to "Runners page", runners_path(@project.gl_project)
-
-%ul.nav.nav-tabs.append-bottom-20
- %li{class: ref_tab_class}
- = link_to 'All commits', ci_project_path(@project)
- - @project.tracked_refs.each do |ref|
- %li{class: ref_tab_class(ref)}
- = link_to ref, ci_project_path(@project, ref: ref)
-
- - if @ref && !@project.tracked_refs.include?(@ref)
- %li{class: 'active'}
- = link_to @ref, ci_project_path(@project, ref: @ref)
-
- %li.pull-right
- = link_to 'Go to project', project_path(gl_project), class: 'btn btn-sm'
-
-- if @ref
- %p
- Paste build status image for #{@ref} with next link
- = link_to '#', class: 'badge-codes-toggle btn btn-default btn-xs' do
- Status Badge
- .badge-codes-block.bs-callout.bs-callout-info.hide
- %p
- Status badge for
- %span.label.label-info #{@ref}
- branch
- %div
- %label Markdown:
- = text_field_tag 'badge_md', markdown_badge_code(@project, @ref), readonly: true, class: 'form-control'
- %label Html:
- = text_field_tag 'badge_html', html_badge_code(@project, @ref), readonly: true, class: 'form-control'
-
-
-
-
-%table.table.builds
- %thead
- %tr
- %th Status
- %th Commit
- %th Message
- %th Branch
- %th Total duration
- %th Finished at
- - if @project.coverage_enabled?
- %th Coverage
-
- = render @commits
-
-= paginate @commits
-
-- if @commits.empty?
- .bs-callout
- %h4 No commits yet
-
diff --git a/app/views/help/ui.html.haml b/app/views/help/ui.html.haml
index 7c89457ace3..2169a821fb2 100644
--- a/app/views/help/ui.html.haml
+++ b/app/views/help/ui.html.haml
@@ -15,6 +15,8 @@
%li
= link_to 'Tables', '#tables'
%li
+ = link_to 'Nav', '#nav'
+ %li
= link_to 'Buttons', '#buttons'
%li
= link_to 'Panels', '#panels'
@@ -30,17 +32,32 @@
%h2#blocks Blocks
%h3
- %code .well
+ %code .gray-content-block
+
- .well
- %h4 Something
+ .gray-content-block.middle-block
+ %h4 Normal block inside content
+ = lorem
+
+ .gray-content-block.second-block
+ %h4 Second block
= lorem
%h2#lists Lists
%h3
+ %code .content-list
+ %ul.content-list
+ %li
+ One item
+ %li
+ One item
+ %li
+ One item
+
+ %h3
%code .well-list
%ul.well-list
%li
@@ -102,11 +119,40 @@
%td the Bird
%td @twitter
+ %h2#navs Navigation
+
+ %h3
+ %code .center-top-menu
+ .example
+ %ul.center-top-menu
+ %li.active
+ %a Open
+ %li
+ %a Closed
+
+ %h3
+ %code .btn-group.btn-group-next
+ .example
+ %div.btn-group.btn-group-next
+ %a.btn.active Open
+ %a.btn Closed
+
+
+ %h3
+ %code .nav.nav-tabs
+ .example
+ %ul.nav.nav-tabs
+ %li.active
+ %a Open
+ %li
+ %a Closed
+
%h2#buttons Buttons
.example
%button.btn.btn-default{:type => "button"} Default
+ %button.btn.btn-gray{:type => "button"} Gray
%button.btn.btn-primary{:type => "button"} Primary
%button.btn.btn-success{:type => "button"} Success
%button.btn.btn-info{:type => "button"} Info
diff --git a/app/views/layouts/ci/_nav_project.html.haml b/app/views/layouts/ci/_nav_project.html.haml
index 545abc23d99..f094edbfa87 100644
--- a/app/views/layouts/ci/_nav_project.html.haml
+++ b/app/views/layouts/ci/_nav_project.html.haml
@@ -5,17 +5,6 @@
%span
Back to project
%li.separate-item
- = nav_link path: ['projects#show', 'commits#show', 'builds#show'] do
- = link_to ci_project_path(@project) do
- = icon('list-alt fw')
- %span
- Commits
- %span.count= @project.commits.count
- = nav_link path: ['services#index', 'services#edit'] do
- = link_to ci_project_services_path(@project) do
- = icon('share fw')
- %span
- Services
= nav_link path: 'events#index' do
= link_to ci_project_events_path(@project) do
= icon('book fw')
diff --git a/app/views/layouts/ci/build.html.haml b/app/views/layouts/ci/build.html.haml
deleted file mode 100644
index a1356f0dc2e..00000000000
--- a/app/views/layouts/ci/build.html.haml
+++ /dev/null
@@ -1,11 +0,0 @@
-!!! 5
-%html{ lang: "en"}
- = render 'layouts/head'
- %body{class: "ci-body #{user_application_theme}", 'data-page' => body_data_page}
- - header_title ci_commit_title(@commit)
- - if current_user
- = render "layouts/header/default", title: header_title
- - else
- = render "layouts/header/public", title: header_title
-
- = render 'layouts/ci/page', sidebar: 'nav_project'
diff --git a/app/views/layouts/ci/commit.html.haml b/app/views/layouts/ci/commit.html.haml
deleted file mode 100644
index a1356f0dc2e..00000000000
--- a/app/views/layouts/ci/commit.html.haml
+++ /dev/null
@@ -1,11 +0,0 @@
-!!! 5
-%html{ lang: "en"}
- = render 'layouts/head'
- %body{class: "ci-body #{user_application_theme}", 'data-page' => body_data_page}
- - header_title ci_commit_title(@commit)
- - if current_user
- = render "layouts/header/default", title: header_title
- - else
- = render "layouts/header/public", title: header_title
-
- = render 'layouts/ci/page', sidebar: 'nav_project'
diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml
index a218ec7486c..e4c285d8023 100644
--- a/app/views/layouts/nav/_project.html.haml
+++ b/app/views/layouts/nav/_project.html.haml
@@ -32,7 +32,7 @@
Files
- if project_nav_tab? :commits
- = nav_link(controller: %w(commit commits compare repositories tags branches)) do
+ = nav_link(controller: %w(commit commits compare repositories tags branches builds)) do
= link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits', data: {placement: 'right'} do
= icon('history fw')
%span
@@ -76,13 +76,6 @@
Merge Requests
%span.count.merge_counter= @project.merge_requests.opened.count
- - if @project.gitlab_ci?
- = nav_link(controller: [:ci, :project]) do
- = link_to ci_project_path(@project.gitlab_ci_project), title: 'Continuous Integration', data: {placement: 'right'} do
- = icon('building fw')
- %span
- Continuous Integration
-
- if project_nav_tab? :settings
= nav_link(controller: [:project_members, :teams]) do
= link_to namespace_project_project_members_path(@project.namespace, @project), title: 'Members', class: 'team-tab tab', data: {placement: 'right'} do
diff --git a/app/views/layouts/nav/_project_settings.html.haml b/app/views/layouts/nav/_project_settings.html.haml
index 9279a846623..954dbe5d2b9 100644
--- a/app/views/layouts/nav/_project_settings.html.haml
+++ b/app/views/layouts/nav/_project_settings.html.haml
@@ -60,3 +60,13 @@
= icon('building fw')
%span
CI Settings
+ = nav_link controller: 'ci_services' do
+ = link_to namespace_project_ci_services_path(@project.namespace, @project) do
+ = icon('share fw')
+ %span
+ CI Services
+ = nav_link path: 'events#index' do
+ = link_to ci_project_events_path(@project.gitlab_ci_project) do
+ = icon('book fw')
+ %span
+ CI Events
diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml
index 60289bfe7cd..01e285a8dfa 100644
--- a/app/views/profiles/preferences/show.html.haml
+++ b/app/views/profiles/preferences/show.html.haml
@@ -33,6 +33,13 @@
Behavior
.panel-body
.form-group
+ = f.label :layout, class: 'control-label' do
+ Layout width
+ .col-sm-10
+ = f.select :layout, layout_choices, {}, class: 'form-control'
+ .help-block
+ Choose between fixed (max. 1200px) and fluid (100%) application layout
+ .form-group
= f.label :dashboard, class: 'control-label' do
Default Dashboard
= link_to('(?)', help_page_path('profile', 'preferences') + '#default-dashboard', target: '_blank')
diff --git a/app/views/profiles/preferences/update.js.erb b/app/views/profiles/preferences/update.js.erb
index 6c4b0ce757d..4433cab7782 100644
--- a/app/views/profiles/preferences/update.js.erb
+++ b/app/views/profiles/preferences/update.js.erb
@@ -2,6 +2,13 @@
$('body').removeClass('<%= Gitlab::Themes.body_classes %>')
$('body').addClass('<%= user_application_theme %>')
+// Toggle container-fluid class
+if ('<%= current_user.layout %>' === 'fluid') {
+ $('.content-wrapper').find('.container-fluid').removeClass('container-limited')
+} else {
+ $('.content-wrapper').find('.container-fluid').addClass('container-limited')
+}
+
// Re-enable the "Save" button
$('input[type=submit]').enable()
diff --git a/app/views/projects/blob/_editor.html.haml b/app/views/projects/blob/_editor.html.haml
index 9c3e1703c89..f1ad0c3c403 100644
--- a/app/views/projects/blob/_editor.html.haml
+++ b/app/views/projects/blob/_editor.html.haml
@@ -11,7 +11,7 @@
- if current_action?(:new) || current_action?(:create)
\/
= text_field_tag 'file_name', params[:file_name], placeholder: "File name",
- required: true, class: 'form-control new-file-name'
+ required: true, class: 'form-control new-file-name js-quick-submit'
.pull-right
= select_tag :encoding, options_for_select([ "base64", "text" ], "text"), class: 'form-control'
diff --git a/app/views/projects/blob/_new_dir.html.haml b/app/views/projects/blob/_new_dir.html.haml
new file mode 100644
index 00000000000..cb1567a2e68
--- /dev/null
+++ b/app/views/projects/blob/_new_dir.html.haml
@@ -0,0 +1,25 @@
+#modal-create-new-dir.modal
+ .modal-dialog
+ .modal-content
+ .modal-header
+ %a.close{href: "#", "data-dismiss" => "modal"} ×
+ %h3.page-title Create New Directory
+ .modal-body
+ = form_tag namespace_project_create_dir_path(@project.namespace, @project, @id), method: :post, remote: false, id: 'dir-create-form', class: 'form-horizontal' do
+ .form-group
+ = label_tag :dir_name, 'Directory Name', class: 'control-label'
+ .col-sm-10
+ = text_field_tag :dir_name, params[:dir_name], placeholder: "Directory name", required: true, class: 'form-control'
+ = render 'shared/commit_message_container', params: params, placeholder: ''
+ - unless @project.empty_repo?
+ .form-group
+ = label_tag :branch_name, 'Branch', class: 'control-label'
+ .col-sm-10
+ = text_field_tag 'new_branch', @ref, class: "form-control"
+ .form-group
+ .col-sm-offset-2.col-sm-10
+ = submit_tag "Create directory", class: 'btn btn-primary btn-create'
+ = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal"
+
+:coffeescript
+ disableButtonIfAnyEmptyField($("#dir-create-form"), ".form-control", ".btn-create");
diff --git a/app/views/projects/blob/_upload.html.haml b/app/views/projects/blob/_upload.html.haml
index 1a1df127703..e27f1707527 100644
--- a/app/views/projects/blob/_upload.html.haml
+++ b/app/views/projects/blob/_upload.html.haml
@@ -4,9 +4,6 @@
.modal-header
%a.close{href: "#", "data-dismiss" => "modal"} ×
%h3.page-title #{title}
- %p.light
- From branch
- %strong= @ref
.modal-body
= form_tag form_path, method: method, class: 'blob-file-upload-form-js form-horizontal' do
.dropzone
@@ -18,6 +15,12 @@
.dropzone-alerts{class: "alert alert-danger data", style: "display:none"}
= render 'shared/commit_message_container', params: params,
placeholder: placeholder
+ - unless @project.empty_repo?
+ .form-group.branch
+ = label_tag 'branch', class: 'control-label' do
+ Branch
+ .col-sm-10
+ = text_field_tag 'new_branch', @ref, class: "form-control"
.form-group
.col-sm-offset-2.col-sm-10
= button_tag button_title, class: 'btn btn-small btn-primary btn-upload-file', id: 'submit-all'
diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml
index 1950586b112..7975137c37f 100644
--- a/app/views/projects/blob/new.html.haml
+++ b/app/views/projects/blob/new.html.haml
@@ -2,12 +2,7 @@
= render "header_title"
.gray-content-block.top-block
- Create a new file or
- = link_to 'upload', '#modal-upload-blob',
- { class: 'upload-link', 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal'}
- an existing one
-
-= render 'projects/blob/upload', title: 'Upload', placeholder: 'Upload new file', button_title: 'Upload file', form_path: namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post
+ Create a new file
.file-editor
= form_tag(namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post, class: 'form-horizontal form-new-file js-requires-input') do
@@ -20,7 +15,7 @@
= label_tag 'branch', class: 'control-label' do
Branch
.col-sm-10
- = text_field_tag 'new_branch', @ref, class: "form-control"
+ = text_field_tag 'new_branch', @ref, class: "form-control js-quick-submit"
= hidden_field_tag 'content', '', id: 'file-content'
= render 'projects/commit_button', ref: @ref,
diff --git a/app/views/ci/builds/_build.html.haml b/app/views/projects/builds/_build.html.haml
index 515b862e992..65fd9413b60 100644
--- a/app/views/ci/builds/_build.html.haml
+++ b/app/views/projects/builds/_build.html.haml
@@ -1,11 +1,16 @@
+- gl_project = build.project.gl_project
%tr.build
%td.status
= ci_status_with_icon(build.status)
%td.build-link
- = link_to ci_project_build_path(build.project, build) do
+ = 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
@@ -38,8 +43,8 @@
- if defined?(controls) && current_user && can?(current_user, :manage_builds, gl_project)
.pull-right
- if build.active?
- = link_to cancel_ci_project_build_path(build.project, build, return_to: request.original_url), title: 'Cancel build' do
+ = 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_ci_project_build_path(build.project, build, return_to: request.original_url), method: :post, title: 'Retry build' do
+ = 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
new file mode 100644
index 00000000000..b561078e8c7
--- /dev/null
+++ b/app/views/projects/builds/show.html.haml
@@ -0,0 +1,159 @@
+.build-page
+ .gray-content-block
+ Build for commit
+ %strong.monospace
+ = link_to @build.commit.short_sha, ci_status_path(@build.commit)
+ from
+ %code #{@build.ref}
+
+ #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|
+ %li{class: ('active' if build == @build) }
+ = link_to namespace_project_build_path(@project.namespace, @project, build) do
+ = ci_icon_for_status(build.status)
+ %span
+ - if build.name
+ = build.name
+ - else
+ = build.id
+
+
+ - unless @commit.builds_without_retry_for_ref(@build.ref).include?(@build)
+ %li.active
+ %a
+ Build ##{@build.id}
+ &middot;
+ %i.fa.fa-warning-sign
+ This build was retried.
+
+ .gray-content-block.second-block
+ .build-head
+ .clearfix
+ = ci_status_with_icon(@build.status)
+ - if @build.duration
+ %span
+ %i.fa.fa-time
+ #{duration_in_words(@build.finished_at, @build.started_at)}
+ .pull-right
+ = @build.updated_at.stamp('19:00 Aug 27')
+
+ .row.prepend-top-default
+ .col-md-9
+ .clearfix
+ - if @build.active?
+ .autoscroll-container
+ %button.btn.btn-success.btn-sm#autoscroll-button{:type => "button", :data => {:state => 'disabled'}} enable autoscroll
+ .clearfix
+ .scroll-controls
+ = link_to '#up-build-trace', class: 'btn' do
+ %i.fa.fa-angle-up
+ = link_to '#down-build-trace', class: 'btn' do
+ %i.fa.fa-angle-down
+
+ %pre.trace#build-trace
+ %code.bash
+ = preserve do
+ = raw @build.trace_html
+ %div#down-build-trace
+
+ .col-md-3
+ - if @build.coverage
+ .build-widget
+ %h4.title
+ Test coverage
+ %h1 #{@build.coverage}%
+
+
+ .build-widget
+ %h4.title
+ Build
+ - if current_user && can?(current_user, :manage_builds, @project)
+ .pull-right
+ - if @build.active?
+ = link_to "Cancel", cancel_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-danger'
+ - elsif @build.commands.present?
+ = link_to "Retry", retry_namespace_project_build_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-primary', method: :post
+
+ - if @build.duration
+ %p
+ %span.attr-name Duration:
+ #{duration_in_words(@build.finished_at, @build.started_at)}
+ %p
+ %span.attr-name Created:
+ #{time_ago_in_words(@build.created_at)} ago
+ - if @build.finished_at
+ %p
+ %span.attr-name Finished:
+ #{time_ago_in_words(@build.finished_at)} ago
+ %p
+ %span.attr-name Runner:
+ - if @build.runner && current_user && current_user.admin
+ \#{link_to "##{@build.runner.id}", ci_admin_runner_path(@build.runner.id)}
+ - elsif @build.runner
+ \##{@build.runner.id}
+
+ - if @build.trigger_request
+ .build-widget
+ %h4.title
+ Trigger
+
+ %p
+ %span.attr-name Token:
+ #{@build.trigger_request.trigger.short_token}
+
+ - if @build.trigger_request.variables
+ %p
+ %span.attr-name Variables:
+
+ %code
+ - @build.trigger_request.variables.each do |key, value|
+ #{key}=#{value}
+
+ .build-widget
+ %h4.title
+ Commit
+ .pull-right
+ %small #{build_commit_link @build}
+ %p
+ %span.attr-name Branch:
+ #{build_ref_link @build}
+ %p
+ %span.attr-name Author:
+ #{@build.commit.git_author_name}
+ %p
+ %span.attr-name Message:
+ #{@build.commit.git_commit_message}
+
+ - if @build.tags.any?
+ .build-widget
+ %h4.title
+ Tags
+ - @build.tag_list.each do |tag|
+ %span.label.label-primary
+ = tag
+
+ - if @builds.present?
+ .build-widget
+ %h4.title #{pluralize(@builds.count, "other build")} for #{@build.short_sha}:
+ %table.table.builds
+ - @builds.each_with_index do |build, i|
+ %tr.build
+ %td
+ = ci_icon_for_status(build.status)
+ %td
+ = link_to namespace_project_build_path(@project.namespace, @project, @build) do
+ - if build.name
+ = build.name
+ - else
+ %span ##{build.id}
+
+ %td.status= build.status
+
+
+ = paginate @builds
+
+
+ :javascript
+ new CiBuild("#{namespace_project_build_path(@project.namespace, @project, @build)}", "#{@build.status}")
diff --git a/app/views/ci/services/_form.html.haml b/app/views/projects/ci_services/_form.html.haml
index 9110aaa0528..397832e56db 100644
--- a/app/views/ci/services/_form.html.haml
+++ b/app/views/projects/ci_services/_form.html.haml
@@ -4,13 +4,10 @@
%p= @service.description
-.back-link
- = link_to ci_project_services_path(@project) do
- &larr; to services
%hr
-= form_for(@service, as: :service, url: ci_project_service_path(@project, @service.to_param), method: :put, html: { class: 'form-horizontal' }) do |f|
+= form_for(@service, as: :service, url: namespace_project_ci_service_path(@project.namespace, @project, @service.to_param), method: :put, html: { class: 'form-horizontal' }) do |f|
- if @service.errors.any?
.alert.alert-danger
%ul
@@ -54,4 +51,4 @@
= f.submit 'Save', class: 'btn btn-save'
&nbsp;
- if @service.valid? && @service.activated? && @service.can_test?
- = link_to 'Test settings', test_ci_project_service_path(@project, @service.to_param), class: 'btn'
+ = link_to 'Test settings', test_namespace_project_ci_service_path(@project.namespace, @project, @service.to_param), class: 'btn'
diff --git a/app/views/ci/services/edit.html.haml b/app/views/projects/ci_services/edit.html.haml
index bcc5832792f..bcc5832792f 100644
--- a/app/views/ci/services/edit.html.haml
+++ b/app/views/projects/ci_services/edit.html.haml
diff --git a/app/views/ci/services/index.html.haml b/app/views/projects/ci_services/index.html.haml
index 37e5723b541..c78b21884a3 100644
--- a/app/views/ci/services/index.html.haml
+++ b/app/views/projects/ci_services/index.html.haml
@@ -13,7 +13,7 @@
%td
= boolean_to_icon service.activated?
%td
- = link_to edit_ci_project_service_path(@project, service.to_param) do
+ = link_to edit_namespace_project_ci_service_path(@project.namespace, @project, service.to_param) do
%strong= service.title
%td
= service.description
diff --git a/app/views/projects/ci_settings/_form.html.haml b/app/views/projects/ci_settings/_form.html.haml
index 9f891f557a9..d711413c6b9 100644
--- a/app/views/projects/ci_settings/_form.html.haml
+++ b/app/views/projects/ci_settings/_form.html.haml
@@ -8,6 +8,22 @@
Edit your
#{link_to ".gitlab-ci.yml using web-editor", yaml_web_editor_link(@ci_project)}
+- unless @project.empty_repo?
+ %p
+ Paste build status image for #{@repository.root_ref} with next link
+ = link_to '#', class: 'badge-codes-toggle btn btn-default btn-xs' do
+ Status Badge
+ .badge-codes-block.bs-callout.bs-callout-info.hide
+ %p
+ Status badge for
+ %span.label.label-info #{@ref}
+ branch
+ %div
+ %label Markdown:
+ = text_field_tag 'badge_md', markdown_badge_code(@ci_project, @repository.root_ref), readonly: true, class: 'form-control'
+ %label Html:
+ = text_field_tag 'badge_html', html_badge_code(@ci_project, @repository.root_ref), readonly: true, class: 'form-control'
+
= nested_form_for @ci_project, url: namespace_project_ci_settings_path(@project.namespace, @project), html: { class: 'form-horizontal' } do |f|
- if @ci_project.errors.any?
#error_explanation
diff --git a/app/views/ci/projects/_no_runners.html.haml b/app/views/projects/ci_settings/_no_runners.html.haml
index c0a296fb17d..33038c52978 100644
--- a/app/views/ci/projects/_no_runners.html.haml
+++ b/app/views/projects/ci_settings/_no_runners.html.haml
@@ -4,5 +4,5 @@
%br
You can add Specific runner for this project on Runners page
- - if current_user.is_admin
+ - if current_user.admin
or add Shared runner for whole application in admin are.
diff --git a/app/views/projects/ci_settings/edit.html.haml b/app/views/projects/ci_settings/edit.html.haml
index e9040fe4337..eedf484bf00 100644
--- a/app/views/projects/ci_settings/edit.html.haml
+++ b/app/views/projects/ci_settings/edit.html.haml
@@ -6,6 +6,9 @@
yaml file which is based on your old jobs.
Put this file to the root of your project and name it .gitlab-ci.yml
+- if no_runners_for_project?(@ci_project)
+ = render 'no_runners'
+
= render 'form'
- if @ci_project.generated_yaml_config
diff --git a/app/views/projects/commit/_ci_menu.html.haml b/app/views/projects/commit/_ci_menu.html.haml
new file mode 100644
index 00000000000..a634ae5dfda
--- /dev/null
+++ b/app/views/projects/commit/_ci_menu.html.haml
@@ -0,0 +1,7 @@
+%ul.center-top-menu.commit-ci-menu
+ = nav_link(path: 'commit#show') do
+ = link_to namespace_project_commit_path(@project.namespace, @project, @commit.id) do
+ Changes
+ = nav_link(path: 'commit#ci') do
+ = link_to ci_namespace_project_commit_path(@project.namespace, @project, @commit.id) do
+ Builds
diff --git a/app/views/projects/commit/ci.html.haml b/app/views/projects/commit/ci.html.haml
new file mode 100644
index 00000000000..26ab38445c2
--- /dev/null
+++ b/app/views/projects/commit/ci.html.haml
@@ -0,0 +1,62 @@
+- page_title "#{@commit.title} (#{@commit.short_id})", "Commits"
+= render "projects/commits/header_title"
+= render "commit_box"
+= render "ci_menu"
+
+- if @ci_project && current_user && can?(current_user, :manage_builds, @project)
+ .pull-right
+ - if @ci_commit.builds.running_or_pending.any?
+ = link_to "Cancel", cancel_builds_namespace_project_commit_path(@project.namespace, @project, @commit.sha), class: 'btn btn-sm btn-danger'
+
+
+- if @ci_commit.yaml_errors.present?
+ .bs-callout.bs-callout-danger
+ %h4 Found errors in your .gitlab-ci.yml:
+ %ul
+ - @ci_commit.yaml_errors.split(",").each do |error|
+ %li= error
+
+- unless @ci_commit.ci_yaml_file
+ .bs-callout.bs-callout-warning
+ \.gitlab-ci.yml not found in this commit
+
+- @ci_commit.refs.each do |ref|
+ .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
+ %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
+ = render partial: "projects/builds/build", collection: @ci_commit.retried_builds, ref: true
diff --git a/app/views/projects/commit/show.html.haml b/app/views/projects/commit/show.html.haml
index f8681024d1b..30a3973828f 100644
--- a/app/views/projects/commit/show.html.haml
+++ b/app/views/projects/commit/show.html.haml
@@ -1,5 +1,6 @@
- page_title "#{@commit.title} (#{@commit.short_id})", "Commits"
= render "projects/commits/header_title"
= render "commit_box"
+= render "ci_menu" if @ci_commit
= render "projects/diffs/diffs", diffs: @diffs, project: @project
= render "projects/notes/notes_with_form", view: params[:view]
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index 90dce739992..1882a82fba5 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -193,13 +193,13 @@
.panel.panel-default.panel.panel-danger
.panel-heading Remove project
.panel-body
- = form_tag(namespace_project_path(@project.namespace, @project), method: :delete, html: { class: 'form-horizontal'}) do
+ = form_tag(namespace_project_path(@project.namespace, @project), method: :delete, class: 'form-horizontal') do
%p
Removing the project will delete its repository and all related resources including issues, merge requests etc.
%br
%strong Removed projects cannot be restored!
- = link_to 'Remove project', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_project_message(@project) }
+ = button_to 'Remove project', '#', class: "btn btn-remove js-confirm-danger", data: { "confirm-danger-message" => remove_project_message(@project) }
- else
.nothing-here-block Only project owner can remove a project
diff --git a/app/views/projects/labels/_form.html.haml b/app/views/projects/labels/_form.html.haml
index 534c545329b..4cf13492e99 100644
--- a/app/views/projects/labels/_form.html.haml
+++ b/app/views/projects/labels/_form.html.haml
@@ -10,7 +10,7 @@
.form-group
= f.label :title, class: 'control-label'
.col-sm-10
- = f.text_field :title, class: "form-control", required: true
+ = f.text_field :title, class: "form-control js-quick-submit", required: true
.form-group
= f.label :color, "Background Color", class: 'control-label'
.col-sm-10
diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml
index 0b0f52c653c..e7ac7a0eaa4 100644
--- a/app/views/projects/merge_requests/_show.html.haml
+++ b/app/views/projects/merge_requests/_show.html.haml
@@ -24,7 +24,7 @@
%ul.dropdown-menu
%li= link_to "Email Patches", merge_request_path(@merge_request, format: :patch)
%li= link_to "Plain Diff", merge_request_path(@merge_request, format: :diff)
- .light
+ .normal
%span Request to merge
%span.label-branch #{source_branch_with_namespace(@merge_request)}
%span into
@@ -34,7 +34,7 @@
= render "projects/merge_requests/widget/show.html.haml"
- if @merge_request.open? && @merge_request.can_be_merged?
- .light
+ .light.append-bottom-20
You can also accept this merge request manually using the
= link_to "command line", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal"
diff --git a/app/views/projects/merge_requests/widget/_heading.html.haml b/app/views/projects/merge_requests/widget/_heading.html.haml
index 10640f746f0..68dda1424cf 100644
--- a/app/views/projects/merge_requests/widget/_heading.html.haml
+++ b/app/views/projects/merge_requests/widget/_heading.html.haml
@@ -1,30 +1,44 @@
- if @merge_request.has_ci?
- .mr-widget-heading
- - [:success, :skipped, :canceled, :failed, :running, :pending].each do |status|
- .ci_widget{class: "ci-#{status}", style: "display:none"}
- - if status == :success
- - status = "passed"
- = icon("check-circle")
- - else
- = icon("circle")
+ - ci_commit = @merge_request.source_project.ci_commit(@merge_request.source_sha)
+ - if ci_commit
+ - status = ci_commit.status
+ .mr-widget-heading
+ .ci_widget{class: "ci-#{status}"}
+ = ci_status_icon(ci_commit)
%span CI build #{status}
for #{@merge_request.last_commit_short_sha}.
%span.ci-coverage
- - if ci_build_details_path(@merge_request)
- = link_to "View build details", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink"
+ = link_to "View build details", ci_status_path(ci_commit)
- .ci_widget
- = icon("spinner spin")
- Checking CI status for #{@merge_request.last_commit_short_sha}&hellip;
+ - else
+ - # Compatibility with old CI integrations (ex jenkins) when you request status from CI server via AJAX
+ - # Remove in later versions when services like Jenkins will set CI status via Commit status API
+ .mr-widget-heading
+ - [:success, :skipped, :canceled, :failed, :running, :pending].each do |status|
+ .ci_widget{class: "ci-#{status}", style: "display:none"}
+ - if status == :success
+ - status = "passed"
+ = icon("check-circle")
+ - else
+ = icon("circle")
+ %span CI build #{status}
+ for #{@merge_request.last_commit_short_sha}.
+ %span.ci-coverage
+ - if ci_build_details_path(@merge_request)
+ = link_to "View build details", ci_build_details_path(@merge_request), :"data-no-turbolink" => "data-no-turbolink"
- .ci_widget.ci-not_found{style: "display:none"}
- = icon("times-circle")
- Could not find CI status for #{@merge_request.last_commit_short_sha}.
+ .ci_widget
+ = icon("spinner spin")
+ Checking CI status for #{@merge_request.last_commit_short_sha}&hellip;
- .ci_widget.ci-error{style: "display:none"}
- = icon("times-circle")
- Could not connect to the CI server. Please check your settings and try again.
+ .ci_widget.ci-not_found{style: "display:none"}
+ = icon("times-circle")
+ Could not find CI status for #{@merge_request.last_commit_short_sha}.
- :coffeescript
- $ ->
- merge_request_widget.getCiStatus()
+ .ci_widget.ci-error{style: "display:none"}
+ = icon("times-circle")
+ Could not connect to the CI server. Please check your settings and try again.
+
+ :coffeescript
+ $ ->
+ merge_request_widget.getCiStatus()
diff --git a/app/views/projects/milestones/_form.html.haml b/app/views/projects/milestones/_form.html.haml
index 74e9668052d..255ddab479f 100644
--- a/app/views/projects/milestones/_form.html.haml
+++ b/app/views/projects/milestones/_form.html.haml
@@ -16,13 +16,13 @@
.form-group
= f.label :title, "Title", class: "control-label"
.col-sm-10
- = f.text_field :title, maxlength: 255, class: "form-control", required: true
+ = f.text_field :title, maxlength: 255, class: "form-control js-quick-submit", required: true
%p.hint Required
.form-group.milestone-description
= f.label :description, "Description", class: "control-label"
.col-sm-10
= render layout: 'projects/md_preview', locals: { preview_class: "md-preview" } do
- = render 'projects/zen', f: f, attr: :description, classes: 'description form-control'
+ = render 'projects/zen', f: f, attr: :description, classes: 'description form-control js-quick-submit'
.hint
.pull-left Milestones are parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"), target: '_blank'}.
.pull-left Attach files by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }.
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index bccea21e7a8..1b093c8f514 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -8,7 +8,7 @@
= form_for @project, html: { class: 'new_project form-horizontal js-requires-input' } do |f|
.form-group.project-name-holder
= f.label :path, class: 'control-label' do
- %strong Project path
+ Project path
.col-sm-10
.input-group
= f.text_field :path, placeholder: "my-awesome-project", class: "form-control", tabindex: 1, autofocus: true, required: true
@@ -23,7 +23,6 @@
= f.select :namespace_id, namespaces_options(params[:namespace_id] || :current_user), {}, {class: 'select2', tabindex: 2}
- if import_sources_enabled?
- %hr
.project-import.js-toggle-container
.form-group
@@ -35,7 +34,7 @@
%i.fa.fa-github
GitHub
- else
- = link_to '#', class: 'how_to_import_link light btn import_github' do
+ = link_to '#', class: 'how_to_import_link btn import_github' do
%i.fa.fa-github
GitHub
= render 'github_import_modal'
@@ -46,7 +45,7 @@
%i.fa.fa-bitbucket
Bitbucket
- else
- = link_to status_import_bitbucket_path, class: 'how_to_import_link light btn import_bitbucket', "data-no-turbolink" => "true" do
+ = link_to status_import_bitbucket_path, class: 'how_to_import_link btn import_bitbucket', "data-no-turbolink" => "true" do
%i.fa.fa-bitbucket
Bitbucket
= render 'bitbucket_import_modal'
@@ -57,7 +56,7 @@
%i.fa.fa-heart
GitLab.com
- else
- = link_to status_import_gitlab_path, class: 'how_to_import_link light btn import_gitlab' do
+ = link_to status_import_gitlab_path, class: 'how_to_import_link btn import_gitlab' do
%i.fa.fa-heart
GitLab.com
= render 'gitlab_import_modal'
@@ -97,7 +96,7 @@
%li
To migrate an SVN repository, check out #{link_to "this document", "http://doc.gitlab.com/ce/workflow/importing/migrating_from_svn.html"}.
- %hr.prepend-botton-10
+ .prepend-botton-10
.form-group
= f.label :description, class: 'control-label' do
@@ -112,10 +111,11 @@
- if current_user.can_create_group?
.pull-right
- .light
- Need a group for several dependent projects?
- = link_to new_group_path, class: "btn btn-xs" do
- Create a group
+ .light.in-line
+ .space-right
+ Need a group for several dependent projects?
+ = link_to new_group_path, class: "btn btn-xs" do
+ Create a group
.save-project-loader.hide
.center
diff --git a/app/views/projects/notes/_edit_form.html.haml b/app/views/projects/notes/_edit_form.html.haml
index a0e26f9827e..a21c019986a 100644
--- a/app/views/projects/notes/_edit_form.html.haml
+++ b/app/views/projects/notes/_edit_form.html.haml
@@ -2,7 +2,7 @@
= form_for note, url: namespace_project_note_path(@project.namespace, @project, note), method: :put, remote: true, authenticity_token: true do |f|
= note_target_fields(note)
= render layout: 'projects/md_preview', locals: { preview_class: 'md-preview' } do
- = render 'projects/zen', f: f, attr: :note, classes: 'note_text js-note-text js-task-list-field'
+ = render 'projects/zen', f: f, attr: :note, classes: 'note_text js-note-text js-task-list-field js-quick-submit'
= render 'projects/notes/hints'
.note-form-actions
diff --git a/app/views/projects/notes/_form.html.haml b/app/views/projects/notes/_form.html.haml
index d99445da59a..13dfa0a1bb3 100644
--- a/app/views/projects/notes/_form.html.haml
+++ b/app/views/projects/notes/_form.html.haml
@@ -8,12 +8,12 @@
= f.hidden_field :noteable_type
= render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do
- = render 'projects/zen', f: f, attr: :note, classes: 'note_text js-note-text'
+ = render 'projects/zen', f: f, attr: :note, classes: 'note_text js-note-text js-quick-submit'
= render 'projects/notes/hints'
.error-alert
.note-form-actions
.buttons.clearfix
- = f.submit 'Add Comment', class: "btn comment-btn btn-grouped js-comment-button"
+ = f.submit 'Add Comment', class: "btn btn-green comment-btn btn-grouped js-comment-button"
= yield(:note_actions)
%a.btn.grouped.js-close-discussion-note-form Cancel
diff --git a/app/views/projects/tree/_tree.html.haml b/app/views/projects/tree/_tree.html.haml
index 367a87927d7..457f8a4a585 100644
--- a/app/views/projects/tree/_tree.html.haml
+++ b/app/views/projects/tree/_tree.html.haml
@@ -8,11 +8,25 @@
= link_to truncate(title, length: 40), namespace_project_tree_path(@project.namespace, @project, path)
- else
= link_to title, '#'
- - if current_user && can_push_branch?(@project, @ref)
+ - if allowed_tree_edit?
%li
- = link_to namespace_project_new_blob_path(@project.namespace, @project, @id), title: 'New file', id: 'new-file-link' do
- %small
- %i.fa.fa-plus
+ %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
%div#tree-content-holder.tree-content-holder.prepend-top-20
%table#tree-slider{class: "table_#{@hex_path} tree-table" }
@@ -46,6 +60,10 @@
%div.tree_progress
+- if allowed_tree_edit?
+ = render 'projects/blob/upload', title: 'Upload', placeholder: 'Upload new file', button_title: 'Upload file', form_path: namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post
+ = render 'projects/blob/new_dir'
+
:javascript
// Load last commit log for each file in tree
$('#tree-slider').waitForImages(function() {
diff --git a/app/views/projects/wikis/_form.html.haml b/app/views/projects/wikis/_form.html.haml
index 05d754adbe5..261d4a92d7d 100644
--- a/app/views/projects/wikis/_form.html.haml
+++ b/app/views/projects/wikis/_form.html.haml
@@ -22,7 +22,7 @@
= f.label :content, class: 'control-label'
.col-sm-10
= render layout: 'projects/md_preview', locals: { preview_class: "md-preview" } do
- = render 'projects/zen', f: f, attr: :content, classes: 'description form-control'
+ = render 'projects/zen', f: f, attr: :content, classes: 'description form-control js-quick-submit'
.col-sm-12.hint
.pull-left Wiki content is parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"), target: '_blank'}
.pull-right Attach files by dragging &amp; dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }.
diff --git a/app/views/shared/_commit_message_container.html.haml b/app/views/shared/_commit_message_container.html.haml
index 5071ff640f1..cc3f1268f8b 100644
--- a/app/views/shared/_commit_message_container.html.haml
+++ b/app/views/shared/_commit_message_container.html.haml
@@ -6,7 +6,7 @@
.max-width-marker
= text_area_tag 'commit_message',
(params[:commit_message] || local_assigns[:text]),
- class: 'form-control', placeholder: local_assigns[:placeholder],
+ class: 'form-control js-quick-submit', placeholder: local_assigns[:placeholder],
required: true, rows: (local_assigns[:rows] || 3)
- if local_assigns[:hint]
%p.hint
diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml
index 8f16773077e..6e6d497c1d2 100644
--- a/app/views/shared/issuable/_filter.html.haml
+++ b/app/views/shared/issuable/_filter.html.haml
@@ -39,13 +39,13 @@
.filter-item.inline.milestone-filter
= select_tag('milestone_title', projects_milestones_options,
- class: 'select2 trigger-submit', include_blank: true,
+ class: 'select2 trigger-submit', include_blank: 'Any',
data: {placeholder: 'Milestone'})
- if @project
.filter-item.inline.labels-filter
= select_tag('label_name', project_labels_options(@project),
- class: 'select2 trigger-submit', include_blank: true,
+ class: 'select2 trigger-submit', include_blank: 'Any',
data: {placeholder: 'Label'})
.pull-right
diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml
index 33ec726e93c..594e54f404c 100644
--- a/app/views/shared/issuable/_form.html.haml
+++ b/app/views/shared/issuable/_form.html.haml
@@ -10,7 +10,7 @@
%strong= 'Title *'
.col-sm-10
= f.text_field :title, maxlength: 255, autofocus: true, autocomplete: 'off',
- class: 'form-control pad js-gfm-input', required: true
+ class: 'form-control pad js-gfm-input js-quick-submit', required: true
- if issuable.is_a?(MergeRequest)
%p.help-block
@@ -26,7 +26,7 @@
= render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do
= render 'projects/zen', f: f, attr: :description,
- classes: 'description form-control'
+ classes: 'description form-control js-quick-submit'
.col-sm-12.hint
.pull-left
Parsed with
diff --git a/config/application.rb b/config/application.rb
index a96e22211e6..bfa2a809dd7 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -74,7 +74,7 @@ module Gitlab
origins '*'
resource '/api/*',
headers: :any,
- methods: [:get, :post, :options, :put, :delete],
+ methods: :any,
expose: ['Link']
end
end
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 4e4a8ecbdb3..4c78bd6e2fa 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -178,7 +178,6 @@ Settings.gitlab['import_sources'] ||= ['github','bitbucket','gitlab','gitorious'
# CI
#
Settings['gitlab_ci'] ||= Settingslogic.new({})
-Settings.gitlab_ci['enabled'] = true if Settings.gitlab_ci['enabled'].nil?
Settings.gitlab_ci['all_broken_builds'] = true if Settings.gitlab_ci['all_broken_builds'].nil?
Settings.gitlab_ci['add_pusher'] = false if Settings.gitlab_ci['add_pusher'].nil?
Settings.gitlab_ci['url'] ||= Settings.send(:build_gitlab_ci_url)
diff --git a/config/mail_room.yml.example b/config/mail_room.yml.example
index 82e1a42058e..ed4a5193a6a 100644
--- a/config/mail_room.yml.example
+++ b/config/mail_room.yml.example
@@ -9,7 +9,7 @@
# # Whether the IMAP server uses StartTLS
# :start_tls: false
# # Email account username. Usually the full email address.
- # :email: "replies@gitlab.example.com"
+ # :email: "gitlab-incoming@gmail.com"
# # Email account password
# :password: "password"
# # The name of the mailbox where incoming mail will end up. Usually "inbox".
diff --git a/config/routes.rb b/config/routes.rb
index 7bda7ade817..8e6fbf6340c 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -22,31 +22,6 @@ Gitlab::Application.routes.draw do
get :dumped_yaml
end
- resources :services, only: [:index, :edit, :update] do
- member do
- get :test
- end
- end
-
- resource :charts, only: [:show]
-
- resources :refs, constraints: { ref_id: /.*/ }, only: [] do
- resources :commits, only: [:show] do
- member do
- get :status
- get :cancel
- end
- end
- end
-
- resources :builds, only: [:show] do
- member do
- get :cancel
- get :status
- post :retry
- end
- end
-
resources :runner_projects, only: [:create, :destroy]
resources :events, only: [:index]
@@ -468,6 +443,15 @@ Gitlab::Application.routes.draw do
end
scope do
+ post(
+ '/create_dir/*id',
+ to: 'tree#create_dir',
+ constraints: { id: /.+/ },
+ as: 'create_dir'
+ )
+ end
+
+ scope do
get(
'/blame/*id',
to: 'blame#show',
@@ -487,7 +471,11 @@ Gitlab::Application.routes.draw do
resource :avatar, only: [:show, :destroy]
resources :commit, only: [:show], constraints: { id: /[[:alnum:]]{6,40}/ } do
- get :branches, on: :member
+ member do
+ get :branches
+ get :ci
+ get :cancel_builds
+ end
end
resources :compare, only: [:index, :create]
@@ -591,6 +579,20 @@ Gitlab::Application.routes.draw do
end
end
+ resources :ci_services, constraints: { id: /[^\/]+/ }, only: [:index, :edit, :update] do
+ member do
+ get :test
+ end
+ end
+
+ resources :builds, only: [:show] do
+ member do
+ get :cancel
+ get :status
+ post :retry
+ end
+ end
+
resources :hooks, only: [:index, :create, :destroy], constraints: { id: /\d+/ } do
member do
get :test
diff --git a/db/migrate/20151002112914_add_stage_idx_to_builds.rb b/db/migrate/20151002112914_add_stage_idx_to_builds.rb
new file mode 100644
index 00000000000..68a745ffef4
--- /dev/null
+++ b/db/migrate/20151002112914_add_stage_idx_to_builds.rb
@@ -0,0 +1,5 @@
+class AddStageIdxToBuilds < ActiveRecord::Migration
+ def change
+ add_column :ci_builds, :stage_idx, :integer
+ end
+end
diff --git a/db/migrate/20151002121400_add_index_for_builds.rb b/db/migrate/20151002121400_add_index_for_builds.rb
new file mode 100644
index 00000000000..4ffc1363910
--- /dev/null
+++ b/db/migrate/20151002121400_add_index_for_builds.rb
@@ -0,0 +1,5 @@
+class AddIndexForBuilds < ActiveRecord::Migration
+ def up
+ add_index :ci_builds, [:commit_id, :stage_idx, :created_at]
+ end
+end
diff --git a/db/migrate/20151002122929_add_ref_and_tag_to_builds.rb b/db/migrate/20151002122929_add_ref_and_tag_to_builds.rb
new file mode 100644
index 00000000000..e3d2ac1cea5
--- /dev/null
+++ b/db/migrate/20151002122929_add_ref_and_tag_to_builds.rb
@@ -0,0 +1,6 @@
+class AddRefAndTagToBuilds < ActiveRecord::Migration
+ def change
+ add_column :ci_builds, :tag, :boolean
+ add_column :ci_builds, :ref, :string
+ end
+end
diff --git a/db/migrate/20151002122943_migrate_ref_and_tag_to_build.rb b/db/migrate/20151002122943_migrate_ref_and_tag_to_build.rb
new file mode 100644
index 00000000000..01d7b3f6773
--- /dev/null
+++ b/db/migrate/20151002122943_migrate_ref_and_tag_to_build.rb
@@ -0,0 +1,6 @@
+class MigrateRefAndTagToBuild < ActiveRecord::Migration
+ def change
+ execute('UPDATE ci_builds SET ref=(SELECT ref FROM ci_commits WHERE ci_commits.id = ci_builds.commit_id) WHERE ref IS NULL')
+ execute('UPDATE ci_builds SET tag=(SELECT tag FROM ci_commits WHERE ci_commits.id = ci_builds.commit_id) WHERE tag IS NULL')
+ end
+end
diff --git a/db/migrate/20151005075649_add_user_id_to_build.rb b/db/migrate/20151005075649_add_user_id_to_build.rb
new file mode 100644
index 00000000000..0f4b92b8b79
--- /dev/null
+++ b/db/migrate/20151005075649_add_user_id_to_build.rb
@@ -0,0 +1,5 @@
+class AddUserIdToBuild < ActiveRecord::Migration
+ def change
+ add_column :ci_builds, :user_id, :integer
+ end
+end
diff --git a/db/migrate/20151005150751_add_layout_option_for_users.rb b/db/migrate/20151005150751_add_layout_option_for_users.rb
new file mode 100644
index 00000000000..ead9b1f8977
--- /dev/null
+++ b/db/migrate/20151005150751_add_layout_option_for_users.rb
@@ -0,0 +1,5 @@
+class AddLayoutOptionForUsers < ActiveRecord::Migration
+ def change
+ add_column :users, :layout, :integer, default: 0
+ end
+end \ No newline at end of file
diff --git a/db/migrate/20151005162154_remove_ci_enabled_from_application_settings.rb b/db/migrate/20151005162154_remove_ci_enabled_from_application_settings.rb
new file mode 100644
index 00000000000..be6aa810bb5
--- /dev/null
+++ b/db/migrate/20151005162154_remove_ci_enabled_from_application_settings.rb
@@ -0,0 +1,5 @@
+class RemoveCiEnabledFromApplicationSettings < ActiveRecord::Migration
+ def change
+ remove_column :application_settings, :ci_enabled, :boolean, null: false, default: true
+ end
+end
diff --git a/db/migrate/20151007120511_namespaces_projects_path_lower_indexes.rb b/db/migrate/20151007120511_namespaces_projects_path_lower_indexes.rb
new file mode 100644
index 00000000000..7f6cd6d5a78
--- /dev/null
+++ b/db/migrate/20151007120511_namespaces_projects_path_lower_indexes.rb
@@ -0,0 +1,17 @@
+class NamespacesProjectsPathLowerIndexes < ActiveRecord::Migration
+ disable_ddl_transaction!
+
+ def up
+ return unless Gitlab::Database.postgresql?
+
+ execute 'CREATE INDEX CONCURRENTLY index_on_namespaces_lower_path ON namespaces (LOWER(path));'
+ execute 'CREATE INDEX CONCURRENTLY index_on_projects_lower_path ON projects (LOWER(path));'
+ end
+
+ def down
+ return unless Gitlab::Database.postgresql?
+
+ remove_index :namespaces, name: :index_on_namespaces_lower_path
+ remove_index :projects, name: :index_on_projects_lower_path
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 72609da93f1..c5c462c2e57 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: 20150930095736) do
+ActiveRecord::Schema.define(version: 20151007120511) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -46,7 +46,6 @@ ActiveRecord::Schema.define(version: 20150930095736) do
t.integer "session_expire_delay", default: 10080, null: false
t.text "import_sources"
t.text "help_page_text"
- t.boolean "ci_enabled", default: true, null: false
end
create_table "audit_events", force: true do |t|
@@ -100,8 +99,13 @@ ActiveRecord::Schema.define(version: 20150930095736) do
t.boolean "allow_failure", default: false, null: false
t.string "stage"
t.integer "trigger_request_id"
+ t.integer "stage_idx"
+ t.boolean "tag"
+ t.string "ref"
+ t.integer "user_id"
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"], 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
@@ -753,6 +757,7 @@ ActiveRecord::Schema.define(version: 20150930095736) do
t.integer "dashboard", default: 0
t.integer "project_view", default: 0
t.integer "consumed_timestep"
+ t.integer "layout", default: 0
end
add_index "users", ["admin"], name: "index_users_on_admin", using: :btree
diff --git a/doc/incoming_email/README.md b/doc/incoming_email/README.md
index 316746ab54d..87267423471 100644
--- a/doc/incoming_email/README.md
+++ b/doc/incoming_email/README.md
@@ -16,24 +16,35 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow [these
## Set it up
-In this example, we'll use the Gmail address `gitlab-incoming@gmail.com`.
-
### Omnibus package installations
1. Find the `incoming_email` section in `/etc/gitlab/gitlab.rb`, enable the feature, enter the email address including a placeholder for the `key` that references the item being replied to and fill in the details for your specific IMAP server and email account:
```ruby
+ # Postfix mail server, assumes mailbox incoming@gitlab.example.com
+ gitlab_rails['incoming_email_enabled'] = true
+ gitlab_rails['incoming_email_address'] = "incoming+%{key}@gitlab.example.com"
+ gitlab_rails['incoming_email_host'] = "gitlab.example.com" # IMAP server host
+ gitlab_rails['incoming_email_port'] = 143 # IMAP server port
+ gitlab_rails['incoming_email_ssl'] = false # Whether the IMAP server uses SSL
+ gitlab_rails['incoming_email_email'] = "incoming" # Email account username. Usually the full email address.
+ gitlab_rails['incoming_email_password'] = "[REDACTED]" # Email account password
+ gitlab_rails['incoming_email_mailbox_name'] = "inbox" # The name of the mailbox where incoming mail will end up. Usually "inbox".
+ ```
+
+ ```ruby
+ # Gmail / Google Apps, assumes mailbox gitlab-incoming@gmail.com
gitlab_rails['incoming_email_enabled'] = true
gitlab_rails['incoming_email_address'] = "gitlab-incoming+%{key}@gmail.com"
gitlab_rails['incoming_email_host'] = "imap.gmail.com" # IMAP server host
gitlab_rails['incoming_email_port'] = 993 # IMAP server port
gitlab_rails['incoming_email_ssl'] = true # Whether the IMAP server uses SSL
gitlab_rails['incoming_email_email'] = "gitlab-incoming@gmail.com" # Email account username. Usually the full email address.
- gitlab_rails['incoming_email_password'] = "password" # Email account password
+ gitlab_rails['incoming_email_password'] = "[REDACTED]" # Email account password
gitlab_rails['incoming_email_mailbox_name'] = "inbox" # The name of the mailbox where incoming mail will end up. Usually "inbox".
```
- As mentioned, the part after `+` in the address is ignored, and any email sent here will end up in the mailbox for `gitlab-incoming@gmail.com`.
+ As mentioned, the part after `+` in the address is ignored, and any email sent here will end up in the mailbox for `incoming@gitlab.example.com`/`gitlab-incoming@gmail.com`.
1. Reconfigure GitLab for the changes to take effect:
@@ -64,12 +75,20 @@ In this example, we'll use the Gmail address `gitlab-incoming@gmail.com`.
```
```yaml
+ # Postfix mail server, assumes mailbox incoming@gitlab.example.com
+ incoming_email:
+ enabled: true
+ address: "incoming+%{key}@gitlab.example.com"
+ ```
+
+ ```yaml
+ # Gmail / Google Apps, assumes mailbox gitlab-incoming@gmail.com
incoming_email:
enabled: true
address: "gitlab-incoming+%{key}@gmail.com"
```
- As mentioned, the part after `+` in the address is ignored, and any email sent here will end up in the mailbox for `gitlab-incoming@gmail.com`.
+ As mentioned, the part after `+` in the address is ignored, and any email sent here will end up in the mailbox for `incoming@gitlab.example.com`/`gitlab-incoming@gmail.com`.
2. Copy `config/mail_room.yml.example` to `config/mail_room.yml`:
@@ -84,6 +103,40 @@ In this example, we'll use the Gmail address `gitlab-incoming@gmail.com`.
```
```yaml
+ # Postfix mail server
+ :mailboxes:
+ -
+ # IMAP server host
+ :host: "gitlab.example.com"
+ # IMAP server port
+ :port: 143
+ # Whether the IMAP server uses SSL
+ :ssl: false
+ # Whether the IMAP server uses StartTLS
+ :start_tls: false
+ # Email account username. Usually the full email address.
+ :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.
+ :delete_after_delivery: true
+ :delivery_options:
+ # The URL to the Redis server used by Sidekiq. Should match the URL in config/resque.yml.
+ :redis_url: redis://localhost:6379
+ # Always "resque:gitlab".
+ :namespace: resque:gitlab
+ # Always "incoming_email".
+ :queue: incoming_email
+ # Always "EmailReceiverWorker"
+ :worker: EmailReceiverWorker
+ ```
+
+ ```yaml
+ # Gmail / Google Apps
:mailboxes:
-
# IMAP server host
@@ -143,6 +196,7 @@ In this example, we'll use the Gmail address `gitlab-incoming@gmail.com`.
1. Find the `incoming_email` section in `config/gitlab.yml`, enable the feature and enter the email address including a placeholder for the `key` that references the item being replied to:
```yaml
+ # Gmail / Google Apps, assumes mailbox gitlab-incoming@gmail.com
incoming_email:
enabled: true
address: "gitlab-incoming+%{key}@gmail.com"
@@ -159,6 +213,7 @@ In this example, we'll use the Gmail address `gitlab-incoming@gmail.com`.
3. Uncomment the configuration options in `config/mail_room.yml` and fill in the details for your specific IMAP server and email account:
```yaml
+ # Gmail / Google Apps, assumes mailbox gitlab-incoming@gmail.com
:mailboxes:
-
# IMAP server host
@@ -208,4 +263,4 @@ In this example, we'll use the Gmail address `gitlab-incoming@gmail.com`.
bundle exec rake gitlab:incoming_email:check RAILS_ENV=development
```
-8. Reply by email should now be working. \ No newline at end of file
+8. Reply by email should now be working.
diff --git a/doc/system_hooks/system_hooks.md b/doc/system_hooks/system_hooks.md
index b0e4613cdef..5cb05b13b3e 100644
--- a/doc/system_hooks/system_hooks.md
+++ b/doc/system_hooks/system_hooks.md
@@ -48,16 +48,17 @@ X-Gitlab-Event: System Hook
```json
{
- "created_at": "2012-07-21T07:30:56Z",
- "event_name": "user_add_to_team",
- "project_access": "Master",
- "project_id": 74,
- "project_name": "StoreCloud",
- "project_path": "storecloud",
- "user_email": "johnsmith@gmail.com",
- "user_name": "John Smith",
- "user_id": 41,
- "project_visibility": "private",
+ "created_at": "2012-07-21T07:30:56Z",
+ "event_name": "user_add_to_team",
+ "project_access": "Master",
+ "project_id": 74,
+ "project_name": "StoreCloud",
+ "project_path": "storecloud",
+ "project_path_with_namespace": "jsmith/storecloud",
+ "user_email": "johnsmith@gmail.com",
+ "user_name": "John Smith",
+ "user_id": 41,
+ "project_visibility": "private",
}
```
@@ -65,16 +66,17 @@ X-Gitlab-Event: System Hook
```json
{
- "created_at": "2012-07-21T07:30:56Z",
- "event_name": "user_remove_from_team",
- "project_access": "Master",
- "project_id": 74,
- "project_name": "StoreCloud",
- "project_path": "storecloud",
- "user_email": "johnsmith@gmail.com",
- "user_name": "John Smith",
- "user_id": 41,
- "project_visibility": "private",
+ "created_at": "2012-07-21T07:30:56Z",
+ "event_name": "user_remove_from_team",
+ "project_access": "Master",
+ "project_id": 74,
+ "project_name": "StoreCloud",
+ "project_path": "storecloud",
+ "project_path_with_namespace": "jsmith/storecloud",
+ "user_email": "johnsmith@gmail.com",
+ "user_name": "John Smith",
+ "user_id": 41,
+ "project_visibility": "private",
}
```
diff --git a/doc/update/7.14-to-8.0.md b/doc/update/7.14-to-8.0.md
index 0e65c32098c..7ad4935e839 100644
--- a/doc/update/7.14-to-8.0.md
+++ b/doc/update/7.14-to-8.0.md
@@ -175,12 +175,21 @@ Also note that because Apache does not support upstreams behind Unix sockets you
Now, GitLab CE and EE has CI integrated. However, migrations don't happen automatically and you need to do it manually.
Please follow the following guide [to migrate](../migrate_ci_to_ce/README.md) your GitLab CI instance to GitLab CE/EE.
-### 10. Start application
+### 10. Use Redis v2.4.0+
+
+Previous versions of GitLab allowed Redis versions >= 2.0 to be used, but
+Sidekiq jobs could fail due to lack of support for the SREM command. GitLab
+8.0 now checks that Redis >= 2.4.0 is used. You can check your Redis version
+with the following command:
+
+ redis-cli info | grep redis_version
+
+### 11. Start application
sudo service gitlab start
sudo service nginx restart
-### 11. Check application status
+### 12. Check application status
Check if GitLab and its environment are configured correctly:
diff --git a/features/project/commits/commits.feature b/features/project/commits/commits.feature
index 34161b81d44..e4beeb59adc 100644
--- a/features/project/commits/commits.feature
+++ b/features/project/commits/commits.feature
@@ -20,6 +20,8 @@ Feature: Project Commits
Given commit has ci status
And I click on commit link
Then I see commit ci info
+ And I click status link
+ Then I see builds list
Scenario: I browse commit with side-by-side diff view
Given I click on commit link
diff --git a/features/project/source/browse_files.feature b/features/project/source/browse_files.feature
index 58574166ef3..377c5e1a9a7 100644
--- a/features/project/source/browse_files.feature
+++ b/features/project/source/browse_files.feature
@@ -21,12 +21,12 @@ Feature: Project Source Browse Files
Then I should see raw file content
Scenario: I can create file
- Given I click on "new file" link in repo
+ Given I click on "New file" link in repo
Then I can see new file page
@javascript
Scenario: I can create and commit file
- Given I click on "new file" link in repo
+ Given I click on "New file" link in repo
And I edit code
And I fill the new file name
And I fill the commit message
@@ -36,14 +36,13 @@ Feature: Project Source Browse Files
@javascript
Scenario: I can upload file and commit
- Given I click on "new file" link in repo
- Then I can see new file page
- And I can see "upload an existing one"
- And I click on "upload"
+ Given I click on "Upload file" link in repo
And I upload a new text file
And I fill the upload file commit message
+ And I fill the new branch name
And I click on "Upload file"
Then I can see the new text file
+ And I am redirected to the uploaded file on new branch
And I can see the new commit message
@javascript
@@ -59,7 +58,7 @@ Feature: Project Source Browse Files
@javascript
Scenario: I can create and commit file and specify new branch
- Given I click on "new file" link in repo
+ Given I click on "New file" link in repo
And I edit code
And I fill the new file name
And I fill the commit message
@@ -83,7 +82,7 @@ Feature: Project Source Browse Files
@javascript
Scenario: If I enter an illegal file name I see an error message
- Given I click on "new file" link in repo
+ Given I click on "New file" link in repo
And I fill the new file name with an illegal name
And I edit code
And I fill the commit message
@@ -139,6 +138,24 @@ Feature: Project Source Browse Files
And I see a commit error message
@javascript
+ Scenario: I can create directory in repo
+ When I click on "New directory" link in repo
+ And I fill the new directory name
+ And I fill the commit message
+ And I fill the new branch name
+ And I click on "Create directory"
+ Then I am redirected to the new directory
+
+ @javascript
+ Scenario: I attempt to create an existing directory
+ When I click on "New directory" link in repo
+ And I fill an existing directory name
+ And I fill the commit message
+ And I click on "Create directory"
+ Then I see "Unable to create directory"
+ And I am redirected to the root directory
+
+ @javascript
Scenario: I can see editing preview
Given I click on ".gitignore" file in repo
And I click button "Edit"
diff --git a/features/steps/project/commits/commits.rb b/features/steps/project/commits/commits.rb
index 5ebc3a49760..ae5f90004e6 100644
--- a/features/steps/project/commits/commits.rb
+++ b/features/steps/project/commits/commits.rb
@@ -104,10 +104,20 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps
step 'commit has ci status' do
@project.enable_ci
- create :ci_commit, gl_project: @project, sha: sample_commit.id
+ ci_commit = create :ci_commit, gl_project: @project, sha: sample_commit.id
+ create :ci_build, commit: ci_commit
end
step 'I see commit ci info' do
- expect(page).to have_content "build: skipped"
+ expect(page).to have_content "build: pending"
+ end
+
+ step 'I click status link' do
+ click_link "Builds"
+ end
+
+ step 'I see builds list' do
+ expect(page).to have_content "build: pending"
+ expect(page).to have_content "Builds for master"
end
end
diff --git a/features/steps/project/source/browse_files.rb b/features/steps/project/source/browse_files.rb
index a1a49dd58a6..cb100ca0f54 100644
--- a/features/steps/project/source/browse_files.rb
+++ b/features/steps/project/source/browse_files.rb
@@ -71,7 +71,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
end
step 'I fill the new branch name' do
- fill_in :new_branch, with: 'new_branch_name'
+ fill_in :new_branch, with: 'new_branch_name', visible: true
end
step 'I fill the new file name with an illegal name' do
@@ -90,6 +90,10 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
click_button 'Commit Changes'
end
+ step 'I click on "Create directory"' do
+ click_button 'Create directory'
+ end
+
step 'I click on "Remove"' do
click_button 'Remove'
end
@@ -110,21 +114,32 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
expect(page).to have_css '.line_holder.new'
end
- step 'I click on "new file" link in repo' do
- click_link 'new-file-link'
+ step 'I click on "New file" link in repo' do
+ find('.add-to-tree').click
+ click_link 'Create file'
end
- step 'I can see new file page' do
- expect(page).to have_content "new file"
- expect(page).to have_content "Commit message"
+ step 'I click on "Upload file" link in repo' do
+ find('.add-to-tree').click
+ click_link 'Upload file'
+ end
+
+ step 'I click on "New directory" link in repo' do
+ find('.add-to-tree').click
+ click_link 'New directory'
+ end
+
+ step 'I fill the new directory name' do
+ fill_in :dir_name, with: new_dir_name
end
- step 'I can see "upload an existing one"' do
- expect(page).to have_content "upload an existing one"
+ step 'I fill an existing directory name' do
+ fill_in :dir_name, with: 'files'
end
- step 'I click on "upload"' do
- click_link 'upload'
+ step 'I can see new file page' do
+ expect(page).to have_content "new file"
+ expect(page).to have_content "Commit message"
end
step 'I click on "Upload file"' do
@@ -228,10 +243,30 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
@project.namespace, @project, 'new_branch_name/' + new_file_name))
end
+ step 'I am redirected to the uploaded file on new branch' do
+ expect(current_path).to eq(namespace_project_blob_path(
+ @project.namespace, @project,
+ 'new_branch_name/' + File.basename(test_text_file)))
+ end
+
+ step 'I am redirected to the new directory' do
+ expect(current_path).to eq(namespace_project_tree_path(
+ @project.namespace, @project, 'new_branch_name/' + new_dir_name))
+ end
+
+ step 'I am redirected to the root directory' do
+ expect(current_path).to eq(namespace_project_tree_path(
+ @project.namespace, @project, 'master/'))
+ end
+
step "I don't see the permalink link" do
expect(page).not_to have_link('permalink')
end
+ step 'I see "Unable to create directory"' do
+ expect(page).to have_content('Directory already exists')
+ end
+
step 'I see a commit error message' do
expect(page).to have_content('Your changes could not be committed')
end
@@ -287,6 +322,12 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
'not_a_file.md'
end
+ # Constant value that is a valid directory and
+ # not a directory present at root of the seed repository.
+ def new_dir_name
+ 'new_dir/subdir'
+ end
+
def drop_in_dropzone(file_path)
# Generate a fake input selector
page.execute_script <<-JS
diff --git a/lib/ci/api/api.rb b/lib/ci/api/api.rb
index 5109c84e0ea..218d8c3adcc 100644
--- a/lib/ci/api/api.rb
+++ b/lib/ci/api/api.rb
@@ -23,10 +23,6 @@ module Ci
rack_response({ 'message' => '500 Internal Server Error' }, 500)
end
- before do
- check_enable_flag!
- end
-
format :json
helpers Helpers
diff --git a/lib/ci/api/commits.rb b/lib/ci/api/commits.rb
index bac463a5909..a60769d8305 100644
--- a/lib/ci/api/commits.rb
+++ b/lib/ci/api/commits.rb
@@ -51,7 +51,7 @@ module Ci
required_attributes! [:project_id, :data, :project_token]
project = Ci::Project.find(params[:project_id])
authenticate_project_token!(project)
- commit = Ci::CreateCommitService.new.execute(project, params[:data])
+ commit = Ci::CreateCommitService.new.execute(project, current_user, params[:data])
if commit.persisted?
present commit, with: Entities::CommitWithBuilds
diff --git a/lib/ci/api/helpers.rb b/lib/ci/api/helpers.rb
index 8e893aa5cc6..e602cda81d6 100644
--- a/lib/ci/api/helpers.rb
+++ b/lib/ci/api/helpers.rb
@@ -3,12 +3,6 @@ module Ci
module Helpers
UPDATE_RUNNER_EVERY = 60
- def check_enable_flag!
- unless current_application_settings.ci_enabled
- render_api_error!('400 (Bad request) CI is disabled', 400)
- end
- end
-
def authenticate_runners!
forbidden! unless params[:token] == GitlabCi::REGISTRATION_TOKEN
end
diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb
index e625e790df8..c47951bc5d1 100644
--- a/lib/ci/gitlab_ci_yaml_processor.rb
+++ b/lib/ci/gitlab_ci_yaml_processor.rb
@@ -85,9 +85,10 @@ module Ci
def build_job(name, job)
{
+ stage_idx: stages.index(job[:stage]),
stage: job[:stage],
- script: "#{@before_script.join("\n")}\n#{normalize_script(job[:script])}",
- tags: job[:tags] || [],
+ commands: "#{@before_script.join("\n")}\n#{normalize_script(job[:script])}",
+ tag_list: job[:tags] || [],
name: name,
only: job[:only],
except: job[:except],
diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb
new file mode 100644
index 00000000000..741a52714ac
--- /dev/null
+++ b/lib/gitlab/database.rb
@@ -0,0 +1,11 @@
+module Gitlab
+ module Database
+ def self.mysql?
+ ActiveRecord::Base.connection.adapter_name.downcase == 'mysql'
+ end
+
+ def self.postgresql?
+ ActiveRecord::Base.connection.adapter_name.downcase == 'postgresql'
+ end
+ end
+end
diff --git a/lib/tasks/gitlab/setup.rake b/lib/tasks/gitlab/setup.rake
index 0ac4b0fa8a3..4cbccf2ca89 100644
--- a/lib/tasks/gitlab/setup.rake
+++ b/lib/tasks/gitlab/setup.rake
@@ -16,6 +16,7 @@ namespace :gitlab do
Rake::Task["db:setup"].invoke
Rake::Task["add_limits_mysql"].invoke
+ Rake::Task["setup_postgresql"].invoke
Rake::Task["db:seed_fu"].invoke
rescue Gitlab::TaskAbortedByUserError
puts "Quitting...".red
diff --git a/lib/tasks/migrate/setup_postgresql.rake b/lib/tasks/migrate/setup_postgresql.rake
new file mode 100644
index 00000000000..bf6894a8351
--- /dev/null
+++ b/lib/tasks/migrate/setup_postgresql.rake
@@ -0,0 +1,6 @@
+require Rails.root.join('db/migrate/20151007120511_namespaces_projects_path_lower_indexes')
+
+desc 'GitLab | Sets up PostgreSQL'
+task setup_postgresql: :environment do
+ NamespacesProjectsPathLowerIndexes.new.up
+end
diff --git a/spec/benchmarks/finders/trending_projects_finder_spec.rb b/spec/benchmarks/finders/trending_projects_finder_spec.rb
new file mode 100644
index 00000000000..551ce21840d
--- /dev/null
+++ b/spec/benchmarks/finders/trending_projects_finder_spec.rb
@@ -0,0 +1,14 @@
+require 'spec_helper'
+
+describe TrendingProjectsFinder, benchmark: true do
+ describe '#execute' do
+ let(:finder) { described_class.new }
+ let(:user) { create(:user) }
+
+ # to_a is used to force actually running the query (instead of just building
+ # it).
+ benchmark_subject { finder.execute(user).non_archived.to_a }
+
+ it { is_expected.to iterate_per_second(500) }
+ end
+end
diff --git a/spec/benchmarks/models/project_spec.rb b/spec/benchmarks/models/project_spec.rb
new file mode 100644
index 00000000000..cee0949edc5
--- /dev/null
+++ b/spec/benchmarks/models/project_spec.rb
@@ -0,0 +1,50 @@
+require 'spec_helper'
+
+describe Project, benchmark: true do
+ describe '.trending' do
+ let(:group) { create(:group) }
+ let(:project1) { create(:empty_project, :public, group: group) }
+ let(:project2) { create(:empty_project, :public, group: group) }
+
+ let(:iterations) { 500 }
+
+ before do
+ 2.times do
+ create(:note_on_commit, project: project1)
+ end
+
+ create(:note_on_commit, project: project2)
+ end
+
+ describe 'without an explicit start date' do
+ benchmark_subject { described_class.trending.to_a }
+
+ it { is_expected.to iterate_per_second(iterations) }
+ end
+
+ describe 'with an explicit start date' do
+ let(:date) { 1.month.ago }
+
+ benchmark_subject { described_class.trending(date).to_a }
+
+ it { is_expected.to iterate_per_second(iterations) }
+ end
+ end
+
+ describe '.find_with_namespace' do
+ let(:group) { create(:group, name: 'sisinmaru') }
+ let(:project) { create(:project, name: 'maru', namespace: group) }
+
+ describe 'using a capitalized namespace' do
+ benchmark_subject { described_class.find_with_namespace('sisinmaru/MARU') }
+
+ it { is_expected.to iterate_per_second(600) }
+ end
+
+ describe 'using a lowercased namespace' do
+ benchmark_subject { described_class.find_with_namespace('sisinmaru/maru') }
+
+ it { is_expected.to iterate_per_second(600) }
+ end
+ end
+end
diff --git a/spec/controllers/ci/commits_controller_spec.rb b/spec/controllers/ci/commits_controller_spec.rb
deleted file mode 100644
index cc39ce7687c..00000000000
--- a/spec/controllers/ci/commits_controller_spec.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-require "spec_helper"
-
-describe Ci::CommitsController do
- describe "GET /status" do
- it "returns status of commit" do
- commit = FactoryGirl.create :ci_commit
- get :status, id: commit.sha, ref_id: commit.ref, project_id: commit.project.id
-
- expect(response).to be_success
- expect(response.code).to eq('200')
- JSON.parse(response.body)["status"] == "pending"
- end
-
- it "returns not_found status" do
- commit = FactoryGirl.create :ci_commit
- get :status, id: commit.sha, ref_id: "deploy", project_id: commit.project.id
-
- expect(response).to be_success
- expect(response.code).to eq('200')
- JSON.parse(response.body)["status"] == "not_found"
- end
- end
-end
diff --git a/spec/controllers/projects/tree_controller_spec.rb b/spec/controllers/projects/tree_controller_spec.rb
index 53915856357..a474574c6e5 100644
--- a/spec/controllers/projects/tree_controller_spec.rb
+++ b/spec/controllers/projects/tree_controller_spec.rb
@@ -88,4 +88,40 @@ describe Projects::TreeController do
end
end
end
+
+ describe '#create_dir' do
+ render_views
+
+ before do
+ post(:create_dir,
+ namespace_id: project.namespace.to_param,
+ project_id: project.to_param,
+ id: 'master',
+ dir_name: path,
+ new_branch: target_branch,
+ commit_message: 'Test commit message')
+ end
+
+ context 'successful creation' do
+ let(:path) { 'files/new_dir'}
+ let(:target_branch) { 'master-test'}
+
+ it 'redirects to the new directory' do
+ expect(subject).
+ to redirect_to("/#{project.path_with_namespace}/blob/#{target_branch}/#{path}")
+ expect(flash[:notice]).to eq('The directory has been successfully created')
+ end
+ end
+
+ context 'unsuccessful creation' do
+ let(:path) { 'README.md' }
+ let(:target_branch) { 'master'}
+
+ it 'does not allow overwriting of existing files' do
+ expect(subject).
+ to redirect_to("/#{project.path_with_namespace}/blob/master")
+ expect(flash[:alert]).to eq('Directory already exists as a file')
+ end
+ end
+ end
end
diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb
index 99da5a18776..21b582afba4 100644
--- a/spec/factories/ci/builds.rb
+++ b/spec/factories/ci/builds.rb
@@ -27,6 +27,8 @@
FactoryGirl.define do
factory :ci_build, class: Ci::Build do
+ ref 'master'
+ tag false
started_at 'Di 29. Okt 09:51:28 CET 2013'
finished_at 'Di 29. Okt 09:53:28 CET 2013'
commands 'ls -a'
@@ -43,5 +45,9 @@ FactoryGirl.define do
started_at nil
finished_at nil
end
+
+ factory :ci_build_tag do
+ tag true
+ end
end
end
diff --git a/spec/factories/ci/commits.rb b/spec/factories/ci/commits.rb
index 9c7a0e9cbe0..79e000b7ccb 100644
--- a/spec/factories/ci/commits.rb
+++ b/spec/factories/ci/commits.rb
@@ -17,60 +17,32 @@
# Read about factories at https://github.com/thoughtbot/factory_girl
FactoryGirl.define do
- factory :ci_commit, class: Ci::Commit do
- ref 'master'
- before_sha '76de212e80737a608d939f648d959671fb0a0142'
+ factory :ci_empty_commit, class: Ci::Commit do
sha '97de212e80737a608d939f648d959671fb0a0142'
- push_data do
- {
- ref: 'refs/heads/master',
- before: '76de212e80737a608d939f648d959671fb0a0142',
- after: '97de212e80737a608d939f648d959671fb0a0142',
- user_name: 'Git User',
- user_email: 'git@example.com',
- repository: {
- name: 'test-data',
- url: 'ssh://git@gitlab.com/test/test-data.git',
- description: '',
- homepage: 'http://gitlab.com/test/test-data'
- },
- commits: [
- {
- id: '97de212e80737a608d939f648d959671fb0a0142',
- message: 'Test commit message',
- timestamp: '2014-09-23T13:12:25+02:00',
- url: 'https://gitlab.com/test/test-data/commit/97de212e80737a608d939f648d959671fb0a0142',
- author: {
- name: 'Git User',
- email: 'git@user.com'
- }
- }
- ],
- total_commits_count: 1,
- ci_yaml_file: File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml'))
- }
- end
gl_project factory: :empty_project
factory :ci_commit_without_jobs do
- after(:create) do |commit, evaluator|
- commit.push_data[:ci_yaml_file] = YAML.dump({})
- commit.save
+ after(:build) do |commit|
+ allow(commit).to receive(:ci_yaml_file) { YAML.dump({}) }
end
end
factory :ci_commit_with_one_job do
- after(:create) do |commit, evaluator|
- commit.push_data[:ci_yaml_file] = YAML.dump({ rspec: { script: "ls" } })
- commit.save
+ after(:build) do |commit|
+ allow(commit).to receive(:ci_yaml_file) { YAML.dump({ rspec: { script: "ls" } }) }
end
end
factory :ci_commit_with_two_jobs do
- after(:create) do |commit, evaluator|
- commit.push_data[:ci_yaml_file] = YAML.dump({ rspec: { script: "ls" }, spinach: { script: "ls" } })
- commit.save
+ after(:build) do |commit|
+ allow(commit).to receive(:ci_yaml_file) { YAML.dump({ rspec: { script: "ls" }, spinach: { script: "ls" } }) }
+ end
+ end
+
+ factory :ci_commit do
+ after(:build) do |commit|
+ allow(commit).to receive(:ci_yaml_file) { File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) }
end
end
end
diff --git a/spec/features/builds_spec.rb b/spec/features/builds_spec.rb
new file mode 100644
index 00000000000..924047a0d8f
--- /dev/null
+++ b/spec/features/builds_spec.rb
@@ -0,0 +1,41 @@
+require 'spec_helper'
+
+describe "Builds" do
+ before do
+ login_as(:user)
+ @commit = FactoryGirl.create :ci_commit
+ @build = FactoryGirl.create :ci_build, commit: @commit
+ @gl_project = @commit.project.gl_project
+ @gl_project.team << [@user, :master]
+ end
+
+ describe "GET /:project/builds/:id" do
+ before do
+ visit namespace_project_build_path(@gl_project.namespace, @gl_project, @build)
+ end
+
+ it { expect(page).to have_content @commit.sha[0..7] }
+ it { expect(page).to have_content @commit.git_commit_message }
+ it { expect(page).to have_content @commit.git_author_name }
+ end
+
+ describe "GET /:project/builds/:id/cancel" do
+ before do
+ @build.run!
+ visit cancel_namespace_project_build_path(@gl_project.namespace, @gl_project, @build)
+ end
+
+ it { expect(page).to have_content 'canceled' }
+ it { expect(page).to have_content 'Retry' }
+ end
+
+ describe "POST /:project/builds/:id/retry" do
+ before do
+ visit cancel_namespace_project_build_path(@gl_project.namespace, @gl_project, @build)
+ click_link 'Retry'
+ end
+
+ it { expect(page).to have_content 'pending' }
+ it { expect(page).to have_content 'Cancel' }
+ end
+end
diff --git a/spec/features/ci/builds_spec.rb b/spec/features/ci/builds_spec.rb
deleted file mode 100644
index d65699dbefa..00000000000
--- a/spec/features/ci/builds_spec.rb
+++ /dev/null
@@ -1,61 +0,0 @@
-require 'spec_helper'
-
-describe "Builds" do
- context :private_project do
- before do
- @commit = FactoryGirl.create :ci_commit
- @build = FactoryGirl.create :ci_build, commit: @commit
- login_as :user
- @commit.project.gl_project.team << [@user, :master]
- end
-
- describe "GET /:project/builds/:id" do
- before do
- visit ci_project_build_path(@commit.project, @build)
- end
-
- it { expect(page).to have_content @commit.sha[0..7] }
- it { expect(page).to have_content @commit.git_commit_message }
- it { expect(page).to have_content @commit.git_author_name }
- end
-
- describe "GET /:project/builds/:id/cancel" do
- before do
- @build.run!
- visit cancel_ci_project_build_path(@commit.project, @build)
- end
-
- it { expect(page).to have_content 'canceled' }
- it { expect(page).to have_content 'Retry' }
- end
-
- describe "POST /:project/builds/:id/retry" do
- before do
- @build.cancel!
- visit ci_project_build_path(@commit.project, @build)
- click_link 'Retry'
- end
-
- it { expect(page).to have_content 'pending' }
- it { expect(page).to have_content 'Cancel' }
- end
- end
-
- context :public_project do
- describe "Show page public accessible" do
- before do
- @commit = FactoryGirl.create :ci_commit
- @commit.project.public = true
- @commit.project.save
-
- @runner = FactoryGirl.create :ci_specific_runner
- @build = FactoryGirl.create :ci_build, commit: @commit, runner: @runner
-
- stub_gitlab_calls
- visit ci_project_build_path(@commit.project, @build)
- end
-
- it { expect(page).to have_content @commit.sha[0..7] }
- end
- end
-end
diff --git a/spec/features/ci/projects_spec.rb b/spec/features/ci/projects_spec.rb
deleted file mode 100644
index c633acf85fb..00000000000
--- a/spec/features/ci/projects_spec.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-require 'spec_helper'
-
-describe "Projects" do
- let(:user) { create(:user) }
-
- before do
- login_as(user)
- @project = FactoryGirl.create :ci_project, name: "GitLab / gitlab-shell"
- @project.gl_project.team << [user, :master]
- end
-
- describe "GET /ci/projects/:id" do
- before do
- visit ci_project_path(@project)
- end
-
- it { expect(page).to have_content @project.name }
- it { expect(page).to have_content 'All commits' }
- end
-end
diff --git a/spec/features/ci/commits_spec.rb b/spec/features/commits_spec.rb
index 657a9dabe9e..5da220859e3 100644
--- a/spec/features/ci/commits_spec.rb
+++ b/spec/features/commits_spec.rb
@@ -1,19 +1,26 @@
require 'spec_helper'
describe "Commits" do
- include Ci::CommitsHelper
+ include CiStatusHelper
- context "Authenticated user" do
+ let(:project) { create(:project) }
+
+ describe "CI" do
before do
- @commit = FactoryGirl.create :ci_commit
- @build = FactoryGirl.create :ci_build, commit: @commit
login_as :user
- @commit.project.gl_project.team << [@user, :master]
+ project.team << [@user, :master]
+ @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
+ end
+
+ before do
+ stub_ci_commit_to_return_yaml_file
end
describe "GET /:project/commits/:sha" do
before do
- visit ci_commit_path(@commit)
+ visit ci_status_path(@commit)
end
it { expect(page).to have_content @commit.sha[0..7] }
@@ -23,48 +30,23 @@ describe "Commits" do
describe "Cancel commit" do
it "cancels commit" do
- visit ci_commit_path(@commit)
+ visit ci_status_path(@commit)
click_on "Cancel"
-
expect(page).to have_content "canceled"
end
end
describe ".gitlab-ci.yml not found warning" do
it "does not show warning" do
- visit ci_commit_path(@commit)
-
+ visit ci_status_path(@commit)
expect(page).not_to have_content ".gitlab-ci.yml not found in this commit"
end
it "shows warning" do
- @commit.push_data[:ci_yaml_file] = nil
- @commit.save
-
- visit ci_commit_path(@commit)
-
+ stub_ci_commit_yaml_file(nil)
+ visit ci_status_path(@commit)
expect(page).to have_content ".gitlab-ci.yml not found in this commit"
end
end
end
-
- context "Public pages" do
- before do
- @commit = FactoryGirl.create :ci_commit
- @commit.project.public = true
- @commit.project.save
-
- @build = FactoryGirl.create :ci_build, commit: @commit
- end
-
- describe "GET /:project/commits/:sha" do
- before do
- visit ci_commit_path(@commit)
- end
-
- it { expect(page).to have_content @commit.sha[0..7] }
- it { expect(page).to have_content @commit.git_commit_message }
- it { expect(page).to have_content @commit.git_author_name }
- end
- end
end
diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb
index a362c54b9ad..aac93b17a38 100644
--- a/spec/features/projects_spec.rb
+++ b/spec/features/projects_spec.rb
@@ -50,7 +50,7 @@ feature 'Project', feature: true do
end
def remove_project
- click_link "Remove project"
+ click_button "Remove project"
fill_in 'confirm_name_input', with: project.path
click_button 'Confirm'
end
diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb
index db20b23f87d..b1648055462 100644
--- a/spec/finders/issues_finder_spec.rb
+++ b/spec/finders/issues_finder_spec.rb
@@ -6,9 +6,11 @@ describe IssuesFinder do
let(:project1) { create(:project) }
let(:project2) { create(:project) }
let(:milestone) { create(:milestone, project: project1) }
+ let(:label) { create(:label, project: project2) }
let(:issue1) { create(:issue, author: user, assignee: user, project: project1, milestone: milestone) }
let(:issue2) { create(:issue, author: user, assignee: user, project: project2) }
let(:issue3) { create(:issue, author: user2, assignee: user2, project: project2) }
+ let!(:label_link) { create(:label_link, label: label, target: issue2) }
before do
project1.team << [user, :master]
@@ -48,6 +50,24 @@ describe IssuesFinder do
expect(issues).to eq([issue1])
end
+ it 'should filter by no milestone id' do
+ params = { scope: "all", milestone_title: Milestone::None.title, state: 'opened' }
+ issues = IssuesFinder.new(user, params).execute
+ expect(issues).to match_array([issue2, issue3])
+ end
+
+ it 'should filter by label name' do
+ params = { scope: "all", label_name: label.title, state: 'opened' }
+ issues = IssuesFinder.new(user, params).execute
+ expect(issues).to eq([issue2])
+ end
+
+ it 'should filter by no label name' do
+ params = { scope: "all", label_name: Label::None.title, state: 'opened' }
+ issues = IssuesFinder.new(user, params).execute
+ expect(issues).to match_array([issue1, issue3])
+ end
+
it 'should be empty for unauthorized user' do
params = { scope: "all", state: 'opened' }
issues = IssuesFinder.new(nil, params).execute
diff --git a/spec/finders/trending_projects_finder_spec.rb b/spec/finders/trending_projects_finder_spec.rb
new file mode 100644
index 00000000000..a49cbfd5160
--- /dev/null
+++ b/spec/finders/trending_projects_finder_spec.rb
@@ -0,0 +1,39 @@
+require 'spec_helper'
+
+describe TrendingProjectsFinder do
+ let(:user) { build(:user) }
+
+ describe '#execute' do
+ describe 'without an explicit start date' do
+ subject { described_class.new }
+
+ it 'returns the trending projects' do
+ relation = double(:ar_relation)
+
+ allow(subject).to receive(:projects_for)
+ .with(user)
+ .and_return(relation)
+
+ allow(relation).to receive(:trending)
+ .with(an_instance_of(ActiveSupport::TimeWithZone))
+ end
+ end
+
+ describe 'with an explicit start date' do
+ let(:date) { 2.months.ago }
+
+ subject { described_class.new }
+
+ it 'returns the trending projects' do
+ relation = double(:ar_relation)
+
+ allow(subject).to receive(:projects_for)
+ .with(user)
+ .and_return(relation)
+
+ allow(relation).to receive(:trending)
+ .with(date)
+ end
+ end
+ end
+end
diff --git a/spec/helpers/labels_helper_spec.rb b/spec/helpers/labels_helper_spec.rb
index 0c8d06b7059..fb70a36dc02 100644
--- a/spec/helpers/labels_helper_spec.rb
+++ b/spec/helpers/labels_helper_spec.rb
@@ -14,6 +14,11 @@ 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/behaviors/quick_submit_spec.js.coffee b/spec/javascripts/behaviors/quick_submit_spec.js.coffee
new file mode 100644
index 00000000000..09708c12ed4
--- /dev/null
+++ b/spec/javascripts/behaviors/quick_submit_spec.js.coffee
@@ -0,0 +1,70 @@
+#= require behaviors/quick_submit
+
+describe 'Quick Submit behavior', ->
+ fixture.preload('behaviors/quick_submit.html')
+
+ beforeEach ->
+ fixture.load('behaviors/quick_submit.html')
+
+ # Prevent a form submit from moving us off the testing page
+ $('form').submit (e) -> e.preventDefault()
+
+ @spies = {
+ submit: spyOnEvent('form', 'submit')
+ }
+
+ it 'does not respond to other keyCodes', ->
+ $('input').trigger(keydownEvent(keyCode: 32))
+
+ expect(@spies.submit).not.toHaveBeenTriggered()
+
+ it 'does not respond to Enter alone', ->
+ $('input').trigger(keydownEvent(ctrlKey: false, metaKey: false))
+
+ expect(@spies.submit).not.toHaveBeenTriggered()
+
+ it 'does not respond to repeated events', ->
+ $('input').trigger(keydownEvent(repeat: true))
+
+ expect(@spies.submit).not.toHaveBeenTriggered()
+
+ it 'disables submit buttons', ->
+ $('textarea').trigger(keydownEvent())
+
+ expect($('input[type=submit]')).toBeDisabled()
+ expect($('button[type=submit]')).toBeDisabled()
+
+ # We cannot stub `navigator.userAgent` for CI's `rake teaspoon` task, so we'll
+ # only run the tests that apply to the current platform
+ if navigator.userAgent.match(/Macintosh/)
+ it 'responds to Meta+Enter', ->
+ $('input').trigger(keydownEvent())
+
+ expect(@spies.submit).toHaveBeenTriggered()
+
+ it 'excludes other modifier keys', ->
+ $('input').trigger(keydownEvent(altKey: true))
+ $('input').trigger(keydownEvent(ctrlKey: true))
+ $('input').trigger(keydownEvent(shiftKey: true))
+
+ expect(@spies.submit).not.toHaveBeenTriggered()
+ else
+ it 'responds to Ctrl+Enter', ->
+ $('input').trigger(keydownEvent())
+
+ expect(@spies.submit).toHaveBeenTriggered()
+
+ it 'excludes other modifier keys', ->
+ $('input').trigger(keydownEvent(altKey: true))
+ $('input').trigger(keydownEvent(metaKey: true))
+ $('input').trigger(keydownEvent(shiftKey: true))
+
+ expect(@spies.submit).not.toHaveBeenTriggered()
+
+ keydownEvent = (options) ->
+ if navigator.userAgent.match(/Macintosh/)
+ defaults = { keyCode: 13, metaKey: true }
+ else
+ defaults = { keyCode: 13, ctrlKey: true }
+
+ $.Event('keydown', $.extend({}, defaults, options))
diff --git a/spec/javascripts/fixtures/behaviors/quick_submit.html.haml b/spec/javascripts/fixtures/behaviors/quick_submit.html.haml
new file mode 100644
index 00000000000..b80a28a33ea
--- /dev/null
+++ b/spec/javascripts/fixtures/behaviors/quick_submit.html.haml
@@ -0,0 +1,6 @@
+%form{ action: '/foo' }
+ %input.js-quick-submit{ type: 'text' }
+ %textarea.js-quick-submit
+
+ %input{ type: 'submit'} Submit
+ %button.btn{ type: 'submit' } Submit
diff --git a/spec/javascripts/spec_helper.coffee b/spec/javascripts/spec_helper.coffee
index 47b41dd2c81..90b02a6aec5 100644
--- a/spec/javascripts/spec_helper.coffee
+++ b/spec/javascripts/spec_helper.coffee
@@ -9,6 +9,7 @@
# require the specific files that are being used in the spec that tests them.
#= require jquery
+#= require jquery.turbolinks
#= require bootstrap
#= require underscore
diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
index 49482ac2b12..aba957da488 100644
--- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
+++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
@@ -17,11 +17,12 @@ module Ci
expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(1)
expect(config_processor.builds_for_stage_and_ref(type, "master").first).to eq({
stage: "test",
+ stage_idx: 1,
except: nil,
name: :rspec,
only: nil,
- script: "pwd\nrspec",
- tags: [],
+ commands: "pwd\nrspec",
+ tag_list: [],
options: {},
allow_failure: false
})
@@ -115,10 +116,11 @@ module Ci
expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({
except: nil,
stage: "test",
+ stage_idx: 1,
name: :rspec,
only: nil,
- script: "pwd\nrspec",
- tags: [],
+ commands: "pwd\nrspec",
+ tag_list: [],
options: {
image: "ruby:2.1",
services: ["mysql"]
@@ -141,10 +143,11 @@ module Ci
expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({
except: nil,
stage: "test",
+ stage_idx: 1,
name: :rspec,
only: nil,
- script: "pwd\nrspec",
- tags: [],
+ commands: "pwd\nrspec",
+ tag_list: [],
options: {
image: "ruby:2.5",
services: ["postgresql"]
diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb
new file mode 100644
index 00000000000..7cdebdf209a
--- /dev/null
+++ b/spec/lib/gitlab/database_spec.rb
@@ -0,0 +1,17 @@
+require 'spec_helper'
+
+describe Gitlab::Database do
+ # These are just simple smoke tests to check if the methods work (regardless
+ # of what they may return).
+ describe '.mysql?' do
+ subject { described_class.mysql? }
+
+ it { is_expected.to satisfy { |val| val == true || val == false } }
+ end
+
+ describe '.postgresql?' do
+ subject { described_class.postgresql? }
+
+ it { is_expected.to satisfy { |val| val == true || val == false } }
+ end
+end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index ca070a14975..da56f6e31ae 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -30,9 +30,12 @@ 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? }
@@ -236,12 +239,6 @@ describe Ci::Build do
it { is_expected.to eq(options) }
end
- describe :ref do
- subject { build.ref }
-
- it { is_expected.to eq(commit.ref) }
- end
-
describe :sha do
subject { build.sha }
@@ -254,12 +251,6 @@ describe Ci::Build do
it { is_expected.to eq(commit.short_sha) }
end
- describe :before_sha do
- subject { build.before_sha }
-
- it { is_expected.to eq(commit.before_sha) }
- end
-
describe :allow_git_fetch do
subject { build.allow_git_fetch }
@@ -359,4 +350,38 @@ describe Ci::Build do
end
end
end
+
+ describe :project_recipients do
+ let(:pusher_email) { 'pusher@gitlab.test' }
+ let(:user) { User.new(notification_email: pusher_email) }
+ subject { build.project_recipients }
+
+ before do
+ build.update_attributes(user: user)
+ end
+
+ it 'should return pusher_email as only recipient when no additional recipients are given' do
+ project.update_attributes(email_add_pusher: true,
+ email_recipients: '')
+ is_expected.to eq([pusher_email])
+ end
+
+ it 'should return pusher_email and additional recipients' do
+ project.update_attributes(email_add_pusher: true,
+ email_recipients: 'rec1 rec2')
+ is_expected.to eq(['rec1', 'rec2', pusher_email])
+ end
+
+ it 'should return recipients' do
+ project.update_attributes(email_add_pusher: false,
+ email_recipients: 'rec1 rec2')
+ is_expected.to eq(['rec1', 'rec2'])
+ end
+
+ it 'should return unique recipients only' do
+ project.update_attributes(email_add_pusher: true,
+ email_recipients: "rec1 rec1 #{pusher_email}")
+ is_expected.to eq(['rec1', pusher_email])
+ end
+ end
end
diff --git a/spec/models/ci/commit_spec.rb b/spec/models/ci/commit_spec.rb
index c04bbcbadc7..acff1ddf0fc 100644
--- a/spec/models/ci/commit_spec.rb
+++ b/spec/models/ci/commit_spec.rb
@@ -21,15 +21,10 @@ describe Ci::Commit do
let(:project) { FactoryGirl.create :ci_project }
let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project }
let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project }
- let(:commit_with_project) { FactoryGirl.create :ci_commit, gl_project: gl_project }
- let(:config_processor) { Ci::GitlabCiYamlProcessor.new(gitlab_ci_yaml) }
it { is_expected.to belong_to(:gl_project) }
it { is_expected.to have_many(:builds) }
- it { is_expected.to validate_presence_of :before_sha }
it { is_expected.to validate_presence_of :sha }
- it { is_expected.to validate_presence_of :ref }
- it { is_expected.to validate_presence_of :push_data }
it { is_expected.to respond_to :git_author_name }
it { is_expected.to respond_to :git_author_email }
@@ -59,53 +54,6 @@ describe Ci::Commit do
end
end
- describe :project_recipients do
-
- context 'always sending notification' do
- it 'should return commit_pusher_email as only recipient when no additional recipients are given' do
- project = FactoryGirl.create :ci_project,
- email_add_pusher: true,
- email_recipients: ''
- gl_project = FactoryGirl.create :empty_project, gitlab_ci_project: project
- commit = FactoryGirl.create :ci_commit, gl_project: gl_project
- expected = 'commit_pusher_email'
- allow(commit).to receive(:push_data) { { user_email: expected } }
- expect(commit.project_recipients).to eq([expected])
- end
-
- it 'should return commit_pusher_email and additional recipients' do
- project = FactoryGirl.create :ci_project,
- email_add_pusher: true,
- email_recipients: 'rec1 rec2'
- gl_project = FactoryGirl.create :empty_project, gitlab_ci_project: project
- commit = FactoryGirl.create :ci_commit, gl_project: gl_project
- expected = 'commit_pusher_email'
- allow(commit).to receive(:push_data) { { user_email: expected } }
- expect(commit.project_recipients).to eq(['rec1', 'rec2', expected])
- end
-
- it 'should return recipients' do
- project = FactoryGirl.create :ci_project,
- email_add_pusher: false,
- email_recipients: 'rec1 rec2'
- gl_project = FactoryGirl.create :empty_project, gitlab_ci_project: project
- commit = FactoryGirl.create :ci_commit, gl_project: gl_project
- expect(commit.project_recipients).to eq(['rec1', 'rec2'])
- end
-
- it 'should return unique recipients only' do
- project = FactoryGirl.create :ci_project,
- email_add_pusher: true,
- email_recipients: 'rec1 rec1 rec2'
- gl_project = FactoryGirl.create :empty_project, gitlab_ci_project: project
- commit = FactoryGirl.create :ci_commit, gl_project: gl_project
- expected = 'rec2'
- allow(commit).to receive(:push_data) { { user_email: expected } }
- expect(commit.project_recipients).to eq(['rec1', 'rec2'])
- end
- end
- end
-
describe :valid_commit_sha do
context 'commit.sha can not start with 00000000' do
before do
@@ -117,63 +65,95 @@ describe Ci::Commit do
end
end
- describe :compare? do
- subject { commit_with_project.compare? }
-
- context 'if commit.before_sha are not nil' do
- it { is_expected.to be_truthy }
- end
- end
-
describe :short_sha do
- subject { commit.short_before_sha }
+ subject { commit.short_sha }
it 'has 8 items' do
expect(subject.size).to eq(8)
end
- it { expect(commit.before_sha).to start_with(subject) }
+ it { expect(commit.sha).to start_with(subject) }
end
- describe :short_sha do
- subject { commit.short_sha }
+ describe :stage do
+ subject { commit.stage }
- it 'has 8 items' do
- expect(subject.size).to eq(8)
+ 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
+ end
+
+ it 'returns first running stage' do
+ is_expected.to eq('test')
+ end
+
+ context 'first build succeeded' do
+ before do
+ @first.update_attributes(status: :success)
+ end
+
+ it 'returns last running stage' do
+ is_expected.to eq('deploy')
+ end
+ end
+
+ context 'all builds succeeded' do
+ before do
+ @first.update_attributes(status: :success)
+ @second.update_attributes(status: :success)
+ end
+
+ it 'returns nil' do
+ is_expected.to be_nil
+ end
end
- it { expect(commit.sha).to start_with(subject) }
end
describe :create_next_builds do
- before do
- allow(commit).to receive(:config_processor).and_return(config_processor)
+ end
+
+ describe :create_builds do
+ let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project }
+
+ def create_builds(trigger_request = nil)
+ commit.create_builds('master', false, nil, trigger_request)
+ end
+
+ def create_next_builds(trigger_request = nil)
+ commit.create_next_builds('master', false, nil, trigger_request)
end
- it "creates builds for next type" do
- expect(commit.create_builds).to be_truthy
+ it 'creates builds' do
+ expect(create_builds).to be_truthy
commit.builds.reload
expect(commit.builds.size).to eq(2)
- expect(commit.create_next_builds(nil)).to be_truthy
+ expect(create_next_builds).to be_truthy
commit.builds.reload
expect(commit.builds.size).to eq(4)
- expect(commit.create_next_builds(nil)).to be_truthy
+ expect(create_next_builds).to be_truthy
commit.builds.reload
expect(commit.builds.size).to eq(5)
- expect(commit.create_next_builds(nil)).to be_falsey
+ expect(create_next_builds).to be_falsey
end
- end
- describe :create_builds do
- before do
- allow(commit).to receive(:config_processor).and_return(config_processor)
- end
+ context 'for different ref' do
+ def create_develop_builds
+ commit.create_builds('develop', false, nil, nil)
+ end
- it 'creates builds' do
- expect(commit.create_builds).to be_truthy
- commit.builds.reload
- expect(commit.builds.size).to eq(2)
+ it 'creates builds' do
+ expect(create_builds).to be_truthy
+ commit.builds.reload
+ expect(commit.builds.size).to eq(2)
+
+ expect(create_develop_builds).to be_truthy
+ commit.builds.reload
+ expect(commit.builds.size).to eq(4)
+ expect(commit.refs.size).to eq(2)
+ expect(commit.builds.pluck(:name).uniq.size).to eq(2)
+ end
end
context 'for build triggers' do
@@ -181,40 +161,39 @@ describe Ci::Commit do
let(:trigger_request) { FactoryGirl.create :ci_trigger_request, commit: commit, trigger: trigger }
it 'creates builds' do
- expect(commit.create_builds(trigger_request)).to be_truthy
+ expect(create_builds(trigger_request)).to be_truthy
commit.builds.reload
expect(commit.builds.size).to eq(2)
end
it 'rebuilds commit' do
- expect(commit.create_builds).to be_truthy
+ expect(create_builds).to be_truthy
commit.builds.reload
expect(commit.builds.size).to eq(2)
- expect(commit.create_builds(trigger_request)).to be_truthy
+ expect(create_builds(trigger_request)).to be_truthy
commit.builds.reload
expect(commit.builds.size).to eq(4)
end
it 'creates next builds' do
- expect(commit.create_builds(trigger_request)).to be_truthy
+ expect(create_builds(trigger_request)).to be_truthy
commit.builds.reload
expect(commit.builds.size).to eq(2)
- expect(commit.create_next_builds(trigger_request)).to be_truthy
+ expect(create_next_builds(trigger_request)).to be_truthy
commit.builds.reload
expect(commit.builds.size).to eq(4)
end
context 'for [ci skip]' do
before do
- commit.push_data[:commits][0][:message] = 'skip this commit [ci skip]'
- commit.save
+ allow(commit).to receive(:git_commit_message) { 'message [ci skip]' }
end
it 'rebuilds commit' do
expect(commit.status).to eq('skipped')
- expect(commit.create_builds(trigger_request)).to be_truthy
+ expect(create_builds(trigger_request)).to be_truthy
commit.builds.reload
expect(commit.builds.size).to eq(2)
expect(commit.status).to eq('pending')
@@ -270,4 +249,59 @@ describe Ci::Commit do
expect(commit.coverage).to be_nil
end
end
+
+ 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
+ end
+
+ context 'for success' do
+ it 'to create if all succeeded' do
+ expect(commit.should_create_next_builds?(@build4)).to be_truthy
+ end
+ end
+
+ context 'for failed' do
+ before do
+ @build4.update_attributes(status: :failed)
+ end
+
+ it 'to not create' do
+ expect(commit.should_create_next_builds?(@build4)).to be_falsey
+ end
+
+ context 'and ignore failures for current' do
+ before do
+ @build4.update_attributes(allow_failure: true)
+ end
+
+ it 'to create' do
+ expect(commit.should_create_next_builds?(@build4)).to be_truthy
+ end
+ end
+ end
+
+ context 'for running' do
+ before do
+ @build4.update_attributes(status: :running)
+ end
+
+ it 'to not create' do
+ expect(commit.should_create_next_builds?(@build4)).to be_falsey
+ end
+ end
+
+ context 'for retried' do
+ before do
+ @build5 = FactoryGirl.create :ci_build, commit: commit, name: 'build4', ref: 'master', tag: false, status: :failed
+ end
+
+ it 'to not create' do
+ expect(commit.should_create_next_builds?(@build4)).to be_falsey
+ end
+ end
+ end
end
diff --git a/spec/models/ci/project_services/hip_chat_message_spec.rb b/spec/models/ci/project_services/hip_chat_message_spec.rb
index 1903c036924..e23d6ae2c28 100644
--- a/spec/models/ci/project_services/hip_chat_message_spec.rb
+++ b/spec/models/ci/project_services/hip_chat_message_spec.rb
@@ -3,70 +3,37 @@ require 'spec_helper'
describe Ci::HipChatMessage do
subject { Ci::HipChatMessage.new(build) }
- context "One build" do
- let(:commit) { FactoryGirl.create(:ci_commit_with_one_job) }
+ let(:commit) { FactoryGirl.create(:ci_commit_with_two_jobs) }
- let(:build) do
- commit.create_builds
- commit.builds.first
- end
-
- context 'when build succeeds' do
- it 'returns a successful message' do
- build.update(status: "success")
-
- expect( subject.status_color ).to eq 'green'
- expect( subject.notify? ).to be_falsey
- expect( subject.to_s ).to match(/Build '[^']+' #\d+/)
- expect( subject.to_s ).to match(/Successful in \d+ second\(s\)\./)
- end
- end
-
- context 'when build fails' do
- it 'returns a failure message' do
- build.update(status: "failed")
-
- expect( subject.status_color ).to eq 'red'
- expect( subject.notify? ).to be_truthy
- expect( subject.to_s ).to match(/Build '[^']+' #\d+/)
- expect( subject.to_s ).to match(/Failed in \d+ second\(s\)\./)
- end
- end
+ let(:build) do
+ commit.builds.first
end
- context "Several builds" do
- let(:commit) { FactoryGirl.create(:ci_commit_with_two_jobs) }
-
- let(:build) do
- commit.builds.first
- end
-
- context 'when all matrix builds succeed' do
- it 'returns a successful message' do
- commit.create_builds
- commit.builds.update_all(status: "success")
- commit.reload
+ context 'when all matrix builds succeed' do
+ it 'returns a successful message' do
+ commit.create_builds('master', false, nil)
+ commit.builds.update_all(status: "success")
+ commit.reload
- expect( subject.status_color ).to eq 'green'
- expect( subject.notify? ).to be_falsey
- expect( subject.to_s ).to match(/Commit #\d+/)
- expect( subject.to_s ).to match(/Successful in \d+ second\(s\)\./)
- end
+ expect(subject.status_color).to eq 'green'
+ expect(subject.notify?).to be_falsey
+ expect(subject.to_s).to match(/Commit #\d+/)
+ expect(subject.to_s).to match(/Successful in \d+ second\(s\)\./)
end
+ end
- context 'when at least one matrix build fails' do
- it 'returns a failure message' do
- commit.create_builds
- first_build = commit.builds.first
- second_build = commit.builds.last
- first_build.update(status: "success")
- second_build.update(status: "failed")
-
- expect( subject.status_color ).to eq 'red'
- expect( subject.notify? ).to be_truthy
- expect( subject.to_s ).to match(/Commit #\d+/)
- expect( subject.to_s ).to match(/Failed in \d+ second\(s\)\./)
- end
+ context 'when at least one matrix build fails' do
+ it 'returns a failure message' do
+ commit.create_builds('master', false, nil)
+ first_build = commit.builds.first
+ second_build = commit.builds.last
+ first_build.update(status: "success")
+ second_build.update(status: "failed")
+
+ expect(subject.status_color).to eq 'red'
+ expect(subject.notify?).to be_truthy
+ expect(subject.to_s).to match(/Commit #\d+/)
+ expect(subject.to_s).to match(/Failed in \d+ second\(s\)\./)
end
end
end
diff --git a/spec/models/ci/mail_service_spec.rb b/spec/models/ci/project_services/mail_service_spec.rb
index 0d9f85959ba..04e870dce7f 100644
--- a/spec/models/ci/mail_service_spec.rb
+++ b/spec/models/ci/project_services/mail_service_spec.rb
@@ -29,12 +29,13 @@ describe Ci::MailService do
describe 'Sends email for' do
let(:mail) { Ci::MailService.new }
+ let(:user) { User.new(notification_email: 'git@example.com')}
describe 'failed build' 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) }
+ let(:build) { FactoryGirl.create(:ci_build, status: :failed, commit: commit, user: user) }
before do
allow(mail).to receive_messages(
@@ -57,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) }
+ let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit, user: user) }
before do
allow(mail).to receive_messages(
@@ -85,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) }
+ let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit, user: user) }
before do
allow(mail).to receive_messages(
@@ -114,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) }
+ let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit, user: user) }
before do
allow(mail).to receive_messages(
@@ -143,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) }
+ let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit, user: user) }
before do
allow(mail).to receive_messages(
@@ -166,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) }
+ let(:build) { FactoryGirl.create(:ci_build, status: :failed, commit: commit, user: user) }
before do
allow(mail).to receive_messages(
diff --git a/spec/models/ci/project_services/slack_message_spec.rb b/spec/models/ci/project_services/slack_message_spec.rb
index 7b541802d7d..8adda6c86cc 100644
--- a/spec/models/ci/project_services/slack_message_spec.rb
+++ b/spec/models/ci/project_services/slack_message_spec.rb
@@ -3,80 +3,41 @@ require 'spec_helper'
describe Ci::SlackMessage do
subject { Ci::SlackMessage.new(commit) }
- context "One build" do
- let(:commit) { FactoryGirl.create(:ci_commit_with_one_job) }
+ let(:commit) { FactoryGirl.create(:ci_commit_with_two_jobs) }
- let(:build) do
- commit.create_builds
- commit.builds.first
- end
-
- context 'when build succeeded' do
- let(:color) { 'good' }
+ context 'when all matrix builds succeeded' do
+ let(:color) { 'good' }
- it 'returns a message with succeeded build' do
- build.update(status: "success")
+ it 'returns a message with success' do
+ commit.create_builds('master', false, nil)
+ commit.builds.update_all(status: "success")
+ commit.reload
- expect(subject.color).to eq(color)
- expect(subject.fallback).to include('Build')
- expect(subject.fallback).to include("\##{build.id}")
- expect(subject.fallback).to include('succeeded')
- expect(subject.attachments.first[:fields]).to be_empty
- end
- end
-
- context 'when build failed' do
- let(:color) { 'danger' }
-
- it 'returns a message with failed build' do
- build.update(status: "failed")
-
- expect(subject.color).to eq(color)
- expect(subject.fallback).to include('Build')
- expect(subject.fallback).to include("\##{build.id}")
- expect(subject.fallback).to include('failed')
- expect(subject.attachments.first[:fields]).to be_empty
- end
+ expect(subject.color).to eq(color)
+ expect(subject.fallback).to include('Commit')
+ expect(subject.fallback).to include("\##{commit.id}")
+ expect(subject.fallback).to include('succeeded')
+ expect(subject.attachments.first[:fields]).to be_empty
end
end
- context "Several builds" do
- let(:commit) { FactoryGirl.create(:ci_commit_with_two_jobs) }
-
- context 'when all matrix builds succeeded' do
- let(:color) { 'good' }
-
- it 'returns a message with success' do
- commit.create_builds
- commit.builds.update_all(status: "success")
- commit.reload
-
- expect(subject.color).to eq(color)
- expect(subject.fallback).to include('Commit')
- expect(subject.fallback).to include("\##{commit.id}")
- expect(subject.fallback).to include('succeeded')
- expect(subject.attachments.first[:fields]).to be_empty
- end
- end
-
- context 'when one of matrix builds failed' do
- let(:color) { 'danger' }
-
- it 'returns a message with information about failed build' do
- commit.create_builds
- first_build = commit.builds.first
- second_build = commit.builds.last
- first_build.update(status: "success")
- second_build.update(status: "failed")
-
- expect(subject.color).to eq(color)
- expect(subject.fallback).to include('Commit')
- expect(subject.fallback).to include("\##{commit.id}")
- expect(subject.fallback).to include('failed')
- expect(subject.attachments.first[:fields].size).to eq(1)
- expect(subject.attachments.first[:fields].first[:title]).to eq(second_build.name)
- expect(subject.attachments.first[:fields].first[:value]).to include("\##{second_build.id}")
- end
+ context 'when one of matrix builds failed' do
+ let(:color) { 'danger' }
+
+ it 'returns a message with information about failed build' do
+ commit.create_builds('master', false, nil)
+ first_build = commit.builds.first
+ second_build = commit.builds.last
+ first_build.update(status: "success")
+ second_build.update(status: "failed")
+
+ expect(subject.color).to eq(color)
+ expect(subject.fallback).to include('Commit')
+ expect(subject.fallback).to include("\##{commit.id}")
+ expect(subject.fallback).to include('failed')
+ expect(subject.attachments.first[:fields].size).to eq(1)
+ expect(subject.attachments.first[:fields].first[:title]).to eq(second_build.name)
+ expect(subject.attachments.first[:fields].first[:value]).to include("\##{second_build.id}")
end
end
end
diff --git a/spec/models/concerns/case_sensitivity_spec.rb b/spec/models/concerns/case_sensitivity_spec.rb
new file mode 100644
index 00000000000..f7ed30f8198
--- /dev/null
+++ b/spec/models/concerns/case_sensitivity_spec.rb
@@ -0,0 +1,189 @@
+require 'spec_helper'
+
+describe CaseSensitivity do
+ describe '.iwhere' do
+ let(:connection) { ActiveRecord::Base.connection }
+ let(:model) { Class.new { include CaseSensitivity } }
+
+ describe 'using PostgreSQL' do
+ before do
+ allow(Gitlab::Database).to receive(:postgresql?).and_return(true)
+ allow(Gitlab::Database).to receive(:mysql?).and_return(false)
+ end
+
+ describe 'with a single column/value pair' do
+ it 'returns the criteria for a column and a value' do
+ criteria = double(:criteria)
+
+ expect(connection).to receive(:quote_table_name).
+ with(:foo).
+ and_return('"foo"')
+
+ expect(model).to receive(:where).
+ with(%q{LOWER("foo") = LOWER(:value)}, value: 'bar').
+ and_return(criteria)
+
+ expect(model.iwhere(foo: 'bar')).to eq(criteria)
+ end
+
+ it 'returns the criteria for a column with a table, and a value' do
+ criteria = double(:criteria)
+
+ expect(connection).to receive(:quote_table_name).
+ with(:'foo.bar').
+ and_return('"foo"."bar"')
+
+ expect(model).to receive(:where).
+ with(%q{LOWER("foo"."bar") = LOWER(:value)}, value: 'bar').
+ and_return(criteria)
+
+ expect(model.iwhere(:'foo.bar' => 'bar')).to eq(criteria)
+ end
+ end
+
+ describe 'with multiple column/value pairs' do
+ it 'returns the criteria for a column and a value' do
+ initial = double(:criteria)
+ final = double(:criteria)
+
+ expect(connection).to receive(:quote_table_name).
+ with(:foo).
+ and_return('"foo"')
+
+ expect(connection).to receive(:quote_table_name).
+ with(:bar).
+ and_return('"bar"')
+
+ expect(model).to receive(:where).
+ with(%q{LOWER("foo") = LOWER(:value)}, value: 'bar').
+ and_return(initial)
+
+ expect(initial).to receive(:where).
+ with(%q{LOWER("bar") = LOWER(:value)}, value: 'baz').
+ and_return(final)
+
+ got = model.iwhere(foo: 'bar', bar: 'baz')
+
+ expect(got).to eq(final)
+ end
+
+ it 'returns the criteria for a column with a table, and a value' do
+ initial = double(:criteria)
+ final = double(:criteria)
+
+ expect(connection).to receive(:quote_table_name).
+ with(:'foo.bar').
+ and_return('"foo"."bar"')
+
+ expect(connection).to receive(:quote_table_name).
+ with(:'foo.baz').
+ and_return('"foo"."baz"')
+
+ expect(model).to receive(:where).
+ with(%q{LOWER("foo"."bar") = LOWER(:value)}, value: 'bar').
+ and_return(initial)
+
+ expect(initial).to receive(:where).
+ with(%q{LOWER("foo"."baz") = LOWER(:value)}, value: 'baz').
+ and_return(final)
+
+ got = model.iwhere(:'foo.bar' => 'bar',
+ :'foo.baz' => 'baz')
+
+ expect(got).to eq(final)
+ end
+ end
+ end
+
+ describe 'using MySQL' do
+ before do
+ allow(Gitlab::Database).to receive(:postgresql?).and_return(false)
+ allow(Gitlab::Database).to receive(:mysql?).and_return(true)
+ end
+
+ describe 'with a single column/value pair' do
+ it 'returns the criteria for a column and a value' do
+ criteria = double(:criteria)
+
+ expect(connection).to receive(:quote_table_name).
+ with(:foo).
+ and_return('`foo`')
+
+ expect(model).to receive(:where).
+ with(%q{`foo` = :value}, value: 'bar').
+ and_return(criteria)
+
+ expect(model.iwhere(foo: 'bar')).to eq(criteria)
+ end
+
+ it 'returns the criteria for a column with a table, and a value' do
+ criteria = double(:criteria)
+
+ expect(connection).to receive(:quote_table_name).
+ with(:'foo.bar').
+ and_return('`foo`.`bar`')
+
+ expect(model).to receive(:where).
+ with(%q{`foo`.`bar` = :value}, value: 'bar').
+ and_return(criteria)
+
+ expect(model.iwhere(:'foo.bar' => 'bar')).
+ to eq(criteria)
+ end
+ end
+
+ describe 'with multiple column/value pairs' do
+ it 'returns the criteria for a column and a value' do
+ initial = double(:criteria)
+ final = double(:criteria)
+
+ expect(connection).to receive(:quote_table_name).
+ with(:foo).
+ and_return('`foo`')
+
+ expect(connection).to receive(:quote_table_name).
+ with(:bar).
+ and_return('`bar`')
+
+ expect(model).to receive(:where).
+ with(%q{`foo` = :value}, value: 'bar').
+ and_return(initial)
+
+ expect(initial).to receive(:where).
+ with(%q{`bar` = :value}, value: 'baz').
+ and_return(final)
+
+ got = model.iwhere(foo: 'bar', bar: 'baz')
+
+ expect(got).to eq(final)
+ end
+
+ it 'returns the criteria for a column with a table, and a value' do
+ initial = double(:criteria)
+ final = double(:criteria)
+
+ expect(connection).to receive(:quote_table_name).
+ with(:'foo.bar').
+ and_return('`foo`.`bar`')
+
+ expect(connection).to receive(:quote_table_name).
+ with(:'foo.baz').
+ and_return('`foo`.`baz`')
+
+ expect(model).to receive(:where).
+ with(%q{`foo`.`bar` = :value}, value: 'bar').
+ and_return(initial)
+
+ expect(initial).to receive(:where).
+ with(%q{`foo`.`baz` = :value}, value: 'baz').
+ and_return(final)
+
+ got = model.iwhere(:'foo.bar' => 'bar',
+ :'foo.baz' => 'baz')
+
+ expect(got).to eq(final)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/models/project_services/gitlab_ci_service_spec.rb b/spec/models/project_services/gitlab_ci_service_spec.rb
index 683a403a907..842089ebe0d 100644
--- a/spec/models/project_services/gitlab_ci_service_spec.rb
+++ b/spec/models/project_services/gitlab_ci_service_spec.rb
@@ -39,8 +39,7 @@ describe GitlabCiService do
end
describe :build_page do
- it { expect(@service.build_page("2ab7834c", 'master')).to eq("http://localhost/ci/projects/#{@ci_project.id}/refs/master/commits/2ab7834c")}
- it { expect(@service.build_page("issue#2", 'master')).to eq("http://localhost/ci/projects/#{@ci_project.id}/refs/master/commits/issue%232")}
+ it { expect(@service.build_page("2ab7834c", 'master')).to eq("http://localhost/#{@ci_project.gl_project.path_with_namespace}/commit/2ab7834c/ci")}
end
describe "execute" do
@@ -48,8 +47,8 @@ describe GitlabCiService do
let(:project) { create(:project, name: 'project') }
let(:push_sample_data) { Gitlab::PushDataBuilder.build_sample(project, user) }
- it "calls ci_yaml_file" do
- expect(@service).to receive(:ci_yaml_file).with(push_sample_data[:checkout_sha])
+ it "calls CreateCommitService" do
+ expect_any_instance_of(Ci::CreateCommitService).to receive(:execute).with(@ci_project, user, push_sample_data)
@service.execute(push_sample_data)
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 8b5d2c3a1c1..f93935ebe3b 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -423,4 +423,42 @@ describe Project do
it { expect(project.gitlab_ci?).to be_truthy }
it { expect(project.gitlab_ci_project).to be_a(Ci::Project) }
end
+
+ describe '.trending' do
+ let(:group) { create(:group) }
+ let(:project1) { create(:empty_project, :public, group: group) }
+ let(:project2) { create(:empty_project, :public, group: group) }
+
+ before do
+ 2.times do
+ create(:note_on_commit, project: project1)
+ end
+
+ create(:note_on_commit, project: project2)
+ end
+
+ describe 'without an explicit start date' do
+ subject { described_class.trending.to_a }
+
+ it 'sorts Projects by the amount of notes in descending order' do
+ expect(subject).to eq([project1, project2])
+ end
+ end
+
+ describe 'with an explicit start date' do
+ let(:date) { 2.months.ago }
+
+ subject { described_class.trending(date).to_a }
+
+ before do
+ 2.times do
+ create(:note_on_commit, project: project2, created_at: date)
+ end
+ end
+
+ it 'sorts Projects by the amount of notes in descending order' do
+ expect(subject).to eq([project2, project1])
+ end
+ end
+ end
end
diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb
index bad250fbf48..54c1d0199f6 100644
--- a/spec/requests/ci/api/builds_spec.rb
+++ b/spec/requests/ci/api/builds_spec.rb
@@ -7,6 +7,10 @@ describe Ci::API::API do
let(:project) { FactoryGirl.create(:ci_project) }
let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
+ before do
+ stub_ci_commit_to_return_yaml_file
+ end
+
describe "Builds API for runners" do
let(:shared_runner) { FactoryGirl.create(:ci_runner, token: "SharedRunner") }
let(:shared_project) { FactoryGirl.create(:ci_project, name: "SharedProject") }
@@ -19,7 +23,7 @@ describe Ci::API::API do
describe "POST /builds/register" do
it "should start a build" do
commit = FactoryGirl.create(:ci_commit, gl_project: gl_project)
- commit.create_builds
+ commit.create_builds('master', false, nil)
build = commit.builds.first
post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin }
@@ -55,7 +59,7 @@ describe Ci::API::API do
it "returns options" do
commit = FactoryGirl.create(:ci_commit, gl_project: gl_project)
- commit.create_builds
+ commit.create_builds('master', false, nil)
post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin }
@@ -65,7 +69,7 @@ describe Ci::API::API do
it "returns variables" do
commit = FactoryGirl.create(:ci_commit, gl_project: gl_project)
- commit.create_builds
+ commit.create_builds('master', false, nil)
project.variables << Ci::Variable.new(key: "SECRET_KEY", value: "secret_value")
post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin }
@@ -82,7 +86,7 @@ describe Ci::API::API do
commit = FactoryGirl.create(:ci_commit, gl_project: gl_project)
trigger_request = FactoryGirl.create(:ci_trigger_request_with_variables, commit: commit, trigger: trigger)
- commit.create_builds(trigger_request)
+ commit.create_builds('master', false, nil, trigger_request)
project.variables << Ci::Variable.new(key: "SECRET_KEY", value: "secret_value")
post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin }
diff --git a/spec/requests/ci/api/commits_spec.rb b/spec/requests/ci/api/commits_spec.rb
index a41c321a300..6049135fd10 100644
--- a/spec/requests/ci/api/commits_spec.rb
+++ b/spec/requests/ci/api/commits_spec.rb
@@ -44,8 +44,7 @@ describe Ci::API::API, 'Commits' do
"email" => "jordi@softcatala.org",
}
}
- ],
- ci_yaml_file: gitlab_ci_yaml
+ ]
}
end
diff --git a/spec/requests/ci/api/triggers_spec.rb b/spec/requests/ci/api/triggers_spec.rb
index bbe98e7dacd..93617fc4b3f 100644
--- a/spec/requests/ci/api/triggers_spec.rb
+++ b/spec/requests/ci/api/triggers_spec.rb
@@ -5,8 +5,8 @@ describe Ci::API::API do
describe 'POST /projects/:project_id/refs/:ref/trigger' do
let!(:trigger_token) { 'secure token' }
- let!(:project) { FactoryGirl.create(:ci_project) }
- let!(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
+ let!(:gl_project) { FactoryGirl.create(:project) }
+ let!(:project) { FactoryGirl.create(:ci_project, gl_project: gl_project) }
let!(:project2) { FactoryGirl.create(:ci_project) }
let!(:trigger) { FactoryGirl.create(:ci_trigger, project: project, token: trigger_token) }
let(:options) do
@@ -15,6 +15,10 @@ describe Ci::API::API do
}
end
+ before do
+ stub_ci_commit_to_return_yaml_file
+ end
+
context 'Handles errors' do
it 'should return bad request if token is missing' do
post ci_api("/projects/#{project.id}/refs/master/trigger")
@@ -33,15 +37,13 @@ describe Ci::API::API do
end
context 'Have a commit' do
- before do
- @commit = FactoryGirl.create(:ci_commit, gl_project: gl_project)
- end
+ let(:commit) { project.commits.last }
it 'should create builds' do
post ci_api("/projects/#{project.id}/refs/master/trigger"), options
expect(response.status).to eq(201)
- @commit.builds.reload
- expect(@commit.builds.size).to eq(2)
+ commit.builds.reload
+ expect(commit.builds.size).to eq(2)
end
it 'should return bad request with no builds created if there\'s no commit for that ref' do
@@ -70,8 +72,8 @@ describe Ci::API::API do
it 'create trigger request with variables' do
post ci_api("/projects/#{project.id}/refs/master/trigger"), options.merge(variables: variables)
expect(response.status).to eq(201)
- @commit.builds.reload
- expect(@commit.builds.first.trigger_request.variables).to eq(variables)
+ commit.builds.reload
+ expect(commit.builds.first.trigger_request.variables).to eq(variables)
end
end
end
diff --git a/spec/requests/ci/builds_spec.rb b/spec/requests/ci/builds_spec.rb
deleted file mode 100644
index f68116c52aa..00000000000
--- a/spec/requests/ci/builds_spec.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-require 'spec_helper'
-
-describe "Builds" do
- before do
- @commit = FactoryGirl.create :ci_commit
- @build = FactoryGirl.create :ci_build, commit: @commit
- end
-
- describe "GET /:project/builds/:id/status.json" do
- before do
- get status_ci_project_build_path(@commit.project, @build), format: :json
- end
-
- it { expect(response.status).to eq(200) }
- it { expect(response.body).to include(@build.sha) }
- end
-end
diff --git a/spec/requests/ci/commits_spec.rb b/spec/requests/ci/commits_spec.rb
deleted file mode 100644
index 3ab8c915dfd..00000000000
--- a/spec/requests/ci/commits_spec.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-require 'spec_helper'
-
-describe "Commits" do
- before do
- @commit = FactoryGirl.create :ci_commit
- end
-
- describe "GET /:project/refs/:ref_name/commits/:id/status.json" do
- before do
- get status_ci_project_ref_commits_path(@commit.project, @commit.ref, @commit.sha), format: :json
- end
-
- it { expect(response.status).to eq(200) }
- it { expect(response.body).to include(@commit.sha) }
- end
-end
diff --git a/spec/services/ci/create_commit_service_spec.rb b/spec/services/ci/create_commit_service_spec.rb
index 84ab0a615dd..e3a8fe9681b 100644
--- a/spec/services/ci/create_commit_service_spec.rb
+++ b/spec/services/ci/create_commit_service_spec.rb
@@ -4,15 +4,19 @@ module Ci
describe CreateCommitService do
let(:service) { CreateCommitService.new }
let(:project) { FactoryGirl.create(:ci_project) }
+ let(:user) { nil }
+
+ before do
+ stub_ci_commit_to_return_yaml_file
+ end
describe :execute do
context 'valid params' do
let(:commit) do
- service.execute(project,
+ service.execute(project, user,
ref: 'refs/heads/master',
before: '00000000',
after: '31das312',
- ci_yaml_file: gitlab_ci_yaml,
commits: [ { message: "Message" } ]
)
end
@@ -26,11 +30,10 @@ module Ci
context "skip tag if there is no build for it" do
it "creates commit if there is appropriate job" do
- result = service.execute(project,
+ result = service.execute(project, user,
ref: 'refs/tags/0_1',
before: '00000000',
after: '31das312',
- ci_yaml_file: gitlab_ci_yaml,
commits: [ { message: "Message" } ]
)
expect(result).to be_persisted
@@ -38,12 +41,12 @@ module Ci
it "creates commit if there is no appropriate job but deploy job has right ref setting" do
config = YAML.dump({ deploy: { deploy: "ls", only: ["0_1"] } })
+ stub_ci_commit_yaml_file(config)
- result = service.execute(project,
+ result = service.execute(project, user,
ref: 'refs/heads/0_1',
before: '00000000',
after: '31das312',
- ci_yaml_file: config,
commits: [ { message: "Message" } ]
)
expect(result).to be_persisted
@@ -51,11 +54,11 @@ module Ci
end
it 'fails commits without .gitlab-ci.yml' do
- result = service.execute(project,
+ stub_ci_commit_yaml_file(nil)
+ result = service.execute(project, user,
ref: 'refs/heads/0_1',
before: '00000000',
after: '31das312',
- ci_yaml_file: config,
commits: [ { message: 'Message' } ]
)
expect(result).to be_persisted
@@ -64,41 +67,46 @@ module Ci
end
describe :ci_skip? do
+ let(:message) { "some message[ci skip]" }
+
+ before do
+ allow_any_instance_of(Ci::Commit).to receive(:git_commit_message) { message }
+ end
+
it "skips builds creation if there is [ci skip] tag in commit message" do
- commits = [{ message: "some message[ci skip]" }]
- commit = service.execute(project,
+ commits = [{ message: message }]
+ commit = service.execute(project, user,
ref: 'refs/tags/0_1',
before: '00000000',
after: '31das312',
- commits: commits,
- ci_yaml_file: gitlab_ci_yaml
+ commits: commits
)
expect(commit.builds.any?).to be false
expect(commit.status).to eq("skipped")
end
it "does not skips builds creation if there is no [ci skip] tag in commit message" do
- commits = [{ message: "some message" }]
+ allow_any_instance_of(Ci::Commit).to receive(:git_commit_message) { "some message" }
- commit = service.execute(project,
+ commits = [{ message: "some message" }]
+ commit = service.execute(project, user,
ref: 'refs/tags/0_1',
before: '00000000',
after: '31das312',
- commits: commits,
- ci_yaml_file: gitlab_ci_yaml
+ commits: commits
)
expect(commit.builds.first.name).to eq("staging")
end
it "skips builds creation if there is [ci skip] tag in commit message and yaml is invalid" do
- commits = [{ message: "some message[ci skip]" }]
- commit = service.execute(project,
+ stub_ci_commit_yaml_file('invalid: file')
+ commits = [{ message: message }]
+ commit = service.execute(project, user,
ref: 'refs/tags/0_1',
before: '00000000',
after: '31das312',
- commits: commits,
- ci_yaml_file: "invalid: file"
+ commits: commits
)
expect(commit.builds.any?).to be false
expect(commit.status).to eq("skipped")
@@ -106,35 +114,36 @@ module Ci
end
it "skips build creation if there are already builds" do
+ allow_any_instance_of(Ci::Commit).to receive(:ci_yaml_file) { gitlab_ci_yaml }
+
commits = [{ message: "message" }]
- commit = service.execute(project,
+ commit = service.execute(project, user,
ref: 'refs/heads/master',
before: '00000000',
after: '31das312',
- commits: commits,
- ci_yaml_file: gitlab_ci_yaml
+ commits: commits
)
expect(commit.builds.count(:all)).to eq(2)
- commit = service.execute(project,
+ commit = service.execute(project, user,
ref: 'refs/heads/master',
before: '00000000',
after: '31das312',
- commits: commits,
- ci_yaml_file: gitlab_ci_yaml
+ commits: commits
)
expect(commit.builds.count(:all)).to eq(2)
end
it "creates commit with failed status if yaml is invalid" do
+ stub_ci_commit_yaml_file('invalid: file')
+
commits = [{ message: "some message" }]
- commit = service.execute(project,
+ commit = service.execute(project, user,
ref: 'refs/tags/0_1',
before: '00000000',
after: '31das312',
- commits: commits,
- ci_yaml_file: "invalid: file"
+ commits: commits
)
expect(commit.status).to eq("failed")
diff --git a/spec/services/ci/create_trigger_request_service_spec.rb b/spec/services/ci/create_trigger_request_service_spec.rb
index 525a24cc200..fcafae38644 100644
--- a/spec/services/ci/create_trigger_request_service_spec.rb
+++ b/spec/services/ci/create_trigger_request_service_spec.rb
@@ -2,20 +2,20 @@ require 'spec_helper'
describe Ci::CreateTriggerRequestService do
let(:service) { Ci::CreateTriggerRequestService.new }
- let(:project) { FactoryGirl.create :ci_project }
- let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project }
- let(:trigger) { FactoryGirl.create :ci_trigger, project: project }
+ let(:gl_project) { create(:project) }
+ let(:project) { create(:ci_project, gl_project: gl_project) }
+ let(:trigger) { create(:ci_trigger, project: project) }
+
+ before do
+ stub_ci_commit_to_return_yaml_file
+ end
describe :execute do
context 'valid params' do
subject { service.execute(project, trigger, 'master') }
- before do
- @commit = FactoryGirl.create :ci_commit, gl_project: gl_project
- end
-
it { expect(subject).to be_kind_of(Ci::TriggerRequest) }
- it { expect(subject.commit).to eq(@commit) }
+ it { expect(subject.builds.first).to be_kind_of(Ci::Build) }
end
context 'no commit for ref' do
@@ -28,26 +28,11 @@ describe Ci::CreateTriggerRequestService do
subject { service.execute(project, trigger, 'master') }
before do
- FactoryGirl.create :ci_commit_without_jobs, gl_project: gl_project
+ stub_ci_commit_yaml_file('{}')
+ FactoryGirl.create :ci_commit, gl_project: gl_project
end
it { expect(subject).to be_nil }
end
-
- context 'for multiple commits' do
- subject { service.execute(project, trigger, 'master') }
-
- before do
- @commit1 = FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, gl_project: gl_project
- @commit2 = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, gl_project: gl_project
- @commit3 = FactoryGirl.create :ci_commit, committed_at: 3.hour.ago, gl_project: gl_project
- end
-
- context 'retries latest one' do
- it { expect(subject).to be_kind_of(Ci::TriggerRequest) }
- it { expect(subject).to be_persisted }
- it { expect(subject.commit).to eq(@commit2) }
- end
- end
end
end
diff --git a/spec/services/system_hooks_service_spec.rb b/spec/services/system_hooks_service_spec.rb
index 48c49e2f717..a31fc1e4b07 100644
--- a/spec/services/system_hooks_service_spec.rb
+++ b/spec/services/system_hooks_service_spec.rb
@@ -13,8 +13,8 @@ describe SystemHooksService do
it { expect(event_data(user, :destroy)).to include(:event_name, :name, :created_at, :email, :user_id) }
it { expect(event_data(project, :create)).to include(:event_name, :name, :created_at, :path, :project_id, :owner_name, :owner_email, :project_visibility) }
it { expect(event_data(project, :destroy)).to include(:event_name, :name, :created_at, :path, :project_id, :owner_name, :owner_email, :project_visibility) }
- it { expect(event_data(project_member, :create)).to include(:event_name, :created_at, :project_name, :project_path, :project_id, :user_name, :user_email, :access_level, :project_visibility) }
- it { expect(event_data(project_member, :destroy)).to include(:event_name, :created_at, :project_name, :project_path, :project_id, :user_name, :user_email, :access_level, :project_visibility) }
+ it { expect(event_data(project_member, :create)).to include(:event_name, :created_at, :project_name, :project_path, :project_path_with_namespace, :project_id, :user_name, :user_email, :access_level, :project_visibility) }
+ it { expect(event_data(project_member, :destroy)).to include(:event_name, :created_at, :project_name, :project_path, :project_path_with_namespace, :project_id, :user_name, :user_email, :access_level, :project_visibility) }
it { expect(event_data(key, :create)).to include(:username, :key, :id) }
it { expect(event_data(key, :destroy)).to include(:username, :key, :id) }
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 05bb32baa90..2be13bb3e6a 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -43,4 +43,8 @@ RSpec.configure do |config|
end
end
+FactoryGirl::SyntaxRunner.class_eval do
+ include RSpec::Mocks::ExampleMethods
+end
+
ActiveRecord::Migration.maintain_test_schema!
diff --git a/spec/support/stub_gitlab_calls.rb b/spec/support/stub_gitlab_calls.rb
index 5e6744afda1..5b3eb1bfc5f 100644
--- a/spec/support/stub_gitlab_calls.rb
+++ b/spec/support/stub_gitlab_calls.rb
@@ -13,6 +13,14 @@ module StubGitlabCalls
allow_any_instance_of(Network).to receive(:projects) { project_hash_array }
end
+ def stub_ci_commit_to_return_yaml_file
+ stub_ci_commit_yaml_file(gitlab_ci_yaml)
+ end
+
+ def stub_ci_commit_yaml_file(ci_yaml)
+ allow_any_instance_of(Ci::Commit).to receive(:ci_yaml_file) { ci_yaml }
+ end
+
private
def gitlab_url
diff --git a/tmp/.gitkeep b/tmp/.gitkeep
deleted file mode 100644
index e69de29bb2d..00000000000
--- a/tmp/.gitkeep
+++ /dev/null