summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.rubocop.yml77
-rw-r--r--CHANGELOG17
-rw-r--r--CONTRIBUTING.md2
-rw-r--r--Gemfile5
-rw-r--r--Gemfile.lock16
-rw-r--r--app/assets/javascripts/application.js1
-rw-r--r--app/assets/javascripts/boards/components/board_list.js.es65
-rw-r--r--app/assets/javascripts/gl_dropdown.js20
-rw-r--r--app/assets/javascripts/layout_nav.js25
-rw-r--r--app/assets/javascripts/merge_request_tabs.js4
-rw-r--r--app/assets/javascripts/shortcuts_navigation.js3
-rw-r--r--app/assets/javascripts/users/calendar.js2
-rw-r--r--app/assets/stylesheets/framework/lists.scss4
-rw-r--r--app/assets/stylesheets/framework/typography.scss2
-rw-r--r--app/assets/stylesheets/pages/boards.scss98
-rw-r--r--app/assets/stylesheets/pages/issues.scss4
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss4
-rw-r--r--app/assets/stylesheets/pages/pipelines.scss25
-rw-r--r--app/assets/stylesheets/pages/snippets.scss39
-rw-r--r--app/controllers/admin/groups_controller.rb10
-rw-r--r--app/controllers/groups_controller.rb12
-rw-r--r--app/controllers/projects/builds_controller.rb2
-rw-r--r--app/controllers/projects/merge_requests_controller.rb4
-rw-r--r--app/controllers/users_controller.rb2
-rw-r--r--app/helpers/groups_helper.rb25
-rw-r--r--app/helpers/projects_helper.rb2
-rw-r--r--app/helpers/snippets_helper.rb6
-rw-r--r--app/models/ci/build.rb13
-rw-r--r--app/models/ci/variable.rb6
-rw-r--r--app/models/concerns/has_status.rb2
-rw-r--r--app/models/environment.rb12
-rw-r--r--app/models/group.rb7
-rw-r--r--app/models/merge_request.rb2
-rw-r--r--app/models/namespace.rb5
-rw-r--r--app/models/project.rb5
-rw-r--r--app/services/commits/change_service.rb20
-rw-r--r--app/services/commits/cherry_pick_service.rb14
-rw-r--r--app/services/commits/revert_service.rb14
-rw-r--r--app/services/create_deployment_service.rb38
-rw-r--r--app/services/milestones/create_service.rb2
-rw-r--r--app/views/admin/groups/_form.html.haml2
-rw-r--r--app/views/admin/groups/show.html.haml6
-rw-r--r--app/views/groups/_group_lfs_settings.html.haml11
-rw-r--r--app/views/groups/edit.html.haml2
-rw-r--r--app/views/help/_shortcuts.html.haml6
-rw-r--r--app/views/layouts/nav/_project.html.haml4
-rw-r--r--app/views/profiles/update_username.js.haml2
-rw-r--r--app/views/projects/ci/pipelines/_pipeline.html.haml12
-rw-r--r--app/views/projects/edit.html.haml17
-rw-r--r--app/views/projects/issues/_issue.html.haml2
-rw-r--r--app/views/projects/merge_requests/_merge_request.html.haml2
-rw-r--r--app/views/projects/pipelines/index.html.haml8
-rw-r--r--app/views/projects/snippets/_actions.html.haml14
-rw-r--r--app/views/projects/snippets/show.html.haml21
-rw-r--r--app/views/projects/tree/_readme.html.haml2
-rw-r--r--app/views/projects/variables/_table.html.haml2
-rw-r--r--app/views/search/_results.html.haml16
-rw-r--r--app/views/search/results/_commit.html.haml3
-rw-r--r--app/views/shared/_visibility_level.html.haml2
-rw-r--r--app/views/shared/issuable/_form.html.haml2
-rw-r--r--app/views/shared/snippets/_header.html.haml8
-rw-r--r--app/views/shared/snippets/_snippet.html.haml21
-rw-r--r--app/views/snippets/_actions.html.haml14
-rw-r--r--app/views/snippets/show.html.haml19
-rw-r--r--app/views/users/calendar.html.haml4
-rw-r--r--db/migrate/20160901213340_add_lfs_enabled_to_namespaces.rb12
-rw-r--r--db/migrate/20160907131111_add_environment_type_to_environments.rb9
-rw-r--r--db/migrate/20160913162434_remove_projects_pushes_since_gc.rb2
-rw-r--r--db/migrate/20160913212128_change_artifacts_size_column.rb15
-rw-r--r--db/schema.rb9
-rw-r--r--doc/api/groups.md2
-rw-r--r--doc/api/notes.md6
-rw-r--r--doc/api/settings.md2
-rw-r--r--doc/ci/examples/README.md2
-rw-r--r--doc/ci/yaml/README.md79
-rw-r--r--doc/container_registry/README.md6
-rw-r--r--doc/development/migration_style_guide.md22
-rw-r--r--doc/raketasks/backup_restore.md6
-rw-r--r--doc/user/project/builds/artifacts.md32
-rw-r--r--doc/user/project/builds/img/build_latest_artifacts_browser.pngbin0 -> 26617 bytes
-rw-r--r--doc/workflow/importing/img/import_projects_from_github_importer.pngbin22711 -> 28989 bytes
-rw-r--r--doc/workflow/importing/img/import_projects_from_github_new_project_page.pngbin13668 -> 24911 bytes
-rw-r--r--doc/workflow/importing/img/import_projects_from_github_select_auth_method.pngbin0 -> 42043 bytes
-rw-r--r--doc/workflow/importing/import_projects_from_github.md129
-rw-r--r--lib/api/entities.rb4
-rw-r--r--lib/api/groups.rb6
-rw-r--r--lib/api/notes.rb8
-rw-r--r--lib/ci/api/entities.rb9
-rw-r--r--lib/ci/gitlab_ci_yaml_processor.rb3
-rw-r--r--lib/expand_variables.rb17
-rw-r--r--lib/gitlab/ci/config/node/environment.rb68
-rw-r--r--lib/gitlab/ci/config/node/job.rb41
-rw-r--r--lib/gitlab/contributions_calendar.rb17
-rw-r--r--lib/gitlab/database/migration_helpers.rb10
-rw-r--r--lib/gitlab/regex.rb4
-rw-r--r--spec/factories/ci/runner_projects.rb11
-rw-r--r--spec/factories/ci/runners.rb19
-rw-r--r--spec/factories/ci/variables.rb14
-rw-r--r--spec/factories/group_members.rb13
-rw-r--r--spec/features/boards/boards_spec.rb202
-rw-r--r--spec/features/boards/keyboard_shortcut_spec.rb24
-rw-r--r--spec/features/calendar_spec.rb39
-rw-r--r--spec/features/environments_spec.rb2
-rw-r--r--spec/features/issues_spec.rb2
-rw-r--r--spec/features/milestone_spec.rb21
-rw-r--r--spec/features/projects/issuable_templates_spec.rb20
-rw-r--r--spec/features/users/snippets_spec.rb5
-rw-r--r--spec/helpers/groups_helper_spec.rb63
-rw-r--r--spec/lib/ci/gitlab_ci_yaml_processor_spec.rb21
-rw-r--r--spec/lib/expand_variables_spec.rb73
-rw-r--r--spec/lib/gitlab/ci/config/node/environment_spec.rb155
-rw-r--r--spec/lib/gitlab/database/migration_helpers_spec.rb99
-rw-r--r--spec/models/environment_spec.rb16
-rw-r--r--spec/models/group_spec.rb46
-rw-r--r--spec/models/hooks/project_hook_spec.rb18
-rw-r--r--spec/models/hooks/service_hook_spec.rb18
-rw-r--r--spec/models/hooks/system_hook_spec.rb20
-rw-r--r--spec/models/hooks/web_hook_spec.rb18
-rw-r--r--spec/models/members/group_member_spec.rb19
-rw-r--r--spec/models/members/project_member_spec.rb19
-rw-r--r--spec/models/merge_request_spec.rb20
-rw-r--r--spec/models/project_services/asana_service_spec.rb20
-rw-r--r--spec/models/project_services/assembla_service_spec.rb20
-rw-r--r--spec/models/project_services/bamboo_service_spec.rb20
-rw-r--r--spec/models/project_services/bugzilla_service_spec.rb20
-rw-r--r--spec/models/project_services/buildkite_service_spec.rb20
-rw-r--r--spec/models/project_services/campfire_service_spec.rb20
-rw-r--r--spec/models/project_services/custom_issue_tracker_service_spec.rb20
-rw-r--r--spec/models/project_services/drone_ci_service_spec.rb20
-rw-r--r--spec/models/project_services/external_wiki_service_spec.rb21
-rw-r--r--spec/models/project_services/flowdock_service_spec.rb20
-rw-r--r--spec/models/project_services/gemnasium_service_spec.rb20
-rw-r--r--spec/models/project_services/gitlab_issue_tracker_service_spec.rb20
-rw-r--r--spec/models/project_services/hipchat_service_spec.rb20
-rw-r--r--spec/models/project_services/irker_service_spec.rb20
-rw-r--r--spec/models/project_services/jira_service_spec.rb20
-rw-r--r--spec/models/project_services/pivotaltracker_service_spec.rb20
-rw-r--r--spec/models/project_services/pushover_service_spec.rb20
-rw-r--r--spec/models/project_services/redmine_service_spec.rb20
-rw-r--r--spec/models/project_services/slack_service_spec.rb20
-rw-r--r--spec/models/project_services/teamcity_service_spec.rb20
-rw-r--r--spec/models/project_spec.rb66
-rw-r--r--spec/requests/api/commits_spec.rb2
-rw-r--r--spec/requests/api/milestones_spec.rb23
-rw-r--r--spec/requests/api/notes_spec.rb9
-rw-r--r--spec/services/create_deployment_service_spec.rb43
-rw-r--r--spec/services/projects/housekeeping_service_spec.rb4
-rw-r--r--spec/services/protected_branches/create_service_spec.rb23
-rw-r--r--spec/support/wait_for_vue_resource.rb7
-rw-r--r--spec/views/projects/pipelines/show.html.haml_spec.rb17
150 files changed, 1674 insertions, 1176 deletions
diff --git a/.rubocop.yml b/.rubocop.yml
index 5bd31ccf329..b054675d677 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -767,26 +767,33 @@ Rails/ScopeArgs:
RSpec/AnyInstance:
Enabled: false
-# Check that the first argument to the top level describe is the tested class or
-# module.
+# Check for expectations where `be(...)` can replace `eql(...)`.
+RSpec/BeEql:
+ Enabled: false
+
+# Check that the first argument to the top level describe is a constant.
RSpec/DescribeClass:
Enabled: false
-# Use `described_class` for tested class / module.
+# Checks that tests use `described_class`.
+RSpec/DescribedClass:
+ Enabled: false
+
+# Checks that the second argument to `describe` specifies a method.
RSpec/DescribeMethod:
Enabled: false
-# Checks that the second argument to top level describe is the tested method
-# name.
-RSpec/DescribedClass:
+# Checks if an example group does not include any tests.
+RSpec/EmptyExampleGroup:
Enabled: false
+ CustomIncludeMethods: []
-# Checks for long example.
+# Checks for long examples.
RSpec/ExampleLength:
Enabled: false
Max: 5
-# Do not use should when describing your tests.
+# Checks that example descriptions do not start with "should".
RSpec/ExampleWording:
Enabled: false
CustomTransform:
@@ -795,6 +802,10 @@ RSpec/ExampleWording:
not: does not
IgnoredWords: []
+# Checks for `expect(...)` calls containing literal values.
+RSpec/ExpectActual:
+ Enabled: false
+
# Checks the file and folder naming of the spec file.
RSpec/FilePath:
Enabled: false
@@ -806,19 +817,65 @@ RSpec/FilePath:
RSpec/Focus:
Enabled: true
+# Checks the arguments passed to `before`, `around`, and `after`.
+RSpec/HookArgument:
+ Enabled: false
+ EnforcedStyle: implicit
+
+# Check that a consistent implict expectation style is used.
+# TODO (rspeicher): Available in rubocop-rspec 1.8.0
+# RSpec/ImplicitExpect:
+# Enabled: true
+# EnforcedStyle: is_expected
+
# Checks for the usage of instance variables.
RSpec/InstanceVariable:
Enabled: false
-# Checks for multiple top-level describes.
+# Checks for `subject` definitions that come after `let` definitions.
+RSpec/LeadingSubject:
+ Enabled: false
+
+# Checks unreferenced `let!` calls being used for test setup.
+RSpec/LetSetup:
+ Enabled: false
+
+# Check that chains of messages are not being stubbed.
+RSpec/MessageChain:
+ Enabled: false
+
+# Checks for consistent message expectation style.
+RSpec/MessageExpectation:
+ Enabled: false
+ EnforcedStyle: allow
+
+# Checks for multiple top level describes.
RSpec/MultipleDescribes:
Enabled: false
-# Enforces the usage of the same method on all negative message expectations.
+# Checks if examples contain too many `expect` calls.
+RSpec/MultipleExpectations:
+ Enabled: false
+ Max: 1
+
+# Checks for explicitly referenced test subjects.
+RSpec/NamedSubject:
+ Enabled: false
+
+# Checks for nested example groups.
+RSpec/NestedGroups:
+ Enabled: false
+ MaxNesting: 2
+
+# Checks for consistent method usage for negating expectations.
RSpec/NotToNot:
EnforcedStyle: not_to
Enabled: true
+# Checks for stubbed test subjects.
+RSpec/SubjectStub:
+ Enabled: false
+
# Prefer using verifying doubles over normal doubles.
RSpec/VerifiedDoubles:
Enabled: false
diff --git a/CHANGELOG b/CHANGELOG
index 9458413669d..8eaeae0cb9c 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -3,10 +3,12 @@ Please view this file on the master branch, on stable branches it's out of date.
v 8.12.0 (unreleased)
- Update the rouge gem to 2.0.6, which adds highlighting support for JSX, Prometheus, and others. !6251
- Only check :can_resolve permission if the note is resolvable
+ - Bump fog-aws to v0.11.0 to support ap-south-1 region
- Add ability to fork to a specific namespace using API. (ritave)
- Cleanup misalignments in Issue list view !6206
- Prune events older than 12 months. (ritave)
- Prepend blank line to `Closes` message on merge request linked to issue (lukehowell)
+ - Fix issues/merge-request templates dropdown for forked projects
- Filter tags by name !6121
- Update gitlab shell secret file also when it is empty. !3774 (glensc)
- Give project selection dropdowns responsive width, make non-wrapping.
@@ -21,27 +23,34 @@ v 8.12.0 (unreleased)
- Use Search::GlobalService.new in the `GET /projects/search/:query` endpoint
- Fix pagination on user snippets page
- Fix sorting of issues in API
+ - Sort project variables by key. !6275 (Diego Souza)
- Ensure specs on sorting of issues in API are deterministic on MySQL
+ - Added ability to use predefined CI variables for environment name
+ - Added ability to specify URL in environment configuration in gitlab-ci.yml
- Escape search term before passing it to Regexp.new !6241 (winniehell)
- Fix pinned sidebar behavior in smaller viewports !6169
- Fix file permissions change when updating a file on the Gitlab UI !5979
- Change merge_error column from string to text type
- Reduce contributions calendar data payload (ClemMakesApps)
+ - Replace contributions calendar timezone payload with dates (ClemMakesApps)
- Add `web_url` field to issue, merge request, and snippet API objects (Ben Boeckel)
- Enable pipeline events by default !6278
- Move parsing of sidekiq ps into helper !6245 (pascalbetz)
+ - Added go to issue boards keyboard shortcut
- Expose `sha` and `merge_commit_sha` in merge request API (Ben Boeckel)
- Set path for all JavaScript cookies to honor GitLab's subdirectory setting !5627 (Mike Greiling)
- Fix blame table layout width
- Fix bug where pagination is still displayed despite all todos marked as done (ClemMakesApps)
- Request only the LDAP attributes we need !6187
- Center build stage columns in pipeline overview (ClemMakesApps)
+ - Fix bug with tooltip not hiding on discussion toggle button
- Rename behaviour to behavior in bug issue template for consistency (ClemMakesApps)
- Fix bug stopping issue description being scrollable after selecting issue template
- Remove suggested colors hover underline (ClemMakesApps)
- Shorten task status phrase (ClemMakesApps)
- Fix project visibility level fields on settings
- Add hover color to emoji icon (ClemMakesApps)
+ - Increase ci_builds artifacts_size column to 8-byte integer to allow larger files
- Add textarea autoresize after comment (ClemMakesApps)
- Refresh todos count cache when an Issue/MR is deleted
- Fix branches page dropdown sort alignment (ClemMakesApps)
@@ -61,10 +70,12 @@ v 8.12.0 (unreleased)
- Use 'git update-ref' for safer web commits !6130
- Sort pipelines requested through the API
- Automatically expand hidden discussions when accessed by a permalink !5585 (Mike Greiling)
+ - Fix issue boards loading on large screens
- Change pipeline duration to be jobs running time instead of simple wall time from start to end !6084
- Show queued time when showing a pipeline !6084
- Remove unused mixins (ClemMakesApps)
- Add search to all issue board lists
+ - Scroll active tab into view on mobile
- Fix groups sort dropdown alignment (ClemMakesApps)
- Add horizontal scrolling to all sub-navs on mobile viewports (ClemMakesApps)
- Use JavaScript tooltips for mentions !5301 (winniehell)
@@ -75,7 +86,8 @@ v 8.12.0 (unreleased)
- Add last commit time to repo view (ClemMakesApps)
- Fix accessibility and visibility of project list dropdown button !6140
- Fix missing flash messages on service edit page (airatshigapov)
- - Added project specific enable/disable setting for LFS !5997
+ - Added project-specific enable/disable setting for LFS !5997
+ - Added group-specific enable/disable setting for LFS !6164
- Don't expose a user's token in the `/api/v3/user` API (!6047)
- Remove redundant js-timeago-pending from user activity log (ClemMakesApps)
- Ability to manage project issues, snippets, wiki, merge requests and builds access level
@@ -130,6 +142,7 @@ v 8.12.0 (unreleased)
- Use default clone protocol on "check out, review, and merge locally" help page URL
- API for Ci Lint !5953 (Katarzyna Kobierska Urszula Budziszewska)
- Allow bulk update merge requests from merge requests index page
+ - Ensure validation messages are shown within the milestone form
- Add notification_settings API calls !5632 (mahcsig)
- Remove duplication between project builds and admin builds view !5680 (Katarzyna Kobierska Ula Budziszewska)
- Fix URLs with anchors in wiki !6300 (houqp)
@@ -138,6 +151,7 @@ v 8.12.0 (unreleased)
- Fix Gitlab::Popen.popen thread-safety issue
- Add specs to removing project (Katarzyna Kobierska Ula Budziszewska)
- Clean environment variables when running git hooks
+ - Fix non-master branch readme display in tree view
v 8.11.6
- Fix unnecessary horizontal scroll area in pipeline visualizations. !6005
@@ -160,6 +174,7 @@ v 8.11.5
- Scope webhooks/services that will run for confidential issues
- Remove gitorious from import_sources
- Fix confidential issues being exposed as public using gitlab.com export
+ - Use oj gem for faster JSON processing
v 8.11.4
- Fix resolving conflicts on forks. !6082
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 549918284c1..d5e15bfce14 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -277,6 +277,8 @@ request is as follows:
1. For more complex migrations, write tests.
1. Merge requests **must** adhere to the [merge request performance
guidelines](doc/development/merge_request_performance_guidelines.md).
+1. For tests that use Capybara or PhantomJS, see this [article on how
+ to write reliable asynchronous tests](https://robots.thoughtbot.com/write-reliable-asynchronous-integration-tests-with-capybara).
The **official merge window** is in the beginning of the month from the 1st to
the 7th day of the month. This is the best time to submit an MR and get
diff --git a/Gemfile b/Gemfile
index 70d8495325f..cb1c619cc64 100644
--- a/Gemfile
+++ b/Gemfile
@@ -206,6 +206,9 @@ gem 'mousetrap-rails', '~> 1.4.6'
# Detect and convert string character encoding
gem 'charlock_holmes', '~> 0.7.3'
+# Faster JSON
+gem 'oj', '~> 2.17.4'
+
# Parse time & duration
gem 'chronic', '~> 0.10.2'
gem 'chronic_duration', '~> 0.10.6'
@@ -296,7 +299,7 @@ group :development, :test do
gem 'spring-commands-teaspoon', '~> 0.0.2'
gem 'rubocop', '~> 0.42.0', require: false
- gem 'rubocop-rspec', '~> 1.5.0', require: false
+ gem 'rubocop-rspec', '~> 1.7.0', require: false
gem 'scss_lint', '~> 0.47.0', require: false
gem 'haml_lint', '~> 0.18.2', require: false
gem 'simplecov', '0.12.0', require: false
diff --git a/Gemfile.lock b/Gemfile.lock
index e2b5a58d973..8e26429df14 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -189,7 +189,7 @@ GEM
erubis (2.7.0)
escape_utils (1.1.1)
eventmachine (1.0.8)
- excon (0.49.0)
+ excon (0.52.0)
execjs (2.6.0)
expression_parser (0.9.0)
factory_girl (4.5.0)
@@ -215,8 +215,8 @@ GEM
flowdock (0.7.1)
httparty (~> 0.7)
multi_json
- fog-aws (0.9.2)
- fog-core (~> 1.27)
+ fog-aws (0.11.0)
+ fog-core (~> 1.38)
fog-json (~> 1.0)
fog-xml (~> 0.1)
ipaddress (~> 0.8)
@@ -225,7 +225,7 @@ GEM
fog-core (~> 1.27)
fog-json (~> 1.0)
fog-xml (~> 0.1)
- fog-core (1.40.0)
+ fog-core (1.42.0)
builder
excon (~> 0.49)
formatador (~> 0.2)
@@ -427,6 +427,7 @@ GEM
rack (>= 1.2, < 3)
octokit (4.3.0)
sawyer (~> 0.7.0, >= 0.5.3)
+ oj (2.17.4)
omniauth (1.3.1)
hashie (>= 1.2, < 4)
rack (>= 1.0, < 3)
@@ -625,8 +626,8 @@ GEM
rainbow (>= 1.99.1, < 3.0)
ruby-progressbar (~> 1.7)
unicode-display_width (~> 1.0, >= 1.0.1)
- rubocop-rspec (1.5.0)
- rubocop (>= 0.40.0)
+ rubocop-rspec (1.7.0)
+ rubocop (>= 0.42.0)
ruby-fogbugz (0.2.1)
crack (~> 0.4)
ruby-prof (0.15.9)
@@ -904,6 +905,7 @@ DEPENDENCIES
nokogiri (~> 1.6.7, >= 1.6.7.2)
oauth2 (~> 1.2.0)
octokit (~> 4.3.0)
+ oj (~> 2.17.4)
omniauth (~> 1.3.1)
omniauth-auth0 (~> 1.4.1)
omniauth-azure-oauth2 (~> 0.0.6)
@@ -945,7 +947,7 @@ DEPENDENCIES
rspec-rails (~> 3.5.0)
rspec-retry (~> 0.4.5)
rubocop (~> 0.42.0)
- rubocop-rspec (~> 1.5.0)
+ rubocop-rspec (~> 1.7.0)
ruby-fogbugz (~> 0.2.1)
ruby-prof (~> 0.15.9)
sanitize (~> 2.0)
diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js
index 31fa508d6c1..c029bf3b5ca 100644
--- a/app/assets/javascripts/application.js
+++ b/app/assets/javascripts/application.js
@@ -251,6 +251,7 @@
} else {
notesHolders.hide();
}
+ $this.trigger('blur');
return e.preventDefault();
});
$document.off("click", '.js-confirm-danger');
diff --git a/app/assets/javascripts/boards/components/board_list.js.es6 b/app/assets/javascripts/boards/components/board_list.js.es6
index 50fc11d7737..474805c1437 100644
--- a/app/assets/javascripts/boards/components/board_list.js.es6
+++ b/app/assets/javascripts/boards/components/board_list.js.es6
@@ -34,6 +34,11 @@
},
issues () {
this.$nextTick(() => {
+ if (this.scrollHeight() <= this.listHeight() && this.list.issuesSize > this.list.issues.length) {
+ this.list.page++;
+ this.list.getIssues(false);
+ }
+
if (this.scrollHeight() > this.listHeight()) {
this.showCount = true;
} else {
diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js
index bea141bae51..087f27d9f4c 100644
--- a/app/assets/javascripts/gl_dropdown.js
+++ b/app/assets/javascripts/gl_dropdown.js
@@ -352,7 +352,13 @@
if (self.options.clicked) {
self.options.clicked(selected, $el, e);
}
- return $el.trigger('blur');
+
+ // Update label right after all modifications in dropdown has been done
+ if (self.options.toggleLabel) {
+ self.updateLabel(selected, $el, self);
+ }
+
+ $el.trigger('blur');
});
}
}
@@ -529,7 +535,7 @@
} else {
if (!selected) {
value = this.options.id ? this.options.id(data) : data.id;
- fieldName = typeof this.options.fieldName === 'function' ? this.options.fieldName() : this.options.fieldName;
+ fieldName = this.options.fieldName;
field = this.dropdown.parent().find("input[name='" + fieldName + "'][value='" + value + "']");
if (field.length) {
@@ -589,6 +595,7 @@
GitLabDropdown.prototype.rowClicked = function(el) {
var field, fieldName, groupName, isInput, selectedIndex, selectedObject, value;
+ fieldName = this.options.fieldName;
isInput = $(this.el).is('input');
if (this.renderedData) {
groupName = el.data('group');
@@ -600,7 +607,6 @@
selectedObject = this.renderedData[selectedIndex];
}
}
- fieldName = typeof this.options.fieldName === 'function' ? this.options.fieldName(selectedObject) : this.options.fieldName;
value = this.options.id ? this.options.id(selectedObject, el) : selectedObject.id;
if (isInput) {
field = $(this.el);
@@ -644,11 +650,6 @@
}
}
- // Update label right after input has been added
- if (this.options.toggleLabel) {
- this.updateLabel(selectedObject, el, this);
- }
-
return selectedObject;
};
@@ -659,9 +660,6 @@
if (this.options.inputId != null) {
$input.attr('id', this.options.inputId);
}
- if (selectedObject && selectedObject.type) {
- $input.attr('data-type', selectedObject.type);
- }
return this.dropdown.before($input);
};
diff --git a/app/assets/javascripts/layout_nav.js b/app/assets/javascripts/layout_nav.js
index ce472f3bcd0..8e2fc0d1479 100644
--- a/app/assets/javascripts/layout_nav.js
+++ b/app/assets/javascripts/layout_nav.js
@@ -10,11 +10,13 @@
};
$(function() {
- hideEndFade($('.scrolling-tabs'));
+ var $scrollingTabs = $('.scrolling-tabs');
+
+ hideEndFade($scrollingTabs);
$(window).off('resize.nav').on('resize.nav', function() {
- return hideEndFade($('.scrolling-tabs'));
+ return hideEndFade($scrollingTabs);
});
- return $('.scrolling-tabs').on('scroll', function(event) {
+ $scrollingTabs.off('scroll').on('scroll', function(event) {
var $this, currentPosition, maxPosition;
$this = $(this);
currentPosition = $this.scrollLeft();
@@ -22,6 +24,23 @@
$this.siblings('.fade-left').toggleClass('scrolling', currentPosition > 0);
return $this.siblings('.fade-right').toggleClass('scrolling', currentPosition < maxPosition - 1);
});
+
+ $scrollingTabs.each(function () {
+ var $this = $(this),
+ scrollingTabWidth = $this.width(),
+ $active = $this.find('.active'),
+ activeWidth = $active.width();
+
+ if ($active.length) {
+ var offset = $active.offset().left + activeWidth;
+
+ if (offset > scrollingTabWidth - 30) {
+ var scrollLeft = scrollingTabWidth / 2;
+ scrollLeft = (offset - scrollLeft) - (activeWidth / 2);
+ $this.scrollLeft(scrollLeft);
+ }
+ }
+ });
});
}).call(this);
diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js
index dcba4a8d275..18bbfa7a459 100644
--- a/app/assets/javascripts/merge_request_tabs.js
+++ b/app/assets/javascripts/merge_request_tabs.js
@@ -232,10 +232,10 @@
$('.hll').removeClass('hll');
locationHash = window.location.hash;
if (locationHash !== '') {
- hashClassString = "." + (locationHash.replace('#', ''));
+ dataLineString = '[data-line-code="' + locationHash.replace('#', '') + '"]';
$diffLine = $(locationHash + ":not(.match)", $('#diffs'));
if (!$diffLine.is('tr')) {
- $diffLine = $('#diffs').find("td" + locationHash + ", td" + hashClassString);
+ $diffLine = $('#diffs').find("td" + locationHash + ", td" + dataLineString);
} else {
$diffLine = $diffLine.find('td');
}
diff --git a/app/assets/javascripts/shortcuts_navigation.js b/app/assets/javascripts/shortcuts_navigation.js
index 469e25482bb..b04159420d1 100644
--- a/app/assets/javascripts/shortcuts_navigation.js
+++ b/app/assets/javascripts/shortcuts_navigation.js
@@ -34,6 +34,9 @@
Mousetrap.bind('g i', function() {
return ShortcutsNavigation.findAndFollowLink('.shortcuts-issues');
});
+ Mousetrap.bind('g l', function() {
+ ShortcutsNavigation.findAndFollowLink('.shortcuts-issue-boards');
+ });
Mousetrap.bind('g m', function() {
return ShortcutsNavigation.findAndFollowLink('.shortcuts-merge_requests');
});
diff --git a/app/assets/javascripts/users/calendar.js b/app/assets/javascripts/users/calendar.js
index b8da7c4f297..3bd4c3c066f 100644
--- a/app/assets/javascripts/users/calendar.js
+++ b/app/assets/javascripts/users/calendar.js
@@ -29,7 +29,7 @@
date.setDate(date.getDate() + i);
var day = date.getDay();
- var count = timestamps[date.getTime() * 0.001];
+ var count = timestamps[dateFormat(date, 'yyyy-mm-dd')];
// Create a new group array if this is the first day of the week
// or if is first object
diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss
index 965fcc06518..46af18580d5 100644
--- a/app/assets/stylesheets/framework/lists.scss
+++ b/app/assets/stylesheets/framework/lists.scss
@@ -162,6 +162,10 @@ ul.content-list {
margin-right: 0;
}
}
+
+ .no-comments {
+ opacity: 0.5;
+ }
}
// When dragging a list item
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
index 3f8433a0e7f..2582cde5a71 100644
--- a/app/assets/stylesheets/framework/typography.scss
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -164,7 +164,7 @@
text-decoration: none;
&:after {
- content: url('icon_anchor.svg');
+ content: image-url('icon_anchor.svg');
visibility: hidden;
}
}
diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss
index 037278bb083..9c84dceed05 100644
--- a/app/assets/stylesheets/pages/boards.scss
+++ b/app/assets/stylesheets/pages/boards.scss
@@ -1,3 +1,4 @@
+lex
[v-cloak] {
display: none;
}
@@ -18,6 +19,10 @@
}
}
+.is-ghost {
+ opacity: 0.3;
+}
+
.dropdown-menu-issues-board-new {
width: 320px;
@@ -34,47 +39,13 @@
> p {
margin: 0;
font-size: 14px;
- color: #9c9c9c;
}
}
.issue-boards-page {
- .content-wrapper {
- display: -webkit-flex;
- display: flex;
- -webkit-flex-direction: column;
- flex-direction: column;
- }
-
- .sub-nav,
- .issues-filters {
- -webkit-flex: none;
- flex: none;
- }
-
.page-with-sidebar {
- display: -webkit-flex;
- display: flex;
- min-height: 100vh;
- max-height: 100vh;
padding-bottom: 0;
}
-
- .issue-boards-content {
- display: -webkit-flex;
- display: flex;
- -webkit-flex: 1;
- flex: 1;
- width: 100%;
-
- .content {
- display: -webkit-flex;
- display: flex;
- -webkit-flex-direction: column;
- flex-direction: column;
- width: 100%;
- }
- }
}
.boards-app-loading {
@@ -83,46 +54,38 @@
}
.boards-list {
- display: -webkit-flex;
- display: flex;
- -webkit-flex: 1;
- flex: 1;
- -webkit-flex-basis: 0;
- flex-basis: 0;
- min-height: calc(100vh - 152px);
- max-height: calc(100vh - 152px);
+ height: calc(100vh - 152px);
+ width: 100%;
padding-top: 25px;
+ padding-bottom: 25px;
padding-right: ($gl-padding / 2);
padding-left: ($gl-padding / 2);
overflow-x: scroll;
+ white-space: nowrap;
@media (min-width: $screen-sm-min) {
+ height: 475px; // Needed for PhantomJS
+ height: calc(100vh - 220px);
min-height: 475px;
- max-height: none;
}
}
.board {
- display: -webkit-flex;
- display: flex;
- min-width: calc(85vw - 15px);
- max-width: calc(85vw - 15px);
- margin-bottom: 25px;
+ display: inline-block;
+ width: calc(85vw - 15px);
+ height: 100%;
padding-right: ($gl-padding / 2);
padding-left: ($gl-padding / 2);
+ white-space: normal;
+ vertical-align: top;
@media (min-width: $screen-sm-min) {
- min-width: 400px;
- max-width: 400px;
+ width: 400px;
}
}
.board-inner {
- display: -webkit-flex;
- display: flex;
- -webkit-flex-direction: column;
- flex-direction: column;
- width: 100%;
+ height: 100%;
font-size: $issue-boards-font-size;
background: $background-color;
border: 1px solid $border-color;
@@ -193,45 +156,31 @@
}
.board-list {
- -webkit-flex: 1;
- flex: 1;
- height: 400px;
+ height: calc(100% - 49px);
margin-bottom: 0;
padding: 5px;
+ list-style: none;
overflow-y: scroll;
overflow-x: hidden;
}
.board-list-loading {
margin-top: 10px;
- font-size: 26px;
-}
-
-.is-ghost {
- opacity: 0.3;
+ font-size: (26px / $issue-boards-font-size) * 1em;
}
.card {
position: relative;
- width: 100%;
padding: 10px $gl-padding;
background: #fff;
border-radius: $border-radius-default;
box-shadow: 0 1px 2px rgba(186, 186, 186, 0.5);
list-style: none;
- &.user-can-drag {
- padding-left: $gl-padding;
- }
-
&:not(:last-child) {
margin-bottom: 5px;
}
- a {
- cursor: pointer;
- }
-
.label {
border: 0;
outline: 0;
@@ -256,14 +205,13 @@
line-height: 25px;
.label {
- margin-right: 4px;
+ margin-right: 5px;
font-size: (14px / $issue-boards-font-size) * 1em;
}
}
.card-number {
- margin-right: 8px;
- font-weight: 500;
+ margin-right: 5px;
}
.issue-boards-search {
diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss
index 60a0d50ba73..3ac34cbc829 100644
--- a/app/assets/stylesheets/pages/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -10,10 +10,6 @@
.issue-labels {
display: inline-block;
}
-
- .issue-no-comments {
- opacity: 0.5;
- }
}
}
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 2a44b95de64..96c06086867 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -231,10 +231,6 @@
.merge-request-labels {
display: inline-block;
}
-
- .merge-request-no-comments {
- opacity: 0.5;
- }
}
.merge-request-angle {
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index cc71b8eb045..1b4d12d3053 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -147,14 +147,37 @@
}
.stage-cell {
- text-align: center;
+ font-size: 0;
svg {
height: 18px;
width: 18px;
+ position: relative;
+ z-index: 2;
vertical-align: middle;
overflow: visible;
}
+
+ .stage-container {
+ display: inline-block;
+ position: relative;
+ margin-right: 6px;
+
+ .tooltip {
+ white-space: nowrap;
+ }
+
+ &:not(:last-child) {
+ &::after {
+ content: '';
+ width: 8px;
+ position: absolute;;
+ right: -7px;
+ bottom: 8px;
+ border-bottom: 2px solid $border-color;
+ }
+ }
+ }
}
.duration,
diff --git a/app/assets/stylesheets/pages/snippets.scss b/app/assets/stylesheets/pages/snippets.scss
index 2aa939b7dc3..5270aea4e79 100644
--- a/app/assets/stylesheets/pages/snippets.scss
+++ b/app/assets/stylesheets/pages/snippets.scss
@@ -2,20 +2,6 @@
padding: 2px;
}
-.snippet-holder {
- margin-bottom: -$gl-padding;
-
- .file-holder {
- border-top: 0;
- }
-
- .file-actions {
- .btn-clipboard {
- @extend .btn;
- }
- }
-}
-
.markdown-snippet-copy {
position: fixed;
top: -10px;
@@ -24,29 +10,18 @@
max-width: 0;
}
-.file-holder.snippet-file-content {
- padding-bottom: $gl-padding;
- border-bottom: 1px solid $border-color;
-
- .file-title {
- padding-top: $gl-padding;
- padding-bottom: $gl-padding;
- }
-
- .file-actions {
- top: 12px;
- }
-
- .file-content {
- border-left: 1px solid $border-color;
- border-right: 1px solid $border-color;
- border-bottom: 1px solid $border-color;
+.snippet-file-content {
+ border-radius: 3px;
+ .btn-clipboard {
+ @extend .btn;
}
}
.snippet-title {
font-size: 24px;
- font-weight: normal;
+ font-weight: 600;
+ padding: $gl-padding;
+ padding-left: 0;
}
.snippet-actions {
diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb
index cdfa8d91a28..aed77d0358a 100644
--- a/app/controllers/admin/groups_controller.rb
+++ b/app/controllers/admin/groups_controller.rb
@@ -60,6 +60,14 @@ class Admin::GroupsController < Admin::ApplicationController
end
def group_params
- params.require(:group).permit(:name, :description, :path, :avatar, :visibility_level, :request_access_enabled)
+ params.require(:group).permit(
+ :avatar,
+ :description,
+ :lfs_enabled,
+ :name,
+ :path,
+ :request_access_enabled,
+ :visibility_level
+ )
end
end
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index cb82d62616c..b83c3a872cf 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -121,7 +121,17 @@ class GroupsController < Groups::ApplicationController
end
def group_params
- params.require(:group).permit(:name, :description, :path, :avatar, :public, :visibility_level, :share_with_group_lock, :request_access_enabled)
+ params.require(:group).permit(
+ :avatar,
+ :description,
+ :lfs_enabled,
+ :name,
+ :path,
+ :public,
+ :request_access_enabled,
+ :share_with_group_lock,
+ :visibility_level
+ )
end
def load_events
diff --git a/app/controllers/projects/builds_controller.rb b/app/controllers/projects/builds_controller.rb
index 9ce5b4de42f..3b2e35a7a05 100644
--- a/app/controllers/projects/builds_controller.rb
+++ b/app/controllers/projects/builds_controller.rb
@@ -78,7 +78,7 @@ class Projects::BuildsController < Projects::ApplicationController
def erase
@build.erase(erased_by: current_user)
redirect_to namespace_project_build_path(project.namespace, project, @build),
- notice: "Build has been sucessfully erased!"
+ notice: "Build has been successfully erased!"
end
def raw
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index aa8645ba8cc..0288ee87717 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -428,6 +428,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
def validates_merge_request
+ # If source project was removed and merge request for some reason
+ # wasn't close (Ex. mr from fork to origin)
+ return invalid_mr if !@merge_request.source_project && @merge_request.open?
+
# Show git not found page
# if there is no saved commits between source & target branch
if @merge_request.commits.blank?
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index a99632454d9..a4bedb3bfe6 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -73,7 +73,7 @@ class UsersController < ApplicationController
def calendar
calendar = contributions_calendar
- @timestamps = calendar.timestamps
+ @activity_dates = calendar.activity_dates
render 'calendar', layout: false
end
diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb
index b9211e88473..ab880ed6de0 100644
--- a/app/helpers/groups_helper.rb
+++ b/app/helpers/groups_helper.rb
@@ -23,4 +23,29 @@ module GroupsHelper
full_title
end
end
+
+ def projects_lfs_status(group)
+ lfs_status =
+ if group.lfs_enabled?
+ group.projects.select(&:lfs_enabled?).size
+ else
+ group.projects.reject(&:lfs_enabled?).size
+ end
+
+ size = group.projects.size
+
+ if lfs_status == size
+ 'for all projects'
+ else
+ "for #{lfs_status} out of #{pluralize(size, 'project')}"
+ end
+ end
+
+ def group_lfs_status(group)
+ status = group.lfs_enabled? ? 'enabled' : 'disabled'
+
+ content_tag(:span, class: "lfs-#{status}") do
+ "#{status.humanize} #{projects_lfs_status(group)}"
+ end
+ end
end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 16a8e52a4ca..56477733ea2 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -27,7 +27,7 @@ module ProjectsHelper
author_html = ""
# Build avatar image tag
- author_html << image_tag(avatar_icon(author, opts[:size]), width: opts[:size], class: "avatar avatar-inline #{"s#{opts[:size]}" if opts[:size]}", alt: '') if opts[:avatar]
+ author_html << image_tag(avatar_icon(author, opts[:size]), width: opts[:size], class: "avatar avatar-inline #{"s#{opts[:size]}" if opts[:size]} #{opts[:avatar_class] if opts[:avatar_class]}", alt: '') if opts[:avatar]
# Build name span tag
if opts[:by_username]
diff --git a/app/helpers/snippets_helper.rb b/app/helpers/snippets_helper.rb
index 0a5a8eb5aee..7e33a562077 100644
--- a/app/helpers/snippets_helper.rb
+++ b/app/helpers/snippets_helper.rb
@@ -1,10 +1,10 @@
module SnippetsHelper
- def reliable_snippet_path(snippet)
+ def reliable_snippet_path(snippet, opts = nil)
if snippet.project_id?
namespace_project_snippet_path(snippet.project.namespace,
- snippet.project, snippet)
+ snippet.project, snippet, opts)
else
- snippet_path(snippet)
+ snippet_path(snippet, opts)
end
end
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 8a9d7555393..dd984aef318 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -85,11 +85,14 @@ module Ci
after_transition any => [:success] do |build|
if build.environment.present?
- service = CreateDeploymentService.new(build.project, build.user,
- environment: build.environment,
- sha: build.sha,
- ref: build.ref,
- tag: build.tag)
+ service = CreateDeploymentService.new(
+ build.project, build.user,
+ environment: build.environment,
+ sha: build.sha,
+ ref: build.ref,
+ tag: build.tag,
+ options: build.options[:environment],
+ variables: build.variables)
service.execute(build)
end
end
diff --git a/app/models/ci/variable.rb b/app/models/ci/variable.rb
index c9c47ec7419..6959223aed9 100644
--- a/app/models/ci/variable.rb
+++ b/app/models/ci/variable.rb
@@ -1,7 +1,7 @@
module Ci
class Variable < ActiveRecord::Base
extend Ci::Model
-
+
belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id
validates_uniqueness_of :key, scope: :gl_project_id
@@ -11,7 +11,9 @@ module Ci
format: { with: /\A[a-zA-Z0-9_]+\z/,
message: "can contain only letters, digits and '_'." }
- attr_encrypted :value,
+ scope :order_key_asc, -> { reorder(key: :asc) }
+
+ attr_encrypted :value,
mode: :per_attribute_iv_and_salt,
insecure_mode: true,
key: Gitlab::Application.secrets.db_key_base,
diff --git a/app/models/concerns/has_status.rb b/app/models/concerns/has_status.rb
index d658552f695..0fa4df0fb56 100644
--- a/app/models/concerns/has_status.rb
+++ b/app/models/concerns/has_status.rb
@@ -20,7 +20,7 @@ module HasStatus
skipped = scope.skipped.select('count(*)').to_sql
deduce_status = "(CASE
- WHEN (#{builds})=(#{created}) THEN NULL
+ WHEN (#{builds})=(#{created}) THEN 'created'
WHEN (#{builds})=(#{skipped}) THEN 'skipped'
WHEN (#{builds})=(#{success})+(#{ignored})+(#{skipped}) THEN 'success'
WHEN (#{builds})=(#{created})+(#{pending})+(#{skipped}) THEN 'pending'
diff --git a/app/models/environment.rb b/app/models/environment.rb
index 75e6f869786..33c9abf382a 100644
--- a/app/models/environment.rb
+++ b/app/models/environment.rb
@@ -4,6 +4,7 @@ class Environment < ActiveRecord::Base
has_many :deployments
before_validation :nullify_external_url
+ before_save :set_environment_type
validates :name,
presence: true,
@@ -26,6 +27,17 @@ class Environment < ActiveRecord::Base
self.external_url = nil if self.external_url.blank?
end
+ def set_environment_type
+ names = name.split('/')
+
+ self.environment_type =
+ if names.many?
+ names.first
+ else
+ nil
+ end
+ end
+
def includes_commit?(commit)
return false unless last_deployment
diff --git a/app/models/group.rb b/app/models/group.rb
index c48869ae465..aefb94b2ada 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -95,6 +95,13 @@ class Group < Namespace
end
end
+ def lfs_enabled?
+ return false unless Gitlab.config.lfs.enabled
+ return Gitlab.config.lfs.enabled if self[:lfs_enabled].nil?
+
+ self[:lfs_enabled]
+ end
+
def add_users(user_ids, access_level, current_user: nil, expires_at: nil)
user_ids.each do |user_id|
Member.add_user(
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index f7d1253d957..75f48fd4ba5 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -652,7 +652,7 @@ class MergeRequest < ActiveRecord::Base
end
def environments
- return unless diff_head_commit
+ return [] unless diff_head_commit
target_project.environments.select do |environment|
environment.includes_commit?(diff_head_commit)
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index 7c29d27ce97..919b3b1f095 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -141,6 +141,11 @@ class Namespace < ActiveRecord::Base
projects.joins(:forked_project_link).find_by('forked_project_links.forked_from_project_id = ?', project.id)
end
+ def lfs_enabled?
+ # User namespace will always default to the global setting
+ Gitlab.config.lfs.enabled
+ end
+
private
def repository_storage_paths
diff --git a/app/models/project.rb b/app/models/project.rb
index 16ca2b688c8..d7f20070be0 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -393,10 +393,9 @@ class Project < ActiveRecord::Base
end
def lfs_enabled?
- return false unless Gitlab.config.lfs.enabled
- return Gitlab.config.lfs.enabled if self[:lfs_enabled].nil?
+ return namespace.lfs_enabled? if self[:lfs_enabled].nil?
- self[:lfs_enabled]
+ self[:lfs_enabled] && Gitlab.config.lfs.enabled
end
def repository_storage_path
diff --git a/app/services/commits/change_service.rb b/app/services/commits/change_service.rb
index ed73d8cb8c2..1c82599c579 100644
--- a/app/services/commits/change_service.rb
+++ b/app/services/commits/change_service.rb
@@ -16,11 +16,29 @@ module Commits
error(ex.message)
end
+ private
+
def commit
raise NotImplementedError
end
- private
+ def commit_change(action)
+ raise NotImplementedError unless repository.respond_to?(action)
+
+ into = @create_merge_request ? @commit.public_send("#{action}_branch_name") : @target_branch
+ tree_id = repository.public_send("check_#{action}_content", @commit, @target_branch)
+
+ if tree_id
+ create_target_branch(into) if @create_merge_request
+
+ repository.public_send(action, current_user, @commit, into, tree_id)
+ success
+ else
+ error_msg = "Sorry, we cannot #{action.to_s.dasherize} this #{@commit.change_type_title} automatically.
+ It may have already been #{action.to_s.dasherize}, or a more recent commit may have updated some of its content."
+ raise ChangeError, error_msg
+ end
+ end
def check_push_permissions
allowed = ::Gitlab::UserAccess.new(current_user, project: project).can_push_to_branch?(@target_branch)
diff --git a/app/services/commits/cherry_pick_service.rb b/app/services/commits/cherry_pick_service.rb
index f9a4efa7182..605cca36f9c 100644
--- a/app/services/commits/cherry_pick_service.rb
+++ b/app/services/commits/cherry_pick_service.rb
@@ -1,19 +1,7 @@
module Commits
class CherryPickService < ChangeService
def commit
- cherry_pick_into = @create_merge_request ? @commit.cherry_pick_branch_name : @target_branch
- cherry_pick_tree_id = repository.check_cherry_pick_content(@commit, @target_branch)
-
- if cherry_pick_tree_id
- create_target_branch(cherry_pick_into) if @create_merge_request
-
- repository.cherry_pick(current_user, @commit, cherry_pick_into, cherry_pick_tree_id)
- success
- else
- error_msg = "Sorry, we cannot cherry-pick this #{@commit.change_type_title} automatically.
- It may have already been cherry-picked, or a more recent commit may have updated some of its content."
- raise ChangeError, error_msg
- end
+ commit_change(:cherry_pick)
end
end
end
diff --git a/app/services/commits/revert_service.rb b/app/services/commits/revert_service.rb
index c7de9f6f35e..addd55cb32f 100644
--- a/app/services/commits/revert_service.rb
+++ b/app/services/commits/revert_service.rb
@@ -1,19 +1,7 @@
module Commits
class RevertService < ChangeService
def commit
- revert_into = @create_merge_request ? @commit.revert_branch_name : @target_branch
- revert_tree_id = repository.check_revert_content(@commit, @target_branch)
-
- if revert_tree_id
- create_target_branch(revert_into) if @create_merge_request
-
- repository.revert(current_user, @commit, revert_into, revert_tree_id)
- success
- else
- error_msg = "Sorry, we cannot revert this #{@commit.change_type_title} automatically.
- It may have already been reverted, or a more recent commit may have updated some of its content."
- raise ChangeError, error_msg
- end
+ commit_change(:revert)
end
end
end
diff --git a/app/services/create_deployment_service.rb b/app/services/create_deployment_service.rb
index efeb9df9527..e6667132e27 100644
--- a/app/services/create_deployment_service.rb
+++ b/app/services/create_deployment_service.rb
@@ -2,9 +2,7 @@ require_relative 'base_service'
class CreateDeploymentService < BaseService
def execute(deployable = nil)
- environment = project.environments.find_or_create_by(
- name: params[:environment]
- )
+ environment = find_or_create_environment
project.deployments.create(
environment: environment,
@@ -15,4 +13,38 @@ class CreateDeploymentService < BaseService
deployable: deployable
)
end
+
+ private
+
+ def find_or_create_environment
+ project.environments.find_or_create_by(name: expanded_name) do |environment|
+ environment.external_url = expanded_url
+ end
+ end
+
+ def expanded_name
+ ExpandVariables.expand(name, variables)
+ end
+
+ def expanded_url
+ return unless url
+
+ @expanded_url ||= ExpandVariables.expand(url, variables)
+ end
+
+ def name
+ params[:environment]
+ end
+
+ def url
+ options[:url]
+ end
+
+ def options
+ params[:options] || {}
+ end
+
+ def variables
+ params[:variables] || []
+ end
end
diff --git a/app/services/milestones/create_service.rb b/app/services/milestones/create_service.rb
index 3b90399af64..b8e08c9f1eb 100644
--- a/app/services/milestones/create_service.rb
+++ b/app/services/milestones/create_service.rb
@@ -3,7 +3,7 @@ module Milestones
def execute
milestone = project.milestones.new(params)
- if milestone.save!
+ if milestone.save
event_service.open_milestone(milestone, current_user)
end
diff --git a/app/views/admin/groups/_form.html.haml b/app/views/admin/groups/_form.html.haml
index 5f7fdfdb011..817910f7ddf 100644
--- a/app/views/admin/groups/_form.html.haml
+++ b/app/views/admin/groups/_form.html.haml
@@ -13,6 +13,8 @@
.col-sm-offset-2.col-sm-10
= render 'shared/allow_request_access', form: f
+ = render 'groups/group_lfs_settings', f: f
+
- if @group.new_record?
.form-group
.col-sm-offset-2.col-sm-10
diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml
index bb374694400..0188ed448ce 100644
--- a/app/views/admin/groups/show.html.haml
+++ b/app/views/admin/groups/show.html.haml
@@ -37,6 +37,12 @@
%strong
= @group.created_at.to_s(:medium)
+ %li
+ %span.light Group Git LFS status:
+ %strong
+ = group_lfs_status(@group)
+ = link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs')
+
.panel.panel-default
.panel-heading
%h3.panel-title
diff --git a/app/views/groups/_group_lfs_settings.html.haml b/app/views/groups/_group_lfs_settings.html.haml
new file mode 100644
index 00000000000..af57065f0fc
--- /dev/null
+++ b/app/views/groups/_group_lfs_settings.html.haml
@@ -0,0 +1,11 @@
+- if current_user.admin?
+ .form-group
+ .col-sm-offset-2.col-sm-10
+ .checkbox
+ = f.label :lfs_enabled do
+ = f.check_box :lfs_enabled, checked: @group.lfs_enabled?
+ %strong
+ Allow projects within this group to use Git LFS
+ = link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs')
+ %br/
+ %span.descr This setting can be overridden in each project. \ No newline at end of file
diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml
index decb89b2fd6..c766370d5a0 100644
--- a/app/views/groups/edit.html.haml
+++ b/app/views/groups/edit.html.haml
@@ -25,6 +25,8 @@
.col-sm-offset-2.col-sm-10
= render 'shared/allow_request_access', form: f
+ = render 'group_lfs_settings', f: f
+
.form-group
%hr
= f.label :share_with_group_lock, class: 'control-label' do
diff --git a/app/views/help/_shortcuts.html.haml b/app/views/help/_shortcuts.html.haml
index 16c16cec137..65842a0479b 100644
--- a/app/views/help/_shortcuts.html.haml
+++ b/app/views/help/_shortcuts.html.haml
@@ -165,6 +165,12 @@
%tr
%td.shortcut
.key g
+ .key l
+ %td
+ Go to issue boards
+ %tr
+ %td.shortcut
+ .key g
.key m
%td
Go to merge requests
diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml
index f7012595a5a..8e4937b7aa0 100644
--- a/app/views/layouts/nav/_project.html.haml
+++ b/app/views/layouts/nav/_project.html.haml
@@ -113,3 +113,7 @@
%li.hidden
= link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits' do
Commits
+
+ -# Shortcut to issue boards
+ %li.hidden
+ = link_to 'Issue Boards', namespace_project_board_path(@project.namespace, @project), title: 'Issue Boards', class: 'shortcuts-issue-boards'
diff --git a/app/views/profiles/update_username.js.haml b/app/views/profiles/update_username.js.haml
index 249680bcab6..de1337a2a24 100644
--- a/app/views/profiles/update_username.js.haml
+++ b/app/views/profiles/update_username.js.haml
@@ -1,6 +1,6 @@
- if @user.valid?
:plain
- new Flash("Username sucessfully changed", "notice")
+ new Flash("Username successfully changed", "notice")
- else
:plain
new Flash("Username change failed - #{@user.errors.full_messages.first}", "alert")
diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml
index bb9493f5158..6391c67021b 100644
--- a/app/views/projects/ci/pipelines/_pipeline.html.haml
+++ b/app/views/projects/ci/pipelines/_pipeline.html.haml
@@ -36,16 +36,14 @@
- stages_status = pipeline.statuses.relevant.latest.stages_status
- - stages.each do |stage|
- %td.stage-cell
+ %td.stage-cell
+ - stages.each do |stage|
- status = stages_status[stage]
- tooltip = "#{stage.titleize}: #{status || 'not found'}"
- if status
- = link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id, anchor: stage), class: "has-tooltip ci-status-icon-#{status}", title: tooltip do
- = ci_icon_for_status(status)
- - else
- .light.has-tooltip{ title: tooltip }
- \-
+ .stage-container
+ = link_to namespace_project_pipeline_path(pipeline.project.namespace, pipeline.project, pipeline.id, anchor: stage), class: "has-tooltip ci-status-icon-#{status}", title: tooltip do
+ = ci_icon_for_status(status)
%td
- if pipeline.duration
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index f6d751a343e..a04d53e02bf 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -84,15 +84,14 @@
= project_feature_access_select(:snippets_access_level)
- if Gitlab.config.lfs.enabled && current_user.admin?
- .form-group
- .checkbox
- = f.label :lfs_enabled do
- = f.check_box :lfs_enabled, checked: @project.lfs_enabled?
- %strong LFS
- %br
- %span.descr
- Git Large File Storage
- = link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs')
+ .row
+ .col-md-9
+ = f.label :lfs_enabled, 'LFS', class: 'label-light'
+ %span.help-block
+ Git Large File Storage
+ = link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs')
+ .col-md-3
+ = f.select :lfs_enabled, [%w(Enabled true), %w(Disabled false)], {}, selected: @project.lfs_enabled?, class: 'pull-right form-control'
- if Gitlab.config.registry.enabled
.form-group
diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml
index 851d4c06990..8b1a8a8a2d9 100644
--- a/app/views/projects/issues/_issue.html.haml
+++ b/app/views/projects/issues/_issue.html.haml
@@ -29,7 +29,7 @@
- note_count = issue.notes.user.count
%li
- = link_to issue_path(issue, anchor: 'notes'), class: ('issue-no-comments' if note_count.zero?) do
+ = link_to issue_path(issue, anchor: 'notes'), class: ('no-comments' if note_count.zero?) do
= icon('comments')
= note_count
diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml
index 31f8d0aeb5b..68fb7d5a414 100644
--- a/app/views/projects/merge_requests/_merge_request.html.haml
+++ b/app/views/projects/merge_requests/_merge_request.html.haml
@@ -41,7 +41,7 @@
- note_count = merge_request.mr_and_commit_notes.user.count
%li
- = link_to merge_request_path(merge_request, anchor: 'notes'), class: ('merge-request-no-comments' if note_count.zero?) do
+ = link_to merge_request_path(merge_request, anchor: 'notes'), class: ('no-comments' if note_count.zero?) do
= icon('comments')
= note_count
diff --git a/app/views/projects/pipelines/index.html.haml b/app/views/projects/pipelines/index.html.haml
index 4d957e0d890..faf28db68d1 100644
--- a/app/views/projects/pipelines/index.html.haml
+++ b/app/views/projects/pipelines/index.html.haml
@@ -47,13 +47,7 @@
%tbody
%th Status
%th Commit
- - stages.each do |stage|
- %th.stage
- - if stage.titleize.length > 12
- %span.has-tooltip{ title: "#{stage.titleize}" }
- = stage.titleize
- - else
- = stage.titleize
+ %th Stages
%th
%th
= render @pipelines, commit_sha: true, stage: true, allow_retry: true, stages: stages
diff --git a/app/views/projects/snippets/_actions.html.haml b/app/views/projects/snippets/_actions.html.haml
index bdbf3e5f4d6..a5a5619fa12 100644
--- a/app/views/projects/snippets/_actions.html.haml
+++ b/app/views/projects/snippets/_actions.html.haml
@@ -3,11 +3,11 @@
= link_to new_namespace_project_snippet_path(@project.namespace, @project), class: 'btn btn-grouped btn-create new-snippet-link', title: "New Snippet" do
New Snippet
- if can?(current_user, :update_project_snippet, @snippet)
- = link_to edit_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-grouped snippable-edit" do
- Edit
- - if can?(current_user, :update_project_snippet, @snippet)
= link_to namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-warning", title: 'Delete Snippet' do
Delete
+ - if can?(current_user, :update_project_snippet, @snippet)
+ = link_to edit_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-grouped snippable-edit" do
+ Edit
- if can?(current_user, :create_project_snippet, @project) || can?(current_user, :update_project_snippet, @snippet)
.visible-xs-block.dropdown
%button.btn.btn-default.btn-block.append-bottom-0.prepend-top-5{ data: { toggle: "dropdown" } }
@@ -21,9 +21,9 @@
New Snippet
- if can?(current_user, :update_project_snippet, @snippet)
%li
- = link_to edit_namespace_project_snippet_path(@project.namespace, @project, @snippet) do
- Edit
- - if can?(current_user, :update_project_snippet, @snippet)
- %li
= link_to namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, title: 'Delete Snippet' do
Delete
+ - if can?(current_user, :update_project_snippet, @snippet)
+ %li
+ = link_to edit_namespace_project_snippet_path(@project.namespace, @project, @snippet) do
+ Edit
diff --git a/app/views/projects/snippets/show.html.haml b/app/views/projects/snippets/show.html.haml
index bae4d8f349f..b70fda88a79 100644
--- a/app/views/projects/snippets/show.html.haml
+++ b/app/views/projects/snippets/show.html.haml
@@ -1,15 +1,14 @@
- page_title @snippet.title, "Snippets"
-.snippet-holder
- = render 'shared/snippets/header'
+= render 'shared/snippets/header'
- %article.file-holder.file-holder-no-border.snippet-file-content
- .file-title.file-title-clear
- = blob_icon 0, @snippet.file_name
- = @snippet.file_name
- .file-actions.hidden-xs
- = clipboard_button(clipboard_target: ".blob-content[data-blob-id='#{@snippet.id}']")
- = link_to 'Raw', raw_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-sm", target: "_blank"
- = render 'shared/snippets/blob'
+%article.file-holder.snippet-file-content
+ .file-title
+ = blob_icon 0, @snippet.file_name
+ = @snippet.file_name
+ .file-actions
+ = clipboard_button(clipboard_target: ".blob-content[data-blob-id='#{@snippet.id}']")
+ = link_to 'Raw', raw_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-sm", target: "_blank"
+ = render 'shared/snippets/blob'
- %div#notes= render "projects/notes/notes_with_form"
+%div#notes= render "projects/notes/notes_with_form"
diff --git a/app/views/projects/tree/_readme.html.haml b/app/views/projects/tree/_readme.html.haml
index baaa2caa6de..a1f4e3e8ed6 100644
--- a/app/views/projects/tree/_readme.html.haml
+++ b/app/views/projects/tree/_readme.html.haml
@@ -1,7 +1,7 @@
%article.file-holder.readme-holder
.file-title
= blob_icon readme.mode, readme.name
- = link_to namespace_project_blob_path(@project.namespace, @project, tree_join(@repository.root_ref, @path, readme.name)) do
+ = link_to namespace_project_blob_path(@project.namespace, @project, tree_join(@ref, @path, readme.name)) do
%strong
= readme.name
.file-content.wiki
diff --git a/app/views/projects/variables/_table.html.haml b/app/views/projects/variables/_table.html.haml
index 6c43f822db4..07cee86ba4c 100644
--- a/app/views/projects/variables/_table.html.haml
+++ b/app/views/projects/variables/_table.html.haml
@@ -9,7 +9,7 @@
%th Value
%th
%tbody
- - @project.variables.each do |variable|
+ - @project.variables.order_key_asc.each do |variable|
- if variable.id?
%tr
%td= variable.key
diff --git a/app/views/search/_results.html.haml b/app/views/search/_results.html.haml
index 252c37532e1..7fe2bce3e7c 100644
--- a/app/views/search/_results.html.haml
+++ b/app/views/search/_results.html.haml
@@ -10,12 +10,16 @@
in group #{link_to @group.name, @group}
.results.prepend-top-10
- .search-results
- - if @scope == 'projects'
- .term
- = render 'shared/projects/list', projects: @search_objects
- - else
- = render partial: "search/results/#{@scope.singularize}", collection: @search_objects
+ - if @scope == 'commits'
+ %ul.list-unstyled
+ = render partial: "search/results/commit", collection: @search_objects
+ - else
+ .search-results
+ - if @scope == 'projects'
+ .term
+ = render 'shared/projects/list', projects: @search_objects
+ - else
+ = render partial: "search/results/#{@scope.singularize}", collection: @search_objects
- if @scope != 'projects'
= paginate(@search_objects, theme: 'gitlab')
diff --git a/app/views/search/results/_commit.html.haml b/app/views/search/results/_commit.html.haml
index 4e6c3965dc6..5b2d83d6b92 100644
--- a/app/views/search/results/_commit.html.haml
+++ b/app/views/search/results/_commit.html.haml
@@ -1,2 +1 @@
-.search-result-row
- = render 'projects/commits/commit', project: @project, commit: commit
+= render 'projects/commits/commit', project: @project, commit: commit
diff --git a/app/views/shared/_visibility_level.html.haml b/app/views/shared/_visibility_level.html.haml
index 107ad19177c..add4536a0a2 100644
--- a/app/views/shared/_visibility_level.html.haml
+++ b/app/views/shared/_visibility_level.html.haml
@@ -1,7 +1,7 @@
.form-group.project-visibility-level-holder
= f.label :visibility_level, class: 'control-label' do
Visibility Level
- = link_to "(?)", help_page_path("public_access/public_access")
+ = link_to icon('question-circle'), help_page_path("public_access/public_access")
.col-sm-10
- if can_change_visibility_level
= render('shared/visibility_radios', model_method: :visibility_level, form: f, selected_level: visibility_level, form_model: form_model)
diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml
index 3856a4917b4..04373684ee9 100644
--- a/app/views/shared/issuable/_form.html.haml
+++ b/app/views/shared/issuable/_form.html.haml
@@ -19,7 +19,7 @@
= dropdown_tag(title, options: { toggle_class: 'js-issuable-selector',
title: title, filter: true, placeholder: 'Filter', footer_content: true,
- data: { data: issuable_template_names, field_name: 'issuable_template', selected: selected_template(issuable), project_path: @project.path, namespace_path: @project.namespace.path } } ) do
+ data: { data: issuable_template_names, field_name: 'issuable_template', selected: selected_template(issuable), project_path: ref_project.path, namespace_path: ref_project.namespace.path } } ) do
%ul.dropdown-footer-list
%li
%a.reset-template
diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml
index af753496260..7ae4211ddfd 100644
--- a/app/views/shared/snippets/_header.html.haml
+++ b/app/views/shared/snippets/_header.html.haml
@@ -6,12 +6,13 @@
%strong.item-title
Snippet #{@snippet.to_reference}
%span.creator
- created by #{link_to_member(@project, @snippet.author, size: 24, author_class: "author item-title")}
+ authored
= time_ago_with_tooltip(@snippet.created_at, placement: 'bottom', html_class: 'snippet_updated_ago')
- if @snippet.updated_at != @snippet.created_at
%span
= icon('edit', title: 'edited')
= time_ago_with_tooltip(@snippet.updated_at, placement: 'bottom', html_class: 'snippet_edited_ago')
+ by #{link_to_member(@project, @snippet.author, size: 24, author_class: "author item-title", avatar_class: "hidden-xs")}
.snippet-actions
- if @snippet.project_id?
@@ -19,6 +20,5 @@
- else
= render "snippets/actions"
-.content-block.second-block
- %h2.snippet-title.prepend-top-0.append-bottom-0
- = markdown escape_once(@snippet.title), pipeline: :single_line, author: @snippet.author
+%h2.snippet-title.prepend-top-0.append-bottom-0
+ = markdown escape_once(@snippet.title), pipeline: :single_line, author: @snippet.author
diff --git a/app/views/shared/snippets/_snippet.html.haml b/app/views/shared/snippets/_snippet.html.haml
index c96dfefe17f..ea17bec8677 100644
--- a/app/views/shared/snippets/_snippet.html.haml
+++ b/app/views/shared/snippets/_snippet.html.haml
@@ -3,19 +3,30 @@
.title
= link_to reliable_snippet_path(snippet) do
- = truncate(snippet.title, length: 60)
+ = snippet.title
- if snippet.private?
- %span.label.label-gray
+ %span.label.label-gray.hidden-xs
= icon('lock')
private
- %span.monospace.pull-right
+ %span.monospace.pull-right.hidden-xs
= snippet.file_name
- %small.pull-right.cgray
+ %ul.controls.visible-xs
+ %li
+ - note_count = snippet.notes.user.count
+ = link_to reliable_snippet_path(snippet, anchor: 'notes'), class: ('no-comments' if note_count.zero?) do
+ = icon('comments')
+ = note_count
+ %li
+ %span.sr-only
+ = visibility_level_label(snippet.visibility_level)
+ = visibility_level_icon(snippet.visibility_level, fw: false)
+
+ %small.pull-right.cgray.hidden-xs
- if snippet.project_id?
= link_to snippet.project.name_with_namespace, namespace_project_path(snippet.project.namespace, snippet.project)
- .snippet-info
+ .snippet-info.hidden-xs
= link_to user_snippets_path(snippet.author) do
= snippet.author_name
authored #{time_ago_with_tooltip(snippet.created_at)}
diff --git a/app/views/snippets/_actions.html.haml b/app/views/snippets/_actions.html.haml
index 160c6cd84da..fdaca199218 100644
--- a/app/views/snippets/_actions.html.haml
+++ b/app/views/snippets/_actions.html.haml
@@ -2,12 +2,12 @@
- if current_user
= link_to new_snippet_path, class: "btn btn-grouped btn-create new-snippet-link", title: "New Snippet" do
New Snippet
- - if can?(current_user, :update_personal_snippet, @snippet)
- = link_to edit_snippet_path(@snippet), class: "btn btn-grouped snippable-edit" do
- Edit
- if can?(current_user, :admin_personal_snippet, @snippet)
= link_to snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-danger", title: 'Delete Snippet' do
Delete
+ - if can?(current_user, :update_personal_snippet, @snippet)
+ = link_to edit_snippet_path(@snippet), class: "btn btn-grouped snippable-edit" do
+ Edit
- if current_user
.visible-xs-block.dropdown
%button.btn.btn-default.btn-block.append-bottom-0.prepend-top-5{ data: { toggle: "dropdown" } }
@@ -18,11 +18,11 @@
%li
= link_to new_snippet_path, title: "New Snippet" do
New Snippet
- - if can?(current_user, :update_personal_snippet, @snippet)
- %li
- = link_to edit_snippet_path(@snippet) do
- Edit
- if can?(current_user, :admin_personal_snippet, @snippet)
%li
= link_to snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, title: 'Delete Snippet' do
Delete
+ - if can?(current_user, :update_personal_snippet, @snippet)
+ %li
+ = link_to edit_snippet_path(@snippet) do
+ Edit
diff --git a/app/views/snippets/show.html.haml b/app/views/snippets/show.html.haml
index ed3992650d4..fa403da8f79 100644
--- a/app/views/snippets/show.html.haml
+++ b/app/views/snippets/show.html.haml
@@ -1,13 +1,12 @@
- page_title @snippet.title, "Snippets"
-.snippet-holder
- = render 'shared/snippets/header'
+= render 'shared/snippets/header'
- %article.file-holder.file-holder-no-border.snippet-file-content
- .file-title.file-title-clear
- = blob_icon 0, @snippet.file_name
- = @snippet.file_name
- .file-actions.hidden-xs
- = clipboard_button(clipboard_target: ".blob-content[data-blob-id='#{@snippet.id}']")
- = link_to 'Raw', raw_snippet_path(@snippet), class: "btn btn-sm", target: "_blank"
- = render 'shared/snippets/blob'
+%article.file-holder.snippet-file-content
+ .file-title
+ = blob_icon 0, @snippet.file_name
+ = @snippet.file_name
+ .file-actions
+ = clipboard_button(clipboard_target: ".blob-content[data-blob-id='#{@snippet.id}']")
+ = link_to 'Raw', raw_snippet_path(@snippet), class: "btn btn-sm", target: "_blank"
+ = render 'shared/snippets/blob'
diff --git a/app/views/users/calendar.html.haml b/app/views/users/calendar.html.haml
index 77f2ddefb1e..09ff8a76d27 100644
--- a/app/views/users/calendar.html.haml
+++ b/app/views/users/calendar.html.haml
@@ -4,6 +4,6 @@
Summary of issues, merge requests, and push events
:javascript
new Calendar(
- #{@timestamps.to_json},
+ #{@activity_dates.to_json},
'#{user_calendar_activities_path}'
- );
+ ); \ No newline at end of file
diff --git a/db/migrate/20160901213340_add_lfs_enabled_to_namespaces.rb b/db/migrate/20160901213340_add_lfs_enabled_to_namespaces.rb
new file mode 100644
index 00000000000..fd413d1ca8c
--- /dev/null
+++ b/db/migrate/20160901213340_add_lfs_enabled_to_namespaces.rb
@@ -0,0 +1,12 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class AddLfsEnabledToNamespaces < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ add_column :namespaces, :lfs_enabled, :boolean
+ end
+end
diff --git a/db/migrate/20160907131111_add_environment_type_to_environments.rb b/db/migrate/20160907131111_add_environment_type_to_environments.rb
new file mode 100644
index 00000000000..fac73753d5b
--- /dev/null
+++ b/db/migrate/20160907131111_add_environment_type_to_environments.rb
@@ -0,0 +1,9 @@
+class AddEnvironmentTypeToEnvironments < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ add_column :environments, :environment_type, :string
+ end
+end
diff --git a/db/migrate/20160913162434_remove_projects_pushes_since_gc.rb b/db/migrate/20160913162434_remove_projects_pushes_since_gc.rb
index c5b8c35e961..18ea9d43a43 100644
--- a/db/migrate/20160913162434_remove_projects_pushes_since_gc.rb
+++ b/db/migrate/20160913162434_remove_projects_pushes_since_gc.rb
@@ -14,6 +14,6 @@ class RemoveProjectsPushesSinceGc < ActiveRecord::Migration
end
def down
- add_column_with_default! :projects, :pushes_since_gc, :integer, default: 0
+ add_column_with_default :projects, :pushes_since_gc, :integer, default: 0
end
end
diff --git a/db/migrate/20160913212128_change_artifacts_size_column.rb b/db/migrate/20160913212128_change_artifacts_size_column.rb
new file mode 100644
index 00000000000..063bbca537c
--- /dev/null
+++ b/db/migrate/20160913212128_change_artifacts_size_column.rb
@@ -0,0 +1,15 @@
+class ChangeArtifactsSizeColumn < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = true
+
+ DOWNTIME_REASON = 'Changing an integer column size requires a full table rewrite.'
+
+ def up
+ change_column :ci_builds, :artifacts_size, :integer, limit: 8
+ end
+
+ def down
+ # do nothing
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index af00094c6b6..3567908de03 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,8 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-
-ActiveRecord::Schema.define(version: 20160913162434) do
+ActiveRecord::Schema.define(version: 20160913212128) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -178,7 +177,7 @@ ActiveRecord::Schema.define(version: 20160913162434) do
t.datetime "erased_at"
t.datetime "artifacts_expire_at"
t.string "environment"
- t.integer "artifacts_size"
+ t.integer "artifacts_size", limit: 8
t.string "when"
t.text "yaml_variables"
t.datetime "queued_at"
@@ -393,10 +392,11 @@ ActiveRecord::Schema.define(version: 20160913162434) do
create_table "environments", force: :cascade do |t|
t.integer "project_id"
- t.string "name", null: false
+ t.string "name", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.string "external_url"
+ t.string "environment_type"
end
add_index "environments", ["project_id", "name"], name: "index_environments_on_project_id_and_name", using: :btree
@@ -653,6 +653,7 @@ ActiveRecord::Schema.define(version: 20160913162434) do
t.integer "visibility_level", default: 20, null: false
t.boolean "request_access_enabled", default: true, null: false
t.datetime "deleted_at"
+ t.boolean "lfs_enabled"
end
add_index "namespaces", ["created_at"], name: "index_namespaces_on_created_at", using: :btree
diff --git a/doc/api/groups.md b/doc/api/groups.md
index a898387eaa2..3e94e1e4efe 100644
--- a/doc/api/groups.md
+++ b/doc/api/groups.md
@@ -288,6 +288,7 @@ Parameters:
- `path` (required) - The path of the group
- `description` (optional) - The group's description
- `visibility_level` (optional) - The group's visibility. 0 for private, 10 for internal, 20 for public.
+- `lfs_enabled` (optional) - Enable/disable Large File Storage (LFS) for the projects in this group
## Transfer project to group
@@ -317,6 +318,7 @@ PUT /groups/:id
| `path` | string | no | The path of the group |
| `description` | string | no | The description of the group |
| `visibility_level` | integer | no | The visibility level of the group. 0 for private, 10 for internal, 20 for public. |
+| `lfs_enabled` (optional) | boolean | no | Enable/disable Large File Storage (LFS) for the projects in this group |
```bash
curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/groups/5?name=Experimental"
diff --git a/doc/api/notes.md b/doc/api/notes.md
index 85d140d06ac..572844b8b3f 100644
--- a/doc/api/notes.md
+++ b/doc/api/notes.md
@@ -78,7 +78,8 @@ Parameters:
### Create new issue note
-Creates a new note to a single project issue.
+Creates a new note to a single project issue. If you create a note where the body
+only contains an Award Emoji, you'll receive this object back.
```
POST /projects/:id/issues/:issue_id/notes
@@ -204,6 +205,7 @@ Parameters:
### Create new snippet note
Creates a new note for a single snippet. Snippet notes are comments users can post to a snippet.
+If you create a note where the body only contains an Award Emoji, you'll receive this object back.
```
POST /projects/:id/snippets/:snippet_id/notes
@@ -332,6 +334,8 @@ Parameters:
### Create new merge request note
Creates a new note for a single merge request.
+If you create a note where the body only contains an Award Emoji, you'll receive
+this object back.
```
POST /projects/:id/merge_requests/:merge_request_id/notes
diff --git a/doc/api/settings.md b/doc/api/settings.md
index a76dad0ebd4..aaa2c99642b 100644
--- a/doc/api/settings.md
+++ b/doc/api/settings.md
@@ -67,7 +67,7 @@ PUT /application/settings
| `default_snippet_visibility` | integer | no | What visibility level new snippets receive. Can take `0` _(Private)_, `1` _(Internal)_ and `2` _(Public)_ as a parameter. Default is `0`.|
| `domain_whitelist` | array of strings | no | Force people to use only corporate emails for sign-up. Default is null, meaning there is no restriction. |
| `domain_blacklist_enabled` | boolean | no | Enable/disable the `domain_blacklist` |
-| `domain_blacklist` | array of strings | yes (if `domain_whitelist_enabled` is `true` | People trying to sign-up with emails from this domain will not be allowed to do so. |
+| `domain_blacklist` | array of strings | yes (if `domain_blacklist_enabled` is `true`) | People trying to sign-up with emails from this domain will not be allowed to do so. |
| `user_oauth_applications` | boolean | no | Allow users to register any application to use GitLab as an OAuth provider |
| `after_sign_out_path` | string | no | Where to redirect users after logout |
| `container_registry_token_expire_delay` | integer | no | Container Registry token duration in minutes |
diff --git a/doc/ci/examples/README.md b/doc/ci/examples/README.md
index 406396deaaa..71670e6247c 100644
--- a/doc/ci/examples/README.md
+++ b/doc/ci/examples/README.md
@@ -16,4 +16,4 @@ Apart from those, here is an collection of tutorials and guides on setting up yo
- [Repo's with examples for various languages](https://gitlab.com/groups/gitlab-examples)
- [The .gitlab-ci.yml file for GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab-ci.yml)
-[gitlab-ci-templates][https://gitlab.com/gitlab-org/gitlab-ci-yml]
+[gitlab-ci-templates]: https://gitlab.com/gitlab-org/gitlab-ci-yml
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index ff4c8ddc54b..16868554c1f 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -90,8 +90,7 @@ builds, including deploy builds. This can be an array or a multi-line string.
### after_script
->**Note:**
-Introduced in GitLab 8.7 and requires Gitlab Runner v1.2
+> Introduced in GitLab 8.7 and requires Gitlab Runner v1.2
`after_script` is used to define the command that will be run after for all
builds. This has to be an array or a multi-line string.
@@ -135,8 +134,7 @@ Alias for [stages](#stages).
### variables
->**Note:**
-Introduced in GitLab Runner v0.5.0.
+> Introduced in GitLab Runner v0.5.0.
GitLab CI allows you to add variables to `.gitlab-ci.yml` that are set in the
build environment. The variables are stored in the Git repository and are meant
@@ -158,8 +156,7 @@ Variables can be also defined on [job level](#job-variables).
### cache
->**Note:**
-Introduced in GitLab Runner v0.7.0.
+> Introduced in GitLab Runner v0.7.0.
`cache` is used to specify a list of files and directories which should be
cached between builds.
@@ -220,8 +217,7 @@ will be always present. For implementation details, please check GitLab Runner.
#### cache:key
->**Note:**
-Introduced in GitLab Runner v1.0.0.
+> Introduced in GitLab Runner v1.0.0.
The `key` directive allows you to define the affinity of caching
between jobs, allowing to have a single cache for all jobs,
@@ -531,8 +527,7 @@ The above script will:
#### Manual actions
->**Note:**
-Introduced in GitLab 8.10.
+> Introduced in GitLab 8.10.
Manual actions are a special type of job that are not executed automatically;
they need to be explicitly started by a user. Manual actions can be started
@@ -543,17 +538,16 @@ An example usage of manual actions is deployment to production.
### environment
->**Note:**
-Introduced in GitLab 8.9.
+> Introduced in GitLab 8.9.
-`environment` is used to define that a job deploys to a specific environment.
+`environment` is used to define that a job deploys to a specific [environment].
This allows easy tracking of all deployments to your environments straight from
GitLab.
If `environment` is specified and no environment under that name exists, a new
one will be created automatically.
-The `environment` name must contain only letters, digits, '-' and '_'. Common
+The `environment` name must contain only letters, digits, '-', '_', '/', '$', '{', '}' and spaces. Common
names are `qa`, `staging`, and `production`, but you can use whatever name works
with your workflow.
@@ -571,6 +565,35 @@ deploy to production:
The `deploy to production` job will be marked as doing deployment to
`production` environment.
+#### dynamic environments
+
+> [Introduced][ce-6323] in GitLab 8.12 and GitLab Runner 1.6.
+
+`environment` can also represent a configuration hash with `name` and `url`.
+These parameters can use any of the defined CI [variables](#variables)
+(including predefined, secure variables and `.gitlab-ci.yml` variables).
+
+The common use case is to create dynamic environments for branches and use them
+as review apps.
+
+---
+
+**Example configurations**
+
+```
+deploy as review app:
+ stage: deploy
+ script: ...
+ environment:
+ name: review-apps/$CI_BUILD_REF_NAME
+ url: https://$CI_BUILD_REF_NAME.review.example.com/
+```
+
+The `deploy as review app` job will be marked as deployment to dynamically
+create the `review-apps/branch-name` environment.
+
+This environment should be accessible under `https://branch-name.review.example.com/`.
+
### artifacts
>**Notes:**
@@ -638,8 +661,7 @@ be available for download in the GitLab UI.
#### artifacts:name
->**Note:**
-Introduced in GitLab 8.6 and GitLab Runner v1.1.0.
+> Introduced in GitLab 8.6 and GitLab Runner v1.1.0.
The `name` directive allows you to define the name of the created artifacts
archive. That way, you can have a unique name for every archive which could be
@@ -702,8 +724,7 @@ job:
#### artifacts:when
->**Note:**
-Introduced in GitLab 8.9 and GitLab Runner v1.3.0.
+> Introduced in GitLab 8.9 and GitLab Runner v1.3.0.
`artifacts:when` is used to upload artifacts on build failure or despite the
failure.
@@ -728,8 +749,7 @@ job:
#### artifacts:expire_in
->**Note:**
-Introduced in GitLab 8.9 and GitLab Runner v1.3.0.
+> Introduced in GitLab 8.9 and GitLab Runner v1.3.0.
`artifacts:expire_in` is used to delete uploaded artifacts after the specified
time. By default, artifacts are stored on GitLab forever. `expire_in` allows you
@@ -764,8 +784,7 @@ job:
### dependencies
->**Note:**
-Introduced in GitLab 8.6 and GitLab Runner v1.1.1.
+> Introduced in GitLab 8.6 and GitLab Runner v1.1.1.
This feature should be used in conjunction with [`artifacts`](#artifacts) and
allows you to define the artifacts to pass between different builds.
@@ -839,9 +858,8 @@ job:
## Git Strategy
->**Note:**
-Introduced in GitLab 8.9 as an experimental feature. May change in future
-releases or be removed completely.
+> Introduced in GitLab 8.9 as an experimental feature. May change in future
+ releases or be removed completely.
You can set the `GIT_STRATEGY` used for getting recent application code. `clone`
is slower, but makes sure you have a clean directory before every build. `fetch`
@@ -863,8 +881,7 @@ variables:
## Shallow cloning
->**Note:**
-Introduced in GitLab 8.9 as an experimental feature. May change in future
+> Introduced in GitLab 8.9 as an experimental feature. May change in future
releases or be removed completely.
You can specify the depth of fetching and cloning using `GIT_DEPTH`. This allows
@@ -894,8 +911,7 @@ variables:
## Hidden keys
->**Note:**
-Introduced in GitLab 8.6 and GitLab Runner v1.1.1.
+> Introduced in GitLab 8.6 and GitLab Runner v1.1.1.
Keys that start with a dot (`.`) will be not processed by GitLab CI. You can
use this feature to ignore jobs, or use the
@@ -923,8 +939,7 @@ Read more about the various [YAML features](https://learnxinyminutes.com/docs/ya
### Anchors
->**Note:**
-Introduced in GitLab 8.6 and GitLab Runner v1.1.1.
+> Introduced in GitLab 8.6 and GitLab Runner v1.1.1.
YAML also has a handy feature called 'anchors', which let you easily duplicate
content across your document. Anchors can be used to duplicate/inherit
@@ -1067,3 +1082,5 @@ Visit the [examples README][examples] to see a list of examples using GitLab
CI with various languages.
[examples]: ../examples/README.md
+[ce-6323]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6323
+[environment]: ../environments.md
diff --git a/doc/container_registry/README.md b/doc/container_registry/README.md
index 047a0b08406..d7740647a91 100644
--- a/doc/container_registry/README.md
+++ b/doc/container_registry/README.md
@@ -78,9 +78,9 @@ delete them.
> **Note:**
This feature requires GitLab 8.8 and GitLab Runner 1.2.
-Make sure that your GitLab Runner is configured to allow building docker images.
-You have to check the [Using Docker Build documentation](../ci/docker/using_docker_build.md).
-Then see the CI documentation on [Using the GitLab Container Registry](../ci/docker/using_docker_build.md#using-the-gitlab-container-registry).
+Make sure that your GitLab Runner is configured to allow building Docker images by
+following the [Using Docker Build](../ci/docker/using_docker_build.md)
+and [Using the GitLab Container Registry documentation](../ci/docker/using_docker_build.md#using-the-gitlab-container-registry).
## Limitations
diff --git a/doc/development/migration_style_guide.md b/doc/development/migration_style_guide.md
index b8fab3aaff7..295eae0a88e 100644
--- a/doc/development/migration_style_guide.md
+++ b/doc/development/migration_style_guide.md
@@ -111,6 +111,28 @@ class MyMigration < ActiveRecord::Migration
end
```
+
+## Integer column type
+
+By default, an integer column can hold up to a 4-byte (32-bit) number. That is
+a max value of 2,147,483,647. Be aware of this when creating a column that will
+hold file sizes in byte units. If you are tracking file size in bytes this
+restricts the maximum file size to just over 2GB.
+
+To allow an integer column to hold up to an 8-byte (64-bit) number, explicitly
+set the limit to 8-bytes. This will allow the column to hold a value up to
+9,223,372,036,854,775,807.
+
+Rails migration example:
+
+```
+add_column_with_default(:projects, :foo, :integer, default: 10, limit: 8)
+
+# or
+
+add_column(:projects, :foo, :integer, default: 10, limit: 8)
+```
+
## Testing
Make sure that your migration works with MySQL and PostgreSQL with data. An empty database does not guarantee that your migration is correct.
diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md
index 835af5443a3..3f4056dc440 100644
--- a/doc/raketasks/backup_restore.md
+++ b/doc/raketasks/backup_restore.md
@@ -79,6 +79,9 @@ gitlab_rails['backup_upload_connection'] = {
'region' => 'eu-west-1',
'aws_access_key_id' => 'AKIAKIAKI',
'aws_secret_access_key' => 'secret123'
+ # If using an IAM Profile, leave aws_access_key_id & aws_secret_access_key empty
+ # ie. 'aws_access_key_id' => '',
+ # 'use_iam_profile' => 'true'
}
gitlab_rails['backup_upload_remote_directory'] = 'my.s3.bucket'
```
@@ -95,6 +98,9 @@ For installations from source:
region: eu-west-1
aws_access_key_id: AKIAKIAKI
aws_secret_access_key: 'secret123'
+ # If using an IAM Profile, leave aws_access_key_id & aws_secret_access_key empty
+ # ie. aws_access_key_id: ''
+ # use_iam_profile: 'true'
# The remote 'directory' to store your backups. For S3, this would be the bucket name.
remote_directory: 'my.s3.bucket'
# Turns on AWS Server-Side Encryption with Amazon S3-Managed Keys for backups, this is optional
diff --git a/doc/user/project/builds/artifacts.md b/doc/user/project/builds/artifacts.md
index c93ae1c369c..88f1863dddb 100644
--- a/doc/user/project/builds/artifacts.md
+++ b/doc/user/project/builds/artifacts.md
@@ -101,4 +101,36 @@ inside GitLab that make that possible.
![Build artifacts browser](img/build_artifacts_browser.png)
+## Downloading the latest build artifacts
+
+It is possible to download the latest artifacts of a build via a well known URL
+so you can use it for scripting purposes.
+
+The structure of the URL is the following:
+
+```
+https://example.com/<namespace>/<project>/builds/artifacts/<ref>/download?job=<job_name>
+```
+
+For example, to download the latest artifacts of the job named `rspec 6 20` of
+the `master` branch of the `gitlab-ce` project that belongs to the `gitlab-org`
+namespace, the URL would be:
+
+```
+https://gitlab.com/gitlab-org/gitlab-ce/builds/artifacts/master/download?job=rspec+6+20
+```
+
+The latest builds are also exposed in the UI in various places. Specifically,
+look for the download button in:
+
+- the main project's page
+- the branches page
+- the tags page
+
+If the latest build has failed to upload the artifacts, you can see that
+information in the UI.
+
+![Latest artifacts button](img/build_latest_artifacts_browser.png)
+
+
[gitlab workhorse]: https://gitlab.com/gitlab-org/gitlab-workhorse "GitLab Workhorse repository"
diff --git a/doc/user/project/builds/img/build_latest_artifacts_browser.png b/doc/user/project/builds/img/build_latest_artifacts_browser.png
new file mode 100644
index 00000000000..d8e9071958c
--- /dev/null
+++ b/doc/user/project/builds/img/build_latest_artifacts_browser.png
Binary files differ
diff --git a/doc/workflow/importing/img/import_projects_from_github_importer.png b/doc/workflow/importing/img/import_projects_from_github_importer.png
index b6ed8dd692a..2082de06f47 100644
--- a/doc/workflow/importing/img/import_projects_from_github_importer.png
+++ b/doc/workflow/importing/img/import_projects_from_github_importer.png
Binary files differ
diff --git a/doc/workflow/importing/img/import_projects_from_github_new_project_page.png b/doc/workflow/importing/img/import_projects_from_github_new_project_page.png
index c8f35a50f48..6e91c430a33 100644
--- a/doc/workflow/importing/img/import_projects_from_github_new_project_page.png
+++ b/doc/workflow/importing/img/import_projects_from_github_new_project_page.png
Binary files differ
diff --git a/doc/workflow/importing/img/import_projects_from_github_select_auth_method.png b/doc/workflow/importing/img/import_projects_from_github_select_auth_method.png
new file mode 100644
index 00000000000..c11863ab10c
--- /dev/null
+++ b/doc/workflow/importing/img/import_projects_from_github_select_auth_method.png
Binary files differ
diff --git a/doc/workflow/importing/import_projects_from_github.md b/doc/workflow/importing/import_projects_from_github.md
index 370d885d366..dd38fe0bc01 100644
--- a/doc/workflow/importing/import_projects_from_github.md
+++ b/doc/workflow/importing/import_projects_from_github.md
@@ -1,54 +1,113 @@
# Import your project from GitHub to GitLab
+Import your projects from GitHub to GitLab with minimal effort.
+
+## Overview
+
>**Note:**
-In order to enable the GitHub import setting, you may also want to
-enable the [GitHub integration][gh-import] in your GitLab instance. This
-configuration is optional, you will be able import your GitHub repositories
-with a Personal Access Token.
+If you are an administrator you can enable the [GitHub integration][gh-import]
+in your GitLab instance sitewide. This configuration is optional, users will be
+able import their GitHub repositories with a [personal access token][gh-token].
-At its current state, GitHub importer can import:
+- At its current state, GitHub importer can import:
+ - the repository description (GitLab 7.7+)
+ - the Git repository data (GitLab 7.7+)
+ - the issues (GitLab 7.7+)
+ - the pull requests (GitLab 8.4+)
+ - the wiki pages (GitLab 8.4+)
+ - the milestones (GitLab 8.7+)
+ - the labels (GitLab 8.7+)
+ - the release note descriptions (GitLab 8.12+)
+- References to pull requests and issues are preserved (GitLab 8.7+)
+- Repository public access is retained. If a repository is private in GitHub
+ it will be created as private in GitLab as well.
-- the repository description (introduced in GitLab 7.7)
-- the git repository data (introduced in GitLab 7.7)
-- the issues (introduced in GitLab 7.7)
-- the pull requests (introduced in GitLab 8.4)
-- the wiki pages (introduced in GitLab 8.4)
-- the milestones (introduced in GitLab 8.7)
-- the labels (introduced in GitLab 8.7)
-- the release note descriptions (introduced in GitLab 8.12)
+## How it works
-With GitLab 8.7+, references to pull requests and issues are preserved.
+When issues/pull requests are being imported, the GitHub importer tries to find
+the GitHub author/assignee in GitLab's database using the GitHub ID. For this
+to work, the GitHub author/assignee should have signed in beforehand in GitLab
+and [**associated their GitHub account**][social sign-in]. If the user is not
+found in GitLab's database, the project creator (most of the times the current
+user that started the import process) is set as the author, but a reference on
+the issue about the original GitHub author is kept.
-The importer page is visible when you [create a new project][new-project].
-Click on the **GitHub** link and, if you are logged in via the GitHub
-integration, you will be redirected to GitHub for permission to access your
-projects. After accepting, you'll be automatically redirected to the importer.
+The importer will create any new namespaces (groups) if they don't exist or in
+the case the namespace is taken, the repository will be imported under the user's
+namespace that started the import process.
-If you are not using the GitHub integration, you can still perform a one-off
-authorization with GitHub to access your projects.
+## Importing your GitHub repositories
-Alternatively, you can also enter a GitHub Personal Access Token. Once you enter
-your token, you'll be taken to the importer.
+The importer page is visible when you create a new project.
![New project page on GitLab](img/import_projects_from_github_new_project_page.png)
----
+Click on the **GitHub** link and the import authorization process will start.
+There are two ways to authorize access to your GitHub repositories:
-While at the GitHub importer page, you can see the import statuses of your
-GitHub projects. Those that are being imported will show a _started_ status,
-those already imported will be green, whereas those that are not yet imported
-have an **Import** button on the right side of the table. If you want, you can
-import all your GitHub projects in one go by hitting **Import all projects**
-in the upper left corner.
+1. [Using the GitHub integration][gh-integration] (if it's enabled by your
+ GitLab administrator). This is the preferred way as it's possible to
+ preserve the GitHub authors/assignees. Read more in the [How it works](#how-it-works)
+ section.
+1. [Using a personal access token][gh-token] provided by GitHub.
-![GitHub importer page](img/import_projects_from_github_importer.png)
+![Select authentication method](img/import_projects_from_github_select_auth_method.png)
+
+### Authorize access to your repositories using the GitHub integration
----
+If the [GitHub integration][gh-import] is enabled by your GitLab administrator,
+you can use it instead of the personal access token.
+
+1. First you may want to connect your GitHub account to GitLab in order for
+ the username mapping to be correct. Follow the [social sign-in] documentation
+ on how to do so.
+1. Once you connect GitHub, click the **List your GitHub repositories** button
+ and you will be redirected to GitHub for permission to access your projects.
+1. After accepting, you'll be automatically redirected to the importer.
+
+You can now go on and [select which repositories to import](#select-which-repositories-to-import).
+
+### Authorize access to your repositories using a personal access token
+
+>**Note:**
+For a proper author/assignee mapping for issues and pull requests, the
+[GitHub integration][gh-integration] should be used instead of the
+[personal access token][gh-token]. If the GitHub integration is enabled by your
+GitLab administrator, it should be the preferred method to import your repositories.
+Read more in the [How it works](#how-it-works) section.
-The importer will create any new namespaces if they don't exist or in the
-case the namespace is taken, the project will be imported on the user's
-namespace.
+If you are not using the GitHub integration, you can still perform a one-off
+authorization with GitHub to grant GitLab access your repositories:
+
+1. Go to <https://github.com/settings/tokens/new>.
+1. Enter a token description.
+1. Check the `repo` scope.
+1. Click **Generate token**.
+1. Copy the token hash.
+1. Go back to GitLab and provide the token to the GitHub importer.
+1. Hit the **List your GitHub repositories** button and wait while GitLab reads
+ your repositories' information. Once done, you'll be taken to the importer
+ page to select the repositories to import.
+
+### Select which repositories to import
+
+After you've authorized access to your GitHub repositories, you will be
+redirected to the GitHub importer page.
+
+From there, you can see the import statuses of your GitHub repositories.
+
+- Those that are being imported will show a _started_ status,
+- those already successfully imported will be green with a _done_ status,
+- whereas those that are not yet imported will have an **Import** button on the
+ right side of the table.
+
+If you want, you can import all your GitHub projects in one go by hitting
+**Import all projects** in the upper left corner.
+
+![GitHub importer page](img/import_projects_from_github_importer.png)
[gh-import]: ../../integration/github.md "GitHub integration"
-[ee-gh]: http://docs.gitlab.com/ee/integration/github.html "GitHub integration for GitLab EE"
[new-project]: ../../gitlab-basics/create-project.md "How to create a new project in GitLab"
+[gh-integration]: #authorize-access-to-your-repositories-using-the-github-integration
+[gh-token]: #authorize-access-to-your-repositories-using-a-personal-access-token
+[social sign-in]: ../../profile/account/social_sign_in.md
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 4f736e4ec2b..bfee4b6c752 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -86,7 +86,8 @@ module API
expose(:snippets_enabled) { |project, options| project.feature_available?(:snippets, options[:user]) }
expose :created_at, :last_activity_at
- expose :shared_runners_enabled, :lfs_enabled
+ expose :shared_runners_enabled
+ expose :lfs_enabled?, as: :lfs_enabled
expose :creator_id
expose :namespace
expose :forked_from_project, using: Entities::BasicProjectDetails, if: lambda{ |project, options| project.forked? }
@@ -121,6 +122,7 @@ module API
class Group < Grape::Entity
expose :id, :name, :path, :description, :visibility_level
+ expose :lfs_enabled?, as: :lfs_enabled
expose :avatar_url
expose :web_url
end
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index d2df77238d5..60ac9bdfa33 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -27,13 +27,14 @@ module API
# path (required) - The path of the group
# description (optional) - The description of the group
# visibility_level (optional) - The visibility level of the group
+ # lfs_enabled (optional) - Enable/disable LFS for the projects in this group
# Example Request:
# POST /groups
post do
authorize! :create_group
required_attributes! [:name, :path]
- attrs = attributes_for_keys [:name, :path, :description, :visibility_level]
+ attrs = attributes_for_keys [:name, :path, :description, :visibility_level, :lfs_enabled]
@group = Group.new(attrs)
if @group.save
@@ -51,13 +52,14 @@ module API
# path (optional) - The path of the group
# description (optional) - The description of the group
# visibility_level (optional) - The visibility level of the group
+ # lfs_enabled (optional) - Enable/disable LFS for the projects in this group
# Example Request:
# PUT /groups/:id
put ':id' do
group = find_group(params[:id])
authorize! :admin_group, group
- attrs = attributes_for_keys [:name, :path, :description, :visibility_level]
+ attrs = attributes_for_keys [:name, :path, :description, :visibility_level, :lfs_enabled]
if ::Groups::UpdateService.new(group, current_user, attrs).execute
present group, with: Entities::GroupDetail
diff --git a/lib/api/notes.rb b/lib/api/notes.rb
index 8bfa998dc53..c5c214d4d13 100644
--- a/lib/api/notes.rb
+++ b/lib/api/notes.rb
@@ -83,12 +83,12 @@ module API
opts[:created_at] = params[:created_at]
end
- @note = ::Notes::CreateService.new(user_project, current_user, opts).execute
+ note = ::Notes::CreateService.new(user_project, current_user, opts).execute
- if @note.valid?
- present @note, with: Entities::Note
+ if note.valid?
+ present note, with: Entities::const_get(note.class.name)
else
- not_found!("Note #{@note.errors.messages}")
+ not_found!("Note #{note.errors.messages}")
end
end
diff --git a/lib/ci/api/entities.rb b/lib/ci/api/entities.rb
index 3f5bdaba3f5..66c05773b68 100644
--- a/lib/ci/api/entities.rb
+++ b/lib/ci/api/entities.rb
@@ -15,6 +15,15 @@ module Ci
expose :filename, :size
end
+ class BuildOptions < Grape::Entity
+ expose :image
+ expose :services
+ expose :artifacts
+ expose :cache
+ expose :dependencies
+ expose :after_script
+ end
+
class Build < Grape::Entity
expose :id, :ref, :tag, :sha, :status
expose :name, :token, :stage
diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb
index caa815f720f..0369e80312a 100644
--- a/lib/ci/gitlab_ci_yaml_processor.rb
+++ b/lib/ci/gitlab_ci_yaml_processor.rb
@@ -60,7 +60,7 @@ module Ci
name: job[:name].to_s,
allow_failure: job[:allow_failure] || false,
when: job[:when] || 'on_success',
- environment: job[:environment],
+ environment: job[:environment_name],
yaml_variables: yaml_variables(name),
options: {
image: job[:image],
@@ -69,6 +69,7 @@ module Ci
cache: job[:cache],
dependencies: job[:dependencies],
after_script: job[:after_script],
+ environment: job[:environment],
}.compact
}
end
diff --git a/lib/expand_variables.rb b/lib/expand_variables.rb
new file mode 100644
index 00000000000..7b1533d0d32
--- /dev/null
+++ b/lib/expand_variables.rb
@@ -0,0 +1,17 @@
+module ExpandVariables
+ class << self
+ def expand(value, variables)
+ # Convert hash array to variables
+ if variables.is_a?(Array)
+ variables = variables.reduce({}) do |hash, variable|
+ hash[variable[:key]] = variable[:value]
+ hash
+ end
+ end
+
+ value.gsub(/\$([a-zA-Z_][a-zA-Z0-9_]*)|\${\g<1>}|%\g<1>%/) do
+ variables[$1 || $2]
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config/node/environment.rb b/lib/gitlab/ci/config/node/environment.rb
new file mode 100644
index 00000000000..d388ab6b879
--- /dev/null
+++ b/lib/gitlab/ci/config/node/environment.rb
@@ -0,0 +1,68 @@
+module Gitlab
+ module Ci
+ class Config
+ module Node
+ ##
+ # Entry that represents an environment.
+ #
+ class Environment < Entry
+ include Validatable
+
+ ALLOWED_KEYS = %i[name url]
+
+ validations do
+ validate do
+ unless hash? || string?
+ errors.add(:config, 'should be a hash or a string')
+ end
+ end
+
+ validates :name, presence: true
+ validates :name,
+ type: {
+ with: String,
+ message: Gitlab::Regex.environment_name_regex_message }
+
+ validates :name,
+ format: {
+ with: Gitlab::Regex.environment_name_regex,
+ message: Gitlab::Regex.environment_name_regex_message }
+
+ with_options if: :hash? do
+ validates :config, allowed_keys: ALLOWED_KEYS
+
+ validates :url,
+ length: { maximum: 255 },
+ addressable_url: true,
+ allow_nil: true
+ end
+ end
+
+ def hash?
+ @config.is_a?(Hash)
+ end
+
+ def string?
+ @config.is_a?(String)
+ end
+
+ def name
+ value[:name]
+ end
+
+ def url
+ value[:url]
+ end
+
+ def value
+ case @config
+ when String then { name: @config }
+ when Hash then @config
+ else {}
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config/node/job.rb b/lib/gitlab/ci/config/node/job.rb
index 0cbdf7619c0..603334d6793 100644
--- a/lib/gitlab/ci/config/node/job.rb
+++ b/lib/gitlab/ci/config/node/job.rb
@@ -13,7 +13,7 @@ module Gitlab
type stage when artifacts cache dependencies before_script
after_script variables environment]
- attributes :tags, :allow_failure, :when, :environment, :dependencies
+ attributes :tags, :allow_failure, :when, :dependencies
validations do
validates :config, allowed_keys: ALLOWED_KEYS
@@ -29,58 +29,53 @@ module Gitlab
inclusion: { in: %w[on_success on_failure always manual],
message: 'should be on_success, on_failure, ' \
'always or manual' }
- validates :environment,
- type: {
- with: String,
- message: Gitlab::Regex.environment_name_regex_message }
- validates :environment,
- format: {
- with: Gitlab::Regex.environment_name_regex,
- message: Gitlab::Regex.environment_name_regex_message }
validates :dependencies, array_of_strings: true
end
end
- node :before_script, Script,
+ node :before_script, Node::Script,
description: 'Global before script overridden in this job.'
- node :script, Commands,
+ node :script, Node::Commands,
description: 'Commands that will be executed in this job.'
- node :stage, Stage,
+ node :stage, Node::Stage,
description: 'Pipeline stage this job will be executed into.'
- node :type, Stage,
+ node :type, Node::Stage,
description: 'Deprecated: stage this job will be executed into.'
- node :after_script, Script,
+ node :after_script, Node::Script,
description: 'Commands that will be executed when finishing job.'
- node :cache, Cache,
+ node :cache, Node::Cache,
description: 'Cache definition for this job.'
- node :image, Image,
+ node :image, Node::Image,
description: 'Image that will be used to execute this job.'
- node :services, Services,
+ node :services, Node::Services,
description: 'Services that will be used to execute this job.'
- node :only, Trigger,
+ node :only, Node::Trigger,
description: 'Refs policy this job will be executed for.'
- node :except, Trigger,
+ node :except, Node::Trigger,
description: 'Refs policy this job will be executed for.'
- node :variables, Variables,
+ node :variables, Node::Variables,
description: 'Environment variables available for this job.'
- node :artifacts, Artifacts,
+ node :artifacts, Node::Artifacts,
description: 'Artifacts configuration for this job.'
+ node :environment, Node::Environment,
+ description: 'Environment configuration for this job.'
+
helpers :before_script, :script, :stage, :type, :after_script,
:cache, :image, :services, :only, :except, :variables,
- :artifacts, :commands
+ :artifacts, :commands, :environment
def compose!(deps = nil)
super do
@@ -133,6 +128,8 @@ module Gitlab
only: only,
except: except,
variables: variables_defined? ? variables : nil,
+ environment: environment_defined? ? environment : nil,
+ environment_name: environment_defined? ? environment[:name] : nil,
artifacts: artifacts,
after_script: after_script }
end
diff --git a/lib/gitlab/contributions_calendar.rb b/lib/gitlab/contributions_calendar.rb
index bd681f03173..b164f5a2eea 100644
--- a/lib/gitlab/contributions_calendar.rb
+++ b/lib/gitlab/contributions_calendar.rb
@@ -1,16 +1,16 @@
module Gitlab
class ContributionsCalendar
- attr_reader :timestamps, :projects, :user
+ attr_reader :activity_dates, :projects, :user
def initialize(projects, user)
@projects = projects
@user = user
end
- def timestamps
- return @timestamps if @timestamps.present?
+ def activity_dates
+ return @activity_dates if @activity_dates.present?
- @timestamps = {}
+ @activity_dates = {}
date_from = 1.year.ago
events = Event.reorder(nil).contributions.where(author_id: user.id).
@@ -19,18 +19,17 @@ module Gitlab
select('date(created_at) as date, count(id) as total_amount').
map(&:attributes)
- dates = (1.year.ago.to_date..Date.today).to_a
+ activity_dates = (1.year.ago.to_date..Date.today).to_a
- dates.each do |date|
- date_id = date.to_time.to_i.to_s
+ activity_dates.each do |date|
day_events = events.find { |day_events| day_events["date"] == date }
if day_events
- @timestamps[date_id] = day_events["total_amount"]
+ @activity_dates[date] = day_events["total_amount"]
end
end
- @timestamps
+ @activity_dates
end
def events_by_date(date)
diff --git a/lib/gitlab/database/migration_helpers.rb b/lib/gitlab/database/migration_helpers.rb
index 927f9dad20b..0bd6e148ba8 100644
--- a/lib/gitlab/database/migration_helpers.rb
+++ b/lib/gitlab/database/migration_helpers.rb
@@ -129,12 +129,14 @@ module Gitlab
# column - The name of the column to add.
# type - The column type (e.g. `:integer`).
# default - The default value for the column.
+ # limit - Sets a column limit. For example, for :integer, the default is
+ # 4-bytes. Set `limit: 8` to allow 8-byte integers.
# allow_null - When set to `true` the column will allow NULL values, the
# default is to not allow NULL values.
#
# This method can also take a block which is passed directly to the
# `update_column_in_batches` method.
- def add_column_with_default(table, column, type, default:, allow_null: false, &block)
+ def add_column_with_default(table, column, type, default:, limit: nil, allow_null: false, &block)
if transaction_open?
raise 'add_column_with_default can not be run inside a transaction, ' \
'you can disable transactions by calling disable_ddl_transaction! ' \
@@ -144,7 +146,11 @@ module Gitlab
disable_statement_timeout
transaction do
- add_column(table, column, type, default: nil)
+ if limit
+ add_column(table, column, type, default: nil, limit: limit)
+ else
+ add_column(table, column, type, default: nil)
+ end
# Changing the default before the update ensures any newly inserted
# rows already use the proper default value.
diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb
index ffad5e17c78..bc8bbf337f3 100644
--- a/lib/gitlab/regex.rb
+++ b/lib/gitlab/regex.rb
@@ -96,11 +96,11 @@ module Gitlab
end
def environment_name_regex
- @environment_name_regex ||= /\A[a-zA-Z0-9_-]+\z/.freeze
+ @environment_name_regex ||= /\A[a-zA-Z0-9_\\\/\${}. -]+\z/.freeze
end
def environment_name_regex_message
- "can contain only letters, digits, '-' and '_'."
+ "can contain only letters, digits, '-', '_', '/', '$', '{', '}', '.' and spaces"
end
end
end
diff --git a/spec/factories/ci/runner_projects.rb b/spec/factories/ci/runner_projects.rb
index 83fccad679f..3372e5ab685 100644
--- a/spec/factories/ci/runner_projects.rb
+++ b/spec/factories/ci/runner_projects.rb
@@ -1,14 +1,3 @@
-# == Schema Information
-#
-# Table name: runner_projects
-#
-# id :integer not null, primary key
-# runner_id :integer not null
-# project_id :integer not null
-# created_at :datetime
-# updated_at :datetime
-#
-
FactoryGirl.define do
factory :ci_runner_project, class: Ci::RunnerProject do
runner_id 1
diff --git a/spec/factories/ci/runners.rb b/spec/factories/ci/runners.rb
index 45eaebb2576..e3b73e29987 100644
--- a/spec/factories/ci/runners.rb
+++ b/spec/factories/ci/runners.rb
@@ -1,22 +1,3 @@
-# == Schema Information
-#
-# Table name: runners
-#
-# id :integer not null, primary key
-# token :string(255)
-# created_at :datetime
-# updated_at :datetime
-# description :string(255)
-# contacted_at :datetime
-# active :boolean default(TRUE), not null
-# is_shared :boolean default(FALSE)
-# name :string(255)
-# version :string(255)
-# revision :string(255)
-# platform :string(255)
-# architecture :string(255)
-#
-
FactoryGirl.define do
factory :ci_runner, class: Ci::Runner do
sequence :description do |n|
diff --git a/spec/factories/ci/variables.rb b/spec/factories/ci/variables.rb
index 856a8e725eb..6653f0bb5c3 100644
--- a/spec/factories/ci/variables.rb
+++ b/spec/factories/ci/variables.rb
@@ -1,17 +1,3 @@
-# == Schema Information
-#
-# Table name: ci_variables
-#
-# id :integer not null, primary key
-# project_id :integer not null
-# key :string(255)
-# value :text
-# encrypted_value :text
-# encrypted_value_salt :string(255)
-# encrypted_value_iv :string(255)
-# gl_project_id :integer
-#
-
FactoryGirl.define do
factory :ci_variable, class: Ci::Variable do
sequence(:key) { |n| "VARIABLE_#{n}" }
diff --git a/spec/factories/group_members.rb b/spec/factories/group_members.rb
index debb86d997f..2044ebec09a 100644
--- a/spec/factories/group_members.rb
+++ b/spec/factories/group_members.rb
@@ -1,16 +1,3 @@
-# == Schema Information
-#
-# Table name: group_members
-#
-# id :integer not null, primary key
-# group_access :integer not null
-# group_id :integer not null
-# user_id :integer not null
-# created_at :datetime
-# updated_at :datetime
-# notification_level :integer default(3), not null
-#
-
FactoryGirl.define do
factory :group_member do
access_level { GroupMember::OWNER }
diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb
index c6c2e2095df..19941978c5f 100644
--- a/spec/features/boards/boards_spec.rb
+++ b/spec/features/boards/boards_spec.rb
@@ -2,6 +2,7 @@ require 'rails_helper'
describe 'Issue Boards', feature: true, js: true do
include WaitForAjax
+ include WaitForVueResource
let(:project) { create(:empty_project, :public) }
let(:user) { create(:user) }
@@ -93,15 +94,8 @@ describe 'Issue Boards', feature: true, js: true do
end
it 'shows issues in lists' do
- page.within(find('.board:nth-child(2)')) do
- expect(page.find('.board-header')).to have_content('2')
- expect(page).to have_selector('.card', count: 2)
- end
-
- page.within(find('.board:nth-child(3)')) do
- expect(page.find('.board-header')).to have_content('2')
- expect(page).to have_selector('.card', count: 2)
- end
+ wait_for_board_cards(2, 2)
+ wait_for_board_cards(3, 2)
end
it 'shows confidential issues with icon' do
@@ -187,13 +181,13 @@ describe 'Issue Boards', feature: true, js: true do
expect(page).to have_content('Showing 20 of 56 issues')
evaluate_script("document.querySelectorAll('.board .board-list')[0].scrollTop = document.querySelectorAll('.board .board-list')[0].scrollHeight")
- wait_for_vue_resource(spinner: false)
+ wait_for_vue_resource
expect(page).to have_selector('.card', count: 40)
expect(page).to have_content('Showing 40 of 56 issues')
evaluate_script("document.querySelectorAll('.board .board-list')[0].scrollTop = document.querySelectorAll('.board .board-list')[0].scrollHeight")
- wait_for_vue_resource(spinner: false)
+ wait_for_vue_resource
expect(page).to have_selector('.card', count: 56)
expect(page).to have_content('Showing all issues')
@@ -202,37 +196,33 @@ describe 'Issue Boards', feature: true, js: true do
context 'backlog' do
it 'shows issues in backlog with no labels' do
- page.within(find('.board', match: :first)) do
- expect(page.find('.board-header')).to have_content('6')
- expect(page).to have_selector('.card', count: 6)
- end
+ wait_for_board_cards(1, 6)
end
it 'moves issue from backlog into list' do
drag_to(list_to_index: 1)
- page.within(find('.board', match: :first)) do
- expect(page.find('.board-header')).to have_content('5')
- expect(page).to have_selector('.card', count: 5)
- end
-
wait_for_vue_resource
-
- page.within(find('.board:nth-child(2)')) do
- expect(page.find('.board-header')).to have_content('3')
- expect(page).to have_selector('.card', count: 3)
- end
+ wait_for_board_cards(1, 5)
+ wait_for_board_cards(2, 3)
end
end
context 'done' do
it 'shows list of done issues' do
- expect(find('.board:nth-child(4)')).to have_selector('.card', count: 1)
+ wait_for_board_cards(4, 1)
+ wait_for_ajax
end
it 'moves issue to done' do
drag_to(list_from_index: 0, list_to_index: 3)
+ wait_for_board_cards(1, 5)
+ wait_for_board_cards(2, 2)
+ wait_for_board_cards(3, 2)
+ wait_for_board_cards(4, 2)
+
+ expect(find('.board:nth-child(1)')).not_to have_content(issue9.title)
expect(find('.board:nth-child(4)')).to have_selector('.card', count: 2)
expect(find('.board:nth-child(4)')).to have_content(issue9.title)
expect(find('.board:nth-child(4)')).not_to have_content(planning.title)
@@ -241,8 +231,12 @@ describe 'Issue Boards', feature: true, js: true do
it 'removes all of the same issue to done' do
drag_to(list_from_index: 1, list_to_index: 3)
- expect(find('.board:nth-child(2)')).to have_selector('.card', count: 1)
- expect(find('.board:nth-child(3)')).to have_selector('.card', count: 1)
+ wait_for_board_cards(1, 6)
+ wait_for_board_cards(2, 1)
+ wait_for_board_cards(3, 1)
+ wait_for_board_cards(4, 2)
+
+ expect(find('.board:nth-child(2)')).not_to have_content(issue6.title)
expect(find('.board:nth-child(4)')).to have_content(issue6.title)
expect(find('.board:nth-child(4)')).not_to have_content(planning.title)
end
@@ -252,6 +246,11 @@ describe 'Issue Boards', feature: true, js: true do
it 'changes position of list' do
drag_to(list_from_index: 1, list_to_index: 2, selector: '.board-header')
+ wait_for_board_cards(1, 6)
+ wait_for_board_cards(2, 2)
+ wait_for_board_cards(3, 2)
+ wait_for_board_cards(4, 1)
+
expect(find('.board:nth-child(2)')).to have_content(development.title)
expect(find('.board:nth-child(2)')).to have_content(planning.title)
end
@@ -259,8 +258,11 @@ describe 'Issue Boards', feature: true, js: true do
it 'issue moves between lists' do
drag_to(list_from_index: 1, card_index: 1, list_to_index: 2)
- expect(find('.board:nth-child(2)')).to have_selector('.card', count: 1)
- expect(find('.board:nth-child(3)')).to have_selector('.card', count: 3)
+ wait_for_board_cards(1, 6)
+ wait_for_board_cards(2, 1)
+ wait_for_board_cards(3, 3)
+ wait_for_board_cards(4, 1)
+
expect(find('.board:nth-child(3)')).to have_content(issue6.title)
expect(find('.board:nth-child(3)').all('.card').last).not_to have_content(development.title)
end
@@ -268,8 +270,11 @@ describe 'Issue Boards', feature: true, js: true do
it 'issue moves between lists' do
drag_to(list_from_index: 2, list_to_index: 1)
- expect(find('.board:nth-child(2)')).to have_selector('.card', count: 3)
- expect(find('.board:nth-child(3)')).to have_selector('.card', count: 1)
+ wait_for_board_cards(1, 6)
+ wait_for_board_cards(2, 3)
+ wait_for_board_cards(3, 1)
+ wait_for_board_cards(4, 1)
+
expect(find('.board:nth-child(2)')).to have_content(issue7.title)
expect(find('.board:nth-child(2)').all('.card').first).not_to have_content(planning.title)
end
@@ -277,8 +282,12 @@ describe 'Issue Boards', feature: true, js: true do
it 'issue moves from done' do
drag_to(list_from_index: 3, list_to_index: 1)
- expect(find('.board:nth-child(2)')).to have_selector('.card', count: 3)
expect(find('.board:nth-child(2)')).to have_content(issue8.title)
+
+ wait_for_board_cards(1, 6)
+ wait_for_board_cards(2, 3)
+ wait_for_board_cards(3, 2)
+ wait_for_board_cards(4, 0)
end
context 'issue card' do
@@ -341,10 +350,7 @@ describe 'Issue Boards', feature: true, js: true do
end
it 'moves issues from backlog into new list' do
- page.within(find('.board', match: :first)) do
- expect(page.find('.board-header')).to have_content('6')
- expect(page).to have_selector('.card', count: 6)
- end
+ wait_for_board_cards(1, 6)
click_button 'Create new list'
wait_for_ajax
@@ -355,10 +361,7 @@ describe 'Issue Boards', feature: true, js: true do
wait_for_vue_resource
- page.within(find('.board', match: :first)) do
- expect(page.find('.board-header')).to have_content('5')
- expect(page).to have_selector('.card', count: 5)
- end
+ wait_for_board_cards(1, 5)
end
end
end
@@ -372,22 +375,14 @@ describe 'Issue Boards', feature: true, js: true do
page.within '.dropdown-menu-author' do
click_link(user2.name)
end
- wait_for_vue_resource(spinner: false)
+ wait_for_vue_resource
expect(find('.js-author-search')).to have_content(user2.name)
end
wait_for_vue_resource
-
- page.within(find('.board', match: :first)) do
- expect(page.find('.board-header')).to have_content('1')
- expect(page).to have_selector('.card', count: 1)
- end
-
- page.within(find('.board:nth-child(2)')) do
- expect(page.find('.board-header')).to have_content('0')
- expect(page).to have_selector('.card', count: 0)
- end
+ wait_for_board_cards(1, 1)
+ wait_for_empty_boards((2..4))
end
it 'filters by assignee' do
@@ -398,22 +393,15 @@ describe 'Issue Boards', feature: true, js: true do
page.within '.dropdown-menu-assignee' do
click_link(user.name)
end
- wait_for_vue_resource(spinner: false)
+ wait_for_vue_resource
expect(find('.js-assignee-search')).to have_content(user.name)
end
wait_for_vue_resource
- page.within(find('.board', match: :first)) do
- expect(page.find('.board-header')).to have_content('1')
- expect(page).to have_selector('.card', count: 1)
- end
-
- page.within(find('.board:nth-child(2)')) do
- expect(page.find('.board-header')).to have_content('0')
- expect(page).to have_selector('.card', count: 0)
- end
+ wait_for_board_cards(1, 1)
+ wait_for_empty_boards((2..4))
end
it 'filters by milestone' do
@@ -424,22 +412,16 @@ describe 'Issue Boards', feature: true, js: true do
page.within '.milestone-filter' do
click_link(milestone.title)
end
- wait_for_vue_resource(spinner: false)
+ wait_for_vue_resource
expect(find('.js-milestone-select')).to have_content(milestone.title)
end
wait_for_vue_resource
-
- page.within(find('.board', match: :first)) do
- expect(page.find('.board-header')).to have_content('0')
- expect(page).to have_selector('.card', count: 0)
- end
-
- page.within(find('.board:nth-child(2)')) do
- expect(page.find('.board-header')).to have_content('1')
- expect(page).to have_selector('.card', count: 1)
- end
+ wait_for_board_cards(1, 0)
+ wait_for_board_cards(2, 1)
+ wait_for_board_cards(3, 0)
+ wait_for_board_cards(4, 0)
end
it 'filters by label' do
@@ -449,22 +431,14 @@ describe 'Issue Boards', feature: true, js: true do
page.within '.dropdown-menu-labels' do
click_link(testing.title)
- wait_for_vue_resource(spinner: false)
+ wait_for_vue_resource
find('.dropdown-menu-close').click
end
end
wait_for_vue_resource
-
- page.within(find('.board', match: :first)) do
- expect(page.find('.board-header')).to have_content('1')
- expect(page).to have_selector('.card', count: 1)
- end
-
- page.within(find('.board:nth-child(2)')) do
- expect(page.find('.board-header')).to have_content('0')
- expect(page).to have_selector('.card', count: 0)
- end
+ wait_for_board_cards(1, 1)
+ wait_for_empty_boards((2..4))
end
it 'infinite scrolls list with label filter' do
@@ -478,7 +452,7 @@ describe 'Issue Boards', feature: true, js: true do
page.within '.dropdown-menu-labels' do
click_link(testing.title)
- wait_for_vue_resource(spinner: false)
+ wait_for_vue_resource
find('.dropdown-menu-close').click
end
end
@@ -509,24 +483,17 @@ describe 'Issue Boards', feature: true, js: true do
page.within(find('.dropdown-menu-labels')) do
click_link(testing.title)
- wait_for_vue_resource(spinner: false)
+ wait_for_vue_resource
click_link(bug.title)
- wait_for_vue_resource(spinner: false)
+ wait_for_vue_resource
find('.dropdown-menu-close').click
end
end
wait_for_vue_resource
- page.within(find('.board', match: :first)) do
- expect(page.find('.board-header')).to have_content('1')
- expect(page).to have_selector('.card', count: 1)
- end
-
- page.within(find('.board:nth-child(2)')) do
- expect(page.find('.board-header')).to have_content('0')
- expect(page).to have_selector('.card', count: 0)
- end
+ wait_for_board_cards(1, 1)
+ wait_for_empty_boards((2..4))
end
it 'filters by no label' do
@@ -536,22 +503,17 @@ describe 'Issue Boards', feature: true, js: true do
page.within '.dropdown-menu-labels' do
click_link("No Label")
- wait_for_vue_resource(spinner: false)
+ wait_for_vue_resource
find('.dropdown-menu-close').click
end
end
wait_for_vue_resource
- page.within(find('.board', match: :first)) do
- expect(page.find('.board-header')).to have_content('5')
- expect(page).to have_selector('.card', count: 5)
- end
-
- page.within(find('.board:nth-child(2)')) do
- expect(page.find('.board-header')).to have_content('0')
- expect(page).to have_selector('.card', count: 0)
- end
+ wait_for_board_cards(1, 5)
+ wait_for_board_cards(2, 0)
+ wait_for_board_cards(3, 0)
+ wait_for_board_cards(4, 1)
end
it 'filters by clicking label button on issue' do
@@ -559,20 +521,13 @@ describe 'Issue Boards', feature: true, js: true do
expect(page).to have_selector('.card', count: 6)
expect(find('.card', match: :first)).to have_content(bug.title)
click_button(bug.title)
- wait_for_vue_resource(spinner: false)
+ wait_for_vue_resource
end
wait_for_vue_resource
- page.within(find('.board', match: :first)) do
- expect(page.find('.board-header')).to have_content('1')
- expect(page).to have_selector('.card', count: 1)
- end
-
- page.within(find('.board:nth-child(2)')) do
- expect(page.find('.board-header')).to have_content('0')
- expect(page).to have_selector('.card', count: 0)
- end
+ wait_for_board_cards(1, 1)
+ wait_for_empty_boards((2..4))
page.within('.labels-filter') do
expect(find('.dropdown-toggle-text')).to have_content(bug.title)
@@ -584,7 +539,7 @@ describe 'Issue Boards', feature: true, js: true do
page.within(find('.card', match: :first)) do
click_button(bug.title)
end
- wait_for_vue_resource(spinner: false)
+ wait_for_vue_resource
expect(page).to have_selector('.card', count: 1)
end
@@ -648,13 +603,16 @@ describe 'Issue Boards', feature: true, js: true do
wait_for_vue_resource
end
- def wait_for_vue_resource(spinner: true)
- Timeout.timeout(Capybara.default_max_wait_time) do
- loop until page.evaluate_script('Vue.activeResources').zero?
+ def wait_for_board_cards(board_number, expected_cards)
+ page.within(find(".board:nth-child(#{board_number})")) do
+ expect(page.find('.board-header')).to have_content(expected_cards.to_s)
+ expect(page).to have_selector('.card', count: expected_cards)
end
+ end
- if spinner
- expect(find('.boards-list')).not_to have_selector('.fa-spinner')
+ def wait_for_empty_boards(board_numbers)
+ board_numbers.each do |board|
+ wait_for_board_cards(board, 0)
end
end
end
diff --git a/spec/features/boards/keyboard_shortcut_spec.rb b/spec/features/boards/keyboard_shortcut_spec.rb
new file mode 100644
index 00000000000..7ef68e9eb8d
--- /dev/null
+++ b/spec/features/boards/keyboard_shortcut_spec.rb
@@ -0,0 +1,24 @@
+require 'rails_helper'
+
+describe 'Issue Boards shortcut', feature: true, js: true do
+ include WaitForVueResource
+
+ let(:project) { create(:empty_project) }
+
+ before do
+ project.create_board
+ project.board.lists.create(list_type: :backlog)
+ project.board.lists.create(list_type: :done)
+
+ login_as :admin
+
+ visit namespace_project_path(project.namespace, project)
+ end
+
+ it 'takes user to issue board index' do
+ find('body').native.send_keys('gl')
+ expect(page).to have_selector('.boards-list')
+
+ wait_for_vue_resource
+ end
+end
diff --git a/spec/features/calendar_spec.rb b/spec/features/calendar_spec.rb
new file mode 100644
index 00000000000..fd5fbaf2af4
--- /dev/null
+++ b/spec/features/calendar_spec.rb
@@ -0,0 +1,39 @@
+require 'spec_helper'
+
+feature 'Contributions Calendar', js: true, feature: true do
+ include WaitForAjax
+
+ let(:contributed_project) { create(:project, :public) }
+
+ before do
+ login_as :user
+
+ issue_params = { title: 'Bug in old browser' }
+ Issues::CreateService.new(contributed_project, @user, issue_params).execute
+
+ # Push code contribution
+ push_params = {
+ project: contributed_project,
+ action: Event::PUSHED,
+ author_id: @user.id,
+ data: { commit_count: 3 }
+ }
+
+ Event.create(push_params)
+
+ visit @user.username
+ wait_for_ajax
+ end
+
+ it 'displays calendar', js: true do
+ expect(page).to have_css('.js-contrib-calendar')
+ end
+
+ it 'displays calendar activity log', js: true do
+ expect(find('.content_list .event-note')).to have_content "Bug in old browser"
+ end
+
+ it 'displays calendar activity square color', js: true do
+ expect(page).to have_selector('.user-contrib-cell[fill=\'#acd5f2\']', count: 1)
+ end
+end
diff --git a/spec/features/environments_spec.rb b/spec/features/environments_spec.rb
index fcd41b38413..4309a726917 100644
--- a/spec/features/environments_spec.rb
+++ b/spec/features/environments_spec.rb
@@ -150,7 +150,7 @@ feature 'Environments', feature: true do
context 'for invalid name' do
before do
- fill_in('Name', with: 'name with spaces')
+ fill_in('Name', with: 'name,with,commas')
click_on 'Save'
end
diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb
index d744d0eb6af..22359c8f938 100644
--- a/spec/features/issues_spec.rb
+++ b/spec/features/issues_spec.rb
@@ -144,7 +144,7 @@ describe 'Issues', feature: true do
visit namespace_project_issues_path(project.namespace, project, assignee_id: @user.id)
expect(page).to have_content 'foobar'
- expect(page.all('.issue-no-comments').first.text).to eq "0"
+ expect(page.all('.no-comments').first.text).to eq "0"
end
end
diff --git a/spec/features/milestone_spec.rb b/spec/features/milestone_spec.rb
index c43661e5681..b8c838bf7ab 100644
--- a/spec/features/milestone_spec.rb
+++ b/spec/features/milestone_spec.rb
@@ -3,9 +3,8 @@ require 'rails_helper'
feature 'Milestone', feature: true do
include WaitForAjax
- let(:project) { create(:project, :public) }
+ let(:project) { create(:empty_project, :public) }
let(:user) { create(:user) }
- let(:milestone) { create(:milestone, project: project, title: 8.7) }
before do
project.team << [user, :master]
@@ -13,7 +12,7 @@ feature 'Milestone', feature: true do
end
feature 'Create a milestone' do
- scenario 'shows an informative message for a new issue' do
+ scenario 'shows an informative message for a new milestone' do
visit new_namespace_project_milestone_path(project.namespace, project)
page.within '.milestone-form' do
fill_in "milestone_title", with: '8.7'
@@ -26,10 +25,26 @@ feature 'Milestone', feature: true do
feature 'Open a milestone with closed issues' do
scenario 'shows an informative message' do
+ milestone = create(:milestone, project: project, title: 8.7)
+
create(:issue, title: "Bugfix1", project: project, milestone: milestone, state: "closed")
visit namespace_project_milestone_path(project.namespace, project, milestone)
expect(find('.alert-success')).to have_content('All issues for this milestone are closed. You may close this milestone now.')
end
end
+
+ feature 'Open a milestone with an existing title' do
+ scenario 'displays validation message' do
+ milestone = create(:milestone, project: project, title: 8.7)
+
+ visit new_namespace_project_milestone_path(project.namespace, project)
+ page.within '.milestone-form' do
+ fill_in "milestone_title", with: milestone.title
+ end
+ find('input[name="commit"]').click
+
+ expect(find('.alert-danger')).to have_content('Title has already been taken')
+ end
+ end
end
diff --git a/spec/features/projects/issuable_templates_spec.rb b/spec/features/projects/issuable_templates_spec.rb
index d0f4e5469ed..f76c4fe8b57 100644
--- a/spec/features/projects/issuable_templates_spec.rb
+++ b/spec/features/projects/issuable_templates_spec.rb
@@ -64,7 +64,7 @@ feature 'issuable templates', feature: true, js: true do
let(:template_content) { 'this is a test "feature-proposal" template' }
let(:fork_user) { create(:user) }
let(:fork_project) { create(:project, :public) }
- let(:merge_request) { create(:merge_request, :with_diffs, source_project: fork_project) }
+ let(:merge_request) { create(:merge_request, :with_diffs, source_project: fork_project, target_project: project) }
background do
logout
@@ -72,16 +72,20 @@ feature 'issuable templates', feature: true, js: true do
fork_project.team << [fork_user, :master]
create(:forked_project_link, forked_to_project: fork_project, forked_from_project: project)
login_as fork_user
- fork_project.repository.commit_file(fork_user, '.gitlab/merge_request_templates/feature-proposal.md', template_content, 'added merge request template', 'master', false)
- visit edit_namespace_project_merge_request_path fork_project.namespace, fork_project, merge_request
+ project.repository.commit_file(fork_user, '.gitlab/merge_request_templates/feature-proposal.md', template_content, 'added merge request template', 'master', false)
+ visit edit_namespace_project_merge_request_path project.namespace, project, merge_request
fill_in :'merge_request[title]', with: 'test merge request title'
end
- scenario 'user selects "feature-proposal" template' do
- select_template 'feature-proposal'
- wait_for_ajax
- preview_template
- save_changes
+ context 'feature proposal template' do
+ context 'template exists in target project' do
+ scenario 'user selects template' do
+ select_template 'feature-proposal'
+ wait_for_ajax
+ preview_template
+ save_changes
+ end
+ end
end
end
diff --git a/spec/features/users/snippets_spec.rb b/spec/features/users/snippets_spec.rb
index 356a8d668b0..f00abd82fea 100644
--- a/spec/features/users/snippets_spec.rb
+++ b/spec/features/users/snippets_spec.rb
@@ -19,7 +19,10 @@ describe 'Snippets tab on a user profile', feature: true, js: true do
end
context 'clicking on the link to the second page' do
- before { click_link('2') }
+ before do
+ click_link('2')
+ wait_for_ajax
+ end
it 'shows the remaining snippets' do
expect(page.all('.snippets-list-holder .snippet-row').count).to eq(5)
diff --git a/spec/helpers/groups_helper_spec.rb b/spec/helpers/groups_helper_spec.rb
index 0807534720a..233d00534e5 100644
--- a/spec/helpers/groups_helper_spec.rb
+++ b/spec/helpers/groups_helper_spec.rb
@@ -18,4 +18,67 @@ describe GroupsHelper do
expect(group_icon(group.path)).to match('group_avatar.png')
end
end
+
+ describe 'group_lfs_status' do
+ let(:group) { create(:group) }
+ let!(:project) { create(:empty_project, namespace_id: group.id) }
+
+ before do
+ allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
+ end
+
+ context 'only one project in group' do
+ before do
+ group.update_attribute(:lfs_enabled, true)
+ end
+
+ it 'returns all projects as enabled' do
+ expect(group_lfs_status(group)).to include('Enabled for all projects')
+ end
+
+ it 'returns all projects as disabled' do
+ project.update_attribute(:lfs_enabled, false)
+
+ expect(group_lfs_status(group)).to include('Enabled for 0 out of 1 project')
+ end
+ end
+
+ context 'more than one project in group' do
+ before do
+ create(:empty_project, namespace_id: group.id)
+ end
+
+ context 'LFS enabled in group' do
+ before do
+ group.update_attribute(:lfs_enabled, true)
+ end
+
+ it 'returns both projects as enabled' do
+ expect(group_lfs_status(group)).to include('Enabled for all projects')
+ end
+
+ it 'returns only one as enabled' do
+ project.update_attribute(:lfs_enabled, false)
+
+ expect(group_lfs_status(group)).to include('Enabled for 1 out of 2 projects')
+ end
+ end
+
+ context 'LFS disabled in group' do
+ before do
+ group.update_attribute(:lfs_enabled, false)
+ end
+
+ it 'returns both projects as disabled' do
+ expect(group_lfs_status(group)).to include('Disabled for all projects')
+ end
+
+ it 'returns only one as disabled' do
+ project.update_attribute(:lfs_enabled, true)
+
+ expect(group_lfs_status(group)).to include('Disabled for 1 out of 2 projects')
+ end
+ end
+ end
+ end
end
diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
index af192664b33..6dedd25e9d3 100644
--- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
+++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
@@ -754,6 +754,20 @@ module Ci
it 'does return production' do
expect(builds.size).to eq(1)
expect(builds.first[:environment]).to eq(environment)
+ expect(builds.first[:options]).to include(environment: { name: environment })
+ end
+ end
+
+ context 'when hash is specified' do
+ let(:environment) do
+ { name: 'production',
+ url: 'http://production.gitlab.com' }
+ end
+
+ it 'does return production and URL' do
+ expect(builds.size).to eq(1)
+ expect(builds.first[:environment]).to eq(environment[:name])
+ expect(builds.first[:options]).to include(environment: environment)
end
end
@@ -770,15 +784,16 @@ module Ci
let(:environment) { 1 }
it 'raises error' do
- expect { builds }.to raise_error("jobs:deploy_to_production environment #{Gitlab::Regex.environment_name_regex_message}")
+ expect { builds }.to raise_error(
+ 'jobs:deploy_to_production:environment config should be a hash or a string')
end
end
context 'is not a valid string' do
- let(:environment) { 'production staging' }
+ let(:environment) { 'production:staging' }
it 'raises error' do
- expect { builds }.to raise_error("jobs:deploy_to_production environment #{Gitlab::Regex.environment_name_regex_message}")
+ expect { builds }.to raise_error("jobs:deploy_to_production:environment name #{Gitlab::Regex.environment_name_regex_message}")
end
end
end
diff --git a/spec/lib/expand_variables_spec.rb b/spec/lib/expand_variables_spec.rb
new file mode 100644
index 00000000000..90bc7dad379
--- /dev/null
+++ b/spec/lib/expand_variables_spec.rb
@@ -0,0 +1,73 @@
+require 'spec_helper'
+
+describe ExpandVariables do
+ describe '#expand' do
+ subject { described_class.expand(value, variables) }
+
+ tests = [
+ { value: 'key',
+ result: 'key',
+ variables: []
+ },
+ { value: 'key$variable',
+ result: 'key',
+ variables: []
+ },
+ { value: 'key$variable',
+ result: 'keyvalue',
+ variables: [
+ { key: 'variable', value: 'value' }
+ ]
+ },
+ { value: 'key${variable}',
+ result: 'keyvalue',
+ variables: [
+ { key: 'variable', value: 'value' }
+ ]
+ },
+ { value: 'key$variable$variable2',
+ result: 'keyvalueresult',
+ variables: [
+ { key: 'variable', value: 'value' },
+ { key: 'variable2', value: 'result' },
+ ]
+ },
+ { value: 'key${variable}${variable2}',
+ result: 'keyvalueresult',
+ variables: [
+ { key: 'variable', value: 'value' },
+ { key: 'variable2', value: 'result' }
+ ]
+ },
+ { value: 'key$variable2$variable',
+ result: 'keyresultvalue',
+ variables: [
+ { key: 'variable', value: 'value' },
+ { key: 'variable2', value: 'result' },
+ ]
+ },
+ { value: 'key${variable2}${variable}',
+ result: 'keyresultvalue',
+ variables: [
+ { key: 'variable', value: 'value' },
+ { key: 'variable2', value: 'result' }
+ ]
+ },
+ { value: 'review/$CI_BUILD_REF_NAME',
+ result: 'review/feature/add-review-apps',
+ variables: [
+ { key: 'CI_BUILD_REF_NAME', value: 'feature/add-review-apps' }
+ ]
+ },
+ ]
+
+ tests.each do |test|
+ context "#{test[:value]} resolves to #{test[:result]}" do
+ let(:value) { test[:value] }
+ let(:variables) { test[:variables] }
+
+ it { is_expected.to eq(test[:result]) }
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/node/environment_spec.rb b/spec/lib/gitlab/ci/config/node/environment_spec.rb
new file mode 100644
index 00000000000..df453223da7
--- /dev/null
+++ b/spec/lib/gitlab/ci/config/node/environment_spec.rb
@@ -0,0 +1,155 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Config::Node::Environment do
+ let(:entry) { described_class.new(config) }
+
+ before { entry.compose! }
+
+ context 'when configuration is a string' do
+ let(:config) { 'production' }
+
+ describe '#string?' do
+ it 'is string configuration' do
+ expect(entry).to be_string
+ end
+ end
+
+ describe '#hash?' do
+ it 'is not hash configuration' do
+ expect(entry).not_to be_hash
+ end
+ end
+
+ describe '#valid?' do
+ it 'is valid' do
+ expect(entry).to be_valid
+ end
+ end
+
+ describe '#value' do
+ it 'returns valid hash' do
+ expect(entry.value).to eq(name: 'production')
+ end
+ end
+
+ describe '#name' do
+ it 'returns environment name' do
+ expect(entry.name).to eq 'production'
+ end
+ end
+
+ describe '#url' do
+ it 'returns environment url' do
+ expect(entry.url).to be_nil
+ end
+ end
+ end
+
+ context 'when configuration is a hash' do
+ let(:config) do
+ { name: 'development', url: 'https://example.gitlab.com' }
+ end
+
+ describe '#string?' do
+ it 'is not string configuration' do
+ expect(entry).not_to be_string
+ end
+ end
+
+ describe '#hash?' do
+ it 'is hash configuration' do
+ expect(entry).to be_hash
+ end
+ end
+
+ describe '#valid?' do
+ it 'is valid' do
+ expect(entry).to be_valid
+ end
+ end
+
+ describe '#value' do
+ it 'returns valid hash' do
+ expect(entry.value).to eq config
+ end
+ end
+
+ describe '#name' do
+ it 'returns environment name' do
+ expect(entry.name).to eq 'development'
+ end
+ end
+
+ describe '#url' do
+ it 'returns environment url' do
+ expect(entry.url).to eq 'https://example.gitlab.com'
+ end
+ end
+ end
+
+ context 'when variables are used for environment' do
+ let(:config) do
+ { name: 'review/$CI_BUILD_REF_NAME',
+ url: 'https://$CI_BUILD_REF_NAME.review.gitlab.com' }
+ end
+
+ describe '#valid?' do
+ it 'is valid' do
+ expect(entry).to be_valid
+ end
+ end
+ end
+
+ context 'when configuration is invalid' do
+ context 'when configuration is an array' do
+ let(:config) { ['env'] }
+
+ describe '#valid?' do
+ it 'is not valid' do
+ expect(entry).not_to be_valid
+ end
+ end
+
+ describe '#errors' do
+ it 'contains error about invalid type' do
+ expect(entry.errors)
+ .to include 'environment config should be a hash or a string'
+ end
+ end
+ end
+
+ context 'when environment name is not present' do
+ let(:config) { { url: 'https://example.gitlab.com' } }
+
+ describe '#valid?' do
+ it 'is not valid' do
+ expect(entry).not_to be_valid
+ end
+ end
+
+ describe '#errors?' do
+ it 'contains error about missing environment name' do
+ expect(entry.errors)
+ .to include "environment name can't be blank"
+ end
+ end
+ end
+
+ context 'when invalid URL is used' do
+ let(:config) { { name: 'test', url: 'invalid-example.gitlab.com' } }
+
+ describe '#valid?' do
+ it 'is not valid' do
+ expect(entry).not_to be_valid
+ end
+ end
+
+ describe '#errors?' do
+ it 'contains error about invalid URL' do
+ expect(entry.errors)
+ .to include "environment url must be a valid url"
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb
index 4ec3f19e03f..7fd25b9e5bf 100644
--- a/spec/lib/gitlab/database/migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/migration_helpers_spec.rb
@@ -91,63 +91,80 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
describe '#add_column_with_default' do
context 'outside of a transaction' do
- before do
- expect(model).to receive(:transaction_open?).and_return(false)
+ context 'when a column limit is not set' do
+ before do
+ expect(model).to receive(:transaction_open?).and_return(false)
- expect(model).to receive(:transaction).and_yield
+ expect(model).to receive(:transaction).and_yield
- expect(model).to receive(:add_column).
- with(:projects, :foo, :integer, default: nil)
+ expect(model).to receive(:add_column).
+ with(:projects, :foo, :integer, default: nil)
- expect(model).to receive(:change_column_default).
- with(:projects, :foo, 10)
- end
+ expect(model).to receive(:change_column_default).
+ with(:projects, :foo, 10)
+ end
- it 'adds the column while allowing NULL values' do
- expect(model).to receive(:update_column_in_batches).
- with(:projects, :foo, 10)
+ it 'adds the column while allowing NULL values' do
+ expect(model).to receive(:update_column_in_batches).
+ with(:projects, :foo, 10)
- expect(model).not_to receive(:change_column_null)
+ expect(model).not_to receive(:change_column_null)
- model.add_column_with_default(:projects, :foo, :integer,
- default: 10,
- allow_null: true)
- end
+ model.add_column_with_default(:projects, :foo, :integer,
+ default: 10,
+ allow_null: true)
+ end
- it 'adds the column while not allowing NULL values' do
- expect(model).to receive(:update_column_in_batches).
- with(:projects, :foo, 10)
+ it 'adds the column while not allowing NULL values' do
+ expect(model).to receive(:update_column_in_batches).
+ with(:projects, :foo, 10)
- expect(model).to receive(:change_column_null).
- with(:projects, :foo, false)
+ expect(model).to receive(:change_column_null).
+ with(:projects, :foo, false)
- model.add_column_with_default(:projects, :foo, :integer, default: 10)
- end
+ model.add_column_with_default(:projects, :foo, :integer, default: 10)
+ end
- it 'removes the added column whenever updating the rows fails' do
- expect(model).to receive(:update_column_in_batches).
- with(:projects, :foo, 10).
- and_raise(RuntimeError)
+ it 'removes the added column whenever updating the rows fails' do
+ expect(model).to receive(:update_column_in_batches).
+ with(:projects, :foo, 10).
+ and_raise(RuntimeError)
- expect(model).to receive(:remove_column).
- with(:projects, :foo)
+ expect(model).to receive(:remove_column).
+ with(:projects, :foo)
- expect do
- model.add_column_with_default(:projects, :foo, :integer, default: 10)
- end.to raise_error(RuntimeError)
+ expect do
+ model.add_column_with_default(:projects, :foo, :integer, default: 10)
+ end.to raise_error(RuntimeError)
+ end
+
+ it 'removes the added column whenever changing a column NULL constraint fails' do
+ expect(model).to receive(:change_column_null).
+ with(:projects, :foo, false).
+ and_raise(RuntimeError)
+
+ expect(model).to receive(:remove_column).
+ with(:projects, :foo)
+
+ expect do
+ model.add_column_with_default(:projects, :foo, :integer, default: 10)
+ end.to raise_error(RuntimeError)
+ end
end
- it 'removes the added column whenever changing a column NULL constraint fails' do
- expect(model).to receive(:change_column_null).
- with(:projects, :foo, false).
- and_raise(RuntimeError)
+ context 'when a column limit is set' do
+ it 'adds the column with a limit' do
+ allow(model).to receive(:transaction_open?).and_return(false)
+ allow(model).to receive(:transaction).and_yield
+ allow(model).to receive(:update_column_in_batches).with(:projects, :foo, 10)
+ allow(model).to receive(:change_column_null).with(:projects, :foo, false)
+ allow(model).to receive(:change_column_default).with(:projects, :foo, 10)
- expect(model).to receive(:remove_column).
- with(:projects, :foo)
+ expect(model).to receive(:add_column).
+ with(:projects, :foo, :integer, default: nil, limit: 8)
- expect do
- model.add_column_with_default(:projects, :foo, :integer, default: 10)
- end.to raise_error(RuntimeError)
+ model.add_column_with_default(:projects, :foo, :integer, default: 10, limit: 8)
+ end
end
end
diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb
index c881897926e..6b1867a44e1 100644
--- a/spec/models/environment_spec.rb
+++ b/spec/models/environment_spec.rb
@@ -63,4 +63,20 @@ describe Environment, models: true do
end
end
end
+
+ describe '#environment_type' do
+ subject { environment.environment_type }
+
+ it 'sets a environment type if name has multiple segments' do
+ environment.update!(name: 'production/worker.gitlab.com')
+
+ is_expected.to eq('production')
+ end
+
+ it 'nullifies a type if it\'s a simple name' do
+ environment.update!(name: 'production')
+
+ is_expected.to be_nil
+ end
+ end
end
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index ea4b59c26b1..0b3ef9b98fd 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -187,6 +187,52 @@ describe Group, models: true do
it { expect(group.has_master?(@members[:requester])).to be_falsey }
end
+ describe '#lfs_enabled?' do
+ context 'LFS enabled globally' do
+ before do
+ allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
+ end
+
+ it 'returns true when nothing is set' do
+ expect(group.lfs_enabled?).to be_truthy
+ end
+
+ it 'returns false when set to false' do
+ group.update_attribute(:lfs_enabled, false)
+
+ expect(group.lfs_enabled?).to be_falsey
+ end
+
+ it 'returns true when set to true' do
+ group.update_attribute(:lfs_enabled, true)
+
+ expect(group.lfs_enabled?).to be_truthy
+ end
+ end
+
+ context 'LFS disabled globally' do
+ before do
+ allow(Gitlab.config.lfs).to receive(:enabled).and_return(false)
+ end
+
+ it 'returns false when nothing is set' do
+ expect(group.lfs_enabled?).to be_falsey
+ end
+
+ it 'returns false when set to false' do
+ group.update_attribute(:lfs_enabled, false)
+
+ expect(group.lfs_enabled?).to be_falsey
+ end
+
+ it 'returns false when set to true' do
+ group.update_attribute(:lfs_enabled, true)
+
+ expect(group.lfs_enabled?).to be_falsey
+ end
+ end
+ end
+
describe '#owners' do
let(:owner) { create(:user) }
let(:developer) { create(:user) }
diff --git a/spec/models/hooks/project_hook_spec.rb b/spec/models/hooks/project_hook_spec.rb
index 4a457997a4f..474ae62ccec 100644
--- a/spec/models/hooks/project_hook_spec.rb
+++ b/spec/models/hooks/project_hook_spec.rb
@@ -1,21 +1,3 @@
-# == Schema Information
-#
-# Table name: web_hooks
-#
-# id :integer not null, primary key
-# url :string(255)
-# project_id :integer
-# created_at :datetime
-# updated_at :datetime
-# type :string(255) default("ProjectHook")
-# service_id :integer
-# push_events :boolean default(TRUE), not null
-# issues_events :boolean default(FALSE), not null
-# merge_requests_events :boolean default(FALSE), not null
-# tag_push_events :boolean default(FALSE)
-# note_events :boolean default(FALSE), not null
-#
-
require 'spec_helper'
describe ProjectHook, models: true do
diff --git a/spec/models/hooks/service_hook_spec.rb b/spec/models/hooks/service_hook_spec.rb
index 534e1b4f128..1a83c836652 100644
--- a/spec/models/hooks/service_hook_spec.rb
+++ b/spec/models/hooks/service_hook_spec.rb
@@ -1,21 +1,3 @@
-# == Schema Information
-#
-# Table name: web_hooks
-#
-# id :integer not null, primary key
-# url :string(255)
-# project_id :integer
-# created_at :datetime
-# updated_at :datetime
-# type :string(255) default("ProjectHook")
-# service_id :integer
-# push_events :boolean default(TRUE), not null
-# issues_events :boolean default(FALSE), not null
-# merge_requests_events :boolean default(FALSE), not null
-# tag_push_events :boolean default(FALSE)
-# note_events :boolean default(FALSE), not null
-#
-
require "spec_helper"
describe ServiceHook, models: true do
diff --git a/spec/models/hooks/system_hook_spec.rb b/spec/models/hooks/system_hook_spec.rb
index cbdf7eec082..ad2b710041a 100644
--- a/spec/models/hooks/system_hook_spec.rb
+++ b/spec/models/hooks/system_hook_spec.rb
@@ -1,21 +1,3 @@
-# == Schema Information
-#
-# Table name: web_hooks
-#
-# id :integer not null, primary key
-# url :string(255)
-# project_id :integer
-# created_at :datetime
-# updated_at :datetime
-# type :string(255) default("ProjectHook")
-# service_id :integer
-# push_events :boolean default(TRUE), not null
-# issues_events :boolean default(FALSE), not null
-# merge_requests_events :boolean default(FALSE), not null
-# tag_push_events :boolean default(FALSE)
-# note_events :boolean default(FALSE), not null
-#
-
require "spec_helper"
describe SystemHook, models: true do
@@ -48,7 +30,7 @@ describe SystemHook, models: true do
it "user_create hook" do
create(:user)
-
+
expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /user_create/,
headers: { 'Content-Type' => 'application/json', 'X-Gitlab-Event' => 'System Hook' }
diff --git a/spec/models/hooks/web_hook_spec.rb b/spec/models/hooks/web_hook_spec.rb
index f9bab487b96..e52b9d75cef 100644
--- a/spec/models/hooks/web_hook_spec.rb
+++ b/spec/models/hooks/web_hook_spec.rb
@@ -1,21 +1,3 @@
-# == Schema Information
-#
-# Table name: web_hooks
-#
-# id :integer not null, primary key
-# url :string(255)
-# project_id :integer
-# created_at :datetime
-# updated_at :datetime
-# type :string(255) default("ProjectHook")
-# service_id :integer
-# push_events :boolean default(TRUE), not null
-# issues_events :boolean default(FALSE), not null
-# merge_requests_events :boolean default(FALSE), not null
-# tag_push_events :boolean default(FALSE)
-# note_events :boolean default(FALSE), not null
-#
-
require 'spec_helper'
describe WebHook, models: true do
diff --git a/spec/models/members/group_member_spec.rb b/spec/models/members/group_member_spec.rb
index 4f875fd257a..56fa7fa6134 100644
--- a/spec/models/members/group_member_spec.rb
+++ b/spec/models/members/group_member_spec.rb
@@ -1,22 +1,3 @@
-# == Schema Information
-#
-# Table name: members
-#
-# id :integer not null, primary key
-# access_level :integer not null
-# source_id :integer not null
-# source_type :string(255) not null
-# user_id :integer
-# notification_level :integer not null
-# type :string(255)
-# created_at :datetime
-# updated_at :datetime
-# created_by_id :integer
-# invite_email :string(255)
-# invite_token :string(255)
-# invite_accepted_at :datetime
-#
-
require 'spec_helper'
describe GroupMember, models: true do
diff --git a/spec/models/members/project_member_spec.rb b/spec/models/members/project_member_spec.rb
index be57957b569..805c15a4e5e 100644
--- a/spec/models/members/project_member_spec.rb
+++ b/spec/models/members/project_member_spec.rb
@@ -1,22 +1,3 @@
-# == Schema Information
-#
-# Table name: members
-#
-# id :integer not null, primary key
-# access_level :integer not null
-# source_id :integer not null
-# source_type :string(255) not null
-# user_id :integer
-# notification_level :integer not null
-# type :string(255)
-# created_at :datetime
-# updated_at :datetime
-# created_by_id :integer
-# invite_email :string(255)
-# invite_token :string(255)
-# invite_accepted_at :datetime
-#
-
require 'spec_helper'
describe ProjectMember, models: true do
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 3b815ded2d3..06feeb1bbba 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -703,16 +703,24 @@ describe MergeRequest, models: true do
describe "#environments" do
let(:project) { create(:project) }
- let!(:environment) { create(:environment, project: project) }
- let!(:environment1) { create(:environment, project: project) }
- let!(:environment2) { create(:environment, project: project) }
let(:merge_request) { create(:merge_request, source_project: project) }
it 'selects deployed environments' do
- create(:deployment, environment: environment, sha: project.commit('master').id)
- create(:deployment, environment: environment1, sha: project.commit('feature').id)
+ environments = create_list(:environment, 3, project: project)
+ create(:deployment, environment: environments.first, sha: project.commit('master').id)
+ create(:deployment, environment: environments.second, sha: project.commit('feature').id)
- expect(merge_request.environments).to eq [environment]
+ expect(merge_request.environments).to eq [environments.first]
+ end
+
+ context 'without a diff_head_commit' do
+ before do
+ expect(merge_request).to receive(:diff_head_commit).and_return(nil)
+ end
+
+ it 'returns an empty array' do
+ expect(merge_request.environments).to be_empty
+ end
end
end
diff --git a/spec/models/project_services/asana_service_spec.rb b/spec/models/project_services/asana_service_spec.rb
index dc702cfc42c..8e5145e824b 100644
--- a/spec/models/project_services/asana_service_spec.rb
+++ b/spec/models/project_services/asana_service_spec.rb
@@ -1,23 +1,3 @@
-# == Schema Information
-#
-# Table name: services
-#
-# id :integer not null, primary key
-# type :string(255)
-# title :string(255)
-# project_id :integer
-# created_at :datetime
-# updated_at :datetime
-# active :boolean default(FALSE), not null
-# properties :text
-# template :boolean default(FALSE)
-# push_events :boolean default(TRUE)
-# issues_events :boolean default(TRUE)
-# merge_requests_events :boolean default(TRUE)
-# tag_push_events :boolean default(TRUE)
-# note_events :boolean default(TRUE), not null
-#
-
require 'spec_helper'
describe AsanaService, models: true do
diff --git a/spec/models/project_services/assembla_service_spec.rb b/spec/models/project_services/assembla_service_spec.rb
index d672d80156c..4c5acb7990b 100644
--- a/spec/models/project_services/assembla_service_spec.rb
+++ b/spec/models/project_services/assembla_service_spec.rb
@@ -1,23 +1,3 @@
-# == Schema Information
-#
-# Table name: services
-#
-# id :integer not null, primary key
-# type :string(255)
-# title :string(255)
-# project_id :integer
-# created_at :datetime
-# updated_at :datetime
-# active :boolean default(FALSE), not null
-# properties :text
-# template :boolean default(FALSE)
-# push_events :boolean default(TRUE)
-# issues_events :boolean default(TRUE)
-# merge_requests_events :boolean default(TRUE)
-# tag_push_events :boolean default(TRUE)
-# note_events :boolean default(TRUE), not null
-#
-
require 'spec_helper'
describe AssemblaService, models: true do
diff --git a/spec/models/project_services/bamboo_service_spec.rb b/spec/models/project_services/bamboo_service_spec.rb
index 9ae461f8c2d..d7e1a4e3b6c 100644
--- a/spec/models/project_services/bamboo_service_spec.rb
+++ b/spec/models/project_services/bamboo_service_spec.rb
@@ -1,23 +1,3 @@
-# == Schema Information
-#
-# Table name: services
-#
-# id :integer not null, primary key
-# type :string(255)
-# title :string(255)
-# project_id :integer
-# created_at :datetime
-# updated_at :datetime
-# active :boolean default(FALSE), not null
-# properties :text
-# template :boolean default(FALSE)
-# push_events :boolean default(TRUE)
-# issues_events :boolean default(TRUE)
-# merge_requests_events :boolean default(TRUE)
-# tag_push_events :boolean default(TRUE)
-# note_events :boolean default(TRUE), not null
-#
-
require 'spec_helper'
describe BambooService, models: true do
diff --git a/spec/models/project_services/bugzilla_service_spec.rb b/spec/models/project_services/bugzilla_service_spec.rb
index a6d9717ccb5..739cc72b2ff 100644
--- a/spec/models/project_services/bugzilla_service_spec.rb
+++ b/spec/models/project_services/bugzilla_service_spec.rb
@@ -1,23 +1,3 @@
-# == Schema Information
-#
-# Table name: services
-#
-# id :integer not null, primary key
-# type :string(255)
-# title :string(255)
-# project_id :integer
-# created_at :datetime
-# updated_at :datetime
-# active :boolean default(FALSE), not null
-# properties :text
-# template :boolean default(FALSE)
-# push_events :boolean default(TRUE)
-# issues_events :boolean default(TRUE)
-# merge_requests_events :boolean default(TRUE)
-# tag_push_events :boolean default(TRUE)
-# note_events :boolean default(TRUE), not null
-#
-
require 'spec_helper'
describe BugzillaService, models: true do
diff --git a/spec/models/project_services/buildkite_service_spec.rb b/spec/models/project_services/buildkite_service_spec.rb
index 0866e1532dd..6f65beb79d0 100644
--- a/spec/models/project_services/buildkite_service_spec.rb
+++ b/spec/models/project_services/buildkite_service_spec.rb
@@ -1,23 +1,3 @@
-# == Schema Information
-#
-# Table name: services
-#
-# id :integer not null, primary key
-# type :string(255)
-# title :string(255)
-# project_id :integer
-# created_at :datetime
-# updated_at :datetime
-# active :boolean default(FALSE), not null
-# properties :text
-# template :boolean default(FALSE)
-# push_events :boolean default(TRUE)
-# issues_events :boolean default(TRUE)
-# merge_requests_events :boolean default(TRUE)
-# tag_push_events :boolean default(TRUE)
-# note_events :boolean default(TRUE), not null
-#
-
require 'spec_helper'
describe BuildkiteService, models: true do
diff --git a/spec/models/project_services/campfire_service_spec.rb b/spec/models/project_services/campfire_service_spec.rb
index c76ae21421b..a3b9d084a75 100644
--- a/spec/models/project_services/campfire_service_spec.rb
+++ b/spec/models/project_services/campfire_service_spec.rb
@@ -1,23 +1,3 @@
-# == Schema Information
-#
-# Table name: services
-#
-# id :integer not null, primary key
-# type :string(255)
-# title :string(255)
-# project_id :integer
-# created_at :datetime
-# updated_at :datetime
-# active :boolean default(FALSE), not null
-# properties :text
-# template :boolean default(FALSE)
-# push_events :boolean default(TRUE)
-# issues_events :boolean default(TRUE)
-# merge_requests_events :boolean default(TRUE)
-# tag_push_events :boolean default(TRUE)
-# note_events :boolean default(TRUE), not null
-#
-
require 'spec_helper'
describe CampfireService, models: true do
diff --git a/spec/models/project_services/custom_issue_tracker_service_spec.rb b/spec/models/project_services/custom_issue_tracker_service_spec.rb
index ff976f8ec59..7020667ea58 100644
--- a/spec/models/project_services/custom_issue_tracker_service_spec.rb
+++ b/spec/models/project_services/custom_issue_tracker_service_spec.rb
@@ -1,23 +1,3 @@
-# == Schema Information
-#
-# Table name: services
-#
-# id :integer not null, primary key
-# type :string(255)
-# title :string(255)
-# project_id :integer
-# created_at :datetime
-# updated_at :datetime
-# active :boolean default(FALSE), not null
-# properties :text
-# template :boolean default(FALSE)
-# push_events :boolean default(TRUE)
-# issues_events :boolean default(TRUE)
-# merge_requests_events :boolean default(TRUE)
-# tag_push_events :boolean default(TRUE)
-# note_events :boolean default(TRUE), not null
-#
-
require 'spec_helper'
describe CustomIssueTrackerService, models: true do
diff --git a/spec/models/project_services/drone_ci_service_spec.rb b/spec/models/project_services/drone_ci_service_spec.rb
index 8ef892259f2..f13bb1e8adf 100644
--- a/spec/models/project_services/drone_ci_service_spec.rb
+++ b/spec/models/project_services/drone_ci_service_spec.rb
@@ -1,23 +1,3 @@
-# == Schema Information
-#
-# Table name: services
-#
-# id :integer not null, primary key
-# type :string(255)
-# title :string(255)
-# project_id :integer
-# created_at :datetime
-# updated_at :datetime
-# active :boolean default(FALSE), not null
-# properties :text
-# template :boolean default(FALSE)
-# push_events :boolean default(TRUE)
-# issues_events :boolean default(TRUE)
-# merge_requests_events :boolean default(TRUE)
-# tag_push_events :boolean default(TRUE)
-# note_events :boolean default(TRUE), not null
-#
-
require 'spec_helper'
describe DroneCiService, models: true do
diff --git a/spec/models/project_services/external_wiki_service_spec.rb b/spec/models/project_services/external_wiki_service_spec.rb
index d7c5ea95d71..342d86aeca9 100644
--- a/spec/models/project_services/external_wiki_service_spec.rb
+++ b/spec/models/project_services/external_wiki_service_spec.rb
@@ -1,24 +1,3 @@
-# == Schema Information
-#
-# Table name: services
-#
-# id :integer not null, primary key
-# type :string(255)
-# title :string(255)
-# project_id :integer
-# created_at :datetime
-# updated_at :datetime
-# active :boolean default(FALSE), not null
-# properties :text
-# template :boolean default(FALSE)
-# push_events :boolean default(TRUE)
-# issues_events :boolean default(TRUE)
-# merge_requests_events :boolean default(TRUE)
-# tag_push_events :boolean default(TRUE)
-# note_events :boolean default(TRUE), not null
-# build_events :boolean default(FALSE), not null
-#
-
require 'spec_helper'
describe ExternalWikiService, models: true do
diff --git a/spec/models/project_services/flowdock_service_spec.rb b/spec/models/project_services/flowdock_service_spec.rb
index d2557019756..d6db02d6e76 100644
--- a/spec/models/project_services/flowdock_service_spec.rb
+++ b/spec/models/project_services/flowdock_service_spec.rb
@@ -1,23 +1,3 @@
-# == Schema Information
-#
-# Table name: services
-#
-# id :integer not null, primary key
-# type :string(255)
-# title :string(255)
-# project_id :integer
-# created_at :datetime
-# updated_at :datetime
-# active :boolean default(FALSE), not null
-# properties :text
-# template :boolean default(FALSE)
-# push_events :boolean default(TRUE)
-# issues_events :boolean default(TRUE)
-# merge_requests_events :boolean default(TRUE)
-# tag_push_events :boolean default(TRUE)
-# note_events :boolean default(TRUE), not null
-#
-
require 'spec_helper'
describe FlowdockService, models: true do
diff --git a/spec/models/project_services/gemnasium_service_spec.rb b/spec/models/project_services/gemnasium_service_spec.rb
index 3d0b6c9816b..529044d1d8b 100644
--- a/spec/models/project_services/gemnasium_service_spec.rb
+++ b/spec/models/project_services/gemnasium_service_spec.rb
@@ -1,23 +1,3 @@
-# == Schema Information
-#
-# Table name: services
-#
-# id :integer not null, primary key
-# type :string(255)
-# title :string(255)
-# project_id :integer
-# created_at :datetime
-# updated_at :datetime
-# active :boolean default(FALSE), not null
-# properties :text
-# template :boolean default(FALSE)
-# push_events :boolean default(TRUE)
-# issues_events :boolean default(TRUE)
-# merge_requests_events :boolean default(TRUE)
-# tag_push_events :boolean default(TRUE)
-# note_events :boolean default(TRUE), not null
-#
-
require 'spec_helper'
describe GemnasiumService, models: true do
diff --git a/spec/models/project_services/gitlab_issue_tracker_service_spec.rb b/spec/models/project_services/gitlab_issue_tracker_service_spec.rb
index 8ef79a17d50..652804fb444 100644
--- a/spec/models/project_services/gitlab_issue_tracker_service_spec.rb
+++ b/spec/models/project_services/gitlab_issue_tracker_service_spec.rb
@@ -1,23 +1,3 @@
-# == Schema Information
-#
-# Table name: services
-#
-# id :integer not null, primary key
-# type :string(255)
-# title :string(255)
-# project_id :integer
-# created_at :datetime
-# updated_at :datetime
-# active :boolean default(FALSE), not null
-# properties :text
-# template :boolean default(FALSE)
-# push_events :boolean default(TRUE)
-# issues_events :boolean default(TRUE)
-# merge_requests_events :boolean default(TRUE)
-# tag_push_events :boolean default(TRUE)
-# note_events :boolean default(TRUE), not null
-#
-
require 'spec_helper'
describe GitlabIssueTrackerService, models: true do
diff --git a/spec/models/project_services/hipchat_service_spec.rb b/spec/models/project_services/hipchat_service_spec.rb
index 34eafbe555d..cf713684463 100644
--- a/spec/models/project_services/hipchat_service_spec.rb
+++ b/spec/models/project_services/hipchat_service_spec.rb
@@ -1,23 +1,3 @@
-# == Schema Information
-#
-# Table name: services
-#
-# id :integer not null, primary key
-# type :string(255)
-# title :string(255)
-# project_id :integer
-# created_at :datetime
-# updated_at :datetime
-# active :boolean default(FALSE), not null
-# properties :text
-# template :boolean default(FALSE)
-# push_events :boolean default(TRUE)
-# issues_events :boolean default(TRUE)
-# merge_requests_events :boolean default(TRUE)
-# tag_push_events :boolean default(TRUE)
-# note_events :boolean default(TRUE), not null
-#
-
require 'spec_helper'
describe HipchatService, models: true do
diff --git a/spec/models/project_services/irker_service_spec.rb b/spec/models/project_services/irker_service_spec.rb
index ffb17fd3259..f8c45b37561 100644
--- a/spec/models/project_services/irker_service_spec.rb
+++ b/spec/models/project_services/irker_service_spec.rb
@@ -1,23 +1,3 @@
-# == Schema Information
-#
-# Table name: services
-#
-# id :integer not null, primary key
-# type :string(255)
-# title :string(255)
-# project_id :integer
-# created_at :datetime
-# updated_at :datetime
-# active :boolean default(FALSE), not null
-# properties :text
-# template :boolean default(FALSE)
-# push_events :boolean default(TRUE)
-# issues_events :boolean default(TRUE)
-# merge_requests_events :boolean default(TRUE)
-# tag_push_events :boolean default(TRUE)
-# note_events :boolean default(TRUE), not null
-#
-
require 'spec_helper'
require 'socket'
require 'json'
diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb
index 9037ca5cc20..b48a3176007 100644
--- a/spec/models/project_services/jira_service_spec.rb
+++ b/spec/models/project_services/jira_service_spec.rb
@@ -1,23 +1,3 @@
-# == Schema Information
-#
-# Table name: services
-#
-# id :integer not null, primary key
-# type :string(255)
-# title :string(255)
-# project_id :integer
-# created_at :datetime
-# updated_at :datetime
-# active :boolean default(FALSE), not null
-# properties :text
-# template :boolean default(FALSE)
-# push_events :boolean default(TRUE)
-# issues_events :boolean default(TRUE)
-# merge_requests_events :boolean default(TRUE)
-# tag_push_events :boolean default(TRUE)
-# note_events :boolean default(TRUE), not null
-#
-
require 'spec_helper'
describe JiraService, models: true do
diff --git a/spec/models/project_services/pivotaltracker_service_spec.rb b/spec/models/project_services/pivotaltracker_service_spec.rb
index d098d988521..45b2f1068bf 100644
--- a/spec/models/project_services/pivotaltracker_service_spec.rb
+++ b/spec/models/project_services/pivotaltracker_service_spec.rb
@@ -1,23 +1,3 @@
-# == Schema Information
-#
-# Table name: services
-#
-# id :integer not null, primary key
-# type :string(255)
-# title :string(255)
-# project_id :integer
-# created_at :datetime
-# updated_at :datetime
-# active :boolean default(FALSE), not null
-# properties :text
-# template :boolean default(FALSE)
-# push_events :boolean default(TRUE)
-# issues_events :boolean default(TRUE)
-# merge_requests_events :boolean default(TRUE)
-# tag_push_events :boolean default(TRUE)
-# note_events :boolean default(TRUE), not null
-#
-
require 'spec_helper'
describe PivotaltrackerService, models: true do
diff --git a/spec/models/project_services/pushover_service_spec.rb b/spec/models/project_services/pushover_service_spec.rb
index 5959c81577d..8fc92a9ab51 100644
--- a/spec/models/project_services/pushover_service_spec.rb
+++ b/spec/models/project_services/pushover_service_spec.rb
@@ -1,23 +1,3 @@
-# == Schema Information
-#
-# Table name: services
-#
-# id :integer not null, primary key
-# type :string(255)
-# title :string(255)
-# project_id :integer
-# created_at :datetime
-# updated_at :datetime
-# active :boolean default(FALSE), not null
-# properties :text
-# template :boolean default(FALSE)
-# push_events :boolean default(TRUE)
-# issues_events :boolean default(TRUE)
-# merge_requests_events :boolean default(TRUE)
-# tag_push_events :boolean default(TRUE)
-# note_events :boolean default(TRUE), not null
-#
-
require 'spec_helper'
describe PushoverService, models: true do
diff --git a/spec/models/project_services/redmine_service_spec.rb b/spec/models/project_services/redmine_service_spec.rb
index 7d14f6e8280..b8679cd2563 100644
--- a/spec/models/project_services/redmine_service_spec.rb
+++ b/spec/models/project_services/redmine_service_spec.rb
@@ -1,23 +1,3 @@
-# == Schema Information
-#
-# Table name: services
-#
-# id :integer not null, primary key
-# type :string(255)
-# title :string(255)
-# project_id :integer
-# created_at :datetime
-# updated_at :datetime
-# active :boolean default(FALSE), not null
-# properties :text
-# template :boolean default(FALSE)
-# push_events :boolean default(TRUE)
-# issues_events :boolean default(TRUE)
-# merge_requests_events :boolean default(TRUE)
-# tag_push_events :boolean default(TRUE)
-# note_events :boolean default(TRUE), not null
-#
-
require 'spec_helper'
describe RedmineService, models: true do
diff --git a/spec/models/project_services/slack_service_spec.rb b/spec/models/project_services/slack_service_spec.rb
index 5afdc4b2f7b..c07a70a8069 100644
--- a/spec/models/project_services/slack_service_spec.rb
+++ b/spec/models/project_services/slack_service_spec.rb
@@ -1,23 +1,3 @@
-# == Schema Information
-#
-# Table name: services
-#
-# id :integer not null, primary key
-# type :string(255)
-# title :string(255)
-# project_id :integer
-# created_at :datetime
-# updated_at :datetime
-# active :boolean default(FALSE), not null
-# properties :text
-# template :boolean default(FALSE)
-# push_events :boolean default(TRUE)
-# issues_events :boolean default(TRUE)
-# merge_requests_events :boolean default(TRUE)
-# tag_push_events :boolean default(TRUE)
-# note_events :boolean default(TRUE), not null
-#
-
require 'spec_helper'
describe SlackService, models: true do
diff --git a/spec/models/project_services/teamcity_service_spec.rb b/spec/models/project_services/teamcity_service_spec.rb
index 474715d24c3..f7e878844dc 100644
--- a/spec/models/project_services/teamcity_service_spec.rb
+++ b/spec/models/project_services/teamcity_service_spec.rb
@@ -1,23 +1,3 @@
-# == Schema Information
-#
-# Table name: services
-#
-# id :integer not null, primary key
-# type :string(255)
-# title :string(255)
-# project_id :integer
-# created_at :datetime
-# updated_at :datetime
-# active :boolean default(FALSE), not null
-# properties :text
-# template :boolean default(FALSE)
-# push_events :boolean default(TRUE)
-# issues_events :boolean default(TRUE)
-# merge_requests_events :boolean default(TRUE)
-# tag_push_events :boolean default(TRUE)
-# note_events :boolean default(TRUE), not null
-#
-
require 'spec_helper'
describe TeamcityService, models: true do
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index f6e811828fc..7ca1bd1e5c9 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -1417,6 +1417,68 @@ describe Project, models: true do
end
end
+ describe '#lfs_enabled?' do
+ let(:project) { create(:project) }
+
+ shared_examples 'project overrides group' do
+ it 'returns true when enabled in project' do
+ project.update_attribute(:lfs_enabled, true)
+
+ expect(project.lfs_enabled?).to be_truthy
+ end
+
+ it 'returns false when disabled in project' do
+ project.update_attribute(:lfs_enabled, false)
+
+ expect(project.lfs_enabled?).to be_falsey
+ end
+
+ it 'returns the value from the namespace, when no value is set in project' do
+ expect(project.lfs_enabled?).to eq(project.namespace.lfs_enabled?)
+ end
+ end
+
+ context 'LFS disabled in group' do
+ before do
+ project.namespace.update_attribute(:lfs_enabled, false)
+ enable_lfs
+ end
+
+ it_behaves_like 'project overrides group'
+ end
+
+ context 'LFS enabled in group' do
+ before do
+ project.namespace.update_attribute(:lfs_enabled, true)
+ enable_lfs
+ end
+
+ it_behaves_like 'project overrides group'
+ end
+
+ describe 'LFS disabled globally' do
+ shared_examples 'it always returns false' do
+ it do
+ expect(project.lfs_enabled?).to be_falsey
+ expect(project.namespace.lfs_enabled?).to be_falsey
+ end
+ end
+
+ context 'when no values are set' do
+ it_behaves_like 'it always returns false'
+ end
+
+ context 'when all values are set to true' do
+ before do
+ project.namespace.update_attribute(:lfs_enabled, true)
+ project.update_attribute(:lfs_enabled, true)
+ end
+
+ it_behaves_like 'it always returns false'
+ end
+ end
+ end
+
describe '.where_paths_in' do
context 'without any paths' do
it 'returns an empty relation' do
@@ -1581,4 +1643,8 @@ describe Project, models: true do
expect(project.pushes_since_gc).to eq(0)
end
end
+
+ def enable_lfs
+ allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
+ end
end
diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb
index 5b3dc60aba2..10f772c5b1a 100644
--- a/spec/requests/api/commits_spec.rb
+++ b/spec/requests/api/commits_spec.rb
@@ -110,7 +110,7 @@ describe API::API, api: true do
get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user)
expect(response).to have_http_status(200)
- expect(json_response['status']).to be_nil
+ expect(json_response['status']).to eq("created")
end
end
diff --git a/spec/requests/api/milestones_spec.rb b/spec/requests/api/milestones_spec.rb
index d6a0c656e74..b89dac01040 100644
--- a/spec/requests/api/milestones_spec.rb
+++ b/spec/requests/api/milestones_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe API::API, api: true do
include ApiHelpers
let(:user) { create(:user) }
- let!(:project) { create(:project, namespace: user.namespace ) }
+ let!(:project) { create(:empty_project, namespace: user.namespace ) }
let!(:closed_milestone) { create(:closed_milestone, project: project) }
let!(:milestone) { create(:milestone, project: project) }
@@ -12,6 +12,7 @@ describe API::API, api: true do
describe 'GET /projects/:id/milestones' do
it 'returns project milestones' do
get api("/projects/#{project.id}/milestones", user)
+
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.first['title']).to eq(milestone.title)
@@ -19,6 +20,7 @@ describe API::API, api: true do
it 'returns a 401 error if user not authenticated' do
get api("/projects/#{project.id}/milestones")
+
expect(response).to have_http_status(401)
end
@@ -44,6 +46,7 @@ describe API::API, api: true do
describe 'GET /projects/:id/milestones/:milestone_id' do
it 'returns a project milestone by id' do
get api("/projects/#{project.id}/milestones/#{milestone.id}", user)
+
expect(response).to have_http_status(200)
expect(json_response['title']).to eq(milestone.title)
expect(json_response['iid']).to eq(milestone.iid)
@@ -60,11 +63,13 @@ describe API::API, api: true do
it 'returns 401 error if user not authenticated' do
get api("/projects/#{project.id}/milestones/#{milestone.id}")
+
expect(response).to have_http_status(401)
end
it 'returns a 404 error if milestone id not found' do
get api("/projects/#{project.id}/milestones/1234", user)
+
expect(response).to have_http_status(404)
end
end
@@ -72,6 +77,7 @@ describe API::API, api: true do
describe 'POST /projects/:id/milestones' do
it 'creates a new project milestone' do
post api("/projects/#{project.id}/milestones", user), title: 'new milestone'
+
expect(response).to have_http_status(201)
expect(json_response['title']).to eq('new milestone')
expect(json_response['description']).to be_nil
@@ -80,6 +86,7 @@ describe API::API, api: true do
it 'creates a new project milestone with description and due date' do
post api("/projects/#{project.id}/milestones", user),
title: 'new milestone', description: 'release', due_date: '2013-03-02'
+
expect(response).to have_http_status(201)
expect(json_response['description']).to eq('release')
expect(json_response['due_date']).to eq('2013-03-02')
@@ -87,6 +94,14 @@ describe API::API, api: true do
it 'returns a 400 error if title is missing' do
post api("/projects/#{project.id}/milestones", user)
+
+ expect(response).to have_http_status(400)
+ end
+
+ it 'returns a 400 error if params are invalid (duplicate title)' do
+ post api("/projects/#{project.id}/milestones", user),
+ title: milestone.title, description: 'release', due_date: '2013-03-02'
+
expect(response).to have_http_status(400)
end
end
@@ -95,6 +110,7 @@ describe API::API, api: true do
it 'updates a project milestone' do
put api("/projects/#{project.id}/milestones/#{milestone.id}", user),
title: 'updated title'
+
expect(response).to have_http_status(200)
expect(json_response['title']).to eq('updated title')
end
@@ -102,6 +118,7 @@ describe API::API, api: true do
it 'returns a 404 error if milestone id not found' do
put api("/projects/#{project.id}/milestones/1234", user),
title: 'updated title'
+
expect(response).to have_http_status(404)
end
end
@@ -131,6 +148,7 @@ describe API::API, api: true do
end
it 'returns project issues for a particular milestone' do
get api("/projects/#{project.id}/milestones/#{milestone.id}/issues", user)
+
expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(json_response.first['milestone']['title']).to eq(milestone.title)
@@ -138,11 +156,12 @@ describe API::API, api: true do
it 'returns a 401 error if user not authenticated' do
get api("/projects/#{project.id}/milestones/#{milestone.id}/issues")
+
expect(response).to have_http_status(401)
end
describe 'confidential issues' do
- let(:public_project) { create(:project, :public) }
+ let(:public_project) { create(:empty_project, :public) }
let(:milestone) { create(:milestone, project: public_project) }
let(:issue) { create(:issue, project: public_project) }
let(:confidential_issue) { create(:issue, confidential: true, project: public_project) }
diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb
index 223444ea39f..063a8706e76 100644
--- a/spec/requests/api/notes_spec.rb
+++ b/spec/requests/api/notes_spec.rb
@@ -220,6 +220,15 @@ describe API::API, api: true do
expect(Time.parse(json_response['created_at'])).to be_within(1.second).of(creation_time)
end
end
+
+ context 'when the user is posting an award emoji' do
+ it 'returns an award emoji' do
+ post api("/projects/#{project.id}/issues/#{issue.id}/notes", user), body: ':+1:'
+
+ expect(response).to have_http_status(201)
+ expect(json_response['awardable_id']).to eq issue.id
+ end
+ end
end
context "when noteable is a Snippet" do
diff --git a/spec/services/create_deployment_service_spec.rb b/spec/services/create_deployment_service_spec.rb
index 8da2a2b3c1b..41b897f36cd 100644
--- a/spec/services/create_deployment_service_spec.rb
+++ b/spec/services/create_deployment_service_spec.rb
@@ -41,7 +41,7 @@ describe CreateDeploymentService, services: true do
context 'for environment with invalid name' do
let(:params) do
- { environment: 'name with spaces',
+ { environment: 'name,with,commas',
ref: 'master',
tag: false,
sha: '97de212e80737a608d939f648d959671fb0a0142',
@@ -56,8 +56,36 @@ describe CreateDeploymentService, services: true do
expect(subject).not_to be_persisted
end
end
+
+ context 'when variables are used' do
+ let(:params) do
+ { environment: 'review-apps/$CI_BUILD_REF_NAME',
+ ref: 'master',
+ tag: false,
+ sha: '97de212e80737a608d939f648d959671fb0a0142',
+ options: {
+ name: 'review-apps/$CI_BUILD_REF_NAME',
+ url: 'http://$CI_BUILD_REF_NAME.review-apps.gitlab.com'
+ },
+ variables: [
+ { key: 'CI_BUILD_REF_NAME', value: 'feature-review-apps' }
+ ]
+ }
+ end
+
+ it 'does create a new environment' do
+ expect { subject }.to change { Environment.count }.by(1)
+
+ expect(subject.environment.name).to eq('review-apps/feature-review-apps')
+ expect(subject.environment.external_url).to eq('http://feature-review-apps.review-apps.gitlab.com')
+ end
+
+ it 'does create a new deployment' do
+ expect(subject).to be_persisted
+ end
+ end
end
-
+
describe 'processing of builds' do
let(:environment) { nil }
@@ -95,6 +123,12 @@ describe CreateDeploymentService, services: true do
expect(Deployment.last.deployable).to eq(deployable)
end
+
+ it 'create environment has URL set' do
+ subject
+
+ expect(Deployment.last.environment.external_url).not_to be_nil
+ end
end
context 'without environment specified' do
@@ -107,7 +141,10 @@ describe CreateDeploymentService, services: true do
context 'when environment is specified' do
let(:pipeline) { create(:ci_pipeline, project: project) }
- let(:build) { create(:ci_build, pipeline: pipeline, environment: 'production') }
+ let(:build) { create(:ci_build, pipeline: pipeline, environment: 'production', options: options) }
+ let(:options) do
+ { environment: { name: 'production', url: 'http://gitlab.com' } }
+ end
context 'when build succeeds' do
it_behaves_like 'does create environment and deployment' do
diff --git a/spec/services/projects/housekeeping_service_spec.rb b/spec/services/projects/housekeeping_service_spec.rb
index c6160f4fa57..cf90b33dfb4 100644
--- a/spec/services/projects/housekeeping_service_spec.rb
+++ b/spec/services/projects/housekeeping_service_spec.rb
@@ -4,6 +4,10 @@ describe Projects::HousekeepingService do
subject { Projects::HousekeepingService.new(project) }
let(:project) { create :project }
+ before do
+ project.reset_pushes_since_gc
+ end
+
after do
project.reset_pushes_since_gc
end
diff --git a/spec/services/protected_branches/create_service_spec.rb b/spec/services/protected_branches/create_service_spec.rb
new file mode 100644
index 00000000000..7d4eff3b6ef
--- /dev/null
+++ b/spec/services/protected_branches/create_service_spec.rb
@@ -0,0 +1,23 @@
+require 'spec_helper'
+
+describe ProtectedBranches::CreateService, services: true do
+ let(:project) { create(:empty_project) }
+ let(:user) { project.owner }
+ let(:params) do
+ {
+ name: 'master',
+ merge_access_levels_attributes: [ { access_level: Gitlab::Access::MASTER } ],
+ push_access_levels_attributes: [ { access_level: Gitlab::Access::MASTER } ]
+ }
+ end
+
+ describe '#execute' do
+ subject(:service) { described_class.new(project, user, params) }
+
+ it 'creates a new protected branch' do
+ expect { service.execute }.to change(ProtectedBranch, :count).by(1)
+ expect(project.protected_branches.last.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::MASTER])
+ expect(project.protected_branches.last.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::MASTER])
+ end
+ end
+end
diff --git a/spec/support/wait_for_vue_resource.rb b/spec/support/wait_for_vue_resource.rb
new file mode 100644
index 00000000000..1029f84716f
--- /dev/null
+++ b/spec/support/wait_for_vue_resource.rb
@@ -0,0 +1,7 @@
+module WaitForVueResource
+ def wait_for_vue_resource(spinner: true)
+ Timeout.timeout(Capybara.default_max_wait_time) do
+ loop until page.evaluate_script('Vue.activeResources').zero?
+ end
+ end
+end
diff --git a/spec/views/projects/pipelines/show.html.haml_spec.rb b/spec/views/projects/pipelines/show.html.haml_spec.rb
index c5b16c1c304..ac7f3ffb157 100644
--- a/spec/views/projects/pipelines/show.html.haml_spec.rb
+++ b/spec/views/projects/pipelines/show.html.haml_spec.rb
@@ -9,11 +9,13 @@ describe 'projects/pipelines/show' do
before do
controller.prepend_view_path('app/views/projects')
- create_build('build', 0, 'build')
- create_build('test', 1, 'rspec 0:2')
- create_build('test', 1, 'rspec 1:2')
- create_build('test', 1, 'audit')
- create_build('deploy', 2, 'production')
+ create_build('build', 0, 'build', :success)
+ create_build('test', 1, 'rspec 0:2', :pending)
+ create_build('test', 1, 'rspec 1:2', :running)
+ create_build('test', 1, 'spinach 0:2', :created)
+ create_build('test', 1, 'spinach 1:2', :created)
+ create_build('test', 1, 'audit', :created)
+ create_build('deploy', 2, 'production', :created)
create(:generic_commit_status, pipeline: pipeline, stage: 'external', name: 'jenkins', stage_idx: 3)
@@ -37,6 +39,7 @@ describe 'projects/pipelines/show' do
# builds
expect(rendered).to have_text('rspec')
+ expect(rendered).to have_text('spinach')
expect(rendered).to have_text('rspec 0:2')
expect(rendered).to have_text('production')
expect(rendered).to have_text('jenkins')
@@ -44,7 +47,7 @@ describe 'projects/pipelines/show' do
private
- def create_build(stage, stage_idx, name)
- create(:ci_build, pipeline: pipeline, stage: stage, stage_idx: stage_idx, name: name)
+ def create_build(stage, stage_idx, name, status)
+ create(:ci_build, pipeline: pipeline, stage: stage, stage_idx: stage_idx, name: name, status: status)
end
end