summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLin Jen-Shin <godfat@godfat.org>2016-07-21 01:24:05 +0800
committerLin Jen-Shin <godfat@godfat.org>2016-07-21 01:24:05 +0800
commite5b05fe8c0e773d850c314b8317e5fa2eb75379d (patch)
tree93d05868c60cd3b3316f63f54881abc07561b6e8
parent122b046b92573f3e1db579b5ff73fa3bdfffc124 (diff)
parentb4717017e7ad601eaa1d53c9238a242c7fdf0daa (diff)
downloadgitlab-ce-e5b05fe8c0e773d850c314b8317e5fa2eb75379d.tar.gz
Merge branch 'master' into artifacts-from-ref-and-build-name-api
* master: (261 commits) Add link to user profile to commit avatar (!5163) Refactor service settings view Fix a problem with processing a pipeline where stage only has manual actions A CHANGELOG entry Don't show other actions of the same name Use limit parameter rather than hardcoded value Remove icons from explore nav Change how we style redirect_to Use flash[:notice] only Create PipelinesSettingsController for showing settings page Fix a few nitpicks Allow to disable user request access to groups/projects Enable Style/MultilineTernaryOperator rubocop cop Fix review comments Update routes Update CHANGELOG Improve implementation of variables Log cron_jobs configuration instead of raising exception Added checks for migration downtime Ensure to_json methods take optional argument ...
-rw-r--r--.gitlab-ci.yml3
-rw-r--r--.rubocop.yml4
-rw-r--r--.rubocop_todo.yml4
-rw-r--r--CHANGELOG18
-rw-r--r--Gemfile.lock2
-rw-r--r--app/assets/javascripts/graphs/graphs_bundle.js.coffee (renamed from app/assets/javascripts/graphs/application.js.coffee)0
-rw-r--r--app/assets/javascripts/network/network_bundle.js.coffee (renamed from app/assets/javascripts/network/application.js.coffee)0
-rw-r--r--app/assets/javascripts/profile/profile_bundle.js.coffee (renamed from app/assets/javascripts/profile/application.js.coffee)0
-rw-r--r--app/assets/javascripts/users/users_bundle.js.coffee (renamed from app/assets/javascripts/users/application.js.coffee)0
-rw-r--r--app/assets/stylesheets/framework/blocks.scss4
-rw-r--r--app/assets/stylesheets/framework/buttons.scss18
-rw-r--r--app/assets/stylesheets/framework/sidebar.scss4
-rw-r--r--app/assets/stylesheets/pages/issuable.scss3
-rw-r--r--app/assets/stylesheets/pages/issues.scss8
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss14
-rw-r--r--app/assets/stylesheets/pages/pipelines.scss17
-rw-r--r--app/assets/stylesheets/pages/projects.scss73
-rw-r--r--app/assets/stylesheets/pages/status.scss8
-rw-r--r--app/assets/stylesheets/pages/tags.scss7
-rw-r--r--app/controllers/admin/groups_controller.rb2
-rw-r--r--app/controllers/admin/services_controller.rb15
-rw-r--r--app/controllers/concerns/creates_commit.rb3
-rw-r--r--app/controllers/concerns/service_params.rb35
-rw-r--r--app/controllers/groups_controller.rb2
-rw-r--r--app/controllers/projects/badges_controller.rb5
-rw-r--r--app/controllers/projects/blob_controller.rb6
-rw-r--r--app/controllers/projects/compare_controller.rb1
-rw-r--r--app/controllers/projects/pipelines_settings_controller.rb30
-rw-r--r--app/controllers/projects/refs_controller.rb2
-rw-r--r--app/controllers/projects/services_controller.rb27
-rw-r--r--app/controllers/projects_controller.rb2
-rw-r--r--app/helpers/avatars_helper.rb30
-rw-r--r--app/helpers/ci_status_helper.rb14
-rw-r--r--app/helpers/commits_helper.rb10
-rw-r--r--app/helpers/external_wiki_helper.rb5
-rw-r--r--app/helpers/services_helper.rb25
-rw-r--r--app/helpers/time_helper.rb26
-rw-r--r--app/models/ability.rb4
-rw-r--r--app/models/ci/build.rb66
-rw-r--r--app/models/ci/pipeline.rb11
-rw-r--r--app/models/ci/runner.rb8
-rw-r--r--app/models/ci/trigger_request.rb8
-rw-r--r--app/models/project.rb46
-rw-r--r--app/models/project_services/slack_service.rb51
-rw-r--r--app/models/repository.rb32
-rw-r--r--app/models/service.rb20
-rw-r--r--app/models/user.rb2
-rw-r--r--app/services/files/base_service.rb2
-rw-r--r--app/services/files/update_service.rb5
-rw-r--r--app/services/projects/import_export/export_service.rb6
-rw-r--r--app/uploaders/avatar_uploader.rb4
-rw-r--r--app/views/admin/groups/_form.html.haml4
-rw-r--r--app/views/events/_event.html.haml6
-rw-r--r--app/views/groups/edit.html.haml4
-rw-r--r--app/views/layouts/_init_auto_complete.html.haml10
-rw-r--r--app/views/layouts/nav/_explore.html.haml4
-rw-r--r--app/views/layouts/nav/_project_settings.html.haml6
-rw-r--r--app/views/profiles/_head.html.haml2
-rw-r--r--app/views/projects/_builds_settings.html.haml65
-rw-r--r--app/views/projects/badges/index.html.haml23
-rw-r--r--app/views/projects/blob/_editor.html.haml4
-rw-r--r--app/views/projects/builds/_sidebar.html.haml2
-rw-r--r--app/views/projects/buttons/_fork.html.haml4
-rw-r--r--app/views/projects/ci/pipelines/_pipeline.html.haml4
-rw-r--r--app/views/projects/commits/_commit.html.haml3
-rw-r--r--app/views/projects/edit.html.haml6
-rw-r--r--app/views/projects/forks/index.html.haml4
-rw-r--r--app/views/projects/forks/new.html.haml91
-rw-r--r--app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml2
-rw-r--r--app/views/projects/graphs/_head.html.haml2
-rw-r--r--app/views/projects/network/show.html.haml2
-rw-r--r--app/views/projects/pipelines_settings/show.html.haml103
-rw-r--r--app/views/projects/services/_form.html.haml1
-rw-r--r--app/views/projects/tags/show.html.haml67
-rw-r--r--app/views/shared/_allow_request_access.html.haml6
-rw-r--r--app/views/shared/_service_settings.html.haml81
-rw-r--r--app/views/shared/icons/_icon_fork.svg1
-rw-r--r--app/views/shared/icons/_icon_status_cancel.svg12
-rw-r--r--app/views/shared/icons/_icon_status_failed.svg12
-rw-r--r--app/views/shared/icons/_icon_status_pending.svg13
-rw-r--r--app/views/shared/icons/_icon_status_running.svg12
-rw-r--r--app/views/shared/icons/_icon_status_success.svg15
-rw-r--r--app/views/shared/icons/_icon_status_warning.svg15
-rw-r--r--app/views/users/show.html.haml2
-rw-r--r--config/application.rb8
-rw-r--r--config/initializers/secure_headers.rb14
-rw-r--r--config/initializers/sidekiq.rb3
-rw-r--r--config/routes.rb13
-rw-r--r--db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb8
-rw-r--r--db/migrate/20160715154212_add_request_access_enabled_to_projects.rb12
-rw-r--r--db/migrate/20160715204316_add_request_access_enabled_to_groups.rb12
-rw-r--r--db/migrate/20160718153603_add_has_external_wiki_to_projects.rb7
-rw-r--r--db/schema.rb15
-rw-r--r--doc/ci/variables/README.md68
-rw-r--r--doc/development/migration_style_guide.md46
-rw-r--r--doc/integration/slack.md5
-rw-r--r--doc/project_services/img/slack_configuration.pngbin0 -> 72072 bytes
-rw-r--r--doc/project_services/project_services.md2
-rw-r--r--doc/project_services/slack.md26
-rw-r--r--doc/workflow/add-user/add-user.md3
-rw-r--r--doc/workflow/groups.md3
-rw-r--r--features/steps/admin/settings.rb16
-rw-r--r--generator_templates/active_record/migration/create_table_migration.rb8
-rw-r--r--generator_templates/active_record/migration/migration.rb8
-rw-r--r--lib/api/commit_statuses.rb2
-rw-r--r--lib/api/helpers.rb6
-rw-r--r--lib/api/merge_requests.rb2
-rw-r--r--lib/api/projects.rb2
-rw-r--r--lib/banzai/filter/relative_link_filter.rb3
-rw-r--r--lib/ci/gitlab_ci_yaml_processor.rb67
-rw-r--r--lib/gitlab/checks/force_push.rb4
-rw-r--r--lib/gitlab/diff/position.rb4
-rw-r--r--lib/gitlab/downtime_check.rb71
-rw-r--r--lib/gitlab/downtime_check/message.rb28
-rw-r--r--lib/gitlab/git_access.rb1
-rw-r--r--lib/gitlab/git_access_status.rb4
-rw-r--r--lib/gitlab/gon_helper.rb2
-rw-r--r--lib/gitlab/import_export/avatar_restorer.rb31
-rw-r--r--lib/gitlab/import_export/avatar_saver.rb31
-rw-r--r--lib/gitlab/import_export/command_line_util.rb9
-rw-r--r--lib/gitlab/import_export/importer.rb6
-rw-r--r--lib/gitlab/import_export/uploads_saver.rb8
-rw-r--r--lib/tasks/downtime_check.rake26
-rw-r--r--lib/tasks/gitlab/check.rake2
-rw-r--r--lib/tasks/gitlab/db.rake15
-rw-r--r--spec/controllers/help_controller_spec.rb9
-rw-r--r--spec/factories/ci/builds.rb2
-rw-r--r--spec/features/groups/members/user_requests_access_spec.rb7
-rw-r--r--spec/features/pipelines_settings_spec.rb35
-rw-r--r--spec/features/projects/badges/list_spec.rb2
-rw-r--r--spec/features/projects/members/user_requests_access_spec.rb7
-rw-r--r--spec/features/projects/slack_service/slack_service_spec.rb26
-rw-r--r--spec/helpers/ci_status_helper_spec.rb10
-rw-r--r--spec/helpers/time_helper_spec.rb25
-rw-r--r--spec/lib/gitlab/badge/build_spec.rb2
-rw-r--r--spec/lib/gitlab/diff/position_spec.rb24
-rw-r--r--spec/lib/gitlab/downtime_check/message_spec.rb17
-rw-r--r--spec/lib/gitlab/downtime_check_spec.rb113
-rw-r--r--spec/lib/gitlab/git_access_spec.rb72
-rw-r--r--spec/lib/gitlab/import_export/avatar_restorer_spec.rb25
-rw-r--r--spec/lib/gitlab/import_export/avatar_saver_spec.rb27
-rw-r--r--spec/models/build_spec.rb279
-rw-r--r--spec/models/ci/pipeline_spec.rb24
-rw-r--r--spec/models/project_services/slack_service_spec.rb71
-rw-r--r--spec/models/project_spec.rb41
-rw-r--r--spec/models/repository_spec.rb78
-rw-r--r--spec/models/user_spec.rb23
-rw-r--r--spec/requests/ci/api/builds_spec.rb10
-rw-r--r--spec/routing/routing_spec.rb7
-rw-r--r--spec/support/api_helpers.rb14
150 files changed, 2182 insertions, 649 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index ff8aa351226..f566dfd76e9 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -148,7 +148,7 @@ spinach 9 10: *spinach-knapsack
.spinach-knapsack-ruby23: &spinach-knapsack-ruby23
<<: *spinach-knapsack
<<: *ruby-23
-
+
rspec 0 20 ruby23: *rspec-knapsack-ruby23
rspec 1 20 ruby23: *rspec-knapsack-ruby23
rspec 2 20 ruby23: *rspec-knapsack-ruby23
@@ -196,6 +196,7 @@ rake flog: *exec
rake flay: *exec
rake db:migrate:reset: *exec
license_finder: *exec
+rake downtime_check: *exec
bundler:audit:
stage: test
diff --git a/.rubocop.yml b/.rubocop.yml
index db0bcfadcf4..6adbda53456 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -291,6 +291,10 @@ Style/MultilineMethodDefinitionBraceLayout:
Style/MultilineOperationIndentation:
Enabled: false
+# Avoid multi-line `? :` (the ternary operator), use if/unless instead.
+Style/MultilineTernaryOperator:
+ Enabled: true
+
# Favor unless over if for negative conditions (or control flow or).
Style/NegatedIf:
Enabled: true
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index 9310e711889..b622b9239d4 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -226,10 +226,6 @@ Style/LineEndConcatenation:
Style/MethodCallParentheses:
Enabled: false
-# Offense count: 3
-Style/MultilineTernaryOperator:
- Enabled: false
-
# Offense count: 62
# Cop supports --auto-correct.
Style/MutableConstant:
diff --git a/CHANGELOG b/CHANGELOG
index 490ff151b76..16c50ef107e 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,21 +1,26 @@
Please view this file on the master branch, on stable branches it's out of date.
v 8.11.0 (unreleased)
- Fix of 'Commits being passed to custom hooks are already reachable when using the UI'
+ - Limit git rev-list output count to one in forced push check
v 8.10.0 (unreleased)
- Fix profile activity heatmap to show correct day name (eanplatter)
+ - Speed up ExternalWikiHelper#get_project_wiki_path
- Expose {should,force}_remove_source_branch (Ben Boeckel)
+ - Add the functionality to be able to rename a file. !5049 (tiagonbotelho)
- Disable PostgreSQL statement timeout during migrations
- Fix projects dropdown loading performance with a simplified api cal. !5113 (tiagonbotelho)
- Fix commit builds API, return all builds for all pipelines for given commit. !4849
- Replace Haml with Hamlit to make view rendering faster. !3666
- Refresh the branch cache after `git gc` runs
+ - Allow to disable request access button on projects/groups
- Refactor repository paths handling to allow multiple git mount points
- Optimize system note visibility checking by memoizing the visible reference count !5070
- Add Application Setting to configure default Repository Path for new projects
- Delete award emoji when deleting a user
- Remove pinTo from Flash and make inline flash messages look nicer !4854 (winniehell)
- Add an API for downloading latest successful build from a particular branch or tag !5347
+ - Add link to profile to commit avatar !5163 (winniehell)
- Wrap code blocks on Activies and Todos page. !4783 (winniehell)
- Align flash messages with left side of page content !4959 (winniehell)
- Display tooltip for "Copy to Clipboard" button !5164 (winniehell)
@@ -25,6 +30,9 @@ v 8.10.0 (unreleased)
- Escape file extension when parsing search results !5141 (winniehell)
- Apply the trusted_proxies config to the rack request object for use with rack_attack
- Upgrade to Rails 4.2.7. !5236
+ - Extend exposed environment variables for CI builds
+ - Allow to pull code with deploy key from public projects
+ - Use limit parameter rather than hardcoded value in `ldap:check` rake task (Mike Ricketts)
- Add Sidekiq queue duration to transaction metrics.
- Add a new column `artifacts_size` to table `ci_builds` !4964
- Let Workhorse serve format-patch diffs
@@ -33,6 +41,7 @@ v 8.10.0 (unreleased)
- Added day name to contribution calendar tooltips
- Make images fit to the size of the viewport !4810
- Fix check for New Branch button on Issue page !4630 (winniehell)
+ - Fix GFM autocomplete not working on wiki pages
- Fix MR-auto-close text added to description. !4836
- Support U2F devices in Firefox. !5177
- Fix issue, preventing users w/o push access to sort tags !5105 (redetection)
@@ -50,6 +59,7 @@ v 8.10.0 (unreleased)
- The Markdown reference parsers now re-use query results to prevent running the same queries multiple times !5020
- Updated project header design
- Issuable collapsed assignee tooltip is now the users name
+ - Fix compare view not changing code view rendering style
- Exclude email check from the standard health check
- Updated layout for Projects, Groups, Users on Admin area !4424
- Fix changing issue state columns in milestone view
@@ -89,6 +99,7 @@ v 8.10.0 (unreleased)
- Avoid to retrieve MR closes_issues as much as possible.
- Add API endpoint for a group issues !4520 (mahcsig)
- Add Bugzilla integration !4930 (iamtjg)
+ - Fix new snippet style bug (elliotec)
- Instrument Rinku usage
- Be explicit to define merge request discussion variables
- Metrics for Rouge::Plugins::Redcarpet and Rouge::Formatters::HTMLGitlab
@@ -99,6 +110,7 @@ v 8.10.0 (unreleased)
- Don't render discussion notes when requesting diff tab through AJAX
- Add basic system information like memory and disk usage to the admin panel
- Don't garbage collect commits that have related DB records like comments
+ - Allow to setup event by channel on slack service
- More descriptive message for git hooks and file locks
- Aliases of award emoji should be stored as original name. !5060 (dixpac)
- Handle custom Git hook result in GitLab UI
@@ -124,13 +136,17 @@ v 8.10.0 (unreleased)
- Fix last update timestamp on issues not preserved on gitlab.com and project imports
- Fix issues importing projects from EE to CE
- Fix creating group with space in group path
- - Improve cron_jobs loading error messages !5318
+ - Improve cron_jobs loading error messages !5318 / !5360
- Create Todos for Issue author when assign or mention himself (Katarzyna Kobierska)
- Limit the number of retries on error to 3 for exporting projects
- Allow empty repositories on project import/export
- Render only commit message title in builds (Katarzyna Kobierska Ula Budziszewska)
- Allow bulk (un)subscription from issues in issue index
- Fix MR diff encoding issues exporting GitLab projects
+ - Move builds settings out of project settings and rename Pipelines
+ - Add builds badge to Pipelines settings page
+ - Export and import avatar as part of project import/export
+ - Fix migration corrupting import data for old version upgrades
v 8.9.6
- Fix importing of events under notes for GitLab projects. !5154
diff --git a/Gemfile.lock b/Gemfile.lock
index 4bb7a8d0604..363904a4baa 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -578,7 +578,7 @@ GEM
railties (>= 4.2.0, < 5.1)
rinku (2.0.0)
rotp (2.1.2)
- rouge (2.0.3)
+ rouge (2.0.5)
rqrcode (0.7.0)
chunky_png
rqrcode-rails3 (0.1.7)
diff --git a/app/assets/javascripts/graphs/application.js.coffee b/app/assets/javascripts/graphs/graphs_bundle.js.coffee
index e0f681acf0b..e0f681acf0b 100644
--- a/app/assets/javascripts/graphs/application.js.coffee
+++ b/app/assets/javascripts/graphs/graphs_bundle.js.coffee
diff --git a/app/assets/javascripts/network/application.js.coffee b/app/assets/javascripts/network/network_bundle.js.coffee
index f75f63869c5..f75f63869c5 100644
--- a/app/assets/javascripts/network/application.js.coffee
+++ b/app/assets/javascripts/network/network_bundle.js.coffee
diff --git a/app/assets/javascripts/profile/application.js.coffee b/app/assets/javascripts/profile/profile_bundle.js.coffee
index 91cacfece46..91cacfece46 100644
--- a/app/assets/javascripts/profile/application.js.coffee
+++ b/app/assets/javascripts/profile/profile_bundle.js.coffee
diff --git a/app/assets/javascripts/users/application.js.coffee b/app/assets/javascripts/users/users_bundle.js.coffee
index 91cacfece46..91cacfece46 100644
--- a/app/assets/javascripts/users/application.js.coffee
+++ b/app/assets/javascripts/users/users_bundle.js.coffee
diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss
index ad94e457cfd..7ce203d2ec7 100644
--- a/app/assets/stylesheets/framework/blocks.scss
+++ b/app/assets/stylesheets/framework/blocks.scss
@@ -232,7 +232,9 @@
.nav-block {
.controls {
float: right;
- margin-top: 11px;
+ margin-top: 8px;
+ padding-bottom: 7px;
+ border-bottom: 1px solid $border-color;
}
}
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index 590b8f54363..f87b8a2ad1c 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -49,6 +49,17 @@
border-color: $border-dark;
color: $color;
}
+
+ svg {
+
+ path {
+ fill: $color;
+ }
+
+ use {
+ stroke: $color;
+ }
+ }
}
@mixin btn-green {
@@ -173,6 +184,13 @@
.caret {
margin-left: 5px;
}
+
+ svg {
+ height: 15px;
+ width: auto;
+ position: relative;
+ top: 2px;
+ }
}
.btn-lg {
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index d52e8f00172..3fa4a22258d 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -198,6 +198,10 @@ header.header-pinned-nav {
.sidebar-collapsed-icon {
cursor: pointer;
+
+ .btn {
+ background-color: $gray-light;
+ }
}
}
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 542fa244689..ded437625ee 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -122,7 +122,8 @@
button {
float: right;
- padding: 3px 5px;
+ padding: 1px 5px;
+ background-color: $gray-light;
}
}
diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss
index 9807c5a808d..ee3b2d2b801 100644
--- a/app/assets/stylesheets/pages/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -78,6 +78,14 @@ form.edit-issue {
}
}
+.merge-request-ci-status {
+ svg {
+ margin-right: 4px;
+ position: relative;
+ top: 1px;
+ }
+}
+
@media (max-width: $screen-xs-max) {
.issue-btn-group {
width: 100%;
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index fbff0c97355..5254faf723d 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -60,8 +60,10 @@
.ci_widget {
border-bottom: 1px solid #eef0f2;
- i {
+ svg {
margin-right: 4px;
+ position: relative;
+ top: 1px;
}
&.ci-success {
@@ -196,6 +198,16 @@
.merge-request-title {
margin-bottom: 2px;
+
+ .ci-status-link {
+
+ svg {
+ height: 16px;
+ width: 16px;
+ position: relative;
+ top: 3px;
+ }
+ }
}
}
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index a3b72ec9574..a404f108dc4 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -49,6 +49,14 @@
.commit-link {
+ .ci-status {
+
+ svg {
+ top: 1px;
+ margin-right: 0;
+ }
+ }
+
a:hover {
text-decoration: none;
}
@@ -124,6 +132,15 @@
}
}
+ .stage-cell {
+
+ svg {
+ height: 18px;
+ width: 18px;
+ vertical-align: middle;
+ }
+ }
+
.duration,
.finished-at {
color: $table-text-gray;
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index ea9f7cf0540..cc3aef5199e 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -129,6 +129,17 @@
color: $layout-link-gray;
}
+ svg {
+
+ path {
+ fill: $layout-link-gray;
+ }
+
+ use {
+ stroke: $layout-link-gray;
+ }
+ }
+
.fa-caret-down {
margin-left: 3px;
}
@@ -322,18 +333,53 @@ a.deploy-project-label {
}
.fork-namespaces {
- .fork-thumbnail {
- text-align: center;
- margin-bottom: $gl-padding;
-
- .caption {
- padding: $gl-padding 0;
- min-height: 30px;
- }
+ .row {
+ -webkit-flex-wrap: wrap;
+ display: -webkit-flex;
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: flex-start;
+
+ .fork-thumbnail {
+ @include border-radius($border-radius-base);
+ background-color: $white-light;
+ border: 1px solid $border-white-light;
+ height: 202px;
+ margin: $gl-padding;
+ text-align: center;
+ width: 169px;
+ &:hover, &.forked {
+ background-color: $row-hover;
+ border-color: $row-hover-border;
+ }
+ .no-avatar {
+ width: 100px;
+ height: 100px;
+ background-color: $gray-light;
+ border: 1px solid $gray-dark;
+ margin: 0 auto;
+ @include border-radius(50%);
+ i {
+ font-size: 100px;
+ color: $gray-dark;
+ }
+ }
+ a {
+ display: block;
+ width: 100%;
+ height: 100%;
+ padding-top: $gl-padding;
+ color: $gl-gray;
+ .caption {
+ min-height: 30px;
+ padding: $gl-padding 0;
+ }
+ }
- img {
- @include border-radius(50%);
- max-width: 100px;
+ img {
+ @include border-radius(50%);
+ max-width: 100px;
+ }
}
}
}
@@ -486,6 +532,11 @@ pre.light-well {
> span {
margin-left: 10px;
}
+
+ svg {
+ position: relative;
+ top: 2px;
+ }
}
}
diff --git a/app/assets/stylesheets/pages/status.scss b/app/assets/stylesheets/pages/status.scss
index c6b053150be..a22d4b6f6be 100644
--- a/app/assets/stylesheets/pages/status.scss
+++ b/app/assets/stylesheets/pages/status.scss
@@ -41,6 +41,14 @@
color: $blue-normal;
border-color: $blue-normal;
}
+
+ svg {
+ height: 13px;
+ width: 13px;
+ position: relative;
+ top: 1px;
+ margin: 0 3px;
+ }
}
.ci-status-icon-success {
diff --git a/app/assets/stylesheets/pages/tags.scss b/app/assets/stylesheets/pages/tags.scss
new file mode 100644
index 00000000000..24ebd3e7cfa
--- /dev/null
+++ b/app/assets/stylesheets/pages/tags.scss
@@ -0,0 +1,7 @@
+.tag-buttons {
+ line-height: 40px;
+
+ .btn:not(.dropdown-toggle) {
+ margin-left: 10px;
+ }
+}
diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb
index 94b5aaa71d0..f3a88a8e6c8 100644
--- a/app/controllers/admin/groups_controller.rb
+++ b/app/controllers/admin/groups_controller.rb
@@ -60,6 +60,6 @@ class Admin::GroupsController < Admin::ApplicationController
end
def group_params
- params.require(:group).permit(:name, :description, :path, :avatar, :visibility_level)
+ params.require(:group).permit(:name, :description, :path, :avatar, :visibility_level, :request_access_enabled)
end
end
diff --git a/app/controllers/admin/services_controller.rb b/app/controllers/admin/services_controller.rb
index 46133588332..7c37f3155da 100644
--- a/app/controllers/admin/services_controller.rb
+++ b/app/controllers/admin/services_controller.rb
@@ -1,4 +1,6 @@
class Admin::ServicesController < Admin::ApplicationController
+ include ServiceParams
+
before_action :service, only: [:edit, :update]
def index
@@ -13,7 +15,7 @@ class Admin::ServicesController < Admin::ApplicationController
end
def update
- if service.update_attributes(application_services_params[:service])
+ if service.update_attributes(service_params[:service])
redirect_to admin_application_settings_services_path,
notice: 'Application settings saved successfully'
else
@@ -37,15 +39,4 @@ class Admin::ServicesController < Admin::ApplicationController
def service
@service ||= Service.where(id: params[:id], template: true).first
end
-
- def application_services_params
- application_services_params = params.permit(:id,
- service: Projects::ServicesController::ALLOWED_PARAMS)
- if application_services_params[:service].is_a?(Hash)
- Projects::ServicesController::FILTER_BLANK_PARAMS.each do |param|
- application_services_params[:service].delete(param) if application_services_params[:service][param].blank?
- end
- end
- application_services_params
- end
end
diff --git a/app/controllers/concerns/creates_commit.rb b/app/controllers/concerns/creates_commit.rb
index dacb5679dd3..f2b8f297bc2 100644
--- a/app/controllers/concerns/creates_commit.rb
+++ b/app/controllers/concerns/creates_commit.rb
@@ -7,7 +7,8 @@ module CreatesCommit
commit_params = @commit_params.merge(
source_project: @project,
source_branch: @ref,
- target_branch: @target_branch
+ target_branch: @target_branch,
+ previous_path: @previous_path
)
result = service.new(@tree_edit_project, current_user, commit_params).execute
diff --git a/app/controllers/concerns/service_params.rb b/app/controllers/concerns/service_params.rb
new file mode 100644
index 00000000000..471d15af913
--- /dev/null
+++ b/app/controllers/concerns/service_params.rb
@@ -0,0 +1,35 @@
+module ServiceParams
+ extend ActiveSupport::Concern
+
+ ALLOWED_PARAMS = [:title, :token, :type, :active, :api_key, :api_url, :api_version, :subdomain,
+ :room, :recipients, :project_url, :webhook,
+ :user_key, :device, :priority, :sound, :bamboo_url, :username, :password,
+ :build_key, :server, :teamcity_url, :drone_url, :build_type,
+ :description, :issues_url, :new_issue_url, :restrict_to_branch, :channel,
+ :colorize_messages, :channels,
+ :push_events, :issues_events, :merge_requests_events, :tag_push_events,
+ :note_events, :build_events, :wiki_page_events,
+ :notify_only_broken_builds, :add_pusher,
+ :send_from_committer_email, :disable_diffs, :external_wiki_url,
+ :notify, :color,
+ :server_host, :server_port, :default_irc_uri, :enable_ssl_verification,
+ :jira_issue_transition_id]
+
+ # Parameters to ignore if no value is specified
+ FILTER_BLANK_PARAMS = [:password]
+
+ def service_params
+ dynamic_params = []
+ dynamic_params.concat(@service.event_channel_names)
+
+ service_params = params.permit(:id, service: ALLOWED_PARAMS + dynamic_params)
+
+ if service_params[:service].is_a?(Hash)
+ FILTER_BLANK_PARAMS.each do |param|
+ service_params[:service].delete(param) if service_params[:service][param].blank?
+ end
+ end
+
+ service_params
+ end
+end
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index a04bf7df722..6780a6d4d87 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -121,7 +121,7 @@ class GroupsController < Groups::ApplicationController
end
def group_params
- params.require(:group).permit(:name, :description, :path, :avatar, :public, :visibility_level, :share_with_group_lock)
+ params.require(:group).permit(:name, :description, :path, :avatar, :public, :visibility_level, :share_with_group_lock, :request_access_enabled)
end
def load_events
diff --git a/app/controllers/projects/badges_controller.rb b/app/controllers/projects/badges_controller.rb
index 824aa41db51..a9f482c8787 100644
--- a/app/controllers/projects/badges_controller.rb
+++ b/app/controllers/projects/badges_controller.rb
@@ -3,11 +3,6 @@ class Projects::BadgesController < Projects::ApplicationController
before_action :authorize_admin_project!, only: [:index]
before_action :no_cache_headers, except: [:index]
- def index
- @ref = params[:ref] || @project.default_branch || 'master'
- @build_badge = Gitlab::Badge::Build.new(@project, @ref)
- end
-
def build
badge = Gitlab::Badge::Build.new(project, params[:ref])
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index 5356fdf010d..eda3727a28d 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -38,6 +38,12 @@ class Projects::BlobController < Projects::ApplicationController
end
def update
+ if params[:file_path].present?
+ @previous_path = @path
+ @path = params[:file_path]
+ @commit_params[:file_path] = @path
+ end
+
after_edit_path =
if from_merge_request && @target_branch == @ref
diffs_namespace_project_merge_request_path(from_merge_request.target_project.namespace, from_merge_request.target_project, from_merge_request) +
diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb
index 5f3ee71444d..10749d0fef8 100644
--- a/app/controllers/projects/compare_controller.rb
+++ b/app/controllers/projects/compare_controller.rb
@@ -15,6 +15,7 @@ class Projects::CompareController < Projects::ApplicationController
end
def show
+ apply_diff_view_cookie!
end
def diff_for_path
diff --git a/app/controllers/projects/pipelines_settings_controller.rb b/app/controllers/projects/pipelines_settings_controller.rb
new file mode 100644
index 00000000000..85ba706e5cd
--- /dev/null
+++ b/app/controllers/projects/pipelines_settings_controller.rb
@@ -0,0 +1,30 @@
+class Projects::PipelinesSettingsController < Projects::ApplicationController
+ before_action :authorize_admin_pipeline!
+
+ def show
+ @ref = params[:ref] || @project.default_branch || 'master'
+ @build_badge = Gitlab::Badge::Build.new(@project, @ref)
+ end
+
+ def update
+ if @project.update_attributes(update_params)
+ flash[:notice] = "CI/CD Pipelines settings for '#{@project.name}' were successfully updated."
+ redirect_to namespace_project_pipelines_settings_path(@project.namespace, @project)
+ else
+ render 'index'
+ end
+ end
+
+ private
+
+ def create_params
+ params.require(:pipeline).permit(:ref)
+ end
+
+ def update_params
+ params.require(:project).permit(
+ :runners_token, :builds_enabled, :build_allow_git_fetch, :build_timeout_in_minutes, :build_coverage_regex,
+ :public_builds
+ )
+ end
+end
diff --git a/app/controllers/projects/refs_controller.rb b/app/controllers/projects/refs_controller.rb
index d79f16e6a5a..3602b3d5e58 100644
--- a/app/controllers/projects/refs_controller.rb
+++ b/app/controllers/projects/refs_controller.rb
@@ -25,7 +25,7 @@ class Projects::RefsController < Projects::ApplicationController
when "graphs_commits"
commits_namespace_project_graph_path(@project.namespace, @project, @id)
when "badges"
- namespace_project_badges_path(@project.namespace, @project, ref: @id)
+ namespace_project_pipelines_settings_path(@project.namespace, @project, ref: @id)
else
namespace_project_commits_path(@project.namespace, @project, @id)
end
diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb
index 1b91882048e..6a227d85f6f 100644
--- a/app/controllers/projects/services_controller.rb
+++ b/app/controllers/projects/services_controller.rb
@@ -1,20 +1,5 @@
class Projects::ServicesController < Projects::ApplicationController
- ALLOWED_PARAMS = [:title, :token, :type, :active, :api_key, :api_url, :api_version, :subdomain,
- :room, :recipients, :project_url, :webhook,
- :user_key, :device, :priority, :sound, :bamboo_url, :username, :password,
- :build_key, :server, :teamcity_url, :drone_url, :build_type,
- :description, :issues_url, :new_issue_url, :restrict_to_branch, :channel,
- :colorize_messages, :channels,
- :push_events, :issues_events, :merge_requests_events, :tag_push_events,
- :note_events, :build_events, :wiki_page_events,
- :notify_only_broken_builds, :add_pusher,
- :send_from_committer_email, :disable_diffs, :external_wiki_url,
- :notify, :color,
- :server_host, :server_port, :default_irc_uri, :enable_ssl_verification,
- :jira_issue_transition_id]
-
- # Parameters to ignore if no value is specified
- FILTER_BLANK_PARAMS = [:password]
+ include ServiceParams
# Authorize
before_action :authorize_admin_project!
@@ -33,7 +18,7 @@ class Projects::ServicesController < Projects::ApplicationController
end
def update
- if @service.update_attributes(service_params)
+ if @service.update_attributes(service_params[:service])
redirect_to(
edit_namespace_project_service_path(@project.namespace, @project,
@service.to_param, notice:
@@ -64,12 +49,4 @@ class Projects::ServicesController < Projects::ApplicationController
def service
@service ||= @project.services.find { |service| service.to_param == params[:id] }
end
-
- def service_params
- service_params = params.require(:service).permit(ALLOWED_PARAMS)
- FILTER_BLANK_PARAMS.each do |param|
- service_params.delete(param) if service_params[param].blank?
- end
- service_params
- end
end
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 4e5bcff9cf8..ec7a2e63b9a 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -296,7 +296,7 @@ class ProjectsController < Projects::ApplicationController
:issues_tracker_id, :default_branch,
:wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id, :avatar,
:builds_enabled, :build_allow_git_fetch, :build_timeout_in_minutes, :build_coverage_regex,
- :public_builds, :only_allow_merge_if_build_succeeds
+ :public_builds, :only_allow_merge_if_build_succeeds, :request_access_enabled
)
end
diff --git a/app/helpers/avatars_helper.rb b/app/helpers/avatars_helper.rb
new file mode 100644
index 00000000000..6ff40c6b461
--- /dev/null
+++ b/app/helpers/avatars_helper.rb
@@ -0,0 +1,30 @@
+module AvatarsHelper
+
+ def author_avatar(commit_or_event, options = {})
+ user_avatar(options.merge({
+ user: commit_or_event.author,
+ user_name: commit_or_event.author_name,
+ user_email: commit_or_event.author_email,
+ }))
+ end
+
+ private
+
+ def user_avatar(options = {})
+ avatar_size = options[:size] || 16
+ user_name = options[:user].try(:name) || options[:user_name]
+ avatar = image_tag(
+ avatar_icon(options[:user] || options[:user_email], avatar_size),
+ class: "avatar has-tooltip hidden-xs s#{avatar_size}",
+ alt: "#{user_name}'s avatar",
+ title: user_name
+ )
+
+ if options[:user]
+ link_to(avatar, user_path(options[:user]))
+ elsif options[:user_email]
+ mail_to(options[:user_email], avatar)
+ end
+ end
+
+end
diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb
index e6c99c9959e..59a8365d60b 100644
--- a/app/helpers/ci_status_helper.rb
+++ b/app/helpers/ci_status_helper.rb
@@ -26,18 +26,20 @@ module CiStatusHelper
icon_name =
case status
when 'success'
- 'check'
+ 'icon_status_success'
+ when 'success_with_warnings'
+ 'icon_status_warning'
when 'failed'
- 'close'
+ 'icon_status_failed'
when 'pending'
- 'clock-o'
+ 'icon_status_pending'
when 'running'
- 'spinner'
+ 'icon_status_running'
else
- 'circle'
+ 'icon_status_cancel'
end
- icon(icon_name + ' fw')
+ custom_icon(icon_name)
end
def render_commit_status(commit, tooltip_placement: 'auto left', cssclass: '')
diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb
index 474041eccbb..052ce56809e 100644
--- a/app/helpers/commits_helper.rb
+++ b/app/helpers/commits_helper.rb
@@ -16,16 +16,6 @@ module CommitsHelper
commit_person_link(commit, options.merge(source: :committer))
end
- def commit_author_avatar(commit, options = {})
- options = options.merge(source: :author)
- user = commit.send(options[:source])
-
- source_email = clean(commit.send "#{options[:source]}_email".to_sym)
- person_email = user.try(:email) || source_email
-
- image_tag(avatar_icon(person_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]} hidden-xs", width: options[:size], alt: "")
- end
-
def image_diff_class(diff)
if diff.deleted_file
"deleted"
diff --git a/app/helpers/external_wiki_helper.rb b/app/helpers/external_wiki_helper.rb
index 1f3401f2906..defd87d6bbe 100644
--- a/app/helpers/external_wiki_helper.rb
+++ b/app/helpers/external_wiki_helper.rb
@@ -1,8 +1,7 @@
module ExternalWikiHelper
def get_project_wiki_path(project)
- external_wiki_service = project.services.
- find { |service| service.to_param == 'external_wiki' }
- if external_wiki_service.present? && external_wiki_service.active?
+ external_wiki_service = project.external_wiki
+ if external_wiki_service
external_wiki_service.properties['external_wiki_url']
else
namespace_project_wiki_path(project.namespace, project, :home)
diff --git a/app/helpers/services_helper.rb b/app/helpers/services_helper.rb
new file mode 100644
index 00000000000..2dd0bf5d71e
--- /dev/null
+++ b/app/helpers/services_helper.rb
@@ -0,0 +1,25 @@
+module ServicesHelper
+ def service_event_description(event)
+ case event
+ when "push"
+ "Event will be triggered by a push to the repository"
+ when "tag_push"
+ "Event will be triggered when a new tag is pushed to the repository"
+ when "note"
+ "Event will be triggered when someone adds a comment"
+ when "issue"
+ "Event will be triggered when an issue is created/updated/merged"
+ when "merge_request"
+ "Event will be triggered when a merge request is created/updated/merged"
+ when "build"
+ "Event will be triggered when a build status changes"
+ when "wiki_page"
+ "Event will be triggered when a wiki page is created/updated"
+ end
+ end
+
+ def service_event_field_name(event)
+ event = event.pluralize if %w[merge_request issue].include?(event)
+ "#{event}_events"
+ end
+end
diff --git a/app/helpers/time_helper.rb b/app/helpers/time_helper.rb
index 8cb82c2d5cc..1926f03af07 100644
--- a/app/helpers/time_helper.rb
+++ b/app/helpers/time_helper.rb
@@ -1,14 +1,4 @@
module TimeHelper
- def duration_in_words(finished_at, started_at)
- if finished_at && started_at
- interval_in_seconds = finished_at.to_i - started_at.to_i
- elsif started_at
- interval_in_seconds = Time.now.to_i - started_at.to_i
- end
-
- time_interval_in_words(interval_in_seconds)
- end
-
def time_interval_in_words(interval_in_seconds)
minutes = interval_in_seconds / 60
seconds = interval_in_seconds - minutes * 60
@@ -25,9 +15,19 @@ module TimeHelper
end
def duration_in_numbers(finished_at, started_at)
- diff_in_seconds = finished_at.to_i - started_at.to_i
- time_format = diff_in_seconds < 1.hour ? "%M:%S" : "%H:%M:%S"
+ interval = interval_in_seconds(started_at, finished_at)
+ time_format = interval < 1.hour ? "%M:%S" : "%H:%M:%S"
- Time.at(diff_in_seconds).utc.strftime(time_format)
+ Time.at(interval).utc.strftime(time_format)
+ end
+
+ private
+
+ def interval_in_seconds(started_at, finished_at = nil)
+ if started_at && finished_at
+ finished_at.to_i - started_at.to_i
+ elsif started_at
+ Time.now.to_i - started_at.to_i
+ end
end
end
diff --git a/app/models/ability.rb b/app/models/ability.rb
index 6fd18f2ee24..f33c8d61d3f 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -172,7 +172,7 @@ class Ability
rules << :read_build if project.public_builds?
unless owner || project.team.member?(user) || project_group_member?(project, user)
- rules << :request_access
+ rules << :request_access if project.request_access_enabled
end
end
@@ -373,7 +373,7 @@ class Ability
end
if group.public? || (group.internal? && !user.external?)
- rules << :request_access unless group.users.include?(user)
+ rules << :request_access if group.request_access_enabled && group.users.exclude?(user)
end
rules.flatten
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index a24665eed72..cbfa14e81f1 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -97,7 +97,7 @@ module Ci
end
def other_actions
- pipeline.manual_actions.where.not(id: self)
+ pipeline.manual_actions.where.not(name: name)
end
def playable?
@@ -145,11 +145,14 @@ module Ci
end
def variables
- variables = []
- variables += predefined_variables
- variables += yaml_variables if yaml_variables
- variables += project_variables
- variables += trigger_variables
+ variables = predefined_variables
+ variables += project.predefined_variables
+ variables += pipeline.predefined_variables
+ variables += runner.predefined_variables if runner
+ variables += project.container_registry_variables
+ variables += yaml_variables
+ variables += project.secret_variables
+ variables += trigger_request.user_variables if trigger_request
variables
end
@@ -409,6 +412,14 @@ module Ci
self.update(artifacts_expire_at: nil)
end
+ def when
+ read_attribute(:when) || build_attributes_from_config[:when] || 'on_success'
+ end
+
+ def yaml_variables
+ read_attribute(:yaml_variables) || build_attributes_from_config[:yaml_variables] || []
+ end
+
private
def update_artifacts_size
@@ -427,29 +438,30 @@ module Ci
self.update(erased_by: user, erased_at: Time.now, artifacts_expire_at: nil)
end
- def project_variables
- project.variables.map do |variable|
- { key: variable.key, value: variable.value, public: false }
- end
- end
-
- def trigger_variables
- if trigger_request && trigger_request.variables
- trigger_request.variables.map do |key, value|
- { key: key, value: value, public: false }
- end
- else
- []
- end
- end
-
def predefined_variables
- variables = []
- variables << { key: :CI_BUILD_TAG, value: ref, public: true } if tag?
- variables << { key: :CI_BUILD_NAME, value: name, public: true }
- variables << { key: :CI_BUILD_STAGE, value: stage, public: true }
- variables << { key: :CI_BUILD_TRIGGERED, value: 'true', public: true } if trigger_request
+ variables = [
+ { key: 'CI', value: 'true', public: true },
+ { key: 'GITLAB_CI', value: 'true', public: true },
+ { key: 'CI_BUILD_ID', value: id.to_s, public: true },
+ { key: 'CI_BUILD_TOKEN', value: token, public: false },
+ { key: 'CI_BUILD_REF', value: sha, public: true },
+ { key: 'CI_BUILD_BEFORE_SHA', value: before_sha, public: true },
+ { key: 'CI_BUILD_REF_NAME', value: ref, public: true },
+ { key: 'CI_BUILD_NAME', value: name, public: true },
+ { key: 'CI_BUILD_STAGE', value: stage, public: true },
+ { key: 'CI_SERVER_NAME', value: 'GitLab', public: true },
+ { key: 'CI_SERVER_VERSION', value: Gitlab::VERSION, public: true },
+ { key: 'CI_SERVER_REVISION', value: Gitlab::REVISION, public: true }
+ ]
+ variables << { key: 'CI_BUILD_TAG', value: ref, public: true } if tag?
+ variables << { key: 'CI_BUILD_TRIGGERED', value: 'true', public: true } if trigger_request
variables
end
+
+ def build_attributes_from_config
+ return {} unless pipeline.config_processor
+
+ pipeline.config_processor.build_attributes(name)
+ end
end
end
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 63246de4692..f0edf3e2731 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -203,6 +203,12 @@ module Ci
Note.for_commit_id(sha)
end
+ def predefined_variables
+ [
+ { key: 'CI_PIPELINE_ID', value: id.to_s, public: true }
+ ]
+ end
+
private
def build_builds_for_stages(stages, user, status, trigger_request)
@@ -211,8 +217,9 @@ module Ci
# build builds only for the first stage that has builds available.
#
stages.any? do |stage|
- CreateBuildsService.new(self)
- .execute(stage, user, status, trigger_request).present?
+ CreateBuildsService.new(self).
+ execute(stage, user, status, trigger_request).
+ any?(&:active?)
end
end
diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb
index b64ec79ec2b..49f05f881a2 100644
--- a/app/models/ci/runner.rb
+++ b/app/models/ci/runner.rb
@@ -114,6 +114,14 @@ module Ci
tag_list.any?
end
+ def predefined_variables
+ [
+ { key: 'CI_RUNNER_ID', value: id.to_s, public: true },
+ { key: 'CI_RUNNER_DESCRIPTION', value: description, public: true },
+ { key: 'CI_RUNNER_TAGS', value: tag_list.to_s, public: true }
+ ]
+ end
+
private
def tag_constraints
diff --git a/app/models/ci/trigger_request.rb b/app/models/ci/trigger_request.rb
index fcf2b6dc5e2..fc674871743 100644
--- a/app/models/ci/trigger_request.rb
+++ b/app/models/ci/trigger_request.rb
@@ -7,5 +7,13 @@ module Ci
has_many :builds, class_name: 'Ci::Build'
serialize :variables
+
+ def user_variables
+ return [] unless variables
+
+ variables.map do |key, value|
+ { key: key, value: value, public: false }
+ end
+ end
end
end
diff --git a/app/models/project.rb b/app/models/project.rb
index 9f0dcb9a212..9b9d497fbd8 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -661,6 +661,22 @@ class Project < ActiveRecord::Base
update_column(:has_external_issue_tracker, services.external_issue_trackers.any?)
end
+ def external_wiki
+ if has_external_wiki.nil?
+ cache_has_external_wiki # Populate
+ end
+
+ if has_external_wiki
+ @external_wiki ||= services.external_wikis.first
+ else
+ nil
+ end
+ end
+
+ def cache_has_external_wiki
+ update_column(:has_external_wiki, services.external_wikis.any?)
+ end
+
def build_missing_services
services_templates = Service.where(template: true)
@@ -1175,4 +1191,34 @@ class Project < ActiveRecord::Base
def ensure_dir_exist
gitlab_shell.add_namespace(repository_storage_path, namespace.path)
end
+
+ def predefined_variables
+ [
+ { key: 'CI_PROJECT_ID', value: id.to_s, public: true },
+ { key: 'CI_PROJECT_NAME', value: path, public: true },
+ { key: 'CI_PROJECT_PATH', value: path_with_namespace, public: true },
+ { key: 'CI_PROJECT_NAMESPACE', value: namespace.path, public: true },
+ { key: 'CI_PROJECT_URL', value: web_url, public: true }
+ ]
+ end
+
+ def container_registry_variables
+ return [] unless Gitlab.config.registry.enabled
+
+ variables = [
+ { key: 'CI_REGISTRY', value: Gitlab.config.registry.host_port, public: true }
+ ]
+
+ if container_registry_enabled?
+ variables << { key: 'CI_REGISTRY_IMAGE', value: container_registry_repository_url, public: true }
+ end
+
+ variables
+ end
+
+ def secret_variables
+ variables.map do |variable|
+ { key: variable.key, value: variable.value, public: false }
+ end
+ end
end
diff --git a/app/models/project_services/slack_service.rb b/app/models/project_services/slack_service.rb
index cf9e4d5a8b6..abbc780dc1a 100644
--- a/app/models/project_services/slack_service.rb
+++ b/app/models/project_services/slack_service.rb
@@ -4,6 +4,9 @@ class SlackService < Service
validates :webhook, presence: true, url: true, if: :activated?
def initialize_properties
+ # Custom serialized properties initialization
+ self.supported_events.each { |event| self.class.prop_accessor(event_channel_name(event)) }
+
if properties.nil?
self.properties = {}
self.notify_only_broken_builds = true
@@ -29,13 +32,15 @@ class SlackService < Service
end
def fields
- [
- { type: 'text', name: 'webhook',
- placeholder: 'https://hooks.slack.com/services/...' },
- { type: 'text', name: 'username', placeholder: 'username' },
- { type: 'text', name: 'channel', placeholder: '#channel' },
- { type: 'checkbox', name: 'notify_only_broken_builds' },
- ]
+ default_fields =
+ [
+ { type: 'text', name: 'webhook', placeholder: 'https://hooks.slack.com/services/...' },
+ { type: 'text', name: 'username', placeholder: 'username' },
+ { type: 'text', name: 'channel', placeholder: "#general" },
+ { type: 'checkbox', name: 'notify_only_broken_builds' },
+ ]
+
+ default_fields + build_event_channels
end
def supported_events
@@ -74,7 +79,10 @@ class SlackService < Service
end
opt = {}
- opt[:channel] = channel if channel
+
+ event_channel = get_channel_field(object_kind) || channel
+
+ opt[:channel] = event_channel if event_channel
opt[:username] = username if username
if message
@@ -83,8 +91,35 @@ class SlackService < Service
end
end
+ def event_channel_names
+ supported_events.map { |event| event_channel_name(event) }
+ end
+
+ def event_field(event)
+ fields.find { |field| field[:name] == event_channel_name(event) }
+ end
+
+ def global_fields
+ fields.reject { |field| field[:name].end_with?('channel') }
+ end
+
private
+ def get_channel_field(event)
+ field_name = event_channel_name(event)
+ self.public_send(field_name)
+ end
+
+ def build_event_channels
+ supported_events.reduce([]) do |channels, event|
+ channels << { type: 'text', name: event_channel_name(event), placeholder: "#general" }
+ end
+ end
+
+ def event_channel_name(event)
+ "#{event}_channel"
+ end
+
def project_name
project.name_with_namespace.gsub(/\s/, '')
end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index c187bad39ad..511df2d67c6 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -392,6 +392,11 @@ class Repository
expire_cache if exists?
+ # expire cache that don't depend on repository data (when expiring)
+ expire_tags_cache
+ expire_tag_count_cache
+ expire_branches_cache
+ expire_branch_count_cache
expire_root_ref_cache
expire_emptiness_caches
expire_exists_cache
@@ -733,6 +738,33 @@ class Repository
end
end
+ def update_file(user, path, content, branch:, previous_path:, message:)
+ commit_with_hooks(user, branch) do |ref|
+ committer = user_to_committer(user)
+ options = {}
+ options[:committer] = committer
+ options[:author] = committer
+ options[:commit] = {
+ message: message,
+ branch: ref,
+ update_ref: false
+ }
+
+ options[:file] = {
+ content: content,
+ path: path,
+ update: true
+ }
+
+ if previous_path
+ options[:file][:previous_path] = previous_path
+ Gitlab::Git::Blob.rename(raw_repository, options)
+ else
+ Gitlab::Git::Blob.commit(raw_repository, options)
+ end
+ end
+ end
+
def remove_file(user, path, message, branch)
commit_with_hooks(user, branch) do |ref|
committer = user_to_committer(user)
diff --git a/app/models/service.rb b/app/models/service.rb
index 5432f8c7ab4..a8e1cc2f422 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -17,6 +17,7 @@ class Service < ActiveRecord::Base
after_commit :reset_updated_properties
after_commit :cache_project_has_external_issue_tracker
+ after_commit :cache_project_has_external_wiki
belongs_to :project, inverse_of: :services
has_one :service_hook
@@ -25,6 +26,7 @@ class Service < ActiveRecord::Base
scope :visible, -> { where.not(type: ['GitlabIssueTrackerService', 'GitlabCiService']) }
scope :issue_trackers, -> { where(category: 'issue_tracker') }
+ scope :external_wikis, -> { where(type: 'ExternalWikiService') }
scope :active, -> { where(active: true) }
scope :without_defaults, -> { where(default: false) }
@@ -80,6 +82,18 @@ class Service < ActiveRecord::Base
Gitlab::PushDataBuilder.build_sample(project, user)
end
+ def event_channel_names
+ []
+ end
+
+ def event_field(event)
+ nil
+ end
+
+ def global_fields
+ fields
+ end
+
def supported_events
%w(push tag_push issue merge_request wiki_page)
end
@@ -212,4 +226,10 @@ class Service < ActiveRecord::Base
project.cache_has_external_issue_tracker
end
end
+
+ def cache_project_has_external_wiki
+ if project && !project.destroyed?
+ project.cache_has_external_wiki
+ end
+ end
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 3d0a033785c..975e935fa20 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -854,7 +854,7 @@ class User < ActiveRecord::Base
groups.joins(:shared_projects).select(:project_id)]
if min_access_level
- scope = { access_level: Gitlab::Access.values.select { |access| access >= min_access_level } }
+ scope = { access_level: Gitlab::Access.all_values.select { |access| access >= min_access_level } }
relations = [relations.shift] + relations.map { |relation| relation.where(members: scope) }
end
diff --git a/app/services/files/base_service.rb b/app/services/files/base_service.rb
index 55da949f56a..c4a206f785e 100644
--- a/app/services/files/base_service.rb
+++ b/app/services/files/base_service.rb
@@ -9,12 +9,14 @@ module Files
@commit_message = params[:commit_message]
@file_path = params[:file_path]
+ @previous_path = params[:previous_path]
@file_content = if params[:file_content_encoding] == 'base64'
Base64.decode64(params[:file_content])
else
params[:file_content]
end
+ # Validate parameters
validate
# Create new branch if it different from source_branch
diff --git a/app/services/files/update_service.rb b/app/services/files/update_service.rb
index 1960dc7d949..8d2b5083179 100644
--- a/app/services/files/update_service.rb
+++ b/app/services/files/update_service.rb
@@ -3,7 +3,10 @@ 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, true)
+ repository.update_file(current_user, @file_path, @file_content,
+ branch: @target_branch,
+ previous_path: @previous_path,
+ message: @commit_message)
end
end
end
diff --git a/app/services/projects/import_export/export_service.rb b/app/services/projects/import_export/export_service.rb
index 998789d64d2..06252c7b625 100644
--- a/app/services/projects/import_export/export_service.rb
+++ b/app/services/projects/import_export/export_service.rb
@@ -9,7 +9,7 @@ module Projects
private
def save_all
- if [version_saver, project_tree_saver, uploads_saver, repo_saver, wiki_repo_saver].all?(&:save)
+ if [version_saver, avatar_saver, project_tree_saver, uploads_saver, repo_saver, wiki_repo_saver].all?(&:save)
Gitlab::ImportExport::Saver.save(project: project, shared: @shared)
notify_success
else
@@ -21,6 +21,10 @@ module Projects
Gitlab::ImportExport::VersionSaver.new(shared: @shared)
end
+ def avatar_saver
+ Gitlab::ImportExport::AvatarSaver.new(project: project, shared: @shared)
+ end
+
def project_tree_saver
Gitlab::ImportExport::ProjectTreeSaver.new(project: project, shared: @shared)
end
diff --git a/app/uploaders/avatar_uploader.rb b/app/uploaders/avatar_uploader.rb
index 6135c3ad96f..03d9329a14e 100644
--- a/app/uploaders/avatar_uploader.rb
+++ b/app/uploaders/avatar_uploader.rb
@@ -14,4 +14,8 @@ class AvatarUploader < CarrierWave::Uploader::Base
def reset_events_cache(file)
model.reset_events_cache if model.is_a?(User)
end
+
+ def exists?
+ model.avatar.file && model.avatar.file.exists?
+ end
end
diff --git a/app/views/admin/groups/_form.html.haml b/app/views/admin/groups/_form.html.haml
index 0cc405401cf..5f7fdfdb011 100644
--- a/app/views/admin/groups/_form.html.haml
+++ b/app/views/admin/groups/_form.html.haml
@@ -9,6 +9,10 @@
= render 'shared/visibility_level', f: f, visibility_level: @group.visibility_level, can_change_visibility_level: can_change_group_visibility_level?(@group), form_model: @group
+ .form-group
+ .col-sm-offset-2.col-sm-10
+ = render 'shared/allow_request_access', form: f
+
- if @group.new_record?
.form-group
.col-sm-offset-2.col-sm-10
diff --git a/app/views/events/_event.html.haml b/app/views/events/_event.html.haml
index e4629bae0e6..5c318cd3b8b 100644
--- a/app/views/events/_event.html.haml
+++ b/app/views/events/_event.html.haml
@@ -4,11 +4,7 @@
#{time_ago_with_tooltip(event.created_at)}
= cache [event, current_application_settings, "v2.2"] do
- - if event.author
- = link_to user_path(event.author) do
- = image_tag avatar_icon(event.author_email, 40), class: "avatar s40", alt:''
- - else
- = image_tag avatar_icon(event.author_email, 40), class: "avatar s40", alt:''
+ = author_avatar(event, size: 40)
- if event.created_project?
= render "events/event/created_project", event: event
diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml
index 92cd4c553d0..decb89b2fd6 100644
--- a/app/views/groups/edit.html.haml
+++ b/app/views/groups/edit.html.haml
@@ -22,6 +22,10 @@
= render 'shared/visibility_level', f: f, visibility_level: @group.visibility_level, can_change_visibility_level: can_change_group_visibility_level?(@group), form_model: @group
.form-group
+ .col-sm-offset-2.col-sm-10
+ = render 'shared/allow_request_access', form: f
+
+ .form-group
%hr
= f.label :share_with_group_lock, class: 'control-label' do
Share with group lock
diff --git a/app/views/layouts/_init_auto_complete.html.haml b/app/views/layouts/_init_auto_complete.html.haml
index 12e7ed0e792..351100f3523 100644
--- a/app/views/layouts/_init_auto_complete.html.haml
+++ b/app/views/layouts/_init_auto_complete.html.haml
@@ -1,7 +1,7 @@
- project = @target_project || @project
+- noteable_class = @noteable.class if @noteable.present?
-- if @noteable
- :javascript
- GitLab.GfmAutoComplete.dataSource = "#{autocomplete_sources_namespace_project_path(project.namespace, project, type: @noteable.class, type_id: params[:id])}"
- GitLab.GfmAutoComplete.cachedData = undefined;
- GitLab.GfmAutoComplete.setup();
+:javascript
+ GitLab.GfmAutoComplete.dataSource = "#{autocomplete_sources_namespace_project_path(project.namespace, project, type: noteable_class, type_id: params[:id])}"
+ GitLab.GfmAutoComplete.cachedData = undefined;
+ GitLab.GfmAutoComplete.setup();
diff --git a/app/views/layouts/nav/_explore.html.haml b/app/views/layouts/nav/_explore.html.haml
index 3b40006a0cc..e5bda7b3a6f 100644
--- a/app/views/layouts/nav/_explore.html.haml
+++ b/app/views/layouts/nav/_explore.html.haml
@@ -1,21 +1,17 @@
%ul.nav.nav-sidebar
= nav_link(path: ['dashboard#show', 'root#show', 'projects#trending', 'projects#starred', 'projects#index'], html_options: {class: 'home'}) do
= link_to explore_root_path, title: 'Projects' do
- = icon('bookmark fw')
%span
Projects
= nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do
= link_to explore_groups_path, title: 'Groups' do
- = icon('group fw')
%span
Groups
= nav_link(controller: :snippets) do
= link_to explore_snippets_path, title: 'Snippets' do
- = icon('clipboard fw')
%span
Snippets
= nav_link(controller: :help) do
= link_to help_path, title: 'Help' do
- = icon('question-circle fw')
%span
Help
diff --git a/app/views/layouts/nav/_project_settings.html.haml b/app/views/layouts/nav/_project_settings.html.haml
index 51a54b4f262..52a5bdc1a1b 100644
--- a/app/views/layouts/nav/_project_settings.html.haml
+++ b/app/views/layouts/nav/_project_settings.html.haml
@@ -39,7 +39,7 @@
= link_to namespace_project_triggers_path(@project.namespace, @project), title: 'Triggers' do
%span
Triggers
- = nav_link(controller: :badges) do
- = link_to namespace_project_badges_path(@project.namespace, @project), title: 'Badges' do
+ = nav_link(controller: :pipelines_settings) do
+ = link_to namespace_project_pipelines_settings_path(@project.namespace, @project), title: 'CI/CD Pipelines' do
%span
- Badges
+ CI/CD Pipelines
diff --git a/app/views/profiles/_head.html.haml b/app/views/profiles/_head.html.haml
index 003884a5bd9..943ebdaeffe 100644
--- a/app/views/profiles/_head.html.haml
+++ b/app/views/profiles/_head.html.haml
@@ -1,3 +1,3 @@
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('lib/cropper.js')
- = page_specific_javascript_tag('profile/application.js')
+ = page_specific_javascript_tag('profile/profile_bundle.js')
diff --git a/app/views/projects/_builds_settings.html.haml b/app/views/projects/_builds_settings.html.haml
deleted file mode 100644
index fff30f11d82..00000000000
--- a/app/views/projects/_builds_settings.html.haml
+++ /dev/null
@@ -1,65 +0,0 @@
-%fieldset.builds-feature
- %h5.prepend-top-0
- Builds
- - unless @repository.gitlab_ci_yml
- .form-group
- %p Builds need to be configured before you can begin using Continuous Integration.
- = link_to 'Get started with Builds', help_page_path('ci/quick_start/README'), class: 'btn btn-info'
- .form-group
- %p Get recent application code using the following command:
- .radio
- = f.label :build_allow_git_fetch_false do
- = f.radio_button :build_allow_git_fetch, 'false'
- %strong git clone
- %br
- %span.descr Slower but makes sure you have a clean dir before every build
- .radio
- = f.label :build_allow_git_fetch_true do
- = f.radio_button :build_allow_git_fetch, 'true'
- %strong git fetch
- %br
- %span.descr Faster
-
- .form-group
- = f.label :build_timeout_in_minutes, 'Timeout', class: 'label-light'
- = f.number_field :build_timeout_in_minutes, class: 'form-control', min: '0'
- %p.help-block per build in minutes
- .form-group
- = f.label :build_coverage_regex, "Test coverage parsing", class: 'label-light'
- .input-group
- %span.input-group-addon /
- = f.text_field :build_coverage_regex, class: 'form-control', placeholder: '\(\d+.\d+\%\) covered'
- %span.input-group-addon /
- %p.help-block
- We will use this regular expression to find test coverage output in build trace.
- Leave blank if you want to disable this feature
- .bs-callout.bs-callout-info
- %p Below are examples of regex for existing tools:
- %ul
- %li
- Simplecov (Ruby) -
- %code \(\d+.\d+\%\) covered
- %li
- pytest-cov (Python) -
- %code \d+\%\s*$
- %li
- phpunit --coverage-text --colors=never (PHP) -
- %code ^\s*Lines:\s*\d+.\d+\%
- %li
- gcovr (C/C++) -
- %code ^TOTAL.*\s+(\d+\%)$
- %li
- tap --coverage-report=text-summary (Node.js) -
- %code ^Statements\s*:\s*([^%]+)
-
- .form-group
- .checkbox
- = f.label :public_builds do
- = f.check_box :public_builds
- %strong Public builds
- .help-block Allow everyone to access builds for Public and Internal projects
-
- .form-group.append-bottom-0
- = f.label :runners_token, "Runners token", class: 'label-light'
- = f.text_field :runners_token, class: "form-control", placeholder: 'xEeFCaDAB89'
- %p.help-block The secure token used to checkout project.
diff --git a/app/views/projects/badges/index.html.haml b/app/views/projects/badges/index.html.haml
deleted file mode 100644
index ac80951dd4f..00000000000
--- a/app/views/projects/badges/index.html.haml
+++ /dev/null
@@ -1,23 +0,0 @@
-- page_title 'Badges'
-- badges_path = namespace_project_badges_path(@project.namespace, @project)
-
-.prepend-top-10
- .panel.panel-default
- .panel-heading
- %b Builds badge &middot;
- = @build_badge.to_html
- .pull-right
- = render 'shared/ref_switcher', destination: 'badges', align_right: true
- .panel-body
- .row
- .col-md-2.text-center
- Markdown
- .col-md-10.code.js-syntax-highlight
- = highlight('.md', @build_badge.to_markdown)
- .row
- %hr
- .row
- .col-md-2.text-center
- HTML
- .col-md-10.code.js-syntax-highlight
- = highlight('.html', @build_badge.to_html)
diff --git a/app/views/projects/blob/_editor.html.haml b/app/views/projects/blob/_editor.html.haml
index 29c7d45074a..ff379bafb26 100644
--- a/app/views/projects/blob/_editor.html.haml
+++ b/app/views/projects/blob/_editor.html.haml
@@ -4,7 +4,9 @@
= icon('code-fork')
= ref
%span.editor-file-name
- = @path
+ - if current_action?(:edit) || current_action?(:update)
+ = text_field_tag 'file_path', (params[:file_path] || @path),
+ class: 'form-control new-file-path'
- if current_action?(:new) || current_action?(:create)
%span.editor-file-name
diff --git a/app/views/projects/builds/_sidebar.html.haml b/app/views/projects/builds/_sidebar.html.haml
index 396cc4ad925..dc57b49f27a 100644
--- a/app/views/projects/builds/_sidebar.html.haml
+++ b/app/views/projects/builds/_sidebar.html.haml
@@ -49,7 +49,7 @@
- if @build.duration
%p.build-detail-row
%span.build-light-text Duration:
- #{duration_in_words(@build.finished_at, @build.started_at)}
+ = time_interval_in_words(@build.duration)
- if @build.finished_at
%p.build-detail-row
%span.build-light-text Finished:
diff --git a/app/views/projects/buttons/_fork.html.haml b/app/views/projects/buttons/_fork.html.haml
index a9eaed4c5f6..a098a082854 100644
--- a/app/views/projects/buttons/_fork.html.haml
+++ b/app/views/projects/buttons/_fork.html.haml
@@ -2,7 +2,7 @@
- if current_user && can?(current_user, :fork_project, @project)
- if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2
= link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn has-tooltip' do
- = icon('code-fork fw')
+ = custom_icon('icon_fork')
Fork
%div.count-with-arrow
%span.arrow
@@ -10,7 +10,7 @@
= @project.forks_count
- else
= link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn has-tooltip' do
- = icon('code-fork fw')
+ = custom_icon('icon_fork')
Fork
%div.count-with-arrow
%span.arrow
diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml
index 996c9073770..2f7d54f0bdd 100644
--- a/app/views/projects/ci/pipelines/_pipeline.html.haml
+++ b/app/views/projects/ci/pipelines/_pipeline.html.haml
@@ -27,7 +27,7 @@
%p.commit-title
- if commit = pipeline.commit
- = commit_author_avatar(commit, size: 20)
+ = author_avatar(commit, size: 20)
= link_to_gfm truncate(commit.title, length: 60), namespace_project_commit_path(@project.namespace, @project, commit.id), class: "commit-row-message"
- else
Cant find HEAD commit for this branch
@@ -35,7 +35,7 @@
- stages_status = pipeline.statuses.latest.stages_status
- stages.each do |stage|
- %td
+ %td.stage-cell
- status = stages_status[stage]
- tooltip = "#{stage.titleize}: #{status || 'not found'}"
- if status
diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml
index c8c7b858baa..ab9afb06afb 100644
--- a/app/views/projects/commits/_commit.html.haml
+++ b/app/views/projects/commits/_commit.html.haml
@@ -9,7 +9,8 @@
= cache(cache_key) do
%li.commit.js-toggle-container{ id: "commit-#{commit.short_id}" }
- = commit_author_avatar(commit, size: 36)
+ = author_avatar(commit, size: 36)
+
.commit-info-block
.commit-row-title
%span.item-title
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index 57af167180b..921155e970b 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -32,6 +32,10 @@
%strong
= visibility_level_label(@project.visibility_level)
.light= visibility_level_description(@project.visibility_level, @project)
+
+ .form-group
+ = render 'shared/allow_request_access', form: f
+
.form-group
= f.label :tag_list, "Tags", class: 'label-light'
= f.text_field :tag_list, value: @project.tag_list.to_s, maxlength: 2000, class: "form-control"
@@ -86,8 +90,6 @@
%hr
= render 'merge_request_settings', f: f
%hr
- = render 'builds_settings', f: f
- %hr
%fieldset.features.append-bottom-default
%h5.prepend-top-0
Project avatar
diff --git a/app/views/projects/forks/index.html.haml b/app/views/projects/forks/index.html.haml
index dbe9ddfde2f..a1d79bdabda 100644
--- a/app/views/projects/forks/index.html.haml
+++ b/app/views/projects/forks/index.html.haml
@@ -31,11 +31,11 @@
- if current_user && can?(current_user, :fork_project, @project)
- if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2
= link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn btn-new' do
- = icon('code-fork fw')
+ = custom_icon('icon_fork')
Fork
- else
= link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn btn-new' do
- = icon('code-fork fw')
+ = custom_icon('icon_fork')
Fork
diff --git a/app/views/projects/forks/new.html.haml b/app/views/projects/forks/new.html.haml
index 73a7fc0e1ac..5242bc72b71 100644
--- a/app/views/projects/forks/new.html.haml
+++ b/app/views/projects/forks/new.html.haml
@@ -1,45 +1,54 @@
- page_title "Fork project"
-- if @namespaces.present?
- %h3.page-title Fork project
- %p.lead
- Click to fork the project to a user or group
- %hr
- .fork-namespaces
- - @namespaces.in_groups_of(6, false) do |group|
- .row
- - group.each do |namespace|
- .col-md-2.col-sm-3
- - if fork = namespace.find_fork_of(@project)
- .fork-thumbnail
- = link_to project_path(fork), title: "Visit project fork", class: 'has-tooltip' do
- = image_tag namespace_icon(namespace, 100)
- .caption
- %strong
- = namespace.human_name
- %div.text-primary
- Already forked
-
- - else
- .fork-thumbnail
- = link_to namespace_project_forks_path(@project.namespace, @project, namespace_key: namespace.id), title: "Fork here", method: "POST", class: 'has-tooltip' do
- = image_tag namespace_icon(namespace, 100)
- .caption
- %strong
- = namespace.human_name
-
- %p.light
- Fork is a copy of a project repository.
+.row.prepend-top-default
+ .col-lg-3
+ %h4.prepend-top-0
+ Fork project
+ %p
+ A fork is a copy of a project.
%br
- Forking a repository allows you to do changes without affecting the original project.
-- else
- %h3 No available namespaces to fork the project
- %p.slead
- You must have permission to create a project in a namespace before forking.
+ Forking a repository allows you to make changes without affecting the original project.
+ .col-lg-9
+ .fork-namespaces
+ - if @namespaces.present?
+ %label.label-light
+ %span
+ Click to fork the project to a user or group
+ - @namespaces.in_groups_of(6, false) do |group|
+ .row
+ - group.each do |namespace|
+ - avatar = namespace_icon(namespace, 100)
+ - if fork = namespace.find_fork_of(@project)
+ .fork-thumbnail.forked
+ = link_to project_path(fork) do
+ - if /no_((\w*)_)*avatar/.match(avatar)
+ .no-avatar
+ = icon 'question'
+ - else
+ = image_tag avatar
+ .caption
+ = namespace.human_name
+ - else
+ .fork-thumbnail
+ = link_to namespace_project_forks_path(@project.namespace, @project, namespace_key: namespace.id), method: "POST" do
+ - if /no_((\w*)_)*avatar/.match(avatar)
+ .no-avatar
+ = icon 'question'
+ - else
+ = image_tag avatar
+ .caption
+ = namespace.human_name
+ - else
+ %label.label-light
+ %span
+ No available namespaces to fork the project.
+ %br
+ %small
+ You must have permission to create a project in a namespace before forking.
-.save-project-loader.hide
- .center
- %h2
- %i.fa.fa-spinner.fa-spin
- Forking repository
- %p Please wait a moment, this page will automatically refresh when ready.
+ .save-project-loader.hide
+ .center
+ %h2
+ %i.fa.fa-spinner.fa-spin
+ Forking repository
+ %p Please wait a moment, this page will automatically refresh when ready.
diff --git a/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml b/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml
index 542827b2f15..331dc1fcc29 100644
--- a/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml
+++ b/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml
@@ -51,7 +51,7 @@
%td.duration
- if generic_commit_status.duration
= icon("clock-o")
- #{duration_in_words(generic_commit_status.finished_at, generic_commit_status.started_at)}
+ = time_interval_in_words(generic_commit_status.duration)
%td.timestamp
- if generic_commit_status.finished_at
diff --git a/app/views/projects/graphs/_head.html.haml b/app/views/projects/graphs/_head.html.haml
index ca347406dfe..45e51389c00 100644
--- a/app/views/projects/graphs/_head.html.haml
+++ b/app/views/projects/graphs/_head.html.haml
@@ -3,7 +3,7 @@
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('lib/chart.js')
- = page_specific_javascript_tag('graphs/application.js')
+ = page_specific_javascript_tag('graphs/graphs_bundle.js')
= nav_link(action: :show) do
= link_to 'Contributors', namespace_project_graph_path
= nav_link(action: :commits) do
diff --git a/app/views/projects/network/show.html.haml b/app/views/projects/network/show.html.haml
index 091af4df4a1..b2ece44d966 100644
--- a/app/views/projects/network/show.html.haml
+++ b/app/views/projects/network/show.html.haml
@@ -1,7 +1,7 @@
- page_title "Network", @ref
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('lib/raphael.js')
- = page_specific_javascript_tag('network/application.js')
+ = page_specific_javascript_tag('network/network_bundle.js')
= render "projects/commits/head"
= render "head"
%div{ class: container_class }
diff --git a/app/views/projects/pipelines_settings/show.html.haml b/app/views/projects/pipelines_settings/show.html.haml
new file mode 100644
index 00000000000..228bad36ebd
--- /dev/null
+++ b/app/views/projects/pipelines_settings/show.html.haml
@@ -0,0 +1,103 @@
+- page_title "CI/CD Pipelines"
+
+.row.prepend-top-default
+ .col-lg-3.profile-settings-sidebar
+ %h4.prepend-top-0
+ = page_title
+ .col-lg-9
+ %h5.prepend-top-0
+ Pipelines
+ = form_for @project, url: namespace_project_pipelines_settings_path(@project.namespace.becomes(Namespace), @project), remote: true, authenticity_token: true do |f|
+ %fieldset.builds-feature
+ - unless @repository.gitlab_ci_yml
+ .form-group
+ %p Pipelines need to be configured before you can begin using Continuous Integration.
+ = link_to 'Get started with CI/CD Pipelines', help_page_path('ci/quick_start/README'), class: 'btn btn-info'
+ .form-group
+ %p Get recent application code using the following command:
+ .radio
+ = f.label :build_allow_git_fetch_false do
+ = f.radio_button :build_allow_git_fetch, 'false'
+ %strong git clone
+ %br
+ %span.descr Slower but makes sure you have a clean dir before every build
+ .radio
+ = f.label :build_allow_git_fetch_true do
+ = f.radio_button :build_allow_git_fetch, 'true'
+ %strong git fetch
+ %br
+ %span.descr Faster
+
+ .form-group
+ = f.label :build_timeout_in_minutes, 'Timeout', class: 'label-light'
+ = f.number_field :build_timeout_in_minutes, class: 'form-control', min: '0'
+ %p.help-block per build in minutes
+ .form-group
+ = f.label :build_coverage_regex, "Test coverage parsing", class: 'label-light'
+ .input-group
+ %span.input-group-addon /
+ = f.text_field :build_coverage_regex, class: 'form-control', placeholder: '\(\d+.\d+\%\) covered'
+ %span.input-group-addon /
+ %p.help-block
+ We will use this regular expression to find test coverage output in build trace.
+ Leave blank if you want to disable this feature
+ .bs-callout.bs-callout-info
+ %p Below are examples of regex for existing tools:
+ %ul
+ %li
+ Simplecov (Ruby) -
+ %code \(\d+.\d+\%\) covered
+ %li
+ pytest-cov (Python) -
+ %code \d+\%\s*$
+ %li
+ phpunit --coverage-text --colors=never (PHP) -
+ %code ^\s*Lines:\s*\d+.\d+\%
+ %li
+ gcovr (C/C++) -
+ %code ^TOTAL.*\s+(\d+\%)$
+ %li
+ tap --coverage-report=text-summary (Node.js) -
+ %code ^Statements\s*:\s*([^%]+)
+
+ .form-group
+ .checkbox
+ = f.label :public_builds do
+ = f.check_box :public_builds
+ %strong Public pipelines
+ .help-block Allow everyone to access pipelines for Public and Internal projects
+
+ .form-group.append-bottom-default
+ = f.label :runners_token, "Runners token", class: 'label-light'
+ = f.text_field :runners_token, class: "form-control", placeholder: 'xEeFCaDAB89'
+ %p.help-block The secure token used to checkout project.
+
+ = f.submit 'Save changes', class: "btn btn-save"
+
+%hr
+
+.row.prepend-top-default
+ .col-lg-3.profile-settings-sidebar
+ %h4.prepend-top-0
+ Builds Badge
+ .col-lg-9
+ .prepend-top-10
+ .panel.panel-default
+ .panel-heading
+ %b Builds badge &middot;
+ = @build_badge.to_html
+ .pull-right
+ = render 'shared/ref_switcher', destination: 'badges', align_right: true
+ .panel-body
+ .row
+ .col-md-2.text-center
+ Markdown
+ .col-md-10.code.js-syntax-highlight
+ = highlight('.md', @build_badge.to_markdown)
+ .row
+ %hr
+ .row
+ .col-md-2.text-center
+ HTML
+ .col-md-10.code.js-syntax-highlight
+ = highlight('.html', @build_badge.to_html)
diff --git a/app/views/projects/services/_form.html.haml b/app/views/projects/services/_form.html.haml
index 166dc4a01fc..752fbc21a11 100644
--- a/app/views/projects/services/_form.html.haml
+++ b/app/views/projects/services/_form.html.haml
@@ -8,6 +8,7 @@
.col-lg-9
= form_for(@service, as: :service, url: namespace_project_service_path(@project.namespace, @project, @service.to_param), method: :put, html: { class: 'form-horizontal' }) do |form|
= render 'shared/service_settings', form: form
+
= form.submit 'Save changes', class: 'btn btn-save'
&nbsp;
- if @service.valid? && @service.activated?
diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml
index b7d7d5c5382..395d7af6cbb 100644
--- a/app/views/projects/tags/show.html.haml
+++ b/app/views/projects/tags/show.html.haml
@@ -1,36 +1,39 @@
+- @no_container = true
- page_title @tag.name, "Tags"
= render "projects/commits/head"
-.row-content-block
- .pull-right
- - if can?(current_user, :push_code, @project)
- = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, @tag.name), class: 'btn-grouped btn has-tooltip', title: 'Edit release notes' do
- = icon("pencil")
- = link_to namespace_project_tree_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped has-tooltip', title: 'Browse files' do
- = icon('files-o')
- = link_to namespace_project_commits_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped has-tooltip', title: 'Browse commits' do
- = icon('history')
- - if can? current_user, :download_code, @project
- = render 'projects/tags/download', ref: @tag.name, project: @project
- - if can?(current_user, :admin_project, @project)
- .pull-right
- = link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row grouped has-tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{@tag.name}' tag cannot be undone. Are you sure?" } do
- %i.fa.fa-trash-o
- .title
- %span.item-title= @tag.name
- - if @commit
- = render 'projects/branches/commit', commit: @commit, project: @project
- - else
- Cant find HEAD commit for this tag
- - if @tag.message.present?
- %pre.body
- = strip_gpg_signature(@tag.message)
+%div{ class: container_class }
+ .sub-header-block
+ .pull-right.tag-buttons
+ - if can?(current_user, :push_code, @project)
+ = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, @tag.name), class: 'btn has-tooltip', title: 'Edit release notes' do
+ = icon("pencil")
+ = link_to namespace_project_tree_path(@project.namespace, @project, @tag.name), class: 'btn has-tooltip', title: 'Browse files' do
+ = icon('files-o')
+ = link_to namespace_project_commits_path(@project.namespace, @project, @tag.name), class: 'btn has-tooltip', title: 'Browse commits' do
+ = icon('history')
+ - if can? current_user, :download_code, @project
+ = render 'projects/tags/download', ref: @tag.name, project: @project
+ - if can?(current_user, :admin_project, @project)
+ .pull-right
+ = link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row grouped has-tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{@tag.name}' tag cannot be undone. Are you sure?" } do
+ %i.fa.fa-trash-o
+ .tag-info.append-bottom-10
+ .title
+ %span.item-title= @tag.name
+ - if @commit
+ = render 'projects/branches/commit', commit: @commit, project: @project
+ - else
+ Cant find HEAD commit for this tag
+ - if @tag.message.present?
+ %pre.body
+ = strip_gpg_signature(@tag.message)
-.append-bottom-default.prepend-top-default
- - if @release.description.present?
- .description
- .wiki
- = preserve do
- = markdown @release.description
- - else
- This tag has no release notes.
+ .append-bottom-default.prepend-top-default
+ - if @release.description.present?
+ .description
+ .wiki
+ = preserve do
+ = markdown @release.description
+ - else
+ This tag has no release notes.
diff --git a/app/views/shared/_allow_request_access.html.haml b/app/views/shared/_allow_request_access.html.haml
new file mode 100644
index 00000000000..53a99a736c0
--- /dev/null
+++ b/app/views/shared/_allow_request_access.html.haml
@@ -0,0 +1,6 @@
+.checkbox
+ = form.label :request_access_enabled do
+ = form.check_box :request_access_enabled
+ %strong Allow users to request access
+ %br
+ %span.descr Allow users to request access if visibility is public or internal.
diff --git a/app/views/shared/_service_settings.html.haml b/app/views/shared/_service_settings.html.haml
index 4eaf7c2a025..5254d265918 100644
--- a/app/views/shared/_service_settings.html.haml
+++ b/app/views/shared/_service_settings.html.haml
@@ -10,69 +10,28 @@
.col-sm-10
= form.check_box :active
-- if @service.supported_events.length > 1
- .form-group
- = form.label :url, "Trigger", class: 'control-label'
- .col-sm-10
- - if @service.supported_events.include?("push")
- %div
- = form.check_box :push_events, class: 'pull-left'
- .prepend-left-20
- = form.label :push_events, class: 'list-label' do
- %strong Push events
- %p.light
- This url will be triggered by a push to the repository
- - if @service.supported_events.include?("tag_push")
- %div
- = form.check_box :tag_push_events, class: 'pull-left'
- .prepend-left-20
- = form.label :tag_push_events, class: 'list-label' do
- %strong Tag push events
- %p.light
- This url will be triggered when a new tag is pushed to the repository
- - if @service.supported_events.include?("note")
- %div
- = form.check_box :note_events, class: 'pull-left'
- .prepend-left-20
- = form.label :note_events, class: 'list-label' do
- %strong Comments
- %p.light
- This url will be triggered when someone adds a comment
- - if @service.supported_events.include?("issue")
- %div
- = form.check_box :issues_events, class: 'pull-left'
- .prepend-left-20
- = form.label :issues_events, class: 'list-label' do
- %strong Issues events
- %p.light
- This url will be triggered when an issue is created/updated/merged
- - if @service.supported_events.include?("merge_request")
- %div
- = form.check_box :merge_requests_events, class: 'pull-left'
- .prepend-left-20
- = form.label :merge_requests_events, class: 'list-label' do
- %strong Merge Request events
- %p.light
- This url will be triggered when a merge request is created/updated/merged
- - if @service.supported_events.include?("build")
- %div
- = form.check_box :build_events, class: 'pull-left'
- .prepend-left-20
- = form.label :build_events, class: 'list-label' do
- %strong Build events
- %p.light
- This url will be triggered when a build status changes
- - if @service.supported_events.include?("wiki_page")
- %div
- = form.check_box :wiki_page_events, class: 'pull-left'
- .prepend-left-20
- = form.label :wiki_page_events, class: 'list-label' do
- %strong Wiki Page events
- %p.light
- This url will be triggered when a wiki page is created/updated
+.form-group
+ = form.label :url, "Trigger", class: 'control-label'
+
+ .col-sm-10
+ - @service.supported_events.each do |event|
+ %div
+ = form.check_box service_event_field_name(event), class: 'pull-left'
+ .prepend-left-20
+ = form.label service_event_field_name(event), class: 'list-label' do
+ %strong
+ = event.humanize
+
+ - field = @service.event_field(event)
+
+ - if field
+ %p
+ = form.text_field field[:name], class: "form-control", placeholder: field[:placeholder]
+ %p.light
+ = service_event_description(event)
-- @service.fields.each do |field|
+- @service.global_fields.each do |field|
- type = field[:type]
- if type == 'fieldset'
diff --git a/app/views/shared/icons/_icon_fork.svg b/app/views/shared/icons/_icon_fork.svg
new file mode 100644
index 00000000000..420ffe3a55b
--- /dev/null
+++ b/app/views/shared/icons/_icon_fork.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><circle id="a" cx="4" cy="4" r="4"/><mask id="d" width="8" height="8" x="0" y="0" fill="#fff"><use xlink:href="#a"/></mask><circle id="b" cx="20" cy="4" r="4"/><mask id="e" width="8" height="8" x="0" y="0" fill="#fff"><use xlink:href="#b"/></mask><circle id="c" cx="12" cy="30" r="4"/><mask id="f" width="8" height="8" x="0" y="0" fill="#fff"><use xlink:href="#c"/></mask></defs><g fill="none" fill-rule="evenodd" transform="translate(8 3)"><path fill="#7E7E7E" d="M10 19.667c-4.14-1.29-7.389-5.878-7.389-5.878C2.274 13.353 2 12.545 2 12.01V6h4v5.509c0 .276.166.65.367.831 0 0 1.136 1.028 1.746 1.574C9.617 15.261 11.048 16 12.09 16c1.028 0 2.41-.723 3.858-2.048.588-.54 1.84-1.742 1.84-1.742a.784.784 0 0 0 .211-.502V6h4v6.008c0 .548-.259 1.349-.601 1.795 0 0-3.21 4.707-7.399 5.916V27h-4v-7.333z"/><use stroke="#7E7E7E" stroke-width="4" mask="url(#d)" xlink:href="#a"/><use stroke="#7E7E7E" stroke-width="4" mask="url(#e)" xlink:href="#b"/><use stroke="#7E7E7E" stroke-width="4" mask="url(#f)" xlink:href="#c"/></g></svg> \ No newline at end of file
diff --git a/app/views/shared/icons/_icon_status_cancel.svg b/app/views/shared/icons/_icon_status_cancel.svg
new file mode 100644
index 00000000000..6a0bc1490c4
--- /dev/null
+++ b/app/views/shared/icons/_icon_status_cancel.svg
@@ -0,0 +1,12 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs>
+ <circle id="a" cx="7" cy="7" r="7"/>
+ <mask id="b" width="14" height="14" x="0" y="0" fill="white">
+ <use xlink:href="#a"/>
+ </mask>
+ </defs>
+ <g fill="none" fill-rule="evenodd">
+ <use stroke="#5C5C5C" stroke-width="2" mask="url(#b)" xlink:href="#a"/>
+ <rect width="10" height="1" x="2" y="6.5" fill="#5C5C5C" transform="rotate(45 7 7)" rx=".3"/>
+ </g>
+</svg>
diff --git a/app/views/shared/icons/_icon_status_failed.svg b/app/views/shared/icons/_icon_status_failed.svg
new file mode 100644
index 00000000000..c41ca18cae7
--- /dev/null
+++ b/app/views/shared/icons/_icon_status_failed.svg
@@ -0,0 +1,12 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs>
+ <circle id="a" cx="7" cy="7" r="7"/>
+ <mask id="b" width="14" height="14" x="0" y="0" fill="white">
+ <use xlink:href="#a"/>
+ </mask>
+ </defs>
+ <g fill="none" fill-rule="evenodd">
+ <use stroke="#D22852" stroke-width="2" mask="url(#b)" xlink:href="#a"/>
+ <path fill="#D22852" d="M7.5,6.5 L7.5,4.30578971 C7.5,4.12531853 7.36809219,4 7.20537567,4 L6.79462433,4 C6.63904572,4 6.5,4.13690672 6.5,4.30578971 L6.5,6.5 L4.30578971,6.5 C4.12531853,6.5 4,6.63190781 4,6.79462433 L4,7.20537567 C4,7.36095428 4.13690672,7.5 4.30578971,7.5 L6.5,7.5 L6.5,9.69421029 C6.5,9.87468147 6.63190781,10 6.79462433,10 L7.20537567,10 C7.36095428,10 7.5,9.86309328 7.5,9.69421029 L7.5,7.5 L9.69421029,7.5 C9.87468147,7.5 10,7.36809219 10,7.20537567 L10,6.79462433 C10,6.63904572 9.86309328,6.5 9.69421029,6.5 L7.5,6.5 Z" transform="rotate(45 7 7)"/>
+ </g>
+</svg>
diff --git a/app/views/shared/icons/_icon_status_pending.svg b/app/views/shared/icons/_icon_status_pending.svg
new file mode 100644
index 00000000000..035cd8b4ccc
--- /dev/null
+++ b/app/views/shared/icons/_icon_status_pending.svg
@@ -0,0 +1,13 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs>
+ <circle id="a" cx="7" cy="7" r="7"/>
+ <mask id="b" width="14" height="14" x="0" y="0" fill="white">
+ <use xlink:href="#a"/>
+ </mask>
+ </defs>
+ <g fill="none" fill-rule="evenodd">
+ <use stroke="#E75E40" stroke-width="2" mask="url(#b)" xlink:href="#a"/>
+ <rect width="1" height="4" x="5" y="5" fill="#E75E40" rx=".3"/>
+ <rect width="1" height="4" x="8" y="5" fill="#E75E40" rx=".3"/>
+ </g>
+</svg>
diff --git a/app/views/shared/icons/_icon_status_running.svg b/app/views/shared/icons/_icon_status_running.svg
new file mode 100644
index 00000000000..a48b3a25099
--- /dev/null
+++ b/app/views/shared/icons/_icon_status_running.svg
@@ -0,0 +1,12 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs>
+ <circle id="a" cx="7" cy="7" r="7"/>
+ <mask id="b" width="14" height="14" x="0" y="0" fill="white">
+ <use xlink:href="#a"/>
+ </mask>
+ </defs>
+ <g fill="none" fill-rule="evenodd">
+ <use stroke="#2D9FD8" stroke-width="2" mask="url(#b)" xlink:href="#a"/>
+ <path fill="#2D9FD8" d="M7,3.00800862 C9.09023405,3.13960661 10.7448145,4.87657932 10.7448145,7 C10.7448145,9.209139 8.95395346,11 6.74481446,11 C5.4560962,11 4.30972054,10.3905589 3.57817301,9.44416214 L7,7 L7,3.00800862 Z"/>
+ </g>
+</svg>
diff --git a/app/views/shared/icons/_icon_status_success.svg b/app/views/shared/icons/_icon_status_success.svg
new file mode 100644
index 00000000000..260eab013a3
--- /dev/null
+++ b/app/views/shared/icons/_icon_status_success.svg
@@ -0,0 +1,15 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs>
+ <circle id="a" cx="7" cy="7" r="7"/>
+ <mask id="b" width="14" height="14" x="0" y="0" fill="white">
+ <use xlink:href="#a"/>
+ </mask>
+ </defs>
+ <g fill="none" fill-rule="evenodd">
+ <use stroke="#31AF64" stroke-width="2" mask="url(#b)" xlink:href="#a"/>
+ <g fill="#31AF64" transform="rotate(45 -.13 10.953)">
+ <rect width="1" height="5" x="2" rx=".3"/>
+ <rect width="3" height="1" y="4" rx=".3"/>
+ </g>
+ </g>
+</svg>
diff --git a/app/views/shared/icons/_icon_status_warning.svg b/app/views/shared/icons/_icon_status_warning.svg
new file mode 100644
index 00000000000..d47e7a1c93f
--- /dev/null
+++ b/app/views/shared/icons/_icon_status_warning.svg
@@ -0,0 +1,15 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <defs>
+ <circle id="a" cx="7" cy="7" r="7"/>
+ <mask id="b" width="14" height="14" x="0" y="0" fill="white">
+ <use xlink:href="#a"/>
+ </mask>
+ </defs>
+ <g fill="none" fill-rule="evenodd">
+ <g fill="#FF8A24" transform="translate(6 3)">
+ <rect width="2" height="5" rx=".5"/>
+ <rect width="2" height="2" y="6" rx=".5"/>
+ </g>
+ <use stroke="#FF8A24" stroke-width="2" mask="url(#b)" xlink:href="#a"/>
+ </g>
+</svg>
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index db2b4885861..c7f39868e71 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -2,7 +2,7 @@
- page_description @user.bio
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('lib/d3.js')
- = page_specific_javascript_tag('users/application.js')
+ = page_specific_javascript_tag('users/users_bundle.js')
- header_title @user.name, user_path(@user)
- @no_container = true
diff --git a/config/application.rb b/config/application.rb
index 50cc4235eda..06ebb14a5fe 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -81,10 +81,10 @@ module Gitlab
config.assets.precompile << "print.css"
config.assets.precompile << "notify.css"
config.assets.precompile << "mailers/*.css"
- config.assets.precompile << "graphs/application.js"
- config.assets.precompile << "users/application.js"
- config.assets.precompile << "network/application.js"
- config.assets.precompile << "profile/application.js"
+ config.assets.precompile << "graphs/graphs_bundle.js"
+ config.assets.precompile << "users/users_bundle.js"
+ config.assets.precompile << "network/network_bundle.js"
+ config.assets.precompile << "profile/profile_bundle.js"
config.assets.precompile << "lib/utils/*.js"
config.assets.precompile << "lib/*.js"
config.assets.precompile << "u2f.js"
diff --git a/config/initializers/secure_headers.rb b/config/initializers/secure_headers.rb
index 9fd24a667cc..253e3cf7410 100644
--- a/config/initializers/secure_headers.rb
+++ b/config/initializers/secure_headers.rb
@@ -4,14 +4,7 @@
require 'gitlab/current_settings'
include Gitlab::CurrentSettings
-# If Sentry is enabled and the Rails app is running in production mode,
-# this will construct the Report URI for Sentry.
-if Rails.env.production? && current_application_settings.sentry_enabled
- uri = URI.parse(current_application_settings.sentry_dsn)
- CSP_REPORT_URI = "#{uri.scheme}://#{uri.host}/api#{uri.path}/csp-report/?sentry_key=#{uri.user}"
-else
- CSP_REPORT_URI = ''
-end
+CSP_REPORT_URI = ''
# Content Security Policy Headers
# For more information on CSP see:
@@ -71,10 +64,7 @@ SecureHeaders::Configuration.default do |config|
upgrade_insecure_requests: true
}
- # Reports are sent to Sentry if it's enabled.
- if current_application_settings.sentry_enabled
- config.csp[:report_uri] = %W(#{CSP_REPORT_URI})
- end
+ config.csp[:report_uri] = %W(#{CSP_REPORT_URI})
# Allow Bootstrap Linter in development mode.
if Rails.env.development?
diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb
index b40fd81ff96..5e839327e7a 100644
--- a/config/initializers/sidekiq.rb
+++ b/config/initializers/sidekiq.rb
@@ -18,7 +18,8 @@ Sidekiq.configure_server do |config|
if cron_jobs[k] && cron_jobs_required_keys.all? { |s| cron_jobs[k].key?(s) }
cron_jobs[k]['class'] = cron_jobs[k].delete('job_class')
else
- raise("Invalid cron_jobs config key: '#{k}'. Check your gitlab config file.")
+ cron_jobs.delete(k)
+ Rails.logger.error("Invalid cron_jobs config key: '#{k}'. Check your gitlab config file.")
end
end
Sidekiq::Cron::Job.load_from_hash! cron_jobs
diff --git a/config/routes.rb b/config/routes.rb
index be651d8903f..21f3585bacd 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -89,11 +89,10 @@ Rails.application.routes.draw do
mount Grack::AuthSpawner, at: '/', constraints: lambda { |request| /[-\/\w\.]+\.git\/(info\/lfs|gitlab-lfs)/.match(request.path_info) }, via: [:get, :post, :put]
# Help
-
- get 'help' => 'help#index'
- get 'help/*path' => 'help#show', as: :help_page
- get 'help/shortcuts'
- get 'help/ui' => 'help#ui'
+ get 'help' => 'help#index'
+ get 'help/shortcuts' => 'help#shortcuts'
+ get 'help/ui' => 'help#ui'
+ get 'help/*path' => 'help#show', as: :help_page
#
# Global snippets
@@ -733,6 +732,10 @@ Rails.application.routes.draw do
resources :triggers, only: [:index, :create, :destroy]
resources :pipelines, only: [:index, :new, :create, :show] do
+ collection do
+ resource :pipelines_settings, path: 'settings', only: [:show, :update]
+ end
+
member do
post :cancel
post :retry
diff --git a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb
index ac7eac0ea7c..611767ac7fe 100644
--- a/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb
+++ b/db/migrate/20160302152808_remove_wrong_import_url_from_projects.rb
@@ -7,7 +7,13 @@ class RemoveWrongImportUrlFromProjects < ActiveRecord::Migration
class ProjectImportDataFake
extend AttrEncrypted
attr_accessor :credentials
- attr_encrypted :credentials, key: Gitlab::Application.secrets.db_key_base, marshal: true, encode: true, :mode => :per_attribute_iv_and_salt
+ attr_encrypted :credentials,
+ key: Gitlab::Application.secrets.db_key_base,
+ marshal: true,
+ encode: true,
+ :mode => :per_attribute_iv_and_salt,
+ insecure_mode: true,
+ algorithm: 'aes-256-cbc'
end
def up
diff --git a/db/migrate/20160715154212_add_request_access_enabled_to_projects.rb b/db/migrate/20160715154212_add_request_access_enabled_to_projects.rb
new file mode 100644
index 00000000000..bf0131c6d76
--- /dev/null
+++ b/db/migrate/20160715154212_add_request_access_enabled_to_projects.rb
@@ -0,0 +1,12 @@
+class AddRequestAccessEnabledToProjects < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+ disable_ddl_transaction!
+
+ def up
+ add_column_with_default :projects, :request_access_enabled, :boolean, default: true
+ end
+
+ def down
+ remove_column :projects, :request_access_enabled
+ end
+end
diff --git a/db/migrate/20160715204316_add_request_access_enabled_to_groups.rb b/db/migrate/20160715204316_add_request_access_enabled_to_groups.rb
new file mode 100644
index 00000000000..e7b14cd3ee2
--- /dev/null
+++ b/db/migrate/20160715204316_add_request_access_enabled_to_groups.rb
@@ -0,0 +1,12 @@
+class AddRequestAccessEnabledToGroups < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+ disable_ddl_transaction!
+
+ def up
+ add_column_with_default :namespaces, :request_access_enabled, :boolean, default: true
+ end
+
+ def down
+ remove_column :namespaces, :request_access_enabled
+ end
+end
diff --git a/db/migrate/20160718153603_add_has_external_wiki_to_projects.rb b/db/migrate/20160718153603_add_has_external_wiki_to_projects.rb
new file mode 100644
index 00000000000..55a3e954292
--- /dev/null
+++ b/db/migrate/20160718153603_add_has_external_wiki_to_projects.rb
@@ -0,0 +1,7 @@
+class AddHasExternalWikiToProjects < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ def change
+ add_column :projects, :has_external_wiki, :boolean
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 8882377f9f4..72780fb8d03 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: 20160716115710) do
+ActiveRecord::Schema.define(version: 20160718153603) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -664,16 +664,17 @@ ActiveRecord::Schema.define(version: 20160716115710) do
add_index "milestones", ["title"], name: "index_milestones_on_title_trigram", using: :gin, opclasses: {"title"=>"gin_trgm_ops"}
create_table "namespaces", force: :cascade do |t|
- t.string "name", null: false
- t.string "path", null: false
+ t.string "name", null: false
+ t.string "path", null: false
t.integer "owner_id"
t.datetime "created_at"
t.datetime "updated_at"
t.string "type"
- t.string "description", default: "", null: false
+ t.string "description", default: "", null: false
t.string "avatar"
- t.boolean "share_with_group_lock", default: false
- t.integer "visibility_level", default: 20, null: false
+ t.boolean "share_with_group_lock", default: false
+ t.integer "visibility_level", default: 20, null: false
+ t.boolean "request_access_enabled", default: true, null: false
end
add_index "namespaces", ["created_at", "id"], name: "index_namespaces_on_created_at_and_id", using: :btree
@@ -842,6 +843,8 @@ ActiveRecord::Schema.define(version: 20160716115710) do
t.boolean "only_allow_merge_if_build_succeeds", default: false, null: false
t.boolean "has_external_issue_tracker"
t.string "repository_storage", default: "default", null: false
+ t.boolean "has_external_wiki"
+ t.boolean "request_access_enabled", default: true, null: false
end
add_index "projects", ["builds_enabled", "shared_runners_enabled"], name: "index_projects_on_builds_enabled_and_shared_runners_enabled", using: :btree
diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md
index 137b080a8f7..4bf610f0e9a 100644
--- a/doc/ci/variables/README.md
+++ b/doc/ci/variables/README.md
@@ -18,25 +18,35 @@ The `API_TOKEN` will take the Secure Variable value: `SECURE`.
### Predefined variables (Environment Variables)
-| Variable | Runner | Description |
-|-------------------------|-----|--------|
-| **CI** | 0.4 | Mark that build is executed in CI environment |
-| **GITLAB_CI** | all | Mark that build is executed in GitLab CI environment |
-| **CI_SERVER** | all | Mark that build is executed in CI environment |
-| **CI_SERVER_NAME** | all | CI server that is used to coordinate builds |
-| **CI_SERVER_VERSION** | all | Not yet defined |
-| **CI_SERVER_REVISION** | all | Not yet defined |
-| **CI_BUILD_REF** | all | The commit revision for which project is built |
-| **CI_BUILD_TAG** | 0.5 | The commit tag name. Present only when building tags. |
-| **CI_BUILD_NAME** | 0.5 | The name of the build as defined in `.gitlab-ci.yml` |
-| **CI_BUILD_STAGE** | 0.5 | The name of the stage as defined in `.gitlab-ci.yml` |
-| **CI_BUILD_REF_NAME** | all | The branch or tag name for which project is built |
-| **CI_BUILD_ID** | all | The unique id of the current build that GitLab CI uses internally |
-| **CI_BUILD_REPO** | all | The URL to clone the Git repository |
-| **CI_BUILD_TRIGGERED** | 0.5 | The flag to indicate that build was [triggered] |
-| **CI_BUILD_TOKEN** | 1.2 | Token used for authenticating with the GitLab Container Registry |
-| **CI_PROJECT_ID** | all | The unique id of the current project that GitLab CI uses internally |
-| **CI_PROJECT_DIR** | all | The full path where the repository is cloned and where the build is ran |
+| Variable | GitLab | Runner | Description |
+|-------------------------|--------|--------|-------------|
+| **CI** | all | 0.4 | Mark that build is executed in CI environment |
+| **GITLAB_CI** | all | all | Mark that build is executed in GitLab CI environment |
+| **CI_SERVER** | all | all | Mark that build is executed in CI environment |
+| **CI_SERVER_NAME** | all | all | The name of CI server that is used to coordinate builds |
+| **CI_SERVER_VERSION** | all | all | GitLab version that is used to schedule builds |
+| **CI_SERVER_REVISION** | all | all | GitLab revision that is used to schedule builds |
+| **CI_BUILD_ID** | all | all | The unique id of the current build that GitLab CI uses internally |
+| **CI_BUILD_REF** | all | all | The commit revision for which project is built |
+| **CI_BUILD_TAG** | all | 0.5 | The commit tag name. Present only when building tags. |
+| **CI_BUILD_NAME** | all | 0.5 | The name of the build as defined in `.gitlab-ci.yml` |
+| **CI_BUILD_STAGE** | all | 0.5 | The name of the stage as defined in `.gitlab-ci.yml` |
+| **CI_BUILD_REF_NAME** | all | all | The branch or tag name for which project is built |
+| **CI_BUILD_REPO** | all | all | The URL to clone the Git repository |
+| **CI_BUILD_TRIGGERED** | all | 0.5 | The flag to indicate that build was [triggered] |
+| **CI_BUILD_TOKEN** | all | 1.2 | Token used for authenticating with the GitLab Container Registry |
+| **CI_PIPELINE_ID** | 8.10 | 0.5 | The unique id of the current pipeline that GitLab CI uses internally |
+| **CI_PROJECT_ID** | all | all | The unique id of the current project that GitLab CI uses internally |
+| **CI_PROJECT_NAME** | 8.10 | 0.5 | The project name that is currently being built |
+| **CI_PROJECT_NAMESPACE**| 8.10 | 0.5 | The project namespace that is currently being built |
+| **CI_PROJECT_PATH** | 8.10 | 0.5 | The namespace with project name |
+| **CI_PROJECT_URL** | 8.10 | 0.5 | The HTTP address to access project |
+| **CI_PROJECT_DIR** | all | all | The full path where the repository is cloned and where the build is run |
+| **CI_REGISTRY** | 8.10 | 0.5 | If the Container Registry is enabled it returns the address of GitLab's Container Registry |
+| **CI_REGISTRY_IMAGE** | 8.10 | 0.5 | If the Container Registry is enabled for the project it returnes the address of the registry tied to the specific project |
+| **CI_RUNNER_ID** | 8.10 | 0.5 | The unique id of runner being used |
+| **CI_RUNNER_DESCRIPTION** | 8.10 | 0.5 | The description of the runner as saved in GitLab |
+| **CI_RUNNER_TAGS** | 8.10 | 0.5 | The defined runner tags |
**Some of the variables are only available when using runner with at least defined version.**
@@ -46,18 +56,28 @@ Example values:
export CI_BUILD_ID="50"
export CI_BUILD_REF="1ecfd275763eff1d6b4844ea3168962458c9f27a"
export CI_BUILD_REF_NAME="master"
-export CI_BUILD_REPO="https://gitlab.com/gitlab-org/gitlab-ce.git"
+export CI_BUILD_REPO="https://gitab-ci-token:abcde-1234ABCD5678ef@gitlab.com/gitlab-org/gitlab-ce.git"
export CI_BUILD_TAG="1.0.0"
export CI_BUILD_NAME="spec:other"
export CI_BUILD_STAGE="test"
export CI_BUILD_TRIGGERED="true"
export CI_BUILD_TOKEN="abcde-1234ABCD5678ef"
-export CI_PROJECT_DIR="/builds/gitlab-org/gitlab-ce"
+export CI_PIPELINE_ID="1000"
export CI_PROJECT_ID="34"
+export CI_PROJECT_DIR="/builds/gitlab-org/gitlab-ce"
+export CI_PROJECT_NAME="gitlab-ce"
+export CI_PROJECT_NAMESPACE="gitlab-org"
+export CI_PROJECT_PATH="gitlab-org/gitlab-ce"
+export CI_PROJECT_URL="https://gitlab.com/gitlab-org/gitlab-ce"
+export CI_REGISTRY="registry.gitlab.com"
+export CI_REGISTRY_IMAGE="registry.gitlab.com/gitlab-org/gitlab-ce"
+export CI_RUNNER_ID="10"
+export CI_RUNNER_DESCRIPTION="my runner"
+export CI_RUNNER_TAGS="docker, linux"
export CI_SERVER="yes"
-export CI_SERVER_NAME="GitLab CI"
-export CI_SERVER_REVISION=""
-export CI_SERVER_VERSION=""
+export CI_SERVER_NAME="GitLab"
+export CI_SERVER_REVISION="8.9.0"
+export CI_SERVER_VERSION="70606bf"
```
### YAML-defined variables
diff --git a/doc/development/migration_style_guide.md b/doc/development/migration_style_guide.md
index e2ca46504e7..b8fab3aaff7 100644
--- a/doc/development/migration_style_guide.md
+++ b/doc/development/migration_style_guide.md
@@ -11,7 +11,8 @@ migrations are written carefully, can be applied online and adhere to the style
Migrations should not require GitLab installations to be taken offline unless
_absolutely_ necessary. If a migration requires downtime this should be
clearly mentioned during the review process as well as being documented in the
-monthly release post.
+monthly release post. For more information see the "Downtime Tagging" section
+below.
When writing your migrations, also consider that databases might have stale data
or inconsistencies and guard for that. Try to make as little assumptions as possible
@@ -20,35 +21,34 @@ about the state of the database.
Please don't depend on GitLab specific code since it can change in future versions.
If needed copy-paste GitLab code into the migration to make it forward compatible.
-## Comments in the migration
+## Downtime Tagging
-Each migration you write needs to have the two following pieces of information
-as comments.
+Every migration must specify if it requires downtime or not, and if it should
+require downtime it must also specify a reason for this. To do so, add the
+following two constants to the migration class' body:
-### Online, Offline, errors?
+* `DOWNTIME`: a boolean that when set to `true` indicates the migration requires
+ downtime.
+* `DOWNTIME_REASON`: a String containing the reason for the migration requiring
+ downtime. This constant **must** be set when `DOWNTIME` is set to `true`.
-First, you need to provide information on whether the migration can be applied:
+For example:
-1. online without errors (works on previous version and new one)
-2. online with errors on old instances after migrating
-3. online with errors on new instances while migrating
-4. offline (needs to happen without app servers to prevent db corruption)
-
-For example:
-
-```
-# Migration type: online without errors (works on previous version and new one)
+```ruby
class MyMigration < ActiveRecord::Migration
-...
-```
+ DOWNTIME = true
+ DOWNTIME_REASON = 'This migration requires downtime because ...'
-It is always preferable to have a migration run online. If you expect the migration
-to take particularly long (for instance, if it loops through all notes),
-this is valuable information to add.
+ def change
+ ...
+ end
+end
+```
-If you don't provide the information it means that a migration is safe to run online.
+It is an error (that is, CI will fail) if the `DOWNTIME` constant is missing
+from a migration class.
-### Reversibility
+## Reversibility
Your migration should be reversible. This is very important, as it should
be possible to downgrade in case of a vulnerability or bugs.
@@ -100,7 +100,7 @@ value of `10` you'd write the following:
class MyMigration < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
disable_ddl_transaction!
-
+
def up
add_column_with_default(:projects, :foo, :integer, default: 10)
end
diff --git a/doc/integration/slack.md b/doc/integration/slack.md
index f6ba80f46d5..11f956fed3e 100644
--- a/doc/integration/slack.md
+++ b/doc/integration/slack.md
@@ -26,14 +26,13 @@ After Slack is ready we need to setup GitLab. Here are the steps to achieve this
1. Navigate to Settings -> Services -> Slack
-1. Pick the triggers you want to activate
+1. Pick the triggers you want to activate and respective channel (`#general` by default).
1. Fill in your Slack details
- Webhook: Paste the Webhook URL from the step above
- Username: Fill this in if you want to change the username of the bot
- - Channel: Fill this in if you want to change the channel where the messages will be posted
- Mark it as active
-
+
1. Save your settings
Have fun :)
diff --git a/doc/project_services/img/slack_configuration.png b/doc/project_services/img/slack_configuration.png
new file mode 100644
index 00000000000..d3ebe5969af
--- /dev/null
+++ b/doc/project_services/img/slack_configuration.png
Binary files differ
diff --git a/doc/project_services/project_services.md b/doc/project_services/project_services.md
index e15d5db3253..4442b7c1742 100644
--- a/doc/project_services/project_services.md
+++ b/doc/project_services/project_services.md
@@ -45,7 +45,7 @@ further configuration instructions and details. Contributions are welcome.
| PivotalTracker | Project Management Software (Source Commits Endpoint) |
| Pushover | Pushover makes it easy to get real-time notifications on your Android device, iPhone, iPad, and Desktop |
| [Redmine](redmine.md) | Redmine issue tracker |
-| Slack | A team communication tool for the 21st century |
+| [Slack](slack.md) | A team communication tool for the 21st century |
## Services Templates
diff --git a/doc/project_services/slack.md b/doc/project_services/slack.md
new file mode 100644
index 00000000000..4ed33838f7c
--- /dev/null
+++ b/doc/project_services/slack.md
@@ -0,0 +1,26 @@
+# Slack Service
+
+Go to your project's **Settings > Services > Slack** and you will see a checkbox with the following events that can be triggered:
+
+* Push
+* Issue
+* Merge request
+* Note
+* Tag push
+* Build
+* Wiki page
+
+Below each of these event checkboxes you will have an input to insert which Slack channel do you want to send that event message,
+`#general` channel is default.
+
+
+![Slack configuration](img/slack_configuration.png)
+
+
+| Field | Description |
+| ----- | ----------- |
+| `Webhook` | The incoming webhook url which you have to setup on slack. (https://my.slack.com/services/new/incoming-webhook/) |
+| `Username` | Optional username which can be on messages sent to slack. |
+| `notify only broken builds` | Notify only about broken builds, when build events are marked to be sent.|
+
+
diff --git a/doc/workflow/add-user/add-user.md b/doc/workflow/add-user/add-user.md
index 0537ce0bcd4..e541111d7b3 100644
--- a/doc/workflow/add-user/add-user.md
+++ b/doc/workflow/add-user/add-user.md
@@ -90,6 +90,9 @@ GitLab account using the same e-mail address the invitation was sent to.
## Request access to a project
+As a project owner you can enable or disable non members to request access to
+your project. Go to the project settings and click on **Allow users to request access**.
+
As a user, you can request to be a member of a project. Go to the project you'd
like to be a member of, and click the **Request Access** button on the right
side of your screen.
diff --git a/doc/workflow/groups.md b/doc/workflow/groups.md
index 9b50286b179..a693cc3d0fd 100644
--- a/doc/workflow/groups.md
+++ b/doc/workflow/groups.md
@@ -53,6 +53,9 @@ If necessary, you can increase the access level of an individual user for a spec
## Requesting access to a group
+As a group owner you can enable or disable non members to request access to
+your group. Go to the group settings and click on **Allow users to request access**.
+
As a user, you can request to be a member of a group. Go to the group you'd
like to be a member of, and click the **Request Access** button on the right
side of your screen.
diff --git a/features/steps/admin/settings.rb b/features/steps/admin/settings.rb
index 037f7494a77..03f87df7a60 100644
--- a/features/steps/admin/settings.rb
+++ b/features/steps/admin/settings.rb
@@ -27,19 +27,19 @@ class Spinach::Features::AdminSettings < Spinach::FeatureSteps
step 'I check all events and submit form' do
page.check('Active')
- page.check('Push events')
- page.check('Tag push events')
- page.check('Comments')
- page.check('Issues events')
- page.check('Merge Request events')
- page.check('Build events')
+ page.check('Push')
+ page.check('Tag push')
+ page.check('Note')
+ page.check('Issue')
+ page.check('Merge request')
+ page.check('Build')
click_on 'Save'
end
step 'I fill out Slack settings' do
fill_in 'Webhook', with: 'http://localhost'
fill_in 'Username', with: 'test_user'
- fill_in 'Channel', with: '#test_channel'
+ fill_in 'service_push_channel', with: '#test_channel'
page.check('Notify only broken builds')
end
@@ -56,6 +56,6 @@ class Spinach::Features::AdminSettings < Spinach::FeatureSteps
step 'I should see Slack settings saved' do
expect(find_field('Webhook').value).to eq 'http://localhost'
expect(find_field('Username').value).to eq 'test_user'
- expect(find_field('Channel').value).to eq '#test_channel'
+ expect(find('#service_push_channel').value).to eq '#test_channel'
end
end
diff --git a/generator_templates/active_record/migration/create_table_migration.rb b/generator_templates/active_record/migration/create_table_migration.rb
index 27acc75dcc4..aad8626a720 100644
--- a/generator_templates/active_record/migration/create_table_migration.rb
+++ b/generator_templates/active_record/migration/create_table_migration.rb
@@ -4,6 +4,14 @@
class <%= migration_class_name %> < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+
+ # When a migration requires downtime you **must** uncomment the following
+ # constant and define a short and easy to understand explanation as to why the
+ # migration requires downtime.
+ # DOWNTIME_REASON = ''
+
# When using the methods "add_concurrent_index" or "add_column_with_default"
# you must disable the use of transactions as these methods can not run in an
# existing transaction. When using "add_concurrent_index" make sure that this
diff --git a/generator_templates/active_record/migration/migration.rb b/generator_templates/active_record/migration/migration.rb
index 06bdea11367..825bc8bdf61 100644
--- a/generator_templates/active_record/migration/migration.rb
+++ b/generator_templates/active_record/migration/migration.rb
@@ -4,6 +4,14 @@
class <%= migration_class_name %> < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+
+ # When a migration requires downtime you **must** uncomment the following
+ # constant and define a short and easy to understand explanation as to why the
+ # migration requires downtime.
+ # DOWNTIME_REASON = ''
+
# When using the methods "add_concurrent_index" or "add_column_with_default"
# you must disable the use of transactions as these methods can not run in an
# existing transaction. When using "add_concurrent_index" make sure that this
diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb
index acb4812b5cf..4df6ca8333e 100644
--- a/lib/api/commit_statuses.rb
+++ b/lib/api/commit_statuses.rb
@@ -24,7 +24,7 @@ module API
pipelines = user_project.pipelines.where(sha: params[:sha])
statuses = ::CommitStatus.where(pipeline: pipelines)
- statuses = statuses.latest unless parse_boolean(params[:all])
+ statuses = statuses.latest unless to_boolean(params[:all])
statuses = statuses.where(ref: params[:ref]) if params[:ref].present?
statuses = statuses.where(stage: params[:stage]) if params[:stage].present?
statuses = statuses.where(name: params[:name]) if params[:name].present?
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index d6e4eb2afd7..130509cdad6 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -5,10 +5,6 @@ module API
SUDO_HEADER = "HTTP_SUDO"
SUDO_PARAM = :sudo
- def parse_boolean(value)
- [ true, 1, '1', 't', 'T', 'true', 'TRUE', 'on', 'ON' ].include?(value)
- end
-
def to_boolean(value)
return true if value =~ /^(true|t|yes|y|1|on)$/i
return false if value =~ /^(false|f|no|n|0|off)$/i
@@ -297,7 +293,7 @@ module API
def filter_projects(projects)
# If the archived parameter is passed, limit results accordingly
if params[:archived].present?
- projects = projects.where(archived: parse_boolean(params[:archived]))
+ projects = projects.where(archived: to_boolean(params[:archived]))
end
if params[:search].present?
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index 4fcdf8968c9..2b685621da9 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -242,7 +242,7 @@ module API
should_remove_source_branch: params[:should_remove_source_branch]
}
- if parse_boolean(params[:merge_when_build_succeeds]) && merge_request.pipeline && merge_request.pipeline.active?
+ if to_boolean(params[:merge_when_build_succeeds]) && merge_request.pipeline && merge_request.pipeline.active?
::MergeRequests::MergeWhenBuildSucceedsService.new(merge_request.target_project, current_user, merge_params).
execute(merge_request)
else
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 6d2a6f3946c..8fed7db8803 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -8,7 +8,7 @@ module API
def map_public_to_visibility_level(attrs)
publik = attrs.delete(:public)
if publik.present? && !attrs[:visibility_level].present?
- publik = parse_boolean(publik)
+ publik = to_boolean(publik)
# Since setting the public attribute to private could mean either
# private or internal, use the more conservative option, private.
attrs[:visibility_level] = (publik == true) ? Gitlab::VisibilityLevel::PUBLIC : Gitlab::VisibilityLevel::PRIVATE
diff --git a/lib/banzai/filter/relative_link_filter.rb b/lib/banzai/filter/relative_link_filter.rb
index c78da404607..21ed0410f7f 100644
--- a/lib/banzai/filter/relative_link_filter.rb
+++ b/lib/banzai/filter/relative_link_filter.rb
@@ -112,8 +112,7 @@ module Banzai
end
def current_commit
- @current_commit ||= context[:commit] ||
- ref ? repository.commit(ref) : repository.head_commit
+ @current_commit ||= context[:commit] || ref ? repository.commit(ref) : repository.head_commit
end
def relative_url_root
diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb
index 41449d720b3..83afed9f49f 100644
--- a/lib/ci/gitlab_ci_yaml_processor.rb
+++ b/lib/ci/gitlab_ci_yaml_processor.rb
@@ -44,23 +44,51 @@ module Ci
end
def builds_for_ref(ref, tag = false, trigger_request = nil)
- jobs_for_ref(ref, tag, trigger_request).map do |name, job|
- build_job(name, job)
+ jobs_for_ref(ref, tag, trigger_request).map do |name, _|
+ build_attributes(name)
end
end
def builds_for_stage_and_ref(stage, ref, tag = false, trigger_request = nil)
- jobs_for_stage_and_ref(stage, ref, tag, trigger_request).map do |name, job|
- build_job(name, job)
+ jobs_for_stage_and_ref(stage, ref, tag, trigger_request).map do |name, _|
+ build_attributes(name)
end
end
def builds
- @jobs.map do |name, job|
- build_job(name, job)
+ @jobs.map do |name, _|
+ build_attributes(name)
end
end
+ def build_attributes(name)
+ job = @jobs[name.to_sym] || {}
+ {
+ stage_idx: @stages.index(job[:stage]),
+ stage: job[:stage],
+ ##
+ # Refactoring note:
+ # - before script behaves differently than after script
+ # - after script returns an array of commands
+ # - before script should be a concatenated command
+ commands: [job[:before_script] || @before_script, job[:script]].flatten.compact.join("\n"),
+ tag_list: job[:tags] || [],
+ name: name,
+ allow_failure: job[:allow_failure] || false,
+ when: job[:when] || 'on_success',
+ environment: job[:environment],
+ yaml_variables: yaml_variables(name),
+ options: {
+ image: job[:image] || @image,
+ services: job[:services] || @services,
+ artifacts: job[:artifacts],
+ cache: job[:cache] || @cache,
+ dependencies: job[:dependencies],
+ after_script: job[:after_script] || @after_script,
+ }.compact
+ }
+ end
+
private
def initial_parsing
@@ -89,33 +117,6 @@ module Ci
@jobs[name] = { stage: stage }.merge(job)
end
- def build_job(name, job)
- {
- stage_idx: @stages.index(job[:stage]),
- stage: job[:stage],
- ##
- # Refactoring note:
- # - before script behaves differently than after script
- # - after script returns an array of commands
- # - before script should be a concatenated command
- commands: [job[:before_script] || @before_script, job[:script]].flatten.compact.join("\n"),
- tag_list: job[:tags] || [],
- name: name,
- allow_failure: job[:allow_failure] || false,
- when: job[:when] || 'on_success',
- environment: job[:environment],
- yaml_variables: yaml_variables(name),
- options: {
- image: job[:image] || @image,
- services: job[:services] || @services,
- artifacts: job[:artifacts],
- cache: job[:cache] || @cache,
- dependencies: job[:dependencies],
- after_script: job[:after_script] || @after_script,
- }.compact
- }
- end
-
def yaml_variables(name)
variables = global_variables.merge(job_variables(name))
variables.map do |key, value|
diff --git a/lib/gitlab/checks/force_push.rb b/lib/gitlab/checks/force_push.rb
index dfa83a0eab3..5fe86553bd0 100644
--- a/lib/gitlab/checks/force_push.rb
+++ b/lib/gitlab/checks/force_push.rb
@@ -8,8 +8,8 @@ module Gitlab
if Gitlab::Git.blank_ref?(oldrev) || Gitlab::Git.blank_ref?(newrev)
false
else
- missed_refs, _ = Gitlab::Popen.popen(%W(#{Gitlab.config.git.bin_path} --git-dir=#{project.repository.path_to_repo} rev-list #{oldrev} ^#{newrev}))
- missed_refs.split("\n").size > 0
+ missed_ref, _ = Gitlab::Popen.popen(%W(#{Gitlab.config.git.bin_path} --git-dir=#{project.repository.path_to_repo} rev-list --max-count=1 #{oldrev} ^#{newrev}))
+ missed_ref.present?
end
end
end
diff --git a/lib/gitlab/diff/position.rb b/lib/gitlab/diff/position.rb
index 989fff8918e..2fdcf8d7838 100644
--- a/lib/gitlab/diff/position.rb
+++ b/lib/gitlab/diff/position.rb
@@ -73,8 +73,8 @@ module Gitlab
diff_refs.complete?
end
- def to_json
- JSON.generate(self.to_h)
+ def to_json(opts = nil)
+ JSON.generate(self.to_h, opts)
end
def type
diff --git a/lib/gitlab/downtime_check.rb b/lib/gitlab/downtime_check.rb
new file mode 100644
index 00000000000..ab9537ed7d7
--- /dev/null
+++ b/lib/gitlab/downtime_check.rb
@@ -0,0 +1,71 @@
+module Gitlab
+ # Checks if a set of migrations requires downtime or not.
+ class DowntimeCheck
+ # The constant containing the boolean that indicates if downtime is needed
+ # or not.
+ DOWNTIME_CONST = :DOWNTIME
+
+ # The constant that specifies the reason for the migration requiring
+ # downtime.
+ DOWNTIME_REASON_CONST = :DOWNTIME_REASON
+
+ # Checks the given migration paths and returns an Array of
+ # `Gitlab::DowntimeCheck::Message` instances.
+ #
+ # migrations - The migration file paths to check.
+ def check(migrations)
+ migrations.map do |path|
+ require(path)
+
+ migration_class = class_for_migration_file(path)
+
+ unless migration_class.const_defined?(DOWNTIME_CONST)
+ raise "The migration in #{path} does not specify if it requires " \
+ "downtime or not"
+ end
+
+ if online?(migration_class)
+ Message.new(path)
+ else
+ reason = downtime_reason(migration_class)
+
+ unless reason
+ raise "The migration in #{path} requires downtime but no reason " \
+ "was given"
+ end
+
+ Message.new(path, true, reason)
+ end
+ end
+ end
+
+ # Checks the given migrations and prints the results to STDOUT/STDERR.
+ #
+ # migrations - The migration file paths to check.
+ def check_and_print(migrations)
+ check(migrations).each do |message|
+ puts message.to_s # rubocop: disable Rails/Output
+ end
+ end
+
+ # Returns the class for the given migration file path.
+ def class_for_migration_file(path)
+ File.basename(path, File.extname(path)).split('_', 2).last.camelize.
+ constantize
+ end
+
+ # Returns true if the given migration can be performed without downtime.
+ def online?(migration)
+ migration.const_get(DOWNTIME_CONST) == false
+ end
+
+ # Returns the downtime reason, or nil if none was defined.
+ def downtime_reason(migration)
+ if migration.const_defined?(DOWNTIME_REASON_CONST)
+ migration.const_get(DOWNTIME_REASON_CONST)
+ else
+ nil
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/downtime_check/message.rb b/lib/gitlab/downtime_check/message.rb
new file mode 100644
index 00000000000..4446e921e0d
--- /dev/null
+++ b/lib/gitlab/downtime_check/message.rb
@@ -0,0 +1,28 @@
+module Gitlab
+ class DowntimeCheck
+ class Message
+ attr_reader :path, :offline, :reason
+
+ OFFLINE = "\e[32moffline\e[0m"
+ ONLINE = "\e[31monline\e[0m"
+
+ # path - The file path of the migration.
+ # offline - When set to `true` the migration will require downtime.
+ # reason - The reason as to why the migration requires downtime.
+ def initialize(path, offline = false, reason = nil)
+ @path = path
+ @offline = offline
+ @reason = reason
+ end
+
+ def to_s
+ label = offline ? OFFLINE : ONLINE
+
+ message = "[#{label}]: #{path}"
+ message += ": #{reason}" if reason
+
+ message
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb
index 308f23bc9bc..8e8f39d9cb2 100644
--- a/lib/gitlab/git_access.rb
+++ b/lib/gitlab/git_access.rb
@@ -110,6 +110,7 @@ module Gitlab
def deploy_key_can_read_project?
if deploy_key
+ return true if project.public?
deploy_key.projects.include?(project)
else
false
diff --git a/lib/gitlab/git_access_status.rb b/lib/gitlab/git_access_status.rb
index 5a806ff6e0d..09bb01be694 100644
--- a/lib/gitlab/git_access_status.rb
+++ b/lib/gitlab/git_access_status.rb
@@ -8,8 +8,8 @@ module Gitlab
@message = message
end
- def to_json
- { status: @status, message: @message }.to_json
+ def to_json(opts = nil)
+ { status: @status, message: @message }.to_json(opts)
end
end
end
diff --git a/lib/gitlab/gon_helper.rb b/lib/gitlab/gon_helper.rb
index d4f12cb1df9..c5a11148d33 100644
--- a/lib/gitlab/gon_helper.rb
+++ b/lib/gitlab/gon_helper.rb
@@ -5,7 +5,7 @@ module Gitlab
gon.default_avatar_url = URI::join(Gitlab.config.gitlab.url, ActionController::Base.helpers.image_path('no_avatar.png')).to_s
gon.max_file_size = current_application_settings.max_attachment_size
gon.relative_url_root = Gitlab.config.gitlab.relative_url_root
- gon.shortcuts_path = help_shortcuts_path
+ gon.shortcuts_path = help_page_path('shortcuts')
gon.user_color_scheme = Gitlab::ColorSchemes.for_user(current_user).css_class
gon.award_menu_url = emojis_path
diff --git a/lib/gitlab/import_export/avatar_restorer.rb b/lib/gitlab/import_export/avatar_restorer.rb
new file mode 100644
index 00000000000..352539eb594
--- /dev/null
+++ b/lib/gitlab/import_export/avatar_restorer.rb
@@ -0,0 +1,31 @@
+module Gitlab
+ module ImportExport
+ class AvatarRestorer
+
+ def initialize(project:, shared:)
+ @project = project
+ @shared = shared
+ end
+
+ def restore
+ return true unless avatar_export_file
+
+ @project.avatar = File.open(avatar_export_file)
+ @project.save!
+ rescue => e
+ @shared.error(e)
+ false
+ end
+
+ private
+
+ def avatar_export_file
+ @avatar_export_file ||= Dir["#{avatar_export_path}/*"].first
+ end
+
+ def avatar_export_path
+ File.join(@shared.export_path, 'avatar')
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/import_export/avatar_saver.rb b/lib/gitlab/import_export/avatar_saver.rb
new file mode 100644
index 00000000000..998c21e2586
--- /dev/null
+++ b/lib/gitlab/import_export/avatar_saver.rb
@@ -0,0 +1,31 @@
+module Gitlab
+ module ImportExport
+ class AvatarSaver
+ include Gitlab::ImportExport::CommandLineUtil
+
+ def initialize(project:, shared:)
+ @project = project
+ @shared = shared
+ end
+
+ def save
+ return true unless @project.avatar.exists?
+
+ copy_files(avatar_path, avatar_export_path)
+ rescue => e
+ @shared.error(e)
+ false
+ end
+
+ private
+
+ def avatar_export_path
+ File.join(@shared.export_path, 'avatar', @project.avatar_identifier)
+ end
+
+ def avatar_path
+ @project.avatar.path
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/import_export/command_line_util.rb b/lib/gitlab/import_export/command_line_util.rb
index 2249904145c..5dd0e34c18e 100644
--- a/lib/gitlab/import_export/command_line_util.rb
+++ b/lib/gitlab/import_export/command_line_util.rb
@@ -36,6 +36,15 @@ module Gitlab
def git_bin_path
Gitlab.config.git.bin_path
end
+
+ def copy_files(source, destination)
+ # if we are copying files, create the destination folder
+ destination_folder = File.file?(source) ? File.dirname(destination) : destination
+
+ FileUtils.mkdir_p(destination_folder)
+ FileUtils.copy_entry(source, destination)
+ true
+ end
end
end
end
diff --git a/lib/gitlab/import_export/importer.rb b/lib/gitlab/import_export/importer.rb
index 6b69a653f12..e9ee47fc090 100644
--- a/lib/gitlab/import_export/importer.rb
+++ b/lib/gitlab/import_export/importer.rb
@@ -9,7 +9,7 @@ module Gitlab
end
def execute
- if import_file && check_version! && [project_tree, repo_restorer, wiki_restorer, uploads_restorer].all?(&:restore)
+ if import_file && check_version! && [project_tree, avatar_restorer, repo_restorer, wiki_restorer, uploads_restorer].all?(&:restore)
project_tree.restored_project
else
raise Projects::ImportService::Error.new(@shared.errors.join(', '))
@@ -35,6 +35,10 @@ module Gitlab
project: @project)
end
+ def avatar_restorer
+ Gitlab::ImportExport::AvatarRestorer.new(project: project_tree.restored_project, shared: @shared)
+ end
+
def repo_restorer
Gitlab::ImportExport::RepoRestorer.new(path_to_bundle: repo_path,
shared: @shared,
diff --git a/lib/gitlab/import_export/uploads_saver.rb b/lib/gitlab/import_export/uploads_saver.rb
index d6f4fa57510..62a2553675c 100644
--- a/lib/gitlab/import_export/uploads_saver.rb
+++ b/lib/gitlab/import_export/uploads_saver.rb
@@ -1,6 +1,8 @@
module Gitlab
module ImportExport
class UploadsSaver
+ include Gitlab::ImportExport::CommandLineUtil
+
def initialize(project:, shared:)
@project = project
@shared = shared
@@ -17,12 +19,6 @@ module Gitlab
private
- def copy_files(source, destination)
- FileUtils.mkdir_p(destination)
- FileUtils.copy_entry(source, destination)
- true
- end
-
def uploads_export_path
File.join(@shared.export_path, 'uploads')
end
diff --git a/lib/tasks/downtime_check.rake b/lib/tasks/downtime_check.rake
new file mode 100644
index 00000000000..30a2e9be5ce
--- /dev/null
+++ b/lib/tasks/downtime_check.rake
@@ -0,0 +1,26 @@
+desc 'Checks if migrations in a branch require downtime'
+task downtime_check: :environment do
+ # First we'll want to make sure we're comparing with the right upstream
+ # repository/branch.
+ current_branch = `git rev-parse --abbrev-ref HEAD`.strip
+
+ # Either the developer ran this task directly on the master branch, or they're
+ # making changes directly on the master branch.
+ if current_branch == 'master'
+ if defined?(Gitlab::License)
+ repo = 'gitlab-ee'
+ else
+ repo = 'gitlab-ce'
+ end
+
+ `git fetch https://gitlab.com/gitlab-org/#{repo}.git --depth 1`
+
+ compare_with = 'FETCH_HEAD'
+ # The developer is working on a different branch, in this case we can just
+ # compare with the master branch.
+ else
+ compare_with = 'master'
+ end
+
+ Rake::Task['gitlab:db:downtime_check'].invoke(compare_with)
+end
diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake
index e9a4e37ec48..60f4636e737 100644
--- a/lib/tasks/gitlab/check.rake
+++ b/lib/tasks/gitlab/check.rake
@@ -784,7 +784,7 @@ namespace :gitlab do
servers.each do |server|
puts "Server: #{server}"
Gitlab::LDAP::Adapter.open(server) do |adapter|
- users = adapter.users(adapter.config.uid, '*', 100)
+ users = adapter.users(adapter.config.uid, '*', limit)
users.each do |user|
puts "\tDN: #{user.dn}\t #{adapter.config.uid}: #{user.uid}"
end
diff --git a/lib/tasks/gitlab/db.rake b/lib/tasks/gitlab/db.rake
index 7230b9485be..0ec19e1a625 100644
--- a/lib/tasks/gitlab/db.rake
+++ b/lib/tasks/gitlab/db.rake
@@ -46,5 +46,20 @@ namespace :gitlab do
Rake::Task['db:seed_fu'].invoke
end
end
+
+ desc 'Checks if migrations require downtime or not'
+ task :downtime_check, [:ref] => :environment do |_, args|
+ abort 'You must specify a Git reference to compare with' unless args[:ref]
+
+ require 'shellwords'
+
+ ref = Shellwords.escape(args[:ref])
+
+ migrations = `git diff #{ref}.. --name-only -- db/migrate`.lines.
+ map { |file| Rails.root.join(file.strip).to_s }.
+ select { |file| File.file?(file) }
+
+ Gitlab::DowntimeCheck.new.check_and_print(migrations)
+ end
end
end
diff --git a/spec/controllers/help_controller_spec.rb b/spec/controllers/help_controller_spec.rb
index 267d511c2db..347bef1e129 100644
--- a/spec/controllers/help_controller_spec.rb
+++ b/spec/controllers/help_controller_spec.rb
@@ -63,4 +63,13 @@ describe HelpController do
end
end
end
+
+ describe 'GET #ui' do
+ context 'for UI Development Kit' do
+ it 'renders found' do
+ get :ui
+ expect(response).to have_http_status(200)
+ end
+ end
+ end
end
diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb
index fb111889501..5e19e403c6b 100644
--- a/spec/factories/ci/builds.rb
+++ b/spec/factories/ci/builds.rb
@@ -3,6 +3,8 @@ include ActionDispatch::TestProcess
FactoryGirl.define do
factory :ci_build, class: Ci::Build do
name 'test'
+ stage 'test'
+ stage_idx 0
ref 'master'
tag false
created_at 'Di 29. Okt 09:50:00 CET 2013'
diff --git a/spec/features/groups/members/user_requests_access_spec.rb b/spec/features/groups/members/user_requests_access_spec.rb
index d1a6a98ab72..b3baa2ab57c 100644
--- a/spec/features/groups/members/user_requests_access_spec.rb
+++ b/spec/features/groups/members/user_requests_access_spec.rb
@@ -12,6 +12,13 @@ feature 'Groups > Members > User requests access', feature: true do
visit group_path(group)
end
+ scenario 'request access feature is disabled' do
+ group.update_attributes(request_access_enabled: false)
+ visit group_path(group)
+
+ expect(page).not_to have_content 'Request Access'
+ end
+
scenario 'user can request access to a group' do
perform_enqueued_jobs { click_link 'Request Access' }
diff --git a/spec/features/pipelines_settings_spec.rb b/spec/features/pipelines_settings_spec.rb
new file mode 100644
index 00000000000..dcc364a3d01
--- /dev/null
+++ b/spec/features/pipelines_settings_spec.rb
@@ -0,0 +1,35 @@
+require 'spec_helper'
+
+feature "Pipelines settings", feature: true do
+ include GitlabRoutingHelper
+
+ let(:project) { create(:empty_project) }
+ let(:user) { create(:user) }
+ let(:role) { :developer }
+
+ background do
+ login_as(user)
+ project.team << [user, role]
+ visit namespace_project_pipelines_settings_path(project.namespace, project)
+ end
+
+ context 'for developer' do
+ given(:role) { :developer }
+
+ scenario 'to be disallowed to view' do
+ expect(page.status_code).to eq(404)
+ end
+ end
+
+ context 'for master' do
+ given(:role) { :master }
+
+ scenario 'be allowed to change' do
+ fill_in('Test coverage parsing', with: 'coverage_regex')
+ click_on 'Save changes'
+
+ expect(page.status_code).to eq(200)
+ expect(page).to have_field('Test coverage parsing', with: 'coverage_regex')
+ end
+ end
+end
diff --git a/spec/features/projects/badges/list_spec.rb b/spec/features/projects/badges/list_spec.rb
index 01e90618a98..75166bca119 100644
--- a/spec/features/projects/badges/list_spec.rb
+++ b/spec/features/projects/badges/list_spec.rb
@@ -6,7 +6,7 @@ feature 'list of badges' do
project = create(:project)
project.team << [user, :master]
login_as(user)
- visit namespace_project_badges_path(project.namespace, project)
+ visit namespace_project_pipelines_settings_path(project.namespace, project)
end
scenario 'user displays list of badges' do
diff --git a/spec/features/projects/members/user_requests_access_spec.rb b/spec/features/projects/members/user_requests_access_spec.rb
index f2fe3ef364d..56ede8eb5be 100644
--- a/spec/features/projects/members/user_requests_access_spec.rb
+++ b/spec/features/projects/members/user_requests_access_spec.rb
@@ -11,6 +11,13 @@ feature 'Projects > Members > User requests access', feature: true do
visit namespace_project_path(project.namespace, project)
end
+ scenario 'request access feature is disabled' do
+ project.update_attributes(request_access_enabled: false)
+ visit namespace_project_path(project.namespace, project)
+
+ expect(page).not_to have_content 'Request Access'
+ end
+
scenario 'user can request access to a project' do
perform_enqueued_jobs { click_link 'Request Access' }
diff --git a/spec/features/projects/slack_service/slack_service_spec.rb b/spec/features/projects/slack_service/slack_service_spec.rb
new file mode 100644
index 00000000000..16541f51d98
--- /dev/null
+++ b/spec/features/projects/slack_service/slack_service_spec.rb
@@ -0,0 +1,26 @@
+require 'spec_helper'
+
+feature 'Projects > Slack service > Setup events', feature: true do
+ let(:user) { create(:user) }
+ let(:service) { SlackService.new }
+ let(:project) { create(:project, slack_service: service) }
+
+ background do
+ service.fields
+ service.update_attributes(push_channel: 1, issue_channel: 2, merge_request_channel: 3, note_channel: 4, tag_push_channel: 5, build_channel: 6, wiki_page_channel: 7)
+ project.team << [user, :master]
+ login_as(user)
+ end
+
+ scenario 'user can filter events by channel' do
+ visit edit_namespace_project_service_path(project.namespace, project, service)
+
+ expect(page.find_field("service_push_channel").value).to have_content '1'
+ expect(page.find_field("service_issue_channel").value).to have_content '2'
+ expect(page.find_field("service_merge_request_channel").value).to have_content '3'
+ expect(page.find_field("service_note_channel").value).to have_content '4'
+ expect(page.find_field("service_tag_push_channel").value).to have_content '5'
+ expect(page.find_field("service_build_channel").value).to have_content '6'
+ expect(page.find_field("service_wiki_page_channel").value).to have_content '7'
+ end
+end
diff --git a/spec/helpers/ci_status_helper_spec.rb b/spec/helpers/ci_status_helper_spec.rb
index 45199d0f09d..637b02d9388 100644
--- a/spec/helpers/ci_status_helper_spec.rb
+++ b/spec/helpers/ci_status_helper_spec.rb
@@ -7,7 +7,13 @@ describe CiStatusHelper do
let(:failed_commit) { double("Ci::Pipeline", status: 'failed') }
describe 'ci_icon_for_status' do
- it { expect(helper.ci_icon_for_status(success_commit.status)).to include('fa-check') }
- it { expect(helper.ci_icon_for_status(failed_commit.status)).to include('fa-close') }
+ it 'renders to correct svg on success' do
+ expect(helper).to receive(:render).with('shared/icons/icon_status_success.svg', anything)
+ helper.ci_icon_for_status(success_commit.status)
+ end
+ it 'renders the correct svg on failure' do
+ expect(helper).to receive(:render).with('shared/icons/icon_status_failed.svg', anything)
+ helper.ci_icon_for_status(failed_commit.status)
+ end
end
end
diff --git a/spec/helpers/time_helper_spec.rb b/spec/helpers/time_helper_spec.rb
index 3f62527c5bb..413ead944b9 100644
--- a/spec/helpers/time_helper_spec.rb
+++ b/spec/helpers/time_helper_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe TimeHelper do
- describe "#duration_in_words" do
+ describe "#time_interval_in_words" do
it "returns minutes and seconds" do
intervals_in_words = {
100 => "1 minute 40 seconds",
@@ -11,26 +11,23 @@ describe TimeHelper do
}
intervals_in_words.each do |interval, expectation|
- expect(duration_in_words(Time.now + interval, Time.now)).to eq(expectation)
+ expect(time_interval_in_words(interval)).to eq(expectation)
end
end
-
- it "calculates interval from now if there is no finished_at" do
- expect(duration_in_words(nil, Time.now - 5)).to eq("5 seconds")
- end
end
- describe "#time_interval_in_words" do
+ describe "#duration_in_numbers" do
it "returns minutes and seconds" do
- intervals_in_words = {
- 100 => "1 minute 40 seconds",
- 121 => "2 minutes 1 second",
- 3721 => "62 minutes 1 second",
- 0 => "0 seconds"
+ duration_in_numbers = {
+ [100, 0] => "01:40",
+ [121, 0] => "02:01",
+ [3721, 0] => "01:02:01",
+ [0, 0] => "00:00",
+ [nil, Time.now.to_i - 42] => "00:42"
}
- intervals_in_words.each do |interval, expectation|
- expect(time_interval_in_words(interval)).to eq(expectation)
+ duration_in_numbers.each do |interval, expectation|
+ expect(duration_in_numbers(*interval)).to eq(expectation)
end
end
end
diff --git a/spec/lib/gitlab/badge/build_spec.rb b/spec/lib/gitlab/badge/build_spec.rb
index 2034445a197..f3b522a02f5 100644
--- a/spec/lib/gitlab/badge/build_spec.rb
+++ b/spec/lib/gitlab/badge/build_spec.rb
@@ -113,7 +113,7 @@ describe Gitlab::Badge::Build do
sha: sha,
ref: branch)
- create(:ci_build, pipeline: pipeline)
+ create(:ci_build, pipeline: pipeline, stage: 'notify')
end
def status_node(data, status)
diff --git a/spec/lib/gitlab/diff/position_spec.rb b/spec/lib/gitlab/diff/position_spec.rb
index cf28628cb96..10537bea008 100644
--- a/spec/lib/gitlab/diff/position_spec.rb
+++ b/spec/lib/gitlab/diff/position_spec.rb
@@ -338,4 +338,28 @@ describe Gitlab::Diff::Position, lib: true do
end
end
end
+
+ describe "#to_json" do
+ let(:hash) do
+ {
+ old_path: "files/ruby/popen.rb",
+ new_path: "files/ruby/popen.rb",
+ old_line: nil,
+ new_line: 14,
+ base_sha: nil,
+ head_sha: nil,
+ start_sha: nil
+ }
+ end
+
+ let(:diff_position) { described_class.new(hash) }
+
+ it "returns the position as JSON" do
+ expect(JSON.parse(diff_position.to_json)).to eq(hash.stringify_keys)
+ end
+
+ it "works when nested under another hash" do
+ expect(JSON.parse(JSON.generate(pos: diff_position))).to eq('pos' => hash.stringify_keys)
+ end
+ end
end
diff --git a/spec/lib/gitlab/downtime_check/message_spec.rb b/spec/lib/gitlab/downtime_check/message_spec.rb
new file mode 100644
index 00000000000..93094cda776
--- /dev/null
+++ b/spec/lib/gitlab/downtime_check/message_spec.rb
@@ -0,0 +1,17 @@
+require 'spec_helper'
+
+describe Gitlab::DowntimeCheck::Message do
+ describe '#to_s' do
+ it 'returns an ANSI formatted String for an offline migration' do
+ message = described_class.new('foo.rb', true, 'hello')
+
+ expect(message.to_s).to eq("[\e[32moffline\e[0m]: foo.rb: hello")
+ end
+
+ it 'returns an ANSI formatted String for an online migration' do
+ message = described_class.new('foo.rb')
+
+ expect(message.to_s).to eq("[\e[31monline\e[0m]: foo.rb")
+ end
+ end
+end
diff --git a/spec/lib/gitlab/downtime_check_spec.rb b/spec/lib/gitlab/downtime_check_spec.rb
new file mode 100644
index 00000000000..42d895e548e
--- /dev/null
+++ b/spec/lib/gitlab/downtime_check_spec.rb
@@ -0,0 +1,113 @@
+require 'spec_helper'
+
+describe Gitlab::DowntimeCheck do
+ subject { described_class.new }
+ let(:path) { 'foo.rb' }
+
+ describe '#check' do
+ before do
+ expect(subject).to receive(:require).with(path)
+ end
+
+ context 'when a migration does not specify if downtime is required' do
+ it 'raises RuntimeError' do
+ expect(subject).to receive(:class_for_migration_file).
+ with(path).
+ and_return(Class.new)
+
+ expect { subject.check([path]) }.
+ to raise_error(RuntimeError, /it requires downtime/)
+ end
+ end
+
+ context 'when a migration requires downtime' do
+ context 'when no reason is specified' do
+ it 'raises RuntimeError' do
+ stub_const('TestMigration::DOWNTIME', true)
+
+ expect(subject).to receive(:class_for_migration_file).
+ with(path).
+ and_return(TestMigration)
+
+ expect { subject.check([path]) }.
+ to raise_error(RuntimeError, /no reason was given/)
+ end
+ end
+
+ context 'when a reason is specified' do
+ it 'returns an Array of messages' do
+ stub_const('TestMigration::DOWNTIME', true)
+ stub_const('TestMigration::DOWNTIME_REASON', 'foo')
+
+ expect(subject).to receive(:class_for_migration_file).
+ with(path).
+ and_return(TestMigration)
+
+ messages = subject.check([path])
+
+ expect(messages).to be_an_instance_of(Array)
+ expect(messages[0]).to be_an_instance_of(Gitlab::DowntimeCheck::Message)
+
+ message = messages[0]
+
+ expect(message.path).to eq(path)
+ expect(message.offline).to eq(true)
+ expect(message.reason).to eq('foo')
+ end
+ end
+ end
+ end
+
+ describe '#check_and_print' do
+ it 'checks the migrations and prints the results to STDOUT' do
+ stub_const('TestMigration::DOWNTIME', true)
+ stub_const('TestMigration::DOWNTIME_REASON', 'foo')
+
+ expect(subject).to receive(:require).with(path)
+
+ expect(subject).to receive(:class_for_migration_file).
+ with(path).
+ and_return(TestMigration)
+
+ expect(subject).to receive(:puts).with(an_instance_of(String))
+
+ subject.check_and_print([path])
+ end
+ end
+
+ describe '#class_for_migration_file' do
+ it 'returns the class for a migration file path' do
+ expect(subject.class_for_migration_file('123_string.rb')).to eq(String)
+ end
+ end
+
+ describe '#online?' do
+ it 'returns true when a migration can be performed online' do
+ stub_const('TestMigration::DOWNTIME', false)
+
+ expect(subject.online?(TestMigration)).to eq(true)
+ end
+
+ it 'returns false when a migration can not be performed online' do
+ stub_const('TestMigration::DOWNTIME', true)
+
+ expect(subject.online?(TestMigration)).to eq(false)
+ end
+ end
+
+ describe '#downtime_reason' do
+ context 'when a reason is defined' do
+ it 'returns the downtime reason' do
+ stub_const('TestMigration::DOWNTIME_REASON', 'hello')
+
+ expect(subject.downtime_reason(TestMigration)).to eq('hello')
+ end
+ end
+
+ context 'when a reason is not defined' do
+ it 'returns nil' do
+ expect(subject.downtime_reason(Class.new)).to be_nil
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb
index db33c7a22bb..ae064a878b0 100644
--- a/spec/lib/gitlab/git_access_spec.rb
+++ b/spec/lib/gitlab/git_access_spec.rb
@@ -44,12 +44,12 @@ describe Gitlab::GitAccess, lib: true do
end
describe 'download_access_check' do
+ subject { access.check('git-upload-pack') }
+
describe 'master permissions' do
before { project.team << [user, :master] }
context 'pull code' do
- subject { access.download_access_check }
-
it { expect(subject.allowed?).to be_truthy }
end
end
@@ -58,8 +58,6 @@ describe Gitlab::GitAccess, lib: true do
before { project.team << [user, :guest] }
context 'pull code' do
- subject { access.download_access_check }
-
it { expect(subject.allowed?).to be_falsey }
end
end
@@ -71,16 +69,12 @@ describe Gitlab::GitAccess, lib: true do
end
context 'pull code' do
- subject { access.download_access_check }
-
it { expect(subject.allowed?).to be_falsey }
end
end
describe 'without acccess to project' do
context 'pull code' do
- subject { access.download_access_check }
-
it { expect(subject.allowed?).to be_falsey }
end
end
@@ -90,10 +84,31 @@ describe Gitlab::GitAccess, lib: true do
let(:actor) { key }
context 'pull code' do
- before { key.projects << project }
- subject { access.download_access_check }
+ context 'when project is authorized' do
+ before { key.projects << project }
- it { expect(subject.allowed?).to be_truthy }
+ it { expect(subject).to be_allowed }
+ end
+
+ context 'when unauthorized' do
+ context 'from public project' do
+ let(:project) { create(:project, :public) }
+
+ it { expect(subject).to be_allowed }
+ end
+
+ context 'from internal project' do
+ let(:project) { create(:project, :internal) }
+
+ it { expect(subject).not_to be_allowed }
+ end
+
+ context 'from private project' do
+ let(:project) { create(:project, :internal) }
+
+ it { expect(subject).not_to be_allowed }
+ end
+ end
end
end
end
@@ -240,5 +255,40 @@ describe Gitlab::GitAccess, lib: true do
run_permission_checks(permissions_matrix.deep_merge(developer: { push_protected_branch: true, push_all: true, merge_into_protected_branch: true }))
end
end
+
+ describe 'deploy key permissions' do
+ let(:key) { create(:deploy_key) }
+ let(:actor) { key }
+
+ context 'push code' do
+ subject { access.check('git-receive-pack') }
+
+ context 'when project is authorized' do
+ before { key.projects << project }
+
+ it { expect(subject).not_to be_allowed }
+ end
+
+ context 'when unauthorized' do
+ context 'to public project' do
+ let(:project) { create(:project, :public) }
+
+ it { expect(subject).not_to be_allowed }
+ end
+
+ context 'to internal project' do
+ let(:project) { create(:project, :internal) }
+
+ it { expect(subject).not_to be_allowed }
+ end
+
+ context 'to private project' do
+ let(:project) { create(:project, :internal) }
+
+ it { expect(subject).not_to be_allowed }
+ end
+ end
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/import_export/avatar_restorer_spec.rb b/spec/lib/gitlab/import_export/avatar_restorer_spec.rb
new file mode 100644
index 00000000000..5ae178414cc
--- /dev/null
+++ b/spec/lib/gitlab/import_export/avatar_restorer_spec.rb
@@ -0,0 +1,25 @@
+require 'spec_helper'
+
+describe Gitlab::ImportExport::AvatarRestorer, lib: true do
+ let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: 'test') }
+ let(:project) { create(:empty_project) }
+
+ before do
+ allow_any_instance_of(described_class).to receive(:avatar_export_file)
+ .and_return(Rails.root + "spec/fixtures/dk.png")
+ end
+
+ after do
+ project.remove_avatar!
+ end
+
+ it 'restores a project avatar' do
+ expect(described_class.new(project: project, shared: shared).restore).to be true
+ end
+
+ it 'saves the avatar into the project' do
+ described_class.new(project: project, shared: shared).restore
+
+ expect(project.reload.avatar.file.exists?).to be true
+ end
+end
diff --git a/spec/lib/gitlab/import_export/avatar_saver_spec.rb b/spec/lib/gitlab/import_export/avatar_saver_spec.rb
new file mode 100644
index 00000000000..d6ee94442cb
--- /dev/null
+++ b/spec/lib/gitlab/import_export/avatar_saver_spec.rb
@@ -0,0 +1,27 @@
+require 'spec_helper'
+
+describe Gitlab::ImportExport::AvatarSaver, lib: true do
+ let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: 'test') }
+ let(:export_path) { "#{Dir::tmpdir}/project_tree_saver_spec" }
+ let(:project_with_avatar) { create(:empty_project, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) }
+ let(:project) { create(:empty_project) }
+
+ before do
+ FileUtils.mkdir_p("#{shared.export_path}/avatar/")
+ allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
+ end
+
+ after do
+ FileUtils.rm_rf("#{shared.export_path}/avatar")
+ end
+
+ it 'saves a project avatar' do
+ described_class.new(project: project_with_avatar, shared: shared).save
+
+ expect(File).to exist("#{shared.export_path}/avatar/dk.png")
+ end
+
+ it 'is fine not to have an avatar' do
+ expect(described_class.new(project: project, shared: shared).save).to be true
+ end
+end
diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb
index 95573f0a419..978ad9c52d5 100644
--- a/spec/models/build_spec.rb
+++ b/spec/models/build_spec.rb
@@ -193,77 +193,185 @@ describe Ci::Build, models: true do
end
describe '#variables' do
+ let(:container_registry_enabled) { false }
+ let(:predefined_variables) do
+ [
+ { key: 'CI', value: 'true', public: true },
+ { key: 'GITLAB_CI', value: 'true', public: true },
+ { key: 'CI_BUILD_ID', value: build.id.to_s, public: true },
+ { key: 'CI_BUILD_TOKEN', value: build.token, public: false },
+ { key: 'CI_BUILD_REF', value: build.sha, public: true },
+ { key: 'CI_BUILD_BEFORE_SHA', value: build.before_sha, public: true },
+ { key: 'CI_BUILD_REF_NAME', value: 'master', public: true },
+ { key: 'CI_BUILD_NAME', value: 'test', public: true },
+ { key: 'CI_BUILD_STAGE', value: 'test', public: true },
+ { key: 'CI_SERVER_NAME', value: 'GitLab', public: true },
+ { key: 'CI_SERVER_VERSION', value: Gitlab::VERSION, public: true },
+ { key: 'CI_SERVER_REVISION', value: Gitlab::REVISION, public: true },
+ { key: 'CI_PROJECT_ID', value: project.id.to_s, public: true },
+ { key: 'CI_PROJECT_NAME', value: project.path, public: true },
+ { key: 'CI_PROJECT_PATH', value: project.path_with_namespace, public: true },
+ { key: 'CI_PROJECT_NAMESPACE', value: project.namespace.path, public: true },
+ { key: 'CI_PROJECT_URL', value: project.web_url, public: true },
+ { key: 'CI_PIPELINE_ID', value: pipeline.id.to_s, public: true }
+ ]
+ end
+
+ before do
+ stub_container_registry_config(enabled: container_registry_enabled, host_port: 'registry.example.com')
+ end
+
+ subject { build.variables }
+
context 'returns variables' do
- subject { build.variables }
+ before do
+ build.yaml_variables = []
+ end
- let(:predefined_variables) do
- [
- { key: :CI_BUILD_NAME, value: 'test', public: true },
- { key: :CI_BUILD_STAGE, value: 'stage', public: true },
- ]
+ it { is_expected.to eq(predefined_variables) }
+ end
+
+ context 'when build is for tag' do
+ let(:tag_variable) do
+ { key: 'CI_BUILD_TAG', value: 'master', public: true }
end
- let(:yaml_variables) do
- [
- { key: :DB_NAME, value: 'postgres', public: true }
- ]
+ before do
+ build.update_attributes(tag: true)
+ end
+
+ it { is_expected.to include(tag_variable) }
+ end
+
+ context 'when secure variable is defined' do
+ let(:secure_variable) do
+ { key: 'SECRET_KEY', value: 'secret_value', public: false }
end
before do
- build.update_attributes(stage: 'stage', yaml_variables: yaml_variables)
+ build.project.variables << Ci::Variable.new(key: 'SECRET_KEY', value: 'secret_value')
end
- it { is_expected.to eq(predefined_variables + yaml_variables) }
+ it { is_expected.to include(secure_variable) }
+ end
- context 'for tag' do
- let(:tag_variable) do
- [
- { key: :CI_BUILD_TAG, value: 'master', public: true }
- ]
- end
+ context 'when build is for triggers' do
+ let(:trigger) { create(:ci_trigger, project: project) }
+ let(:trigger_request) { create(:ci_trigger_request_with_variables, pipeline: pipeline, trigger: trigger) }
+ let(:user_trigger_variable) do
+ { key: :TRIGGER_KEY, value: 'TRIGGER_VALUE', public: false }
+ end
+ let(:predefined_trigger_variable) do
+ { key: 'CI_BUILD_TRIGGERED', value: 'true', public: true }
+ end
- before do
- build.update_attributes(tag: true)
- end
+ before do
+ build.trigger_request = trigger_request
+ end
- it { is_expected.to eq(tag_variable + predefined_variables + yaml_variables) }
+ it { is_expected.to include(user_trigger_variable) }
+ it { is_expected.to include(predefined_trigger_variable) }
+ end
+
+ context 'when yaml_variables are undefined' do
+ before do
+ build.yaml_variables = nil
end
- context 'and secure variables' do
- let(:secure_variables) do
- [
- { key: 'SECRET_KEY', value: 'secret_value', public: false }
- ]
+ context 'use from gitlab-ci.yml' do
+ before do
+ stub_ci_pipeline_yaml_file(config)
end
- before do
- build.project.variables << Ci::Variable.new(key: 'SECRET_KEY', value: 'secret_value')
+ context 'if config is not found' do
+ let(:config) { nil }
+
+ it { is_expected.to eq(predefined_variables) }
end
- it { is_expected.to eq(predefined_variables + yaml_variables + secure_variables) }
+ context 'if config does not have a questioned job' do
+ let(:config) do
+ YAML.dump({
+ test_other: {
+ script: 'Hello World'
+ }
+ })
+ end
- context 'and trigger variables' do
- let(:trigger) { create(:ci_trigger, project: project) }
- let(:trigger_request) { create(:ci_trigger_request_with_variables, pipeline: pipeline, trigger: trigger) }
- let(:trigger_variables) do
- [
- { key: :TRIGGER_KEY, value: 'TRIGGER_VALUE', public: false }
- ]
+ it { is_expected.to eq(predefined_variables) }
+ end
+
+ context 'if config has variables' do
+ let(:config) do
+ YAML.dump({
+ test: {
+ script: 'Hello World',
+ variables: {
+ KEY: 'value'
+ }
+ }
+ })
end
- let(:predefined_trigger_variable) do
- [
- { key: :CI_BUILD_TRIGGERED, value: 'true', public: true }
- ]
+ let(:variables) do
+ [{ key: :KEY, value: 'value', public: true }]
end
- before do
- build.trigger_request = trigger_request
- end
+ it { is_expected.to eq(predefined_variables + variables) }
+ end
+ end
+ end
- it { is_expected.to eq(predefined_variables + predefined_trigger_variable + yaml_variables + secure_variables + trigger_variables) }
+ context 'when container registry is enabled' do
+ let(:container_registry_enabled) { true }
+ let(:ci_registry) do
+ { key: 'CI_REGISTRY', value: 'registry.example.com', public: true }
+ end
+ let(:ci_registry_image) do
+ { key: 'CI_REGISTRY_IMAGE', value: project.container_registry_repository_url, public: true }
+ end
+
+ context 'and is disabled for project' do
+ before do
+ project.update(container_registry_enabled: false)
+ end
+
+ it { is_expected.to include(ci_registry) }
+ it { is_expected.not_to include(ci_registry_image) }
+ end
+
+ context 'and is enabled for project' do
+ before do
+ project.update(container_registry_enabled: true)
end
+
+ it { is_expected.to include(ci_registry) }
+ it { is_expected.to include(ci_registry_image) }
end
end
+
+ context 'when runner is assigned to build' do
+ let(:runner) { create(:ci_runner, description: 'description', tag_list: ['docker', 'linux']) }
+
+ before do
+ build.update(runner: runner)
+ end
+
+ it { is_expected.to include({ key: 'CI_RUNNER_ID', value: runner.id.to_s, public: true }) }
+ it { is_expected.to include({ key: 'CI_RUNNER_DESCRIPTION', value: 'description', public: true }) }
+ it { is_expected.to include({ key: 'CI_RUNNER_TAGS', value: 'docker, linux', public: true }) }
+ end
+
+ context 'returns variables in valid order' do
+ before do
+ allow(build).to receive(:predefined_variables) { ['predefined'] }
+ allow(project).to receive(:predefined_variables) { ['project'] }
+ allow(pipeline).to receive(:predefined_variables) { ['pipeline'] }
+ allow(build).to receive(:yaml_variables) { ['yaml'] }
+ allow(project).to receive(:secret_variables) { ['secret'] }
+ end
+
+ it { is_expected.to eq(%w[predefined project pipeline yaml secret]) }
+ end
end
describe '#has_tags?' do
@@ -703,6 +811,22 @@ describe Ci::Build, models: true do
it 'returns other actions' do
is_expected.to contain_exactly(other_build)
end
+
+ context 'when build is retried' do
+ let!(:new_build) { Ci::Build.retry(build) }
+
+ it 'does not return any of them' do
+ is_expected.not_to include(build, new_build)
+ end
+ end
+
+ context 'when other build is retried' do
+ let!(:retried_build) { Ci::Build.retry(other_build) }
+
+ it 'returns a retried build' do
+ is_expected.to contain_exactly(retried_build)
+ end
+ end
end
describe '#play' do
@@ -724,4 +848,69 @@ describe Ci::Build, models: true do
end
end
end
+
+ describe '#when' do
+ subject { build.when }
+
+ context 'if is undefined' do
+ before do
+ build.when = nil
+ end
+
+ context 'use from gitlab-ci.yml' do
+ before do
+ stub_ci_pipeline_yaml_file(config)
+ end
+
+ context 'if config is not found' do
+ let(:config) { nil }
+
+ it { is_expected.to eq('on_success') }
+ end
+
+ context 'if config does not have a questioned job' do
+ let(:config) do
+ YAML.dump({
+ test_other: {
+ script: 'Hello World'
+ }
+ })
+ end
+
+ it { is_expected.to eq('on_success') }
+ end
+
+ context 'if config has when' do
+ let(:config) do
+ YAML.dump({
+ test: {
+ script: 'Hello World',
+ when: 'always'
+ }
+ })
+ end
+
+ it { is_expected.to eq('always') }
+ end
+ end
+ end
+ end
+
+ describe '#retryable?' do
+ context 'when build is running' do
+ before { build.run! }
+
+ it 'should return false' do
+ expect(build.retryable?).to be false
+ end
+ end
+
+ context 'when build is finished' do
+ before { build.success! }
+
+ it 'should return true' do
+ expect(build.retryable?).to be true
+ end
+ end
+ end
end
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index c29e4811385..a3bd8fdf30b 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -264,7 +264,7 @@ describe Ci::Pipeline, models: true do
context 'when listing manual actions' do
let(:yaml) do
{
- stages: ["build", "test", "test_failure", "deploy", "cleanup"],
+ stages: ["build", "test", "staging", "production", "cleanup"],
build: {
stage: "build",
script: "BUILD",
@@ -273,17 +273,12 @@ describe Ci::Pipeline, models: true do
stage: "test",
script: "TEST",
},
- test_failure: {
- stage: "test_failure",
- script: "ON test failure",
- when: "on_failure",
- },
- deploy: {
- stage: "deploy",
+ staging: {
+ stage: "staging",
script: "PUBLISH",
},
production: {
- stage: "deploy",
+ stage: "production",
script: "PUBLISH",
when: "manual",
},
@@ -311,11 +306,18 @@ describe Ci::Pipeline, models: true do
# succeed stage test
pipeline.builds.running_or_pending.each(&:success)
- expect(manual_actions).to be_one # production
+ expect(manual_actions).to be_empty
- # succeed stage deploy
+ # succeed stage staging and skip stage production
pipeline.builds.running_or_pending.each(&:success)
expect(manual_actions).to be_many # production and clear cache
+
+ # succeed stage cleanup
+ pipeline.builds.running_or_pending.each(&:success)
+
+ # after processing a pipeline we should have 6 builds, 5 succeeded
+ expect(pipeline.builds.count).to eq(6)
+ expect(pipeline.builds.success.count).to eq(4)
end
def manual_actions
diff --git a/spec/models/project_services/slack_service_spec.rb b/spec/models/project_services/slack_service_spec.rb
index 155f3e74e0d..df511b1bc4c 100644
--- a/spec/models/project_services/slack_service_spec.rb
+++ b/spec/models/project_services/slack_service_spec.rb
@@ -124,6 +124,7 @@ describe SlackService, models: true do
and_return(
double(:slack_service).as_null_object
)
+
slack.execute(push_sample_data)
end
@@ -136,6 +137,76 @@ describe SlackService, models: true do
)
slack.execute(push_sample_data)
end
+
+ context "event channels" do
+ it "uses the right channel for push event" do
+ slack.update_attributes(push_channel: "random")
+
+ expect(Slack::Notifier).to receive(:new).
+ with(webhook_url, channel: "random").
+ and_return(
+ double(:slack_service).as_null_object
+ )
+
+ slack.execute(push_sample_data)
+ end
+
+ it "uses the right channel for merge request event" do
+ slack.update_attributes(merge_request_channel: "random")
+
+ expect(Slack::Notifier).to receive(:new).
+ with(webhook_url, channel: "random").
+ and_return(
+ double(:slack_service).as_null_object
+ )
+
+ slack.execute(@merge_sample_data)
+ end
+
+ it "uses the right channel for issue event" do
+ slack.update_attributes(issue_channel: "random")
+
+ expect(Slack::Notifier).to receive(:new).
+ with(webhook_url, channel: "random").
+ and_return(
+ double(:slack_service).as_null_object
+ )
+
+ slack.execute(@issues_sample_data)
+ end
+
+ it "uses the right channel for wiki event" do
+ slack.update_attributes(wiki_page_channel: "random")
+
+ expect(Slack::Notifier).to receive(:new).
+ with(webhook_url, channel: "random").
+ and_return(
+ double(:slack_service).as_null_object
+ )
+
+ slack.execute(@wiki_page_sample_data)
+ end
+
+ context "note event" do
+ let(:issue_note) do
+ create(:note_on_issue, project: project, note: "issue note")
+ end
+
+ it "uses the right channel" do
+ slack.update_attributes(note_channel: "random")
+
+ note_data = Gitlab::NoteDataBuilder.build(issue_note, user)
+
+ expect(Slack::Notifier).to receive(:new).
+ with(webhook_url, channel: "random").
+ and_return(
+ double(:slack_service).as_null_object
+ )
+
+ slack.execute(note_data)
+ end
+ end
+ end
end
describe "Note events" do
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 76dc70ae893..5629c75665a 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -458,6 +458,47 @@ describe Project, models: true do
end
end
+ describe "#cache_has_external_wiki" do
+ let(:project) { create(:project) }
+
+ it "stores true if there is an external wiki" do
+ services = double(:service, external_wikis: [ExternalWikiService.new])
+ expect(project).to receive(:services).and_return(services)
+
+ expect do
+ project.cache_has_external_wiki
+ end.to change { project.has_external_wiki }.to(true)
+ end
+
+ it "stores false if there is no external wiki" do
+ services = double(:service, external_wikis: [])
+ expect(project).to receive(:services).and_return(services)
+
+ expect do
+ project.cache_has_external_wiki
+ end.to change { project.has_external_wiki }.to(false)
+ end
+
+ it "changes to true if an external wiki service is created later" do
+ expect do
+ project.cache_has_external_wiki
+ end.to change { project.has_external_wiki }.to(false)
+
+ expect do
+ create(:service, type: "ExternalWikiService", project: project)
+ end.to change { project.has_external_wiki }.to(true)
+ end
+
+ it "changes to false if an external wiki service is destroyed later" do
+ service = create(:service, type: "ExternalWikiService", project: project)
+ expect(project.has_external_wiki).to be_truthy
+
+ expect do
+ service.destroy
+ end.to change { project.has_external_wiki }.to(false)
+ end
+ end
+
describe '#open_branches' do
let(:project) { create(:project) }
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index e14cec589fe..59c5732c075 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -130,6 +130,36 @@ describe Repository, models: true do
end
end
+ describe :commit_file do
+ it 'commits change to a file successfully' do
+ expect do
+ repository.commit_file(user, 'CHANGELOG', 'Changelog!',
+ 'Updates file content',
+ 'master', true)
+ end.to change { repository.commits('master').count }.by(1)
+
+ blob = repository.blob_at('master', 'CHANGELOG')
+
+ expect(blob.data).to eq('Changelog!')
+ end
+ end
+
+ describe :update_file do
+ it 'updates filename successfully' do
+ expect do
+ repository.update_file(user, 'NEWLICENSE', 'Copyright!',
+ branch: 'master',
+ previous_path: 'LICENSE',
+ message: 'Changes filename')
+ end.to change { repository.commits('master').count }.by(1)
+
+ files = repository.ls_files('master')
+
+ expect(files).not_to include('LICENSE')
+ expect(files).to include('NEWLICENSE')
+ end
+ end
+
describe "search_files" do
let(:results) { repository.search_files('feature', 'master') }
subject { results }
@@ -719,6 +749,30 @@ describe Repository, models: true do
repository.before_delete
end
+ it 'flushes the tags cache' do
+ expect(repository).to receive(:expire_tags_cache)
+
+ repository.before_delete
+ end
+
+ it 'flushes the tag count cache' do
+ expect(repository).to receive(:expire_tag_count_cache)
+
+ repository.before_delete
+ end
+
+ it 'flushes the branches cache' do
+ expect(repository).to receive(:expire_branches_cache)
+
+ repository.before_delete
+ end
+
+ it 'flushes the branch count cache' do
+ expect(repository).to receive(:expire_branch_count_cache)
+
+ repository.before_delete
+ end
+
it 'flushes the root ref cache' do
expect(repository).to receive(:expire_root_ref_cache)
@@ -749,6 +803,30 @@ describe Repository, models: true do
repository.before_delete
end
+ it 'flushes the tags cache' do
+ expect(repository).to receive(:expire_tags_cache)
+
+ repository.before_delete
+ end
+
+ it 'flushes the tag count cache' do
+ expect(repository).to receive(:expire_tag_count_cache)
+
+ repository.before_delete
+ end
+
+ it 'flushes the branches cache' do
+ expect(repository).to receive(:expire_branches_cache)
+
+ repository.before_delete
+ end
+
+ it 'flushes the branch count cache' do
+ expect(repository).to receive(:expire_branch_count_cache)
+
+ repository.before_delete
+ end
+
it 'flushes the root ref cache' do
expect(repository).to receive(:expire_root_ref_cache)
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index fc74488ac0e..3bf82cf2668 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -887,16 +887,25 @@ describe User, models: true do
end
describe '#authorized_projects' do
- let!(:user) { create(:user) }
- let!(:private_project) { create(:project, :private) }
+ context 'with a minimum access level' do
+ it 'includes projects for which the user is an owner' do
+ user = create(:user)
+ project = create(:empty_project, :private, namespace: user.namespace)
- before do
- private_project.team << [user, Gitlab::Access::MASTER]
- end
+ expect(user.authorized_projects(Gitlab::Access::REPORTER))
+ .to contain_exactly(project)
+ end
- subject { user.authorized_projects }
+ it 'includes projects for which the user is a master' do
+ user = create(:user)
+ project = create(:empty_project, :private)
+
+ project.team << [user, Gitlab::Access::MASTER]
- it { is_expected.to eq([private_project]) }
+ expect(user.authorized_projects(Gitlab::Access::REPORTER))
+ .to contain_exactly(project)
+ end
+ end
end
describe '#ci_authorized_runners' do
diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb
index e7cbc3dd3a7..1c7c60ec644 100644
--- a/spec/requests/ci/api/builds_spec.rb
+++ b/spec/requests/ci/api/builds_spec.rb
@@ -73,12 +73,12 @@ describe Ci::API::API do
post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin }
expect(response).to have_http_status(201)
- expect(json_response["variables"]).to eq([
+ expect(json_response["variables"]).to include(
{ "key" => "CI_BUILD_NAME", "value" => "spinach", "public" => true },
{ "key" => "CI_BUILD_STAGE", "value" => "test", "public" => true },
{ "key" => "DB_NAME", "value" => "postgres", "public" => true },
{ "key" => "SECRET_KEY", "value" => "secret_value", "public" => false }
- ])
+ )
end
it "returns variables for triggers" do
@@ -92,14 +92,14 @@ describe Ci::API::API do
post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin }
expect(response).to have_http_status(201)
- expect(json_response["variables"]).to eq([
+ expect(json_response["variables"]).to include(
{ "key" => "CI_BUILD_NAME", "value" => "spinach", "public" => true },
{ "key" => "CI_BUILD_STAGE", "value" => "test", "public" => true },
{ "key" => "CI_BUILD_TRIGGERED", "value" => "true", "public" => true },
{ "key" => "DB_NAME", "value" => "postgres", "public" => true },
{ "key" => "SECRET_KEY", "value" => "secret_value", "public" => false },
- { "key" => "TRIGGER_KEY", "value" => "TRIGGER_VALUE", "public" => false },
- ])
+ { "key" => "TRIGGER_KEY", "value" => "TRIGGER_VALUE", "public" => false }
+ )
end
it "returns dependent builds" do
diff --git a/spec/routing/routing_spec.rb b/spec/routing/routing_spec.rb
index 2c755919456..0a52c1ab933 100644
--- a/spec/routing/routing_spec.rb
+++ b/spec/routing/routing_spec.rb
@@ -116,12 +116,9 @@ describe HelpController, "routing" do
expect(get(path)).to route_to('help#show',
path: 'workflow/protected_branches/protected_branches1',
format: 'png')
- path = '/help/shortcuts'
- expect(get(path)).to route_to('help#show',
- path: 'shortcuts')
+
path = '/help/ui'
- expect(get(path)).to route_to('help#show',
- path: 'ui')
+ expect(get(path)).to route_to('help#ui')
end
end
diff --git a/spec/support/api_helpers.rb b/spec/support/api_helpers.rb
index 1b3cafb497c..68b196d9033 100644
--- a/spec/support/api_helpers.rb
+++ b/spec/support/api_helpers.rb
@@ -24,8 +24,11 @@ module ApiHelpers
(path.index('?') ? '' : '?') +
# Append private_token if given a User object
- (user.respond_to?(:private_token) ?
- "&private_token=#{user.private_token}" : "")
+ if user.respond_to?(:private_token)
+ "&private_token=#{user.private_token}"
+ else
+ ''
+ end
end
def ci_api(path, user = nil)
@@ -35,8 +38,11 @@ module ApiHelpers
(path.index('?') ? '' : '?') +
# Append private_token if given a User object
- (user.respond_to?(:private_token) ?
- "&private_token=#{user.private_token}" : "")
+ if user.respond_to?(:private_token)
+ "&private_token=#{user.private_token}"
+ else
+ ''
+ end
end
def json_response