summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhil Hughes <me@iamphill.com>2017-04-13 12:14:17 +0000
committerPhil Hughes <me@iamphill.com>2017-04-13 12:14:17 +0000
commit8aad34774870a6365d5b1704f5b6bcacc4495ed3 (patch)
tree7212d1349318ac7889cf0a4fd2a5374ccc0ebe6c
parente547eb3b698fe17fe1c47a92050146cb1bde9370 (diff)
parent7629c9ec57cb9b14f41c1163b61df1e6ea6b0ca1 (diff)
downloadgitlab-ce-8aad34774870a6365d5b1704f5b6bcacc4495ed3.tar.gz
Merge branch 'master' into 'board-component-in-js'
# Conflicts: # app/assets/javascripts/boards/components/board.js # app/assets/javascripts/boards/components/board_delete.js # app/assets/javascripts/boards/components/issue_card_inner.js # app/assets/javascripts/boards/stores/boards_store.js
-rw-r--r--.eslintrc4
-rw-r--r--.gitlab-ci.yml4
-rw-r--r--.gitlab/issue_templates/Bug.md8
-rw-r--r--CHANGELOG.md6
-rw-r--r--Gemfile.lock2
-rw-r--r--app/assets/javascripts/boards/boards_bundle.js1
-rw-r--r--app/assets/javascripts/boards/components/board.js2
-rw-r--r--app/assets/javascripts/boards/components/board_delete.js2
-rw-r--r--app/assets/javascripts/boards/components/board_sidebar.js112
-rw-r--r--app/assets/javascripts/boards/components/issue_card_inner.js188
-rw-r--r--app/assets/javascripts/boards/components/modal/empty_state.js120
-rw-r--r--app/assets/javascripts/boards/components/modal/footer.js126
-rw-r--r--app/assets/javascripts/boards/components/modal/header.js130
-rw-r--r--app/assets/javascripts/boards/components/modal/index.js272
-rw-r--r--app/assets/javascripts/boards/components/modal/list.js268
-rw-r--r--app/assets/javascripts/boards/components/modal/lists_dropdown.js102
-rw-r--r--app/assets/javascripts/boards/components/modal/tabs.js86
-rw-r--r--app/assets/javascripts/boards/components/new_list_dropdown.js120
-rw-r--r--app/assets/javascripts/boards/components/sidebar/remove_issue.js98
-rw-r--r--app/assets/javascripts/boards/mixins/modal_mixins.js22
-rw-r--r--app/assets/javascripts/boards/mixins/sortable_default_options.js60
-rw-r--r--app/assets/javascripts/boards/stores/boards_store.js176
-rw-r--r--app/assets/javascripts/boards/stores/modal_store.js156
-rw-r--r--app/assets/javascripts/build.js2
-rw-r--r--app/assets/javascripts/commit/pipelines/pipelines_table.js4
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_code_component.js81
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_issue_component.js85
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_plan_component.js85
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_production_component.js85
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_review_component.js103
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_staging_component.js85
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_test_component.js85
-rw-r--r--app/assets/javascripts/cycle_analytics/components/total_time_component.js41
-rw-r--r--app/assets/javascripts/cycle_analytics/cycle_analytics_service.js64
-rw-r--r--app/assets/javascripts/cycle_analytics/cycle_analytics_store.js171
-rw-r--r--app/assets/javascripts/gfm_auto_complete.js2
-rw-r--r--app/assets/javascripts/issue.js108
-rw-r--r--app/assets/javascripts/lib/utils/text_utility.js346
-rw-r--r--app/assets/javascripts/lib/utils/url_utility.js171
-rw-r--r--app/assets/javascripts/vue_pipelines_index/components/async_button.vue (renamed from app/assets/javascripts/vue_pipelines_index/components/async_button.js)49
-rw-r--r--app/assets/javascripts/vue_pipelines_index/components/empty_state.js33
-rw-r--r--app/assets/javascripts/vue_pipelines_index/components/empty_state.vue34
-rw-r--r--app/assets/javascripts/vue_pipelines_index/components/error_state.js19
-rw-r--r--app/assets/javascripts/vue_pipelines_index/components/error_state.vue21
-rw-r--r--app/assets/javascripts/vue_pipelines_index/pipelines.js4
-rw-r--r--app/assets/javascripts/vue_shared/components/pipelines_table_row.js2
-rw-r--r--app/assets/stylesheets/framework/calendar.scss2
-rw-r--r--app/assets/stylesheets/framework/filters.scss28
-rw-r--r--app/assets/stylesheets/pages/builds.scss6
-rw-r--r--app/assets/stylesheets/pages/events.scss22
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss3
-rw-r--r--app/assets/stylesheets/pages/notes.scss16
-rw-r--r--app/controllers/projects/compare_controller.rb1
-rw-r--r--app/controllers/projects/hooks_controller.rb4
-rwxr-xr-xapp/controllers/projects/merge_requests_controller.rb78
-rw-r--r--app/controllers/projects_controller.rb10
-rw-r--r--app/helpers/blob_helper.rb4
-rw-r--r--app/helpers/diff_helper.rb2
-rw-r--r--app/helpers/events_helper.rb28
-rw-r--r--app/helpers/javascript_helper.rb5
-rw-r--r--app/helpers/notes_helper.rb19
-rw-r--r--app/helpers/preferences_helper.rb6
-rw-r--r--app/helpers/webpack_helper.rb30
-rw-r--r--app/models/blob.rb30
-rw-r--r--app/models/ci/trigger.rb2
-rw-r--r--app/models/commit.rb15
-rw-r--r--app/models/concerns/discussion_on_diff.rb12
-rw-r--r--app/models/concerns/note_on_diff.rb14
-rw-r--r--app/models/concerns/noteable.rb4
-rw-r--r--app/models/container_repository.rb4
-rw-r--r--app/models/diff_discussion.rb1
-rw-r--r--app/models/diff_note.rb14
-rw-r--r--app/models/issue.rb2
-rw-r--r--app/models/label.rb6
-rw-r--r--app/models/legacy_diff_discussion.rb8
-rw-r--r--app/models/legacy_diff_note.rb3
-rw-r--r--app/models/merge_request.rb9
-rw-r--r--app/models/merge_request_diff.rb10
-rw-r--r--app/models/note.rb9
-rw-r--r--app/models/repository.rb18
-rw-r--r--app/models/user.rb4
-rw-r--r--app/policies/group_policy.rb1
-rw-r--r--app/uploaders/file_uploader.rb4
-rw-r--r--app/views/admin/application_settings/_form.html.haml1
-rw-r--r--app/views/admin/health_check/show.html.haml2
-rw-r--r--app/views/admin/runners/index.html.haml2
-rw-r--r--app/views/discussions/_discussion.html.haml13
-rw-r--r--app/views/events/event/_common.html.haml11
-rw-r--r--app/views/events/event/_created_project.html.haml3
-rw-r--r--app/views/events/event/_note.html.haml3
-rw-r--r--app/views/events/event/_push.html.haml6
-rw-r--r--app/views/groups/subgroups.html.haml2
-rw-r--r--app/views/help/ui.html.haml8
-rw-r--r--app/views/layouts/_head.html.haml6
-rw-r--r--app/views/layouts/nav/_explore.html.haml19
-rw-r--r--app/views/layouts/nav/_project.html.haml4
-rw-r--r--app/views/notify/project_was_exported_email.html.haml2
-rw-r--r--app/views/projects/blob/_blob.html.haml15
-rw-r--r--app/views/projects/blob/_header.html.haml9
-rw-r--r--app/views/projects/blob/_image.html.haml15
-rw-r--r--app/views/projects/blob/_markup.html.haml4
-rw-r--r--app/views/projects/blob/_svg.html.haml9
-rw-r--r--app/views/projects/blob/_text.html.haml21
-rw-r--r--app/views/projects/blob/_too_large.html.haml5
-rw-r--r--app/views/projects/builds/_sidebar.html.haml2
-rw-r--r--app/views/projects/ci/builds/_build.html.haml2
-rw-r--r--app/views/projects/diffs/_parallel_view.html.haml3
-rw-r--r--app/views/projects/diffs/_text_file.html.haml3
-rw-r--r--app/views/projects/labels/edit.html.haml2
-rw-r--r--app/views/projects/labels/index.html.haml2
-rw-r--r--app/views/projects/labels/new.html.haml2
-rw-r--r--app/views/projects/merge_requests/_head.html.haml21
-rw-r--r--app/views/projects/merge_requests/index.html.haml3
-rw-r--r--app/views/projects/merge_requests/show/_versions.html.haml11
-rw-r--r--app/views/projects/merge_requests/widget/open/_accept.html.haml2
-rw-r--r--app/views/projects/milestones/edit.html.haml2
-rw-r--r--app/views/projects/milestones/index.html.haml2
-rw-r--r--app/views/projects/milestones/new.html.haml2
-rw-r--r--app/views/projects/milestones/show.html.haml2
-rw-r--r--app/views/projects/registry/repositories/_image.html.haml2
-rw-r--r--app/views/projects/registry/repositories/_tag.html.haml2
-rw-r--r--app/views/projects/show.html.haml6
-rw-r--r--app/views/shared/_mr_head.html.haml4
-rw-r--r--app/views/shared/icons/_icon_merged.svg2
-rw-r--r--app/views/shared/icons/_icon_trash_o.svg (renamed from app/views/shared/icons/_trash_o.svg)0
-rw-r--r--app/workers/build_coverage_worker.rb3
-rw-r--r--changelogs/unreleased/2120-issues-search-bar-not-picking-up.yml4
-rw-r--r--changelogs/unreleased/22714-update-all-instances-of-fa-refresh.yml4
-rw-r--r--changelogs/unreleased/27729-improve-webpack-dev-environment.yml4
-rw-r--r--changelogs/unreleased/28017-separate-ce-params-on-api.yml4
-rw-r--r--changelogs/unreleased/29056-backport-ee-cleanup-database-file.yml4
-rw-r--r--changelogs/unreleased/29866-navbar-counters.yml4
-rw-r--r--changelogs/unreleased/30024-owner-can-t-initialize-git-repo-for-new-project-in-group.yml4
-rw-r--r--changelogs/unreleased/30457-expire-note-destroy.yml4
-rw-r--r--changelogs/unreleased/30678-improve-dev-server-process.yml4
-rw-r--r--changelogs/unreleased/30779-show-mr-subnav-issue-tracker.yml4
-rw-r--r--changelogs/unreleased/add-vue-loader.yml4
-rw-r--r--changelogs/unreleased/bb_save_trace.yml5
-rw-r--r--changelogs/unreleased/boards-done-add-tooltip.yml4
-rw-r--r--changelogs/unreleased/clean_carrierwave_tempfiles.yml4
-rw-r--r--changelogs/unreleased/dm-link-discussion-to-outdated-diff.yml4
-rw-r--r--changelogs/unreleased/dz-fix-project-view.yml4
-rw-r--r--changelogs/unreleased/fix-missing-capitalisation-buttons.yml4
-rw-r--r--changelogs/unreleased/fix-preloading-merge_request_diff.yml4
-rw-r--r--changelogs/unreleased/fix_cache_expiration_in_repository.yml4
-rw-r--r--changelogs/unreleased/fix_spaces_in_label_title.yml4
-rw-r--r--changelogs/unreleased/mr-widget-bug-fix.yml5
-rw-r--r--changelogs/unreleased/omnibus-gitlab-1993-check-shell-repositories-path-group-is-root.yml4
-rw-r--r--changelogs/unreleased/reset-new-branch-button.yml4
-rw-r--r--changelogs/unreleased/sh-fix-ssh-keys-with-spaces.yml4
-rw-r--r--changelogs/unreleased/siemens-gitlab-ce-fix-subgroup-hide-button.yml4
-rw-r--r--changelogs/unreleased/spec_for_schema.yml4
-rw-r--r--changelogs/unreleased/user-activity-scroll-bar.yml4
-rw-r--r--changelogs/unreleased/zj-api-fix-build-events.yml4
-rw-r--r--changelogs/unreleased/zj-fk-ci-triggers.yml4
-rw-r--r--config/dependency_decisions.yml54
-rw-r--r--config/gitlab.yml.example4
-rw-r--r--config/webpack.config.js22
-rw-r--r--db/fixtures/development/17_cycle_analytics.rb6
-rw-r--r--db/fixtures/development/20_nested_groups.rb68
-rw-r--r--db/migrate/20130218141258_convert_closed_to_state_in_issue.rb2
-rw-r--r--db/migrate/20130218141327_convert_closed_to_state_in_merge_request.rb2
-rw-r--r--db/migrate/20130218141344_convert_closed_to_state_in_milestone.rb2
-rw-r--r--db/migrate/20130315124931_user_color_scheme.rb2
-rw-r--r--db/migrate/20131112220935_add_visibility_level_to_projects.rb2
-rw-r--r--db/migrate/20140313092127_migrate_already_imported_projects.rb2
-rw-r--r--db/migrate/20141007100818_add_visibility_level_to_snippet.rb2
-rw-r--r--db/migrate/20151209144329_migrate_ci_web_hooks.rb2
-rw-r--r--db/migrate/20151209145909_migrate_ci_emails.rb2
-rw-r--r--db/migrate/20151210125232_migrate_ci_slack_service.rb2
-rw-r--r--db/migrate/20151210125927_migrate_ci_hip_chat_service.rb2
-rw-r--r--db/migrate/20170407135259_add_foreigh_key_trigger_requests_trigger.rb15
-rw-r--r--db/post_migrate/20170301205640_migrate_build_events_to_pipeline_events.rb1
-rw-r--r--db/post_migrate/20170406142253_migrate_user_project_view.rb19
-rw-r--r--db/post_migrate/20170408033905_remove_old_cache_directories.rb23
-rw-r--r--db/schema.rb4
-rw-r--r--doc/README.md1
-rw-r--r--doc/administration/polling.md24
-rw-r--r--doc/api/README.md12
-rw-r--r--doc/api/access_requests.md8
-rw-r--r--doc/api/award_emoji.md18
-rw-r--r--doc/api/boards.md12
-rw-r--r--doc/api/branches.md14
-rw-r--r--doc/api/build_variables.md10
-rw-r--r--doc/api/commits.md18
-rw-r--r--doc/api/deploy_keys.md10
-rw-r--r--doc/api/deployments.md4
-rw-r--r--doc/api/enviroments.md10
-rw-r--r--doc/api/groups.md6
-rw-r--r--doc/api/issues.md30
-rw-r--r--doc/api/jobs.md22
-rw-r--r--doc/api/labels.md12
-rw-r--r--doc/api/members.md10
-rw-r--r--doc/api/merge_requests.md40
-rw-r--r--doc/api/milestones.md14
-rw-r--r--doc/api/notes.md30
-rw-r--r--doc/api/pipeline_triggers.md12
-rw-r--r--doc/api/pipelines.md12
-rw-r--r--doc/api/project_snippets.md12
-rw-r--r--doc/api/projects.md50
-rw-r--r--doc/api/repositories.md12
-rw-r--r--doc/api/runners.md6
-rw-r--r--doc/api/tags.md12
-rw-r--r--doc/ci/README.md5
-rw-r--r--doc/ci/img/pipelines.pngbin7516 -> 6298 bytes
-rw-r--r--doc/development/fe_guide/performance.md8
-rw-r--r--doc/development/fe_guide/testing.md45
-rw-r--r--doc/development/migration_style_guide.md133
-rw-r--r--doc/development/polling.md1
-rw-r--r--doc/development/testing.md429
-rw-r--r--doc/development/what_requires_downtime.md237
-rw-r--r--doc/install/README.md4
-rw-r--r--doc/install/digitaloceandocker.md5
-rw-r--r--doc/install/installation.md6
-rw-r--r--doc/topics/authentication/index.md46
-rw-r--r--doc/topics/git/index.md61
-rw-r--r--doc/topics/index.md8
-rw-r--r--doc/update/9.0-to-9.1.md4
-rw-r--r--doc/update/README.md17
-rw-r--r--doc/update/patch_versions.md2
-rw-r--r--doc/user/profile/account/two_factor_authentication.md2
-rw-r--r--doc/user/project/cycle_analytics.md4
-rw-r--r--doc/user/project/integrations/kubernetes.md2
-rw-r--r--doc/workflow/gitlab_flow.md1
-rw-r--r--doc/workflow/todos.md2
-rw-r--r--lib/api/groups.rb7
-rw-r--r--lib/api/helpers/internal_helpers.rb6
-rw-r--r--lib/api/internal.rb24
-rw-r--r--lib/api/issues.rb6
-rw-r--r--lib/api/merge_requests.rb23
-rw-r--r--lib/api/project_hooks.rb12
-rw-r--r--lib/api/projects.rb36
-rw-r--r--lib/api/settings.rb67
-rw-r--r--lib/api/users.rb2
-rw-r--r--lib/banzai/reference_parser/merge_request_parser.rb41
-rw-r--r--lib/bitbucket/representation/base.rb6
-rw-r--r--lib/container_registry/tag.rb4
-rw-r--r--lib/gitlab/bitbucket_import/importer.rb4
-rw-r--r--lib/gitlab/checks/change_access.rb5
-rw-r--r--lib/gitlab/checks/force_push.rb12
-rw-r--r--lib/gitlab/ci/trace/stream.rb5
-rw-r--r--lib/gitlab/database.rb8
-rw-r--r--lib/gitlab/database/migration_helpers.rb250
-rw-r--r--lib/gitlab/database/multi_threaded_migration.rb52
-rw-r--r--lib/gitlab/diff/diff_refs.rb6
-rw-r--r--lib/gitlab/etag_caching/middleware.rb36
-rw-r--r--lib/gitlab/etag_caching/router.rb39
-rw-r--r--lib/gitlab/git/env.rb38
-rw-r--r--lib/gitlab/git/repository.rb80
-rw-r--r--lib/gitlab/git/rev_list.rb49
-rw-r--r--lib/gitlab/git_access.rb4
-rw-r--r--lib/tasks/gitlab/check.rake14
-rw-r--r--lib/tasks/gitlab/update_templates.rake2
-rw-r--r--package.json16
-rw-r--r--qa/qa/page/main/menu.rb2
-rwxr-xr-xscripts/prepare_build.sh23
-rw-r--r--spec/factories/notes.rb3
-rw-r--r--spec/features/dashboard/shortcuts_spec.rb48
-rw-r--r--spec/features/discussion_comments/commit_spec.rb18
-rw-r--r--spec/features/discussion_comments/issue_spec.rb16
-rw-r--r--spec/features/discussion_comments/merge_request_spec.rb16
-rw-r--r--spec/features/discussion_comments/snippets_spec.rb16
-rw-r--r--spec/features/merge_requests/discussion_spec.rb51
-rw-r--r--spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb13
-rw-r--r--spec/features/merge_requests/versions_spec.rb (renamed from spec/features/merge_requests/merge_request_versions_spec.rb)19
-rw-r--r--spec/helpers/preferences_helper_spec.rb4
-rw-r--r--spec/javascripts/build_spec.js6
-rw-r--r--spec/javascripts/environments/environment_spec.js5
-rw-r--r--spec/javascripts/environments/folder/environments_folder_view_spec.js5
-rw-r--r--spec/javascripts/issue_spec.js194
-rw-r--r--spec/javascripts/lib/utils/poll_spec.js93
-rw-r--r--spec/javascripts/lib/utils/text_utility_spec.js148
-rw-r--r--spec/javascripts/vue_pipelines_index/async_button_spec.js2
-rw-r--r--spec/javascripts/vue_pipelines_index/empty_state_spec.js2
-rw-r--r--spec/javascripts/vue_pipelines_index/error_state_spec.js2
-rw-r--r--spec/lib/container_registry/tag_spec.rb7
-rw-r--r--spec/lib/gitlab/ci/trace/stream_spec.rb6
-rw-r--r--spec/lib/gitlab/ci/trace_spec.rb20
-rw-r--r--spec/lib/gitlab/database/migration_helpers_spec.rb432
-rw-r--r--spec/lib/gitlab/database/multi_threaded_migration_spec.rb41
-rw-r--r--spec/lib/gitlab/database_spec.rb8
-rw-r--r--spec/lib/gitlab/etag_caching/router_spec.rb83
-rw-r--r--spec/lib/gitlab/git/env_spec.rb102
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb107
-rw-r--r--spec/lib/gitlab/git/rev_list_spec.rb92
-rw-r--r--spec/migrations/migrate_user_project_view_spec.rb17
-rw-r--r--spec/migrations/schema_spec.rb23
-rw-r--r--spec/models/blob_spec.rb10
-rw-r--r--spec/models/commit_spec.rb55
-rw-r--r--spec/models/concerns/ignorable_column_spec.rb38
-rw-r--r--spec/models/container_repository_spec.rb55
-rw-r--r--spec/models/diff_note_spec.rb17
-rw-r--r--spec/models/environment_spec.rb23
-rw-r--r--spec/models/issue_spec.rb9
-rw-r--r--spec/models/label_spec.rb16
-rw-r--r--spec/models/note_spec.rb12
-rw-r--r--spec/models/repository_spec.rb27
-rw-r--r--spec/policies/group_policy_spec.rb3
-rw-r--r--spec/requests/api/internal_spec.rb21
-rw-r--r--spec/requests/api/project_hooks_spec.rb13
-rw-r--r--spec/requests/api/projects_spec.rb7
-rw-r--r--spec/services/notification_service_spec.rb16
-rw-r--r--spec/spec_helper.rb4
-rw-r--r--spec/support/features/discussion_comments_shared_example.rb (renamed from spec/features/discussion_comments_spec.rb)106
-rw-r--r--spec/support/gitaly.rb7
-rw-r--r--spec/support/test_env.rb30
-rw-r--r--spec/views/projects/registry/repositories/index.html.haml_spec.rb36
-rw-r--r--vendor/assets/javascripts/notebooklab.js247
-rw-r--r--vendor/assets/javascripts/pdf.worker.js56
-rw-r--r--vendor/assets/javascripts/pdflab.js271
-rw-r--r--vendor/gitignore/C.gitignore1
-rw-r--r--vendor/gitignore/Dart.gitignore27
-rw-r--r--vendor/gitignore/Global/Eclipse.gitignore6
-rw-r--r--vendor/gitignore/Global/JetBrains.gitignore3
-rw-r--r--vendor/gitignore/Global/macOS.gitignore51
-rw-r--r--vendor/gitignore/Python.gitignore3
-rw-r--r--vendor/gitignore/Rails.gitignore2
-rw-r--r--vendor/gitignore/TeX.gitignore3
-rw-r--r--vendor/gitignore/Unity.gitignore1
-rw-r--r--vendor/gitignore/VisualStudio.gitignore3
-rw-r--r--vendor/gitlab-ci-yml/CONTRIBUTING.md5
-rw-r--r--vendor/gitlab-ci-yml/Django.gitlab-ci.yml17
-rw-r--r--vendor/gitlab-ci-yml/Pages/Hexo.gitlab-ci.yml13
-rw-r--r--vendor/gitlab-ci-yml/Pages/Jekyll.gitlab-ci.yml6
-rw-r--r--vendor/gitlab-ci-yml/Scala.gitlab-ci.yml2
-rw-r--r--vendor/licenses.csv468
-rw-r--r--yarn.lock1202
327 files changed, 7662 insertions, 3926 deletions
diff --git a/.eslintrc b/.eslintrc
index b0ae2a31919..57a08a06527 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -13,9 +13,11 @@
},
"plugins": [
"filenames",
- "import"
+ "import",
+ "html"
],
"settings": {
+ "html/html-extensions": [".html", ".html.raw", ".vue"],
"import/resolver": {
"webpack": {
"config": "./config/webpack.config.js"
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 2d3e3dcd976..4e6fcab3808 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,4 +1,4 @@
-image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-git-2.7-phantomjs-2.1-node-7.1"
+image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.3-golang-1.8-git-2.7-phantomjs-2.1-node-7.1"
cache:
key: "ruby-233"
@@ -349,8 +349,6 @@ migration paths:
script:
- git fetch origin v8.14.10
- git checkout -f FETCH_HEAD
- - cp config/resque.yml.example config/resque.yml
- - sed -i 's/localhost/redis/g' config/resque.yml
- bundle install --without postgres production --jobs $(nproc) $FLAGS --retry=3
- bundle exec rake db:drop db:create db:schema:load db:seed_fu
- git checkout $CI_COMMIT_SHA
diff --git a/.gitlab/issue_templates/Bug.md b/.gitlab/issue_templates/Bug.md
index 34c2e097ba8..6bb21e6a3af 100644
--- a/.gitlab/issue_templates/Bug.md
+++ b/.gitlab/issue_templates/Bug.md
@@ -25,14 +25,20 @@ logs, and code as it's very hard to read otherwise.)
#### Results of GitLab environment info
+<details>
+
(For installations with omnibus-gitlab package run and paste the output of:
`sudo gitlab-rake gitlab:env:info`)
(For installations from source run and paste the output of:
`sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production`)
+</details>
+
#### Results of GitLab application Check
+<details>
+
(For installations with omnibus-gitlab package run and paste the output of:
`sudo gitlab-rake gitlab:check SANITIZE=true`)
@@ -41,6 +47,8 @@ logs, and code as it's very hard to read otherwise.)
(we will only investigate if the tests are passing)
+</details>
+
### Possible fixes
(If you can, link to the line of code that might be responsible for the problem)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 712a4970a41..4047a5b6f32 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,12 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 9.0.5 (2017-04-10)
+
+- Add shortcuts and counters to MRs and issues in navbar.
+- Disable invalid service templates.
+- Handle SSH keys that have multiple spaces between each marker.
+
## 9.0.4 (2017-04-05)
- Don’t show source project name when user does not have access.
diff --git a/Gemfile.lock b/Gemfile.lock
index d7e3f7343d0..965c888ca79 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -330,7 +330,7 @@ GEM
grape-entity (0.6.0)
activesupport
multi_json (>= 1.3.2)
- grpc (1.1.2)
+ grpc (1.2.2)
google-protobuf (~> 3.1)
googleauth (~> 0.5.1)
haml (4.0.7)
diff --git a/app/assets/javascripts/boards/boards_bundle.js b/app/assets/javascripts/boards/boards_bundle.js
index 7ea8c4f0714..080f1cc497a 100644
--- a/app/assets/javascripts/boards/boards_bundle.js
+++ b/app/assets/javascripts/boards/boards_bundle.js
@@ -89,6 +89,7 @@ $(() => {
if (list.type === 'closed') {
list.position = Infinity;
+ list.label = { description: 'Shows all closed issues. Moving an issue to this list closes it' };
}
});
diff --git a/app/assets/javascripts/boards/components/board.js b/app/assets/javascripts/boards/components/board.js
index 209585a8505..2c670e072b3 100644
--- a/app/assets/javascripts/boards/components/board.js
+++ b/app/assets/javascripts/boards/components/board.js
@@ -175,4 +175,4 @@ export default {
</div>
</div>
`,
-};
+}; \ No newline at end of file
diff --git a/app/assets/javascripts/boards/components/board_delete.js b/app/assets/javascripts/boards/components/board_delete.js
index 5caa7b4c146..bb2a739f536 100644
--- a/app/assets/javascripts/boards/components/board_delete.js
+++ b/app/assets/javascripts/boards/components/board_delete.js
@@ -29,4 +29,4 @@ export default {
</i>
</button>
`,
-};
+}; \ No newline at end of file
diff --git a/app/assets/javascripts/boards/components/board_sidebar.js b/app/assets/javascripts/boards/components/board_sidebar.js
index 3c080008244..004bac09f59 100644
--- a/app/assets/javascripts/boards/components/board_sidebar.js
+++ b/app/assets/javascripts/boards/components/board_sidebar.js
@@ -8,66 +8,64 @@ import Vue from 'vue';
require('./sidebar/remove_issue');
-(() => {
- const Store = gl.issueBoards.BoardsStore;
+const Store = gl.issueBoards.BoardsStore;
- window.gl = window.gl || {};
- window.gl.issueBoards = window.gl.issueBoards || {};
+window.gl = window.gl || {};
+window.gl.issueBoards = window.gl.issueBoards || {};
- gl.issueBoards.BoardSidebar = Vue.extend({
- props: {
- currentUser: Object
- },
- data() {
- return {
- detail: Store.detail,
- issue: {},
- list: {},
- };
- },
- computed: {
- showSidebar () {
- return Object.keys(this.issue).length;
- }
- },
- watch: {
- detail: {
- handler () {
- if (this.issue.id !== this.detail.issue.id) {
- $('.js-issue-board-sidebar', this.$el).each((i, el) => {
- $(el).data('glDropdown').clearMenu();
- });
- }
-
- this.issue = this.detail.issue;
- this.list = this.detail.list;
- },
- deep: true
- },
- issue () {
- if (this.showSidebar) {
- this.$nextTick(() => {
- $('.right-sidebar').getNiceScroll(0).doScrollTop(0, 0);
- $('.right-sidebar').getNiceScroll().resize();
+gl.issueBoards.BoardSidebar = Vue.extend({
+ props: {
+ currentUser: Object
+ },
+ data() {
+ return {
+ detail: Store.detail,
+ issue: {},
+ list: {},
+ };
+ },
+ computed: {
+ showSidebar () {
+ return Object.keys(this.issue).length;
+ }
+ },
+ watch: {
+ detail: {
+ handler () {
+ if (this.issue.id !== this.detail.issue.id) {
+ $('.js-issue-board-sidebar', this.$el).each((i, el) => {
+ $(el).data('glDropdown').clearMenu();
});
}
- }
+
+ this.issue = this.detail.issue;
+ this.list = this.detail.list;
+ },
+ deep: true
},
- methods: {
- closeSidebar () {
- this.detail.issue = {};
+ issue () {
+ if (this.showSidebar) {
+ this.$nextTick(() => {
+ $('.right-sidebar').getNiceScroll(0).doScrollTop(0, 0);
+ $('.right-sidebar').getNiceScroll().resize();
+ });
}
- },
- mounted () {
- new IssuableContext(this.currentUser);
- new MilestoneSelect();
- new gl.DueDateSelectors();
- new LabelsSelect();
- new Sidebar();
- gl.Subscription.bindAll('.subscription');
- },
- components: {
- removeBtn: gl.issueBoards.RemoveIssueBtn,
- },
- });
-})();
+ }
+ },
+ methods: {
+ closeSidebar () {
+ this.detail.issue = {};
+ }
+ },
+ mounted () {
+ new IssuableContext(this.currentUser);
+ new MilestoneSelect();
+ new gl.DueDateSelectors();
+ new LabelsSelect();
+ new Sidebar();
+ gl.Subscription.bindAll('.subscription');
+ },
+ components: {
+ removeBtn: gl.issueBoards.RemoveIssueBtn,
+ },
+});
diff --git a/app/assets/javascripts/boards/components/issue_card_inner.js b/app/assets/javascripts/boards/components/issue_card_inner.js
index f01d0c2219a..e0feb72a532 100644
--- a/app/assets/javascripts/boards/components/issue_card_inner.js
+++ b/app/assets/javascripts/boards/components/issue_card_inner.js
@@ -35,29 +35,48 @@ import eventHub from '../eventhub';
default: () => ({}),
},
},
- computed: {
- cardUrl() {
- return `${this.issueLinkBase}/${this.issue.id}`;
- },
- assigneeUrl() {
- return `${this.rootPath}${this.issue.assignee.username}`;
- },
- assigneeUrlTitle() {
- return `Assigned to ${this.issue.assignee.name}`;
- },
- avatarUrlTitle() {
- return `Avatar for ${this.issue.assignee.name}`;
- },
- issueId() {
- return `#${this.issue.id}`;
- },
- showLabelFooter() {
- return this.issue.labels.find(l => this.showLabel(l)) !== undefined;
- },
+ issueLinkBase: {
+ type: String,
+ required: true,
+ },
+ list: {
+ type: Object,
+ required: false,
+ default: () => ({}),
+ },
+ rootPath: {
+ type: String,
+ required: true,
+ },
+ updateFilters: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ computed: {
+ cardUrl() {
+ return `${this.issueLinkBase}/${this.issue.id}`;
},
- methods: {
- showLabel(label) {
- if (!this.list) return true;
+ assigneeUrl() {
+ return `${this.rootPath}${this.issue.assignee.username}`;
+ },
+ assigneeUrlTitle() {
+ return `Assigned to ${this.issue.assignee.name}`;
+ },
+ avatarUrlTitle() {
+ return `Avatar for ${this.issue.assignee.name}`;
+ },
+ issueId() {
+ return `#${this.issue.id}`;
+ },
+ showLabelFooter() {
+ return this.issue.labels.find(l => this.showLabel(l)) !== undefined;
+ },
+ },
+ methods: {
+ showLabel(label) {
+ if (!this.list) return true;
return !this.list.label || label.id !== this.list.label.id;
},
@@ -70,75 +89,74 @@ import eventHub from '../eventhub';
const labelIndex = filterPath.indexOf(param);
$(e.currentTarget).tooltip('hide');
- if (labelIndex === -1) {
- filterPath.push(param);
- } else {
- filterPath.splice(labelIndex, 1);
- }
+ if (labelIndex === -1) {
+ filterPath.push(param);
+ } else {
+ filterPath.splice(labelIndex, 1);
+ }
this.store.filter.path = filterPath.join('&');
this.store.updateFiltersUrl();
- eventHub.$emit('updateTokens');
- },
- labelStyle(label) {
- return {
- backgroundColor: label.color,
- color: label.textColor,
- };
- },
+ eventHub.$emit('updateTokens');
},
- template: `
- <div>
- <div class="card-header">
- <h4 class="card-title">
- <i
- class="fa fa-eye-slash confidential-icon"
- v-if="issue.confidential"
- aria-hidden="true"
- />
- <a
- class="js-no-trigger"
- :href="cardUrl"
- :title="issue.title">{{ issue.title }}</a>
- <span
- class="card-number"
- v-if="issue.id"
- >
- {{ issueId }}
- </span>
- </h4>
+ labelStyle(label) {
+ return {
+ backgroundColor: label.color,
+ color: label.textColor,
+ };
+ },
+ },
+ template: `
+ <div>
+ <div class="card-header">
+ <h4 class="card-title">
+ <i
+ class="fa fa-eye-slash confidential-icon"
+ v-if="issue.confidential"
+ aria-hidden="true"
+ />
<a
- class="card-assignee has-tooltip js-no-trigger"
- :href="assigneeUrl"
- :title="assigneeUrlTitle"
- v-if="issue.assignee"
- data-container="body"
+ class="js-no-trigger"
+ :href="cardUrl"
+ :title="issue.title">{{ issue.title }}</a>
+ <span
+ class="card-number"
+ v-if="issue.id"
>
- <img
- class="avatar avatar-inline s20 js-no-trigger"
- :src="issue.assignee.avatar"
- width="20"
- height="20"
- :alt="avatarUrlTitle"
- />
- </a>
- </div>
- <div class="card-footer" v-if="showLabelFooter">
- <button
- class="label color-label has-tooltip js-no-trigger"
- v-for="label in issue.labels"
- type="button"
- v-if="showLabel(label)"
- @click="filterByLabel(label, $event)"
- :style="labelStyle(label)"
- :title="label.description"
- data-container="body">
- {{ label.title }}
- </button>
- </div>
+ {{ issueId }}
+ </span>
+ </h4>
+ <a
+ class="card-assignee has-tooltip js-no-trigger"
+ :href="assigneeUrl"
+ :title="assigneeUrlTitle"
+ v-if="issue.assignee"
+ data-container="body"
+ >
+ <img
+ class="avatar avatar-inline s20 js-no-trigger"
+ :src="issue.assignee.avatar"
+ width="20"
+ height="20"
+ :alt="avatarUrlTitle"
+ />
+ </a>
+ </div>
+ <div class="card-footer" v-if="showLabelFooter">
+ <button
+ class="label color-label has-tooltip js-no-trigger"
+ v-for="label in issue.labels"
+ type="button"
+ v-if="showLabel(label)"
+ @click="filterByLabel(label, $event)"
+ :style="labelStyle(label)"
+ :title="label.description"
+ data-container="body">
+ {{ label.title }}
+ </button>
</div>
- `,
- });
-})();
+ </div>
+ `,
+}); \ No newline at end of file
diff --git a/app/assets/javascripts/boards/components/modal/empty_state.js b/app/assets/javascripts/boards/components/modal/empty_state.js
index 823319df6e7..13569df0c20 100644
--- a/app/assets/javascripts/boards/components/modal/empty_state.js
+++ b/app/assets/javascripts/boards/components/modal/empty_state.js
@@ -1,71 +1,69 @@
import Vue from 'vue';
-(() => {
- const ModalStore = gl.issueBoards.ModalStore;
+const ModalStore = gl.issueBoards.ModalStore;
- gl.issueBoards.ModalEmptyState = Vue.extend({
- mixins: [gl.issueBoards.ModalMixins],
- data() {
- return ModalStore.store;
+gl.issueBoards.ModalEmptyState = Vue.extend({
+ mixins: [gl.issueBoards.ModalMixins],
+ data() {
+ return ModalStore.store;
+ },
+ props: {
+ image: {
+ type: String,
+ required: true,
},
- props: {
- image: {
- type: String,
- required: true,
- },
- newIssuePath: {
- type: String,
- required: true,
- },
+ newIssuePath: {
+ type: String,
+ required: true,
},
- computed: {
- contents() {
- const obj = {
- title: 'You haven\'t added any issues to your project yet',
- content: `
- An issue can be a bug, a todo or a feature request that needs to be
- discussed in a project. Besides, issues are searchable and filterable.
- `,
- };
+ },
+ computed: {
+ contents() {
+ const obj = {
+ title: 'You haven\'t added any issues to your project yet',
+ content: `
+ An issue can be a bug, a todo or a feature request that needs to be
+ discussed in a project. Besides, issues are searchable and filterable.
+ `,
+ };
- if (this.activeTab === 'selected') {
- obj.title = 'You haven\'t selected any issues yet';
- obj.content = `
- Go back to <strong>Open issues</strong> and select some issues
- to add to your board.
- `;
- }
+ if (this.activeTab === 'selected') {
+ obj.title = 'You haven\'t selected any issues yet';
+ obj.content = `
+ Go back to <strong>Open issues</strong> and select some issues
+ to add to your board.
+ `;
+ }
- return obj;
- },
+ return obj;
},
- template: `
- <section class="empty-state">
- <div class="row">
- <div class="col-xs-12 col-sm-6 col-sm-push-6">
- <aside class="svg-content" v-html="image"></aside>
- </div>
- <div class="col-xs-12 col-sm-6 col-sm-pull-6">
- <div class="text-content">
- <h4>{{ contents.title }}</h4>
- <p v-html="contents.content"></p>
- <a
- :href="newIssuePath"
- class="btn btn-success btn-inverted"
- v-if="activeTab === 'all'">
- New issue
- </a>
- <button
- type="button"
- class="btn btn-default"
- @click="changeTab('all')"
- v-if="activeTab === 'selected'">
- Open issues
- </button>
- </div>
+ },
+ template: `
+ <section class="empty-state">
+ <div class="row">
+ <div class="col-xs-12 col-sm-6 col-sm-push-6">
+ <aside class="svg-content" v-html="image"></aside>
+ </div>
+ <div class="col-xs-12 col-sm-6 col-sm-pull-6">
+ <div class="text-content">
+ <h4>{{ contents.title }}</h4>
+ <p v-html="contents.content"></p>
+ <a
+ :href="newIssuePath"
+ class="btn btn-success btn-inverted"
+ v-if="activeTab === 'all'">
+ New issue
+ </a>
+ <button
+ type="button"
+ class="btn btn-default"
+ @click="changeTab('all')"
+ v-if="activeTab === 'selected'">
+ Open issues
+ </button>
</div>
</div>
- </section>
- `,
- });
-})();
+ </div>
+ </section>
+ `,
+});
diff --git a/app/assets/javascripts/boards/components/modal/footer.js b/app/assets/javascripts/boards/components/modal/footer.js
index 887ce373096..ccd270b27da 100644
--- a/app/assets/javascripts/boards/components/modal/footer.js
+++ b/app/assets/javascripts/boards/components/modal/footer.js
@@ -5,80 +5,78 @@ import Vue from 'vue';
require('./lists_dropdown');
-(() => {
- const ModalStore = gl.issueBoards.ModalStore;
+const ModalStore = gl.issueBoards.ModalStore;
- gl.issueBoards.ModalFooter = Vue.extend({
- mixins: [gl.issueBoards.ModalMixins],
- data() {
- return {
- modal: ModalStore.store,
- state: gl.issueBoards.BoardsStore.state,
- };
+gl.issueBoards.ModalFooter = Vue.extend({
+ mixins: [gl.issueBoards.ModalMixins],
+ data() {
+ return {
+ modal: ModalStore.store,
+ state: gl.issueBoards.BoardsStore.state,
+ };
+ },
+ computed: {
+ submitDisabled() {
+ return !ModalStore.selectedCount();
},
- computed: {
- submitDisabled() {
- return !ModalStore.selectedCount();
- },
- submitText() {
- const count = ModalStore.selectedCount();
+ submitText() {
+ const count = ModalStore.selectedCount();
- return `Add ${count > 0 ? count : ''} ${gl.text.pluralize('issue', count)}`;
- },
+ return `Add ${count > 0 ? count : ''} ${gl.text.pluralize('issue', count)}`;
},
- methods: {
- addIssues() {
- const list = this.modal.selectedList || this.state.lists[0];
- const selectedIssues = ModalStore.getSelectedIssues();
- const issueIds = selectedIssues.map(issue => issue.globalId);
+ },
+ methods: {
+ addIssues() {
+ const list = this.modal.selectedList || this.state.lists[0];
+ const selectedIssues = ModalStore.getSelectedIssues();
+ const issueIds = selectedIssues.map(issue => issue.globalId);
- // Post the data to the backend
- gl.boardService.bulkUpdate(issueIds, {
- add_label_ids: [list.label.id],
- }).catch(() => {
- new Flash('Failed to update issues, please try again.', 'alert');
+ // Post the data to the backend
+ gl.boardService.bulkUpdate(issueIds, {
+ add_label_ids: [list.label.id],
+ }).catch(() => {
+ new Flash('Failed to update issues, please try again.', 'alert');
- selectedIssues.forEach((issue) => {
- list.removeIssue(issue);
- list.issuesSize -= 1;
- });
- });
-
- // Add the issues on the frontend
selectedIssues.forEach((issue) => {
- list.addIssue(issue);
- list.issuesSize += 1;
+ list.removeIssue(issue);
+ list.issuesSize -= 1;
});
+ });
- this.toggleModal(false);
- },
- },
- components: {
- 'lists-dropdown': gl.issueBoards.ModalFooterListsDropdown,
+ // Add the issues on the frontend
+ selectedIssues.forEach((issue) => {
+ list.addIssue(issue);
+ list.issuesSize += 1;
+ });
+
+ this.toggleModal(false);
},
- template: `
- <footer
- class="form-actions add-issues-footer">
- <div class="pull-left">
- <button
- class="btn btn-success"
- type="button"
- :disabled="submitDisabled"
- @click="addIssues">
- {{ submitText }}
- </button>
- <span class="inline add-issues-footer-to-list">
- to list
- </span>
- <lists-dropdown></lists-dropdown>
- </div>
+ },
+ components: {
+ 'lists-dropdown': gl.issueBoards.ModalFooterListsDropdown,
+ },
+ template: `
+ <footer
+ class="form-actions add-issues-footer">
+ <div class="pull-left">
<button
- class="btn btn-default pull-right"
+ class="btn btn-success"
type="button"
- @click="toggleModal(false)">
- Cancel
+ :disabled="submitDisabled"
+ @click="addIssues">
+ {{ submitText }}
</button>
- </footer>
- `,
- });
-})();
+ <span class="inline add-issues-footer-to-list">
+ to list
+ </span>
+ <lists-dropdown></lists-dropdown>
+ </div>
+ <button
+ class="btn btn-default pull-right"
+ type="button"
+ @click="toggleModal(false)">
+ Cancel
+ </button>
+ </footer>
+ `,
+});
diff --git a/app/assets/javascripts/boards/components/modal/header.js b/app/assets/javascripts/boards/components/modal/header.js
index 116e29cd177..e2b3f9ae7e2 100644
--- a/app/assets/javascripts/boards/components/modal/header.js
+++ b/app/assets/javascripts/boards/components/modal/header.js
@@ -3,80 +3,78 @@ import modalFilters from './filters';
require('./tabs');
-(() => {
- const ModalStore = gl.issueBoards.ModalStore;
+const ModalStore = gl.issueBoards.ModalStore;
- gl.issueBoards.ModalHeader = Vue.extend({
- mixins: [gl.issueBoards.ModalMixins],
- props: {
- projectId: {
- type: Number,
- required: true,
- },
- milestonePath: {
- type: String,
- required: true,
- },
- labelPath: {
- type: String,
- required: true,
- },
+gl.issueBoards.ModalHeader = Vue.extend({
+ mixins: [gl.issueBoards.ModalMixins],
+ props: {
+ projectId: {
+ type: Number,
+ required: true,
},
- data() {
- return ModalStore.store;
+ milestonePath: {
+ type: String,
+ required: true,
},
- computed: {
- selectAllText() {
- if (ModalStore.selectedCount() !== this.issues.length || this.issues.length === 0) {
- return 'Select all';
- }
-
- return 'Deselect all';
- },
- showSearch() {
- return this.activeTab === 'all' && !this.loading && this.issuesCount > 0;
- },
+ labelPath: {
+ type: String,
+ required: true,
},
- methods: {
- toggleAll() {
- this.$refs.selectAllBtn.blur();
+ },
+ data() {
+ return ModalStore.store;
+ },
+ computed: {
+ selectAllText() {
+ if (ModalStore.selectedCount() !== this.issues.length || this.issues.length === 0) {
+ return 'Select all';
+ }
- ModalStore.toggleAll();
- },
+ return 'Deselect all';
+ },
+ showSearch() {
+ return this.activeTab === 'all' && !this.loading && this.issuesCount > 0;
},
- components: {
- 'modal-tabs': gl.issueBoards.ModalTabs,
- modalFilters,
+ },
+ methods: {
+ toggleAll() {
+ this.$refs.selectAllBtn.blur();
+
+ ModalStore.toggleAll();
},
- template: `
- <div>
- <header class="add-issues-header form-actions">
- <h2>
- Add issues
- <button
- type="button"
- class="close"
- data-dismiss="modal"
- aria-label="Close"
- @click="toggleModal(false)">
- <span aria-hidden="true">×</span>
- </button>
- </h2>
- </header>
- <modal-tabs v-if="!loading && issuesCount > 0"></modal-tabs>
- <div
- class="add-issues-search append-bottom-10"
- v-if="showSearch">
- <modal-filters :store="filter" />
+ },
+ components: {
+ 'modal-tabs': gl.issueBoards.ModalTabs,
+ modalFilters,
+ },
+ template: `
+ <div>
+ <header class="add-issues-header form-actions">
+ <h2>
+ Add issues
<button
type="button"
- class="btn btn-success btn-inverted prepend-left-10"
- ref="selectAllBtn"
- @click="toggleAll">
- {{ selectAllText }}
+ class="close"
+ data-dismiss="modal"
+ aria-label="Close"
+ @click="toggleModal(false)">
+ <span aria-hidden="true">×</span>
</button>
- </div>
+ </h2>
+ </header>
+ <modal-tabs v-if="!loading && issuesCount > 0"></modal-tabs>
+ <div
+ class="add-issues-search append-bottom-10"
+ v-if="showSearch">
+ <modal-filters :store="filter" />
+ <button
+ type="button"
+ class="btn btn-success btn-inverted prepend-left-10"
+ ref="selectAllBtn"
+ @click="toggleAll">
+ {{ selectAllText }}
+ </button>
</div>
- `,
- });
-})();
+ </div>
+ `,
+});
diff --git a/app/assets/javascripts/boards/components/modal/index.js b/app/assets/javascripts/boards/components/modal/index.js
index 91c08cde13a..fb0aac3c0e4 100644
--- a/app/assets/javascripts/boards/components/modal/index.js
+++ b/app/assets/javascripts/boards/components/modal/index.js
@@ -8,160 +8,158 @@ require('./list');
require('./footer');
require('./empty_state');
-(() => {
- const ModalStore = gl.issueBoards.ModalStore;
+const ModalStore = gl.issueBoards.ModalStore;
- gl.issueBoards.IssuesModal = Vue.extend({
- props: {
- blankStateImage: {
- type: String,
- required: true,
- },
- newIssuePath: {
- type: String,
- required: true,
- },
- issueLinkBase: {
- type: String,
- required: true,
- },
- rootPath: {
- type: String,
- required: true,
- },
- projectId: {
- type: Number,
- required: true,
- },
- milestonePath: {
- type: String,
- required: true,
- },
- labelPath: {
- type: String,
- required: true,
- },
+gl.issueBoards.IssuesModal = Vue.extend({
+ props: {
+ blankStateImage: {
+ type: String,
+ required: true,
},
- data() {
- return ModalStore.store;
+ newIssuePath: {
+ type: String,
+ required: true,
},
- watch: {
- page() {
- this.loadIssues();
- },
- showAddIssuesModal() {
- if (this.showAddIssuesModal && !this.issues.length) {
- this.loading = true;
+ issueLinkBase: {
+ type: String,
+ required: true,
+ },
+ rootPath: {
+ type: String,
+ required: true,
+ },
+ projectId: {
+ type: Number,
+ required: true,
+ },
+ milestonePath: {
+ type: String,
+ required: true,
+ },
+ labelPath: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return ModalStore.store;
+ },
+ watch: {
+ page() {
+ this.loadIssues();
+ },
+ showAddIssuesModal() {
+ if (this.showAddIssuesModal && !this.issues.length) {
+ this.loading = true;
+
+ this.loadIssues()
+ .then(() => {
+ this.loading = false;
+ });
+ } else if (!this.showAddIssuesModal) {
+ this.issues = [];
+ this.selectedIssues = [];
+ this.issuesCount = false;
+ }
+ },
+ filter: {
+ handler() {
+ if (this.$el.tagName) {
+ this.page = 1;
+ this.filterLoading = true;
- this.loadIssues()
+ this.loadIssues(true)
.then(() => {
- this.loading = false;
+ this.filterLoading = false;
});
- } else if (!this.showAddIssuesModal) {
- this.issues = [];
- this.selectedIssues = [];
- this.issuesCount = false;
}
},
- filter: {
- handler() {
- if (this.$el.tagName) {
- this.page = 1;
- this.filterLoading = true;
-
- this.loadIssues(true)
- .then(() => {
- this.filterLoading = false;
- });
- }
- },
- deep: true,
- },
+ deep: true,
},
- methods: {
- loadIssues(clearIssues = false) {
- if (!this.showAddIssuesModal) return false;
-
- return gl.boardService.getBacklog(queryData(this.filter.path, {
- page: this.page,
- per: this.perPage,
- })).then((res) => {
- const data = res.json();
-
- if (clearIssues) {
- this.issues = [];
- }
+ },
+ methods: {
+ loadIssues(clearIssues = false) {
+ if (!this.showAddIssuesModal) return false;
- data.issues.forEach((issueObj) => {
- const issue = new ListIssue(issueObj);
- const foundSelectedIssue = ModalStore.findSelectedIssue(issue);
- issue.selected = !!foundSelectedIssue;
+ return gl.boardService.getBacklog(queryData(this.filter.path, {
+ page: this.page,
+ per: this.perPage,
+ })).then((res) => {
+ const data = res.json();
- this.issues.push(issue);
- });
+ if (clearIssues) {
+ this.issues = [];
+ }
- this.loadingNewPage = false;
+ data.issues.forEach((issueObj) => {
+ const issue = new ListIssue(issueObj);
+ const foundSelectedIssue = ModalStore.findSelectedIssue(issue);
+ issue.selected = !!foundSelectedIssue;
- if (!this.issuesCount) {
- this.issuesCount = data.size;
- }
+ this.issues.push(issue);
});
- },
- },
- computed: {
- showList() {
- if (this.activeTab === 'selected') {
- return this.selectedIssues.length > 0;
- }
- return this.issuesCount > 0;
- },
- showEmptyState() {
- if (!this.loading && this.issuesCount === 0) {
- return true;
- }
+ this.loadingNewPage = false;
- return this.activeTab === 'selected' && this.selectedIssues.length === 0;
- },
+ if (!this.issuesCount) {
+ this.issuesCount = data.size;
+ }
+ });
},
- created() {
- this.page = 1;
+ },
+ computed: {
+ showList() {
+ if (this.activeTab === 'selected') {
+ return this.selectedIssues.length > 0;
+ }
+
+ return this.issuesCount > 0;
},
- components: {
- 'modal-header': gl.issueBoards.ModalHeader,
- 'modal-list': gl.issueBoards.ModalList,
- 'modal-footer': gl.issueBoards.ModalFooter,
- 'empty-state': gl.issueBoards.ModalEmptyState,
+ showEmptyState() {
+ if (!this.loading && this.issuesCount === 0) {
+ return true;
+ }
+
+ return this.activeTab === 'selected' && this.selectedIssues.length === 0;
},
- template: `
- <div
- class="add-issues-modal"
- v-if="showAddIssuesModal">
- <div class="add-issues-container">
- <modal-header
- :project-id="projectId"
- :milestone-path="milestonePath"
- :label-path="labelPath">
- </modal-header>
- <modal-list
- :image="blankStateImage"
- :issue-link-base="issueLinkBase"
- :root-path="rootPath"
- v-if="!loading && showList && !filterLoading"></modal-list>
- <empty-state
- v-if="showEmptyState"
- :image="blankStateImage"
- :new-issue-path="newIssuePath"></empty-state>
- <section
- class="add-issues-list text-center"
- v-if="loading || filterLoading">
- <div class="add-issues-list-loading">
- <i class="fa fa-spinner fa-spin"></i>
- </div>
- </section>
- <modal-footer></modal-footer>
- </div>
+ },
+ created() {
+ this.page = 1;
+ },
+ components: {
+ 'modal-header': gl.issueBoards.ModalHeader,
+ 'modal-list': gl.issueBoards.ModalList,
+ 'modal-footer': gl.issueBoards.ModalFooter,
+ 'empty-state': gl.issueBoards.ModalEmptyState,
+ },
+ template: `
+ <div
+ class="add-issues-modal"
+ v-if="showAddIssuesModal">
+ <div class="add-issues-container">
+ <modal-header
+ :project-id="projectId"
+ :milestone-path="milestonePath"
+ :label-path="labelPath">
+ </modal-header>
+ <modal-list
+ :image="blankStateImage"
+ :issue-link-base="issueLinkBase"
+ :root-path="rootPath"
+ v-if="!loading && showList && !filterLoading"></modal-list>
+ <empty-state
+ v-if="showEmptyState"
+ :image="blankStateImage"
+ :new-issue-path="newIssuePath"></empty-state>
+ <section
+ class="add-issues-list text-center"
+ v-if="loading || filterLoading">
+ <div class="add-issues-list-loading">
+ <i class="fa fa-spinner fa-spin"></i>
+ </div>
+ </section>
+ <modal-footer></modal-footer>
</div>
- `,
- });
-})();
+ </div>
+ `,
+});
diff --git a/app/assets/javascripts/boards/components/modal/list.js b/app/assets/javascripts/boards/components/modal/list.js
index aba56d4aa31..363269c0d5d 100644
--- a/app/assets/javascripts/boards/components/modal/list.js
+++ b/app/assets/javascripts/boards/components/modal/list.js
@@ -3,159 +3,157 @@
import Vue from 'vue';
-(() => {
- const ModalStore = gl.issueBoards.ModalStore;
+const ModalStore = gl.issueBoards.ModalStore;
- gl.issueBoards.ModalList = Vue.extend({
- props: {
- issueLinkBase: {
- type: String,
- required: true,
- },
- rootPath: {
- type: String,
- required: true,
- },
- image: {
- type: String,
- required: true,
- },
+gl.issueBoards.ModalList = Vue.extend({
+ props: {
+ issueLinkBase: {
+ type: String,
+ required: true,
},
- data() {
- return ModalStore.store;
+ rootPath: {
+ type: String,
+ required: true,
},
- watch: {
- activeTab() {
- if (this.activeTab === 'all') {
- ModalStore.purgeUnselectedIssues();
- }
- },
+ image: {
+ type: String,
+ required: true,
},
- computed: {
- loopIssues() {
- if (this.activeTab === 'all') {
- return this.issues;
- }
-
- return this.selectedIssues;
- },
- groupedIssues() {
- const groups = [];
- this.loopIssues.forEach((issue, i) => {
- const index = i % this.columns;
-
- if (!groups[index]) {
- groups.push([]);
- }
-
- groups[index].push(issue);
- });
+ },
+ data() {
+ return ModalStore.store;
+ },
+ watch: {
+ activeTab() {
+ if (this.activeTab === 'all') {
+ ModalStore.purgeUnselectedIssues();
+ }
+ },
+ },
+ computed: {
+ loopIssues() {
+ if (this.activeTab === 'all') {
+ return this.issues;
+ }
- return groups;
- },
+ return this.selectedIssues;
},
- methods: {
- scrollHandler() {
- const currentPage = Math.floor(this.issues.length / this.perPage);
+ groupedIssues() {
+ const groups = [];
+ this.loopIssues.forEach((issue, i) => {
+ const index = i % this.columns;
- if ((this.scrollTop() > this.scrollHeight() - 100) && !this.loadingNewPage
- && currentPage === this.page) {
- this.loadingNewPage = true;
- this.page += 1;
+ if (!groups[index]) {
+ groups.push([]);
}
- },
- toggleIssue(e, issue) {
- if (e.target.tagName !== 'A') {
- ModalStore.toggleIssue(issue);
- }
- },
- listHeight() {
- return this.$refs.list.getBoundingClientRect().height;
- },
- scrollHeight() {
- return this.$refs.list.scrollHeight;
- },
- scrollTop() {
- return this.$refs.list.scrollTop + this.listHeight();
- },
- showIssue(issue) {
- if (this.activeTab === 'all') return true;
-
- const index = ModalStore.selectedIssueIndex(issue);
- return index !== -1;
- },
- setColumnCount() {
- const breakpoint = bp.getBreakpointSize();
+ groups[index].push(issue);
+ });
- if (breakpoint === 'lg' || breakpoint === 'md') {
- this.columns = 3;
- } else if (breakpoint === 'sm') {
- this.columns = 2;
- } else {
- this.columns = 1;
- }
- },
+ return groups;
},
- mounted() {
- this.scrollHandlerWrapper = this.scrollHandler.bind(this);
- this.setColumnCountWrapper = this.setColumnCount.bind(this);
- this.setColumnCount();
+ },
+ methods: {
+ scrollHandler() {
+ const currentPage = Math.floor(this.issues.length / this.perPage);
- this.$refs.list.addEventListener('scroll', this.scrollHandlerWrapper);
- window.addEventListener('resize', this.setColumnCountWrapper);
+ if ((this.scrollTop() > this.scrollHeight() - 100) && !this.loadingNewPage
+ && currentPage === this.page) {
+ this.loadingNewPage = true;
+ this.page += 1;
+ }
+ },
+ toggleIssue(e, issue) {
+ if (e.target.tagName !== 'A') {
+ ModalStore.toggleIssue(issue);
+ }
+ },
+ listHeight() {
+ return this.$refs.list.getBoundingClientRect().height;
},
- beforeDestroy() {
- this.$refs.list.removeEventListener('scroll', this.scrollHandlerWrapper);
- window.removeEventListener('resize', this.setColumnCountWrapper);
+ scrollHeight() {
+ return this.$refs.list.scrollHeight;
},
- components: {
- 'issue-card-inner': gl.issueBoards.IssueCardInner,
+ scrollTop() {
+ return this.$refs.list.scrollTop + this.listHeight();
},
- template: `
- <section
- class="add-issues-list add-issues-list-columns"
- ref="list">
+ showIssue(issue) {
+ if (this.activeTab === 'all') return true;
+
+ const index = ModalStore.selectedIssueIndex(issue);
+
+ return index !== -1;
+ },
+ setColumnCount() {
+ const breakpoint = bp.getBreakpointSize();
+
+ if (breakpoint === 'lg' || breakpoint === 'md') {
+ this.columns = 3;
+ } else if (breakpoint === 'sm') {
+ this.columns = 2;
+ } else {
+ this.columns = 1;
+ }
+ },
+ },
+ mounted() {
+ this.scrollHandlerWrapper = this.scrollHandler.bind(this);
+ this.setColumnCountWrapper = this.setColumnCount.bind(this);
+ this.setColumnCount();
+
+ this.$refs.list.addEventListener('scroll', this.scrollHandlerWrapper);
+ window.addEventListener('resize', this.setColumnCountWrapper);
+ },
+ beforeDestroy() {
+ this.$refs.list.removeEventListener('scroll', this.scrollHandlerWrapper);
+ window.removeEventListener('resize', this.setColumnCountWrapper);
+ },
+ components: {
+ 'issue-card-inner': gl.issueBoards.IssueCardInner,
+ },
+ template: `
+ <section
+ class="add-issues-list add-issues-list-columns"
+ ref="list">
+ <div
+ class="empty-state add-issues-empty-state-filter text-center"
+ v-if="issuesCount > 0 && issues.length === 0">
<div
- class="empty-state add-issues-empty-state-filter text-center"
- v-if="issuesCount > 0 && issues.length === 0">
- <div
- class="svg-content"
- v-html="image">
- </div>
- <div class="text-content">
- <h4>
- There are no issues to show.
- </h4>
- </div>
+ class="svg-content"
+ v-html="image">
+ </div>
+ <div class="text-content">
+ <h4>
+ There are no issues to show.
+ </h4>
</div>
+ </div>
+ <div
+ v-for="group in groupedIssues"
+ class="add-issues-list-column">
<div
- v-for="group in groupedIssues"
- class="add-issues-list-column">
+ v-for="issue in group"
+ v-if="showIssue(issue)"
+ class="card-parent">
<div
- v-for="issue in group"
- v-if="showIssue(issue)"
- class="card-parent">
- <div
- class="card"
- :class="{ 'is-active': issue.selected }"
- @click="toggleIssue($event, issue)">
- <issue-card-inner
- :issue="issue"
- :issue-link-base="issueLinkBase"
- :root-path="rootPath">
- </issue-card-inner>
- <span
- :aria-label="'Issue #' + issue.id + ' selected'"
- aria-checked="true"
- v-if="issue.selected"
- class="issue-card-selected text-center">
- <i class="fa fa-check"></i>
- </span>
- </div>
+ class="card"
+ :class="{ 'is-active': issue.selected }"
+ @click="toggleIssue($event, issue)">
+ <issue-card-inner
+ :issue="issue"
+ :issue-link-base="issueLinkBase"
+ :root-path="rootPath">
+ </issue-card-inner>
+ <span
+ :aria-label="'Issue #' + issue.id + ' selected'"
+ aria-checked="true"
+ v-if="issue.selected"
+ class="issue-card-selected text-center">
+ <i class="fa fa-check"></i>
+ </span>
</div>
</div>
- </section>
- `,
- });
-})();
+ </div>
+ </section>
+ `,
+});
diff --git a/app/assets/javascripts/boards/components/modal/lists_dropdown.js b/app/assets/javascripts/boards/components/modal/lists_dropdown.js
index 9e9ed46ab8d..8cd15df90fa 100644
--- a/app/assets/javascripts/boards/components/modal/lists_dropdown.js
+++ b/app/assets/javascripts/boards/components/modal/lists_dropdown.js
@@ -1,57 +1,55 @@
import Vue from 'vue';
-(() => {
- const ModalStore = gl.issueBoards.ModalStore;
+const ModalStore = gl.issueBoards.ModalStore;
- gl.issueBoards.ModalFooterListsDropdown = Vue.extend({
- data() {
- return {
- modal: ModalStore.store,
- state: gl.issueBoards.BoardsStore.state,
- };
+gl.issueBoards.ModalFooterListsDropdown = Vue.extend({
+ data() {
+ return {
+ modal: ModalStore.store,
+ state: gl.issueBoards.BoardsStore.state,
+ };
+ },
+ computed: {
+ selected() {
+ return this.modal.selectedList || this.state.lists[0];
},
- computed: {
- selected() {
- return this.modal.selectedList || this.state.lists[0];
- },
- },
- destroyed() {
- this.modal.selectedList = null;
- },
- template: `
- <div class="dropdown inline">
- <button
- class="dropdown-menu-toggle"
- type="button"
- data-toggle="dropdown"
- aria-expanded="false">
- <span
- class="dropdown-label-box"
- :style="{ backgroundColor: selected.label.color }">
- </span>
- {{ selected.title }}
- <i class="fa fa-chevron-down"></i>
- </button>
- <div class="dropdown-menu dropdown-menu-selectable dropdown-menu-drop-up">
- <ul>
- <li
- v-for="list in state.lists"
- v-if="list.type == 'label'">
- <a
- href="#"
- role="button"
- :class="{ 'is-active': list.id == selected.id }"
- @click.prevent="modal.selectedList = list">
- <span
- class="dropdown-label-box"
- :style="{ backgroundColor: list.label.color }">
- </span>
- {{ list.title }}
- </a>
- </li>
- </ul>
- </div>
+ },
+ destroyed() {
+ this.modal.selectedList = null;
+ },
+ template: `
+ <div class="dropdown inline">
+ <button
+ class="dropdown-menu-toggle"
+ type="button"
+ data-toggle="dropdown"
+ aria-expanded="false">
+ <span
+ class="dropdown-label-box"
+ :style="{ backgroundColor: selected.label.color }">
+ </span>
+ {{ selected.title }}
+ <i class="fa fa-chevron-down"></i>
+ </button>
+ <div class="dropdown-menu dropdown-menu-selectable dropdown-menu-drop-up">
+ <ul>
+ <li
+ v-for="list in state.lists"
+ v-if="list.type == 'label'">
+ <a
+ href="#"
+ role="button"
+ :class="{ 'is-active': list.id == selected.id }"
+ @click.prevent="modal.selectedList = list">
+ <span
+ class="dropdown-label-box"
+ :style="{ backgroundColor: list.label.color }">
+ </span>
+ {{ list.title }}
+ </a>
+ </li>
+ </ul>
</div>
- `,
- });
-})();
+ </div>
+ `,
+});
diff --git a/app/assets/javascripts/boards/components/modal/tabs.js b/app/assets/javascripts/boards/components/modal/tabs.js
index 23cb1b13d11..3e5d08e3d75 100644
--- a/app/assets/javascripts/boards/components/modal/tabs.js
+++ b/app/assets/javascripts/boards/components/modal/tabs.js
@@ -1,48 +1,46 @@
import Vue from 'vue';
-(() => {
- const ModalStore = gl.issueBoards.ModalStore;
+const ModalStore = gl.issueBoards.ModalStore;
- gl.issueBoards.ModalTabs = Vue.extend({
- mixins: [gl.issueBoards.ModalMixins],
- data() {
- return ModalStore.store;
+gl.issueBoards.ModalTabs = Vue.extend({
+ mixins: [gl.issueBoards.ModalMixins],
+ data() {
+ return ModalStore.store;
+ },
+ computed: {
+ selectedCount() {
+ return ModalStore.selectedCount();
},
- computed: {
- selectedCount() {
- return ModalStore.selectedCount();
- },
- },
- destroyed() {
- this.activeTab = 'all';
- },
- template: `
- <div class="top-area prepend-top-10 append-bottom-10">
- <ul class="nav-links issues-state-filters">
- <li :class="{ 'active': activeTab == 'all' }">
- <a
- href="#"
- role="button"
- @click.prevent="changeTab('all')">
- Open issues
- <span class="badge">
- {{ issuesCount }}
- </span>
- </a>
- </li>
- <li :class="{ 'active': activeTab == 'selected' }">
- <a
- href="#"
- role="button"
- @click.prevent="changeTab('selected')">
- Selected issues
- <span class="badge">
- {{ selectedCount }}
- </span>
- </a>
- </li>
- </ul>
- </div>
- `,
- });
-})();
+ },
+ destroyed() {
+ this.activeTab = 'all';
+ },
+ template: `
+ <div class="top-area prepend-top-10 append-bottom-10">
+ <ul class="nav-links issues-state-filters">
+ <li :class="{ 'active': activeTab == 'all' }">
+ <a
+ href="#"
+ role="button"
+ @click.prevent="changeTab('all')">
+ Open issues
+ <span class="badge">
+ {{ issuesCount }}
+ </span>
+ </a>
+ </li>
+ <li :class="{ 'active': activeTab == 'selected' }">
+ <a
+ href="#"
+ role="button"
+ @click.prevent="changeTab('selected')">
+ Selected issues
+ <span class="badge">
+ {{ selectedCount }}
+ </span>
+ </a>
+ </li>
+ </ul>
+ </div>
+ `,
+});
diff --git a/app/assets/javascripts/boards/components/new_list_dropdown.js b/app/assets/javascripts/boards/components/new_list_dropdown.js
index 556826a9148..22f20305624 100644
--- a/app/assets/javascripts/boards/components/new_list_dropdown.js
+++ b/app/assets/javascripts/boards/components/new_list_dropdown.js
@@ -1,76 +1,74 @@
/* eslint-disable comma-dangle, func-names, no-new, space-before-function-paren, one-var */
-(() => {
- window.gl = window.gl || {};
- window.gl.issueBoards = window.gl.issueBoards || {};
+window.gl = window.gl || {};
+window.gl.issueBoards = window.gl.issueBoards || {};
- const Store = gl.issueBoards.BoardsStore;
+const Store = gl.issueBoards.BoardsStore;
- $(document).off('created.label').on('created.label', (e, label) => {
- Store.new({
+$(document).off('created.label').on('created.label', (e, label) => {
+ Store.new({
+ title: label.title,
+ position: Store.state.lists.length - 2,
+ list_type: 'label',
+ label: {
+ id: label.id,
title: label.title,
- position: Store.state.lists.length - 2,
- list_type: 'label',
- label: {
- id: label.id,
- title: label.title,
- color: label.color
- }
- });
+ color: label.color
+ }
});
+});
- gl.issueBoards.newListDropdownInit = () => {
- $('.js-new-board-list').each(function () {
- const $this = $(this);
- new gl.CreateLabelDropdown($this.closest('.dropdown').find('.dropdown-new-label'), $this.data('namespace-path'), $this.data('project-path'));
+gl.issueBoards.newListDropdownInit = () => {
+ $('.js-new-board-list').each(function () {
+ const $this = $(this);
+ new gl.CreateLabelDropdown($this.closest('.dropdown').find('.dropdown-new-label'), $this.data('namespace-path'), $this.data('project-path'));
- $this.glDropdown({
- data(term, callback) {
- $.get($this.attr('data-labels'))
- .then((resp) => {
- callback(resp);
- });
- },
- renderRow (label) {
- const active = Store.findList('title', label.title);
- const $li = $('<li />');
- const $a = $('<a />', {
- class: (active ? `is-active js-board-list-${active.id}` : ''),
- text: label.title,
- href: '#'
- });
- const $labelColor = $('<span />', {
- class: 'dropdown-label-box',
- style: `background-color: ${label.color}`
+ $this.glDropdown({
+ data(term, callback) {
+ $.get($this.attr('data-labels'))
+ .then((resp) => {
+ callback(resp);
});
+ },
+ renderRow (label) {
+ const active = Store.findList('title', label.title);
+ const $li = $('<li />');
+ const $a = $('<a />', {
+ class: (active ? `is-active js-board-list-${active.id}` : ''),
+ text: label.title,
+ href: '#'
+ });
+ const $labelColor = $('<span />', {
+ class: 'dropdown-label-box',
+ style: `background-color: ${label.color}`
+ });
- return $li.append($a.prepend($labelColor));
- },
- search: {
- fields: ['title']
- },
- filterable: true,
- selectable: true,
- multiSelect: true,
- clicked (label, $el, e) {
- e.preventDefault();
+ return $li.append($a.prepend($labelColor));
+ },
+ search: {
+ fields: ['title']
+ },
+ filterable: true,
+ selectable: true,
+ multiSelect: true,
+ clicked (label, $el, e) {
+ e.preventDefault();
- if (!Store.findList('title', label.title)) {
- Store.new({
+ if (!Store.findList('title', label.title)) {
+ Store.new({
+ title: label.title,
+ position: Store.state.lists.length - 2,
+ list_type: 'label',
+ label: {
+ id: label.id,
title: label.title,
- position: Store.state.lists.length - 2,
- list_type: 'label',
- label: {
- id: label.id,
- title: label.title,
- color: label.color
- }
- });
+ color: label.color
+ }
+ });
- Store.state.lists = _.sortBy(Store.state.lists, 'position');
- }
+ Store.state.lists = _.sortBy(Store.state.lists, 'position');
}
- });
+ }
});
- };
-})();
+ });
+};
diff --git a/app/assets/javascripts/boards/components/sidebar/remove_issue.js b/app/assets/javascripts/boards/components/sidebar/remove_issue.js
index 772ea4c5565..5597f128b80 100644
--- a/app/assets/javascripts/boards/components/sidebar/remove_issue.js
+++ b/app/assets/javascripts/boards/components/sidebar/remove_issue.js
@@ -3,59 +3,57 @@
import Vue from 'vue';
-(() => {
- const Store = gl.issueBoards.BoardsStore;
-
- window.gl = window.gl || {};
- window.gl.issueBoards = window.gl.issueBoards || {};
-
- gl.issueBoards.RemoveIssueBtn = Vue.extend({
- props: {
- issue: {
- type: Object,
- required: true,
- },
- list: {
- type: Object,
- required: true,
- },
+const Store = gl.issueBoards.BoardsStore;
+
+window.gl = window.gl || {};
+window.gl.issueBoards = window.gl.issueBoards || {};
+
+gl.issueBoards.RemoveIssueBtn = Vue.extend({
+ props: {
+ issue: {
+ type: Object,
+ required: true,
},
- methods: {
- removeIssue() {
- const issue = this.issue;
- const lists = issue.getLists();
- const labelIds = lists.map(list => list.label.id);
-
- // Post the remove data
- gl.boardService.bulkUpdate([issue.globalId], {
- remove_label_ids: labelIds,
- }).catch(() => {
- new Flash('Failed to remove issue from board, please try again.', 'alert');
-
- lists.forEach((list) => {
- list.addIssue(issue);
- });
- });
+ list: {
+ type: Object,
+ required: true,
+ },
+ },
+ methods: {
+ removeIssue() {
+ const issue = this.issue;
+ const lists = issue.getLists();
+ const labelIds = lists.map(list => list.label.id);
+
+ // Post the remove data
+ gl.boardService.bulkUpdate([issue.globalId], {
+ remove_label_ids: labelIds,
+ }).catch(() => {
+ new Flash('Failed to remove issue from board, please try again.', 'alert');
- // Remove from the frontend store
lists.forEach((list) => {
- list.removeIssue(issue);
+ list.addIssue(issue);
});
+ });
+
+ // Remove from the frontend store
+ lists.forEach((list) => {
+ list.removeIssue(issue);
+ });
- Store.detail.issue = {};
- },
+ Store.detail.issue = {};
},
- template: `
- <div
- class="block list"
- v-if="list.type !== 'closed'">
- <button
- class="btn btn-default btn-block"
- type="button"
- @click="removeIssue">
- Remove from board
- </button>
- </div>
- `,
- });
-})();
+ },
+ template: `
+ <div
+ class="block list"
+ v-if="list.type !== 'closed'">
+ <button
+ class="btn btn-default btn-block"
+ type="button"
+ @click="removeIssue">
+ Remove from board
+ </button>
+ </div>
+ `,
+});
diff --git a/app/assets/javascripts/boards/mixins/modal_mixins.js b/app/assets/javascripts/boards/mixins/modal_mixins.js
index d378b7d4baf..2b0a1aaa89f 100644
--- a/app/assets/javascripts/boards/mixins/modal_mixins.js
+++ b/app/assets/javascripts/boards/mixins/modal_mixins.js
@@ -1,14 +1,12 @@
-(() => {
- const ModalStore = gl.issueBoards.ModalStore;
+const ModalStore = gl.issueBoards.ModalStore;
- gl.issueBoards.ModalMixins = {
- methods: {
- toggleModal(toggle) {
- ModalStore.store.showAddIssuesModal = toggle;
- },
- changeTab(tab) {
- ModalStore.store.activeTab = tab;
- },
+gl.issueBoards.ModalMixins = {
+ methods: {
+ toggleModal(toggle) {
+ ModalStore.store.showAddIssuesModal = toggle;
},
- };
-})();
+ changeTab(tab) {
+ ModalStore.store.activeTab = tab;
+ },
+ },
+};
diff --git a/app/assets/javascripts/boards/mixins/sortable_default_options.js b/app/assets/javascripts/boards/mixins/sortable_default_options.js
index b6c6d17274f..38a0eb12f92 100644
--- a/app/assets/javascripts/boards/mixins/sortable_default_options.js
+++ b/app/assets/javascripts/boards/mixins/sortable_default_options.js
@@ -1,39 +1,37 @@
/* eslint-disable no-unused-vars, no-mixed-operators, comma-dangle */
/* global DocumentTouch */
-((w) => {
- window.gl = window.gl || {};
- window.gl.issueBoards = window.gl.issueBoards || {};
+window.gl = window.gl || {};
+window.gl.issueBoards = window.gl.issueBoards || {};
- gl.issueBoards.onStart = () => {
- $('.has-tooltip').tooltip('hide')
- .tooltip('disable');
- document.body.classList.add('is-dragging');
- };
-
- gl.issueBoards.onEnd = () => {
- $('.has-tooltip').tooltip('enable');
- document.body.classList.remove('is-dragging');
- };
+gl.issueBoards.onStart = () => {
+ $('.has-tooltip').tooltip('hide')
+ .tooltip('disable');
+ document.body.classList.add('is-dragging');
+};
- gl.issueBoards.touchEnabled = ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch;
+gl.issueBoards.onEnd = () => {
+ $('.has-tooltip').tooltip('enable');
+ document.body.classList.remove('is-dragging');
+};
- gl.issueBoards.getBoardSortableDefaultOptions = (obj) => {
- const defaultSortOptions = {
- animation: 200,
- forceFallback: true,
- fallbackClass: 'is-dragging',
- fallbackOnBody: true,
- ghostClass: 'is-ghost',
- filter: '.board-delete, .btn',
- delay: gl.issueBoards.touchEnabled ? 100 : 0,
- scrollSensitivity: gl.issueBoards.touchEnabled ? 60 : 100,
- scrollSpeed: 20,
- onStart: gl.issueBoards.onStart,
- onEnd: gl.issueBoards.onEnd
- };
+gl.issueBoards.touchEnabled = ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch;
- Object.keys(obj).forEach((key) => { defaultSortOptions[key] = obj[key]; });
- return defaultSortOptions;
+gl.issueBoards.getBoardSortableDefaultOptions = (obj) => {
+ const defaultSortOptions = {
+ animation: 200,
+ forceFallback: true,
+ fallbackClass: 'is-dragging',
+ fallbackOnBody: true,
+ ghostClass: 'is-ghost',
+ filter: '.board-delete, .btn',
+ delay: gl.issueBoards.touchEnabled ? 100 : 0,
+ scrollSensitivity: gl.issueBoards.touchEnabled ? 60 : 100,
+ scrollSpeed: 20,
+ onStart: gl.issueBoards.onStart,
+ onEnd: gl.issueBoards.onEnd
};
-})(window);
+
+ Object.keys(obj).forEach((key) => { defaultSortOptions[key] = obj[key]; });
+ return defaultSortOptions;
+};
diff --git a/app/assets/javascripts/boards/stores/boards_store.js b/app/assets/javascripts/boards/stores/boards_store.js
index 8d89902c58d..dd6a467f0fa 100644
--- a/app/assets/javascripts/boards/stores/boards_store.js
+++ b/app/assets/javascripts/boards/stores/boards_store.js
@@ -3,9 +3,8 @@
import Cookies from 'js-cookie';
-(() => {
- window.gl = window.gl || {};
- window.gl.issueBoards = window.gl.issueBoards || {};
+window.gl = window.gl || {};
+window.gl.issueBoards = window.gl.issueBoards || {};
gl.issueBoards.BoardsStore = {
disabled: false,
@@ -31,100 +30,99 @@ import Cookies from 'js-cookie';
const list = new List(listObj);
this.state.lists.push(list);
- return list;
- },
- new (listObj) {
- const list = this.addList(listObj);
-
- list
- .save()
- .then(() => {
- this.state.lists = _.sortBy(this.state.lists, 'position');
- });
- this.removeBlankState();
- },
- updateNewListDropdown (listId) {
- $(`.js-board-list-${listId}`).removeClass('is-active');
- },
- shouldAddBlankState () {
- // Decide whether to add the blank state
- return !(this.state.lists.filter(list => list.type !== 'closed')[0]);
- },
- addBlankState () {
- if (!this.shouldAddBlankState() || this.welcomeIsHidden() || this.disabled) return;
+ return list;
+ },
+ new (listObj) {
+ const list = this.addList(listObj);
- this.addList({
- id: 'blank',
- list_type: 'blank',
- title: 'Welcome to your Issue Board!',
- position: 0
+ list
+ .save()
+ .then(() => {
+ this.state.lists = _.sortBy(this.state.lists, 'position');
});
+ this.removeBlankState();
+ },
+ updateNewListDropdown (listId) {
+ $(`.js-board-list-${listId}`).removeClass('is-active');
+ },
+ shouldAddBlankState () {
+ // Decide whether to add the blank state
+ return !(this.state.lists.filter(list => list.type !== 'closed')[0]);
+ },
+ addBlankState () {
+ if (!this.shouldAddBlankState() || this.welcomeIsHidden() || this.disabled) return;
- this.state.lists = _.sortBy(this.state.lists, 'position');
- },
- removeBlankState () {
- this.removeList('blank');
-
- Cookies.set('issue_board_welcome_hidden', 'true', {
- expires: 365 * 10,
- path: ''
- });
- },
- welcomeIsHidden () {
- return Cookies.get('issue_board_welcome_hidden') === 'true';
- },
- removeList (id, type = 'blank') {
- const list = this.findList('id', id, type);
+ this.addList({
+ id: 'blank',
+ list_type: 'blank',
+ title: 'Welcome to your Issue Board!',
+ position: 0
+ });
- if (!list) return;
+ this.state.lists = _.sortBy(this.state.lists, 'position');
+ },
+ removeBlankState () {
+ this.removeList('blank');
- this.state.lists = this.state.lists.filter(list => list.id !== id);
- },
- moveList (listFrom, orderLists) {
- orderLists.forEach((id, i) => {
- const list = this.findList('id', parseInt(id, 10));
+ Cookies.set('issue_board_welcome_hidden', 'true', {
+ expires: 365 * 10,
+ path: ''
+ });
+ },
+ welcomeIsHidden () {
+ return Cookies.get('issue_board_welcome_hidden') === 'true';
+ },
+ removeList (id, type = 'blank') {
+ const list = this.findList('id', id, type);
- list.position = i;
- });
- listFrom.update();
- },
- moveIssueToList (listFrom, listTo, issue, newIndex) {
- const issueTo = listTo.findIssue(issue.id);
- const issueLists = issue.getLists();
- const listLabels = issueLists.map(listIssue => listIssue.label);
+ if (!list) return;
- if (!issueTo) {
- // Add to new lists issues if it doesn't already exist
- listTo.addIssue(issue, listFrom, newIndex);
- } else {
- listTo.updateIssueLabel(issue, listFrom);
- issueTo.removeLabel(listFrom.label);
- }
+ this.state.lists = this.state.lists.filter(list => list.id !== id);
+ },
+ moveList (listFrom, orderLists) {
+ orderLists.forEach((id, i) => {
+ const list = this.findList('id', parseInt(id, 10));
- if (listTo.type === 'closed') {
- issueLists.forEach((list) => {
- list.removeIssue(issue);
- });
- issue.removeLabels(listLabels);
- } else {
- listFrom.removeIssue(issue);
- }
- },
- moveIssueInList (list, issue, oldIndex, newIndex, idArray) {
- const beforeId = parseInt(idArray[newIndex - 1], 10) || null;
- const afterId = parseInt(idArray[newIndex + 1], 10) || null;
+ list.position = i;
+ });
+ listFrom.update();
+ },
+ moveIssueToList (listFrom, listTo, issue, newIndex) {
+ const issueTo = listTo.findIssue(issue.id);
+ const issueLists = issue.getLists();
+ const listLabels = issueLists.map(listIssue => listIssue.label);
- list.moveIssue(issue, oldIndex, newIndex, beforeId, afterId);
- },
- findList (key, val, type = 'label') {
- return this.state.lists.filter((list) => {
- const byType = type ? list['type'] === type : true;
+ if (!issueTo) {
+ // Add to new lists issues if it doesn't already exist
+ listTo.addIssue(issue, listFrom, newIndex);
+ } else {
+ listTo.updateIssueLabel(issue, listFrom);
+ issueTo.removeLabel(listFrom.label);
+ }
- return list[key] === val && byType;
- })[0];
- },
- updateFiltersUrl () {
- history.pushState(null, null, `?${this.filter.path}`);
+ if (listTo.type === 'closed') {
+ issueLists.forEach((list) => {
+ list.removeIssue(issue);
+ });
+ issue.removeLabels(listLabels);
+ } else {
+ listFrom.removeIssue(issue);
}
- };
-})();
+ },
+ moveIssueInList (list, issue, oldIndex, newIndex, idArray) {
+ const beforeId = parseInt(idArray[newIndex - 1], 10) || null;
+ const afterId = parseInt(idArray[newIndex + 1], 10) || null;
+
+ list.moveIssue(issue, oldIndex, newIndex, beforeId, afterId);
+ },
+ findList (key, val, type = 'label') {
+ return this.state.lists.filter((list) => {
+ const byType = type ? list['type'] === type : true;
+
+ return list[key] === val && byType;
+ })[0];
+ },
+ updateFiltersUrl () {
+ history.pushState(null, null, `?${this.filter.path}`);
+ }
+}; \ No newline at end of file
diff --git a/app/assets/javascripts/boards/stores/modal_store.js b/app/assets/javascripts/boards/stores/modal_store.js
index 9b009483a3c..4fdc925c825 100644
--- a/app/assets/javascripts/boards/stores/modal_store.js
+++ b/app/assets/javascripts/boards/stores/modal_store.js
@@ -1,100 +1,98 @@
-(() => {
- window.gl = window.gl || {};
- window.gl.issueBoards = window.gl.issueBoards || {};
-
- class ModalStore {
- constructor() {
- this.store = {
- columns: 3,
- issues: [],
- issuesCount: false,
- selectedIssues: [],
- showAddIssuesModal: false,
- activeTab: 'all',
- selectedList: null,
- searchTerm: '',
- loading: false,
- loadingNewPage: false,
- filterLoading: false,
- page: 1,
- perPage: 50,
- filter: {
- path: '',
- },
- };
- }
+window.gl = window.gl || {};
+window.gl.issueBoards = window.gl.issueBoards || {};
+
+class ModalStore {
+ constructor() {
+ this.store = {
+ columns: 3,
+ issues: [],
+ issuesCount: false,
+ selectedIssues: [],
+ showAddIssuesModal: false,
+ activeTab: 'all',
+ selectedList: null,
+ searchTerm: '',
+ loading: false,
+ loadingNewPage: false,
+ filterLoading: false,
+ page: 1,
+ perPage: 50,
+ filter: {
+ path: '',
+ },
+ };
+ }
- selectedCount() {
- return this.getSelectedIssues().length;
- }
+ selectedCount() {
+ return this.getSelectedIssues().length;
+ }
- toggleIssue(issueObj) {
- const issue = issueObj;
- const selected = issue.selected;
+ toggleIssue(issueObj) {
+ const issue = issueObj;
+ const selected = issue.selected;
- issue.selected = !selected;
+ issue.selected = !selected;
- if (!selected) {
- this.addSelectedIssue(issue);
- } else {
- this.removeSelectedIssue(issue);
- }
+ if (!selected) {
+ this.addSelectedIssue(issue);
+ } else {
+ this.removeSelectedIssue(issue);
}
+ }
- toggleAll() {
- const select = this.selectedCount() !== this.store.issues.length;
+ toggleAll() {
+ const select = this.selectedCount() !== this.store.issues.length;
- this.store.issues.forEach((issue) => {
- const issueUpdate = issue;
+ this.store.issues.forEach((issue) => {
+ const issueUpdate = issue;
- if (issueUpdate.selected !== select) {
- issueUpdate.selected = select;
+ if (issueUpdate.selected !== select) {
+ issueUpdate.selected = select;
- if (select) {
- this.addSelectedIssue(issue);
- } else {
- this.removeSelectedIssue(issue);
- }
+ if (select) {
+ this.addSelectedIssue(issue);
+ } else {
+ this.removeSelectedIssue(issue);
}
- });
- }
+ }
+ });
+ }
- getSelectedIssues() {
- return this.store.selectedIssues.filter(issue => issue.selected);
- }
+ getSelectedIssues() {
+ return this.store.selectedIssues.filter(issue => issue.selected);
+ }
- addSelectedIssue(issue) {
- const index = this.selectedIssueIndex(issue);
+ addSelectedIssue(issue) {
+ const index = this.selectedIssueIndex(issue);
- if (index === -1) {
- this.store.selectedIssues.push(issue);
- }
+ if (index === -1) {
+ this.store.selectedIssues.push(issue);
}
+ }
- removeSelectedIssue(issue, forcePurge = false) {
- if (this.store.activeTab === 'all' || forcePurge) {
- this.store.selectedIssues = this.store.selectedIssues
- .filter(fIssue => fIssue.id !== issue.id);
- }
+ removeSelectedIssue(issue, forcePurge = false) {
+ if (this.store.activeTab === 'all' || forcePurge) {
+ this.store.selectedIssues = this.store.selectedIssues
+ .filter(fIssue => fIssue.id !== issue.id);
}
+ }
- purgeUnselectedIssues() {
- this.store.selectedIssues.forEach((issue) => {
- if (!issue.selected) {
- this.removeSelectedIssue(issue, true);
- }
- });
- }
+ purgeUnselectedIssues() {
+ this.store.selectedIssues.forEach((issue) => {
+ if (!issue.selected) {
+ this.removeSelectedIssue(issue, true);
+ }
+ });
+ }
- selectedIssueIndex(issue) {
- return this.store.selectedIssues.indexOf(issue);
- }
+ selectedIssueIndex(issue) {
+ return this.store.selectedIssues.indexOf(issue);
+ }
- findSelectedIssue(issue) {
- return this.store.selectedIssues
- .filter(filteredIssue => filteredIssue.id === issue.id)[0];
- }
+ findSelectedIssue(issue) {
+ return this.store.selectedIssues
+ .filter(filteredIssue => filteredIssue.id === issue.id)[0];
}
+}
- gl.issueBoards.ModalStore = new ModalStore();
-})();
+gl.issueBoards.ModalStore = new ModalStore();
diff --git a/app/assets/javascripts/build.js b/app/assets/javascripts/build.js
index 9e7df9ad350..0aad95c2fe3 100644
--- a/app/assets/javascripts/build.js
+++ b/app/assets/javascripts/build.js
@@ -90,6 +90,8 @@ window.Build = (function () {
success: ((log) => {
const $buildContainer = $('.js-build-output');
+ gl.utils.setCiStatusFavicon(`${this.pageUrl}/status.json`);
+
if (log.state) {
this.state = log.state;
}
diff --git a/app/assets/javascripts/commit/pipelines/pipelines_table.js b/app/assets/javascripts/commit/pipelines/pipelines_table.js
index da9707549f9..1d16c64e07e 100644
--- a/app/assets/javascripts/commit/pipelines/pipelines_table.js
+++ b/app/assets/javascripts/commit/pipelines/pipelines_table.js
@@ -4,8 +4,8 @@ import PipelinesTableComponent from '../../vue_shared/components/pipelines_table
import PipelinesService from '../../vue_pipelines_index/services/pipelines_service';
import PipelineStore from '../../vue_pipelines_index/stores/pipelines_store';
import eventHub from '../../vue_pipelines_index/event_hub';
-import EmptyState from '../../vue_pipelines_index/components/empty_state';
-import ErrorState from '../../vue_pipelines_index/components/error_state';
+import EmptyState from '../../vue_pipelines_index/components/empty_state.vue';
+import ErrorState from '../../vue_pipelines_index/components/error_state.vue';
import '../../lib/utils/common_utils';
import '../../vue_shared/vue_resource_interceptor';
import Poll from '../../lib/utils/poll';
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_code_component.js b/app/assets/javascripts/cycle_analytics/components/stage_code_component.js
index 3f419a96ff9..80bd2df6f42 100644
--- a/app/assets/javascripts/cycle_analytics/components/stage_code_component.js
+++ b/app/assets/javascripts/cycle_analytics/components/stage_code_component.js
@@ -2,46 +2,45 @@
import Vue from 'vue';
-((global) => {
- global.cycleAnalytics = global.cycleAnalytics || {};
+const global = window.gl || (window.gl = {});
+global.cycleAnalytics = global.cycleAnalytics || {};
- global.cycleAnalytics.StageCodeComponent = Vue.extend({
- props: {
- items: Array,
- stage: Object,
- },
- template: `
- <div>
- <div class="events-description">
- {{ stage.description }}
- <limit-warning :count="items.length" />
- </div>
- <ul class="stage-event-list">
- <li v-for="mergeRequest in items" class="stage-event-item">
- <div class="item-details">
- <img class="avatar" :src="mergeRequest.author.avatarUrl">
- <h5 class="item-title merge-merquest-title">
- <a :href="mergeRequest.url">
- {{ mergeRequest.title }}
- </a>
- </h5>
- <a :href="mergeRequest.url" class="issue-link">!{{ mergeRequest.iid }}</a>
- &middot;
- <span>
- Opened
- <a :href="mergeRequest.url" class="issue-date">{{ mergeRequest.createdAt }}</a>
- </span>
- <span>
- by
- <a :href="mergeRequest.author.webUrl" class="issue-author-link">{{ mergeRequest.author.name }}</a>
- </span>
- </div>
- <div class="item-time">
- <total-time :time="mergeRequest.totalTime"></total-time>
- </div>
- </li>
- </ul>
+global.cycleAnalytics.StageCodeComponent = Vue.extend({
+ props: {
+ items: Array,
+ stage: Object,
+ },
+ template: `
+ <div>
+ <div class="events-description">
+ {{ stage.description }}
+ <limit-warning :count="items.length" />
</div>
- `,
- });
-})(window.gl || (window.gl = {}));
+ <ul class="stage-event-list">
+ <li v-for="mergeRequest in items" class="stage-event-item">
+ <div class="item-details">
+ <img class="avatar" :src="mergeRequest.author.avatarUrl">
+ <h5 class="item-title merge-merquest-title">
+ <a :href="mergeRequest.url">
+ {{ mergeRequest.title }}
+ </a>
+ </h5>
+ <a :href="mergeRequest.url" class="issue-link">!{{ mergeRequest.iid }}</a>
+ &middot;
+ <span>
+ Opened
+ <a :href="mergeRequest.url" class="issue-date">{{ mergeRequest.createdAt }}</a>
+ </span>
+ <span>
+ by
+ <a :href="mergeRequest.author.webUrl" class="issue-author-link">{{ mergeRequest.author.name }}</a>
+ </span>
+ </div>
+ <div class="item-time">
+ <total-time :time="mergeRequest.totalTime"></total-time>
+ </div>
+ </li>
+ </ul>
+ </div>
+ `,
+});
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_issue_component.js b/app/assets/javascripts/cycle_analytics/components/stage_issue_component.js
index 7ffa38edd9e..20a43798fbe 100644
--- a/app/assets/javascripts/cycle_analytics/components/stage_issue_component.js
+++ b/app/assets/javascripts/cycle_analytics/components/stage_issue_component.js
@@ -2,48 +2,47 @@
import Vue from 'vue';
-((global) => {
- global.cycleAnalytics = global.cycleAnalytics || {};
+const global = window.gl || (window.gl = {});
+global.cycleAnalytics = global.cycleAnalytics || {};
- global.cycleAnalytics.StageIssueComponent = Vue.extend({
- props: {
- items: Array,
- stage: Object,
- },
- template: `
- <div>
- <div class="events-description">
- {{ stage.description }}
- <limit-warning :count="items.length" />
- </div>
- <ul class="stage-event-list">
- <li v-for="issue in items" class="stage-event-item">
- <div class="item-details">
- <img class="avatar" :src="issue.author.avatarUrl">
- <h5 class="item-title issue-title">
- <a class="issue-title" :href="issue.url">
- {{ issue.title }}
- </a>
- </h5>
- <a :href="issue.url" class="issue-link">#{{ issue.iid }}</a>
- &middot;
- <span>
- Opened
- <a :href="issue.url" class="issue-date">{{ issue.createdAt }}</a>
- </span>
- <span>
- by
- <a :href="issue.author.webUrl" class="issue-author-link">
- {{ issue.author.name }}
- </a>
- </span>
- </div>
- <div class="item-time">
- <total-time :time="issue.totalTime"></total-time>
- </div>
- </li>
- </ul>
+global.cycleAnalytics.StageIssueComponent = Vue.extend({
+ props: {
+ items: Array,
+ stage: Object,
+ },
+ template: `
+ <div>
+ <div class="events-description">
+ {{ stage.description }}
+ <limit-warning :count="items.length" />
</div>
- `,
- });
-})(window.gl || (window.gl = {}));
+ <ul class="stage-event-list">
+ <li v-for="issue in items" class="stage-event-item">
+ <div class="item-details">
+ <img class="avatar" :src="issue.author.avatarUrl">
+ <h5 class="item-title issue-title">
+ <a class="issue-title" :href="issue.url">
+ {{ issue.title }}
+ </a>
+ </h5>
+ <a :href="issue.url" class="issue-link">#{{ issue.iid }}</a>
+ &middot;
+ <span>
+ Opened
+ <a :href="issue.url" class="issue-date">{{ issue.createdAt }}</a>
+ </span>
+ <span>
+ by
+ <a :href="issue.author.webUrl" class="issue-author-link">
+ {{ issue.author.name }}
+ </a>
+ </span>
+ </div>
+ <div class="item-time">
+ <total-time :time="issue.totalTime"></total-time>
+ </div>
+ </li>
+ </ul>
+ </div>
+ `,
+});
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_plan_component.js b/app/assets/javascripts/cycle_analytics/components/stage_plan_component.js
index d736c8b0c28..f33cac3da82 100644
--- a/app/assets/javascripts/cycle_analytics/components/stage_plan_component.js
+++ b/app/assets/javascripts/cycle_analytics/components/stage_plan_component.js
@@ -2,50 +2,49 @@
import Vue from 'vue';
import iconCommit from '../svg/icon_commit.svg';
-((global) => {
- global.cycleAnalytics = global.cycleAnalytics || {};
+const global = window.gl || (window.gl = {});
+global.cycleAnalytics = global.cycleAnalytics || {};
- global.cycleAnalytics.StagePlanComponent = Vue.extend({
- props: {
- items: Array,
- stage: Object,
- },
+global.cycleAnalytics.StagePlanComponent = Vue.extend({
+ props: {
+ items: Array,
+ stage: Object,
+ },
- data() {
- return { iconCommit };
- },
+ data() {
+ return { iconCommit };
+ },
- template: `
- <div>
- <div class="events-description">
- {{ stage.description }}
- <limit-warning :count="items.length" />
- </div>
- <ul class="stage-event-list">
- <li v-for="commit in items" class="stage-event-item">
- <div class="item-details item-conmmit-component">
- <img class="avatar" :src="commit.author.avatarUrl">
- <h5 class="item-title commit-title">
- <a :href="commit.commitUrl">
- {{ commit.title }}
- </a>
- </h5>
- <span>
- First
- <span class="commit-icon">${iconCommit}</span>
- <a :href="commit.commitUrl" class="commit-hash-link monospace">{{ commit.shortSha }}</a>
- pushed by
- <a :href="commit.author.webUrl" class="commit-author-link">
- {{ commit.author.name }}
- </a>
- </span>
- </div>
- <div class="item-time">
- <total-time :time="commit.totalTime"></total-time>
- </div>
- </li>
- </ul>
+ template: `
+ <div>
+ <div class="events-description">
+ {{ stage.description }}
+ <limit-warning :count="items.length" />
</div>
- `,
- });
-})(window.gl || (window.gl = {}));
+ <ul class="stage-event-list">
+ <li v-for="commit in items" class="stage-event-item">
+ <div class="item-details item-conmmit-component">
+ <img class="avatar" :src="commit.author.avatarUrl">
+ <h5 class="item-title commit-title">
+ <a :href="commit.commitUrl">
+ {{ commit.title }}
+ </a>
+ </h5>
+ <span>
+ First
+ <span class="commit-icon">${iconCommit}</span>
+ <a :href="commit.commitUrl" class="commit-hash-link monospace">{{ commit.shortSha }}</a>
+ pushed by
+ <a :href="commit.author.webUrl" class="commit-author-link">
+ {{ commit.author.name }}
+ </a>
+ </span>
+ </div>
+ <div class="item-time">
+ <total-time :time="commit.totalTime"></total-time>
+ </div>
+ </li>
+ </ul>
+ </div>
+ `,
+});
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_production_component.js b/app/assets/javascripts/cycle_analytics/components/stage_production_component.js
index 698a79ca68c..657f5385374 100644
--- a/app/assets/javascripts/cycle_analytics/components/stage_production_component.js
+++ b/app/assets/javascripts/cycle_analytics/components/stage_production_component.js
@@ -2,48 +2,47 @@
import Vue from 'vue';
-((global) => {
- global.cycleAnalytics = global.cycleAnalytics || {};
+const global = window.gl || (window.gl = {});
+global.cycleAnalytics = global.cycleAnalytics || {};
- global.cycleAnalytics.StageProductionComponent = Vue.extend({
- props: {
- items: Array,
- stage: Object,
- },
- template: `
- <div>
- <div class="events-description">
- {{ stage.description }}
- <limit-warning :count="items.length" />
- </div>
- <ul class="stage-event-list">
- <li v-for="issue in items" class="stage-event-item">
- <div class="item-details">
- <img class="avatar" :src="issue.author.avatarUrl">
- <h5 class="item-title issue-title">
- <a class="issue-title" :href="issue.url">
- {{ issue.title }}
- </a>
- </h5>
- <a :href="issue.url" class="issue-link">#{{ issue.iid }}</a>
- &middot;
- <span>
- Opened
- <a :href="issue.url" class="issue-date">{{ issue.createdAt }}</a>
- </span>
- <span>
- by
- <a :href="issue.author.webUrl" class="issue-author-link">
- {{ issue.author.name }}
- </a>
- </span>
- </div>
- <div class="item-time">
- <total-time :time="issue.totalTime"></total-time>
- </div>
- </li>
- </ul>
+global.cycleAnalytics.StageProductionComponent = Vue.extend({
+ props: {
+ items: Array,
+ stage: Object,
+ },
+ template: `
+ <div>
+ <div class="events-description">
+ {{ stage.description }}
+ <limit-warning :count="items.length" />
</div>
- `,
- });
-})(window.gl || (window.gl = {}));
+ <ul class="stage-event-list">
+ <li v-for="issue in items" class="stage-event-item">
+ <div class="item-details">
+ <img class="avatar" :src="issue.author.avatarUrl">
+ <h5 class="item-title issue-title">
+ <a class="issue-title" :href="issue.url">
+ {{ issue.title }}
+ </a>
+ </h5>
+ <a :href="issue.url" class="issue-link">#{{ issue.iid }}</a>
+ &middot;
+ <span>
+ Opened
+ <a :href="issue.url" class="issue-date">{{ issue.createdAt }}</a>
+ </span>
+ <span>
+ by
+ <a :href="issue.author.webUrl" class="issue-author-link">
+ {{ issue.author.name }}
+ </a>
+ </span>
+ </div>
+ <div class="item-time">
+ <total-time :time="issue.totalTime"></total-time>
+ </div>
+ </li>
+ </ul>
+ </div>
+ `,
+});
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_review_component.js b/app/assets/javascripts/cycle_analytics/components/stage_review_component.js
index e63c41f2a57..8a801300647 100644
--- a/app/assets/javascripts/cycle_analytics/components/stage_review_component.js
+++ b/app/assets/javascripts/cycle_analytics/components/stage_review_component.js
@@ -2,58 +2,57 @@
import Vue from 'vue';
-((global) => {
- global.cycleAnalytics = global.cycleAnalytics || {};
+const global = window.gl || (window.gl = {});
+global.cycleAnalytics = global.cycleAnalytics || {};
- global.cycleAnalytics.StageReviewComponent = Vue.extend({
- props: {
- items: Array,
- stage: Object,
- },
- template: `
- <div>
- <div class="events-description">
- {{ stage.description }}
- <limit-warning :count="items.length" />
- </div>
- <ul class="stage-event-list">
- <li v-for="mergeRequest in items" class="stage-event-item">
- <div class="item-details">
- <img class="avatar" :src="mergeRequest.author.avatarUrl">
- <h5 class="item-title merge-merquest-title">
- <a :href="mergeRequest.url">
- {{ mergeRequest.title }}
- </a>
- </h5>
- <a :href="mergeRequest.url" class="issue-link">!{{ mergeRequest.iid }}</a>
- &middot;
- <span>
- Opened
- <a :href="mergeRequest.url" class="issue-date">{{ mergeRequest.createdAt }}</a>
+global.cycleAnalytics.StageReviewComponent = Vue.extend({
+ props: {
+ items: Array,
+ stage: Object,
+ },
+ template: `
+ <div>
+ <div class="events-description">
+ {{ stage.description }}
+ <limit-warning :count="items.length" />
+ </div>
+ <ul class="stage-event-list">
+ <li v-for="mergeRequest in items" class="stage-event-item">
+ <div class="item-details">
+ <img class="avatar" :src="mergeRequest.author.avatarUrl">
+ <h5 class="item-title merge-merquest-title">
+ <a :href="mergeRequest.url">
+ {{ mergeRequest.title }}
+ </a>
+ </h5>
+ <a :href="mergeRequest.url" class="issue-link">!{{ mergeRequest.iid }}</a>
+ &middot;
+ <span>
+ Opened
+ <a :href="mergeRequest.url" class="issue-date">{{ mergeRequest.createdAt }}</a>
+ </span>
+ <span>
+ by
+ <a :href="mergeRequest.author.webUrl" class="issue-author-link">{{ mergeRequest.author.name }}</a>
+ </span>
+ <template v-if="mergeRequest.state === 'closed'">
+ <span class="merge-request-state">
+ <i class="fa fa-ban"></i>
+ {{ mergeRequest.state.toUpperCase() }}
</span>
- <span>
- by
- <a :href="mergeRequest.author.webUrl" class="issue-author-link">{{ mergeRequest.author.name }}</a>
+ </template>
+ <template v-else>
+ <span class="merge-request-branch" v-if="mergeRequest.branch">
+ <i class= "fa fa-code-fork"></i>
+ <a :href="mergeRequest.branch.url">{{ mergeRequest.branch.name }}</a>
</span>
- <template v-if="mergeRequest.state === 'closed'">
- <span class="merge-request-state">
- <i class="fa fa-ban"></i>
- {{ mergeRequest.state.toUpperCase() }}
- </span>
- </template>
- <template v-else>
- <span class="merge-request-branch" v-if="mergeRequest.branch">
- <i class= "fa fa-code-fork"></i>
- <a :href="mergeRequest.branch.url">{{ mergeRequest.branch.name }}</a>
- </span>
- </template>
- </div>
- <div class="item-time">
- <total-time :time="mergeRequest.totalTime"></total-time>
- </div>
- </li>
- </ul>
- </div>
- `,
- });
-})(window.gl || (window.gl = {}));
+ </template>
+ </div>
+ <div class="item-time">
+ <total-time :time="mergeRequest.totalTime"></total-time>
+ </div>
+ </li>
+ </ul>
+ </div>
+ `,
+});
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_staging_component.js b/app/assets/javascripts/cycle_analytics/components/stage_staging_component.js
index d51f7134e25..4a286379588 100644
--- a/app/assets/javascripts/cycle_analytics/components/stage_staging_component.js
+++ b/app/assets/javascripts/cycle_analytics/components/stage_staging_component.js
@@ -2,48 +2,47 @@
import Vue from 'vue';
import iconBranch from '../svg/icon_branch.svg';
-((global) => {
- global.cycleAnalytics = global.cycleAnalytics || {};
+const global = window.gl || (window.gl = {});
+global.cycleAnalytics = global.cycleAnalytics || {};
- global.cycleAnalytics.StageStagingComponent = Vue.extend({
- props: {
- items: Array,
- stage: Object,
- },
- data() {
- return { iconBranch };
- },
- template: `
- <div>
- <div class="events-description">
- {{ stage.description }}
- <limit-warning :count="items.length" />
- </div>
- <ul class="stage-event-list">
- <li v-for="build in items" class="stage-event-item item-build-component">
- <div class="item-details">
- <img class="avatar" :src="build.author.avatarUrl">
- <h5 class="item-title">
- <a :href="build.url" class="pipeline-id">#{{ build.id }}</a>
- <i class="fa fa-code-fork"></i>
- <a :href="build.branch.url" class="branch-name monospace">{{ build.branch.name }}</a>
- <span class="icon-branch">${iconBranch}</span>
- <a :href="build.commitUrl" class="short-sha monospace">{{ build.shortSha }}</a>
- </h5>
- <span>
- <a :href="build.url" class="build-date">{{ build.date }}</a>
- by
- <a :href="build.author.webUrl" class="issue-author-link">
- {{ build.author.name }}
- </a>
- </span>
- </div>
- <div class="item-time">
- <total-time :time="build.totalTime"></total-time>
- </div>
- </li>
- </ul>
+global.cycleAnalytics.StageStagingComponent = Vue.extend({
+ props: {
+ items: Array,
+ stage: Object,
+ },
+ data() {
+ return { iconBranch };
+ },
+ template: `
+ <div>
+ <div class="events-description">
+ {{ stage.description }}
+ <limit-warning :count="items.length" />
</div>
- `,
- });
-})(window.gl || (window.gl = {}));
+ <ul class="stage-event-list">
+ <li v-for="build in items" class="stage-event-item item-build-component">
+ <div class="item-details">
+ <img class="avatar" :src="build.author.avatarUrl">
+ <h5 class="item-title">
+ <a :href="build.url" class="pipeline-id">#{{ build.id }}</a>
+ <i class="fa fa-code-fork"></i>
+ <a :href="build.branch.url" class="branch-name monospace">{{ build.branch.name }}</a>
+ <span class="icon-branch">${iconBranch}</span>
+ <a :href="build.commitUrl" class="short-sha monospace">{{ build.shortSha }}</a>
+ </h5>
+ <span>
+ <a :href="build.url" class="build-date">{{ build.date }}</a>
+ by
+ <a :href="build.author.webUrl" class="issue-author-link">
+ {{ build.author.name }}
+ </a>
+ </span>
+ </div>
+ <div class="item-time">
+ <total-time :time="build.totalTime"></total-time>
+ </div>
+ </li>
+ </ul>
+ </div>
+ `,
+});
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_test_component.js b/app/assets/javascripts/cycle_analytics/components/stage_test_component.js
index 17ae3a9ddc1..e306026429e 100644
--- a/app/assets/javascripts/cycle_analytics/components/stage_test_component.js
+++ b/app/assets/javascripts/cycle_analytics/components/stage_test_component.js
@@ -3,48 +3,47 @@ import Vue from 'vue';
import iconBuildStatus from '../svg/icon_build_status.svg';
import iconBranch from '../svg/icon_branch.svg';
-((global) => {
- global.cycleAnalytics = global.cycleAnalytics || {};
+const global = window.gl || (window.gl = {});
+global.cycleAnalytics = global.cycleAnalytics || {};
- global.cycleAnalytics.StageTestComponent = Vue.extend({
- props: {
- items: Array,
- stage: Object,
- },
- data() {
- return { iconBuildStatus, iconBranch };
- },
- template: `
- <div>
- <div class="events-description">
- {{ stage.description }}
- <limit-warning :count="items.length" />
- </div>
- <ul class="stage-event-list">
- <li v-for="build in items" class="stage-event-item item-build-component">
- <div class="item-details">
- <h5 class="item-title">
- <span class="icon-build-status">${iconBuildStatus}</span>
- <a :href="build.url" class="item-build-name">{{ build.name }}</a>
- &middot;
- <a :href="build.url" class="pipeline-id">#{{ build.id }}</a>
- <i class="fa fa-code-fork"></i>
- <a :href="build.branch.url" class="branch-name monospace">{{ build.branch.name }}</a>
- <span class="icon-branch">${iconBranch}</span>
- <a :href="build.commitUrl" class="short-sha monospace">{{ build.shortSha }}</a>
- </h5>
- <span>
- <a :href="build.url" class="issue-date">
- {{ build.date }}
- </a>
- </span>
- </div>
- <div class="item-time">
- <total-time :time="build.totalTime"></total-time>
- </div>
- </li>
- </ul>
+global.cycleAnalytics.StageTestComponent = Vue.extend({
+ props: {
+ items: Array,
+ stage: Object,
+ },
+ data() {
+ return { iconBuildStatus, iconBranch };
+ },
+ template: `
+ <div>
+ <div class="events-description">
+ {{ stage.description }}
+ <limit-warning :count="items.length" />
</div>
- `,
- });
-})(window.gl || (window.gl = {}));
+ <ul class="stage-event-list">
+ <li v-for="build in items" class="stage-event-item item-build-component">
+ <div class="item-details">
+ <h5 class="item-title">
+ <span class="icon-build-status">${iconBuildStatus}</span>
+ <a :href="build.url" class="item-build-name">{{ build.name }}</a>
+ &middot;
+ <a :href="build.url" class="pipeline-id">#{{ build.id }}</a>
+ <i class="fa fa-code-fork"></i>
+ <a :href="build.branch.url" class="branch-name monospace">{{ build.branch.name }}</a>
+ <span class="icon-branch">${iconBranch}</span>
+ <a :href="build.commitUrl" class="short-sha monospace">{{ build.shortSha }}</a>
+ </h5>
+ <span>
+ <a :href="build.url" class="issue-date">
+ {{ build.date }}
+ </a>
+ </span>
+ </div>
+ <div class="item-time">
+ <total-time :time="build.totalTime"></total-time>
+ </div>
+ </li>
+ </ul>
+ </div>
+ `,
+});
diff --git a/app/assets/javascripts/cycle_analytics/components/total_time_component.js b/app/assets/javascripts/cycle_analytics/components/total_time_component.js
index b4442ea5566..77edcb76273 100644
--- a/app/assets/javascripts/cycle_analytics/components/total_time_component.js
+++ b/app/assets/javascripts/cycle_analytics/components/total_time_component.js
@@ -2,25 +2,24 @@
import Vue from 'vue';
-((global) => {
- global.cycleAnalytics = global.cycleAnalytics || {};
+const global = window.gl || (window.gl = {});
+global.cycleAnalytics = global.cycleAnalytics || {};
- global.cycleAnalytics.TotalTimeComponent = Vue.extend({
- props: {
- time: Object,
- },
- template: `
- <span class="total-time">
- <template v-if="Object.keys(time).length">
- <template v-if="time.days">{{ time.days }} <span>{{ time.days === 1 ? 'day' : 'days' }}</span></template>
- <template v-if="time.hours">{{ time.hours }} <span>hr</span></template>
- <template v-if="time.mins && !time.days">{{ time.mins }} <span>mins</span></template>
- <template v-if="time.seconds && Object.keys(time).length === 1 || time.seconds === 0">{{ time.seconds }} <span>s</span></template>
- </template>
- <template v-else>
- --
- </template>
- </span>
- `,
- });
-})(window.gl || (window.gl = {}));
+global.cycleAnalytics.TotalTimeComponent = Vue.extend({
+ props: {
+ time: Object,
+ },
+ template: `
+ <span class="total-time">
+ <template v-if="Object.keys(time).length">
+ <template v-if="time.days">{{ time.days }} <span>{{ time.days === 1 ? 'day' : 'days' }}</span></template>
+ <template v-if="time.hours">{{ time.hours }} <span>hr</span></template>
+ <template v-if="time.mins && !time.days">{{ time.mins }} <span>mins</span></template>
+ <template v-if="time.seconds && Object.keys(time).length === 1 || time.seconds === 0">{{ time.seconds }} <span>s</span></template>
+ </template>
+ <template v-else>
+ --
+ </template>
+ </span>
+ `,
+});
diff --git a/app/assets/javascripts/cycle_analytics/cycle_analytics_service.js b/app/assets/javascripts/cycle_analytics/cycle_analytics_service.js
index 9f74b14c4b9..681d6eef565 100644
--- a/app/assets/javascripts/cycle_analytics/cycle_analytics_service.js
+++ b/app/assets/javascripts/cycle_analytics/cycle_analytics_service.js
@@ -1,41 +1,41 @@
/* eslint-disable no-param-reassign */
-((global) => {
- global.cycleAnalytics = global.cycleAnalytics || {};
- class CycleAnalyticsService {
- constructor(options) {
- this.requestPath = options.requestPath;
- }
+const global = window.gl || (window.gl = {});
+global.cycleAnalytics = global.cycleAnalytics || {};
- fetchCycleAnalyticsData(options) {
- options = options || { startDate: 30 };
-
- return $.ajax({
- url: this.requestPath,
- method: 'GET',
- dataType: 'json',
- contentType: 'application/json',
- data: {
- cycle_analytics: {
- start_date: options.startDate,
- },
- },
- });
- }
+class CycleAnalyticsService {
+ constructor(options) {
+ this.requestPath = options.requestPath;
+ }
- fetchStageData(options) {
- const {
- stage,
- startDate,
- } = options;
+ fetchCycleAnalyticsData(options) {
+ options = options || { startDate: 30 };
- return $.get(`${this.requestPath}/events/${stage.title.toLowerCase()}.json`, {
+ return $.ajax({
+ url: this.requestPath,
+ method: 'GET',
+ dataType: 'json',
+ contentType: 'application/json',
+ data: {
cycle_analytics: {
- start_date: startDate,
+ start_date: options.startDate,
},
- });
- }
+ },
+ });
+ }
+
+ fetchStageData(options) {
+ const {
+ stage,
+ startDate,
+ } = options;
+
+ return $.get(`${this.requestPath}/events/${stage.title.toLowerCase()}.json`, {
+ cycle_analytics: {
+ start_date: startDate,
+ },
+ });
}
+}
- global.cycleAnalytics.CycleAnalyticsService = CycleAnalyticsService;
-})(window.gl || (window.gl = {}));
+global.cycleAnalytics.CycleAnalyticsService = CycleAnalyticsService;
diff --git a/app/assets/javascripts/cycle_analytics/cycle_analytics_store.js b/app/assets/javascripts/cycle_analytics/cycle_analytics_store.js
index 7ae9de7297c..6536a8fd7fa 100644
--- a/app/assets/javascripts/cycle_analytics/cycle_analytics_store.js
+++ b/app/assets/javascripts/cycle_analytics/cycle_analytics_store.js
@@ -3,102 +3,101 @@
require('../lib/utils/text_utility');
const DEFAULT_EVENT_OBJECTS = require('./default_event_objects');
-((global) => {
- global.cycleAnalytics = global.cycleAnalytics || {};
+const global = window.gl || (window.gl = {});
+global.cycleAnalytics = global.cycleAnalytics || {};
- const EMPTY_STAGE_TEXTS = {
- issue: 'The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.',
- plan: 'The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.',
- code: 'The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request.',
- test: 'The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.',
- review: 'The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.',
- staging: 'The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.',
- production: 'The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.',
- };
+const EMPTY_STAGE_TEXTS = {
+ issue: 'The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.',
+ plan: 'The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.',
+ code: 'The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request.',
+ test: 'The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.',
+ review: 'The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.',
+ staging: 'The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.',
+ production: 'The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.',
+};
- global.cycleAnalytics.CycleAnalyticsStore = {
- state: {
- summary: '',
- stats: '',
- analytics: '',
- events: [],
- stages: [],
- },
- setCycleAnalyticsData(data) {
- this.state = Object.assign(this.state, this.decorateData(data));
- },
- decorateData(data) {
- const newData = {};
+global.cycleAnalytics.CycleAnalyticsStore = {
+ state: {
+ summary: '',
+ stats: '',
+ analytics: '',
+ events: [],
+ stages: [],
+ },
+ setCycleAnalyticsData(data) {
+ this.state = Object.assign(this.state, this.decorateData(data));
+ },
+ decorateData(data) {
+ const newData = {};
- newData.stages = data.stats || [];
- newData.summary = data.summary || [];
+ newData.stages = data.stats || [];
+ newData.summary = data.summary || [];
- newData.summary.forEach((item) => {
- item.value = item.value || '-';
- });
+ newData.summary.forEach((item) => {
+ item.value = item.value || '-';
+ });
- newData.stages.forEach((item) => {
- const stageSlug = gl.text.dasherize(item.title.toLowerCase());
- item.active = false;
- item.isUserAllowed = data.permissions[stageSlug];
- item.emptyStageText = EMPTY_STAGE_TEXTS[stageSlug];
- item.component = `stage-${stageSlug}-component`;
- item.slug = stageSlug;
- });
- newData.analytics = data;
- return newData;
- },
- setLoadingState(state) {
- this.state.isLoading = state;
- },
- setErrorState(state) {
- this.state.hasError = state;
- },
- deactivateAllStages() {
- this.state.stages.forEach((stage) => {
- stage.active = false;
- });
- },
- setActiveStage(stage) {
- this.deactivateAllStages();
- stage.active = true;
- },
- setStageEvents(events, stage) {
- this.state.events = this.decorateEvents(events, stage);
- },
- decorateEvents(events, stage) {
- const newEvents = [];
+ newData.stages.forEach((item) => {
+ const stageSlug = gl.text.dasherize(item.title.toLowerCase());
+ item.active = false;
+ item.isUserAllowed = data.permissions[stageSlug];
+ item.emptyStageText = EMPTY_STAGE_TEXTS[stageSlug];
+ item.component = `stage-${stageSlug}-component`;
+ item.slug = stageSlug;
+ });
+ newData.analytics = data;
+ return newData;
+ },
+ setLoadingState(state) {
+ this.state.isLoading = state;
+ },
+ setErrorState(state) {
+ this.state.hasError = state;
+ },
+ deactivateAllStages() {
+ this.state.stages.forEach((stage) => {
+ stage.active = false;
+ });
+ },
+ setActiveStage(stage) {
+ this.deactivateAllStages();
+ stage.active = true;
+ },
+ setStageEvents(events, stage) {
+ this.state.events = this.decorateEvents(events, stage);
+ },
+ decorateEvents(events, stage) {
+ const newEvents = [];
- events.forEach((item) => {
- if (!item) return;
+ events.forEach((item) => {
+ if (!item) return;
- const eventItem = Object.assign({}, DEFAULT_EVENT_OBJECTS[stage.slug], item);
+ const eventItem = Object.assign({}, DEFAULT_EVENT_OBJECTS[stage.slug], item);
- eventItem.totalTime = eventItem.total_time;
+ eventItem.totalTime = eventItem.total_time;
- if (eventItem.author) {
- eventItem.author.webUrl = eventItem.author.web_url;
- eventItem.author.avatarUrl = eventItem.author.avatar_url;
- }
+ if (eventItem.author) {
+ eventItem.author.webUrl = eventItem.author.web_url;
+ eventItem.author.avatarUrl = eventItem.author.avatar_url;
+ }
- if (eventItem.created_at) eventItem.createdAt = eventItem.created_at;
- if (eventItem.short_sha) eventItem.shortSha = eventItem.short_sha;
- if (eventItem.commit_url) eventItem.commitUrl = eventItem.commit_url;
+ if (eventItem.created_at) eventItem.createdAt = eventItem.created_at;
+ if (eventItem.short_sha) eventItem.shortSha = eventItem.short_sha;
+ if (eventItem.commit_url) eventItem.commitUrl = eventItem.commit_url;
- delete eventItem.author.web_url;
- delete eventItem.author.avatar_url;
- delete eventItem.total_time;
- delete eventItem.created_at;
- delete eventItem.short_sha;
- delete eventItem.commit_url;
+ delete eventItem.author.web_url;
+ delete eventItem.author.avatar_url;
+ delete eventItem.total_time;
+ delete eventItem.created_at;
+ delete eventItem.short_sha;
+ delete eventItem.commit_url;
- newEvents.push(eventItem);
- });
+ newEvents.push(eventItem);
+ });
- return newEvents;
- },
- currentActiveStage() {
- return this.state.stages.find(stage => stage.active);
- },
- };
-})(window.gl || (window.gl = {}));
+ return newEvents;
+ },
+ currentActiveStage() {
+ return this.state.stages.find(stage => stage.active);
+ },
+};
diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js
index 9ac4c49d697..b62b2cec4d8 100644
--- a/app/assets/javascripts/gfm_auto_complete.js
+++ b/app/assets/javascripts/gfm_auto_complete.js
@@ -50,7 +50,7 @@ window.gl.GfmAutoComplete = {
template: '<li>${title}</li>'
},
Loading: {
- template: '<li style="pointer-events: none;"><i class="fa fa-refresh fa-spin"></i> Loading...</li>'
+ template: '<li style="pointer-events: none;"><i class="fa fa-spinner fa-spin"></i> Loading...</li>'
},
DefaultOptions: {
sorter: function(query, items, searchKey) {
diff --git a/app/assets/javascripts/issue.js b/app/assets/javascripts/issue.js
index 47e675f537e..011043e992f 100644
--- a/app/assets/javascripts/issue.js
+++ b/app/assets/javascripts/issue.js
@@ -20,57 +20,60 @@ class Issue {
});
Issue.initIssueBtnEventListeners();
}
+
+ Issue.$btnNewBranch = $('#new-branch');
+
Issue.initMergeRequests();
Issue.initRelatedBranches();
Issue.initCanCreateBranch();
}
static initIssueBtnEventListeners() {
- var issueFailMessage;
- issueFailMessage = 'Unable to update this issue at this time.';
- return $('a.btn-close, a.btn-reopen').on('click', function(e) {
- var $this, isClose, shouldSubmit, url;
+ const issueFailMessage = 'Unable to update this issue at this time.';
+
+ const closeButtons = $('a.btn-close');
+ const isClosedBadge = $('div.status-box-closed');
+ const isOpenBadge = $('div.status-box-open');
+ const projectIssuesCounter = $('.issue_counter');
+ const reopenButtons = $('a.btn-reopen');
+
+ return closeButtons.add(reopenButtons).on('click', function(e) {
+ var $this, shouldSubmit, url;
e.preventDefault();
e.stopImmediatePropagation();
$this = $(this);
- isClose = $this.hasClass('btn-close');
shouldSubmit = $this.hasClass('btn-comment');
if (shouldSubmit) {
Issue.submitNoteForm($this.closest('form'));
}
$this.prop('disabled', true);
+ Issue.setNewBranchButtonState(true, null);
url = $this.attr('href');
return $.ajax({
type: 'PUT',
- url: url,
- error: function(jqXHR, textStatus, errorThrown) {
- var issueStatus;
- issueStatus = isClose ? 'close' : 'open';
- return new Flash(issueFailMessage, 'alert');
- },
- success: function(data, textStatus, jqXHR) {
- if ('id' in data) {
- $(document).trigger('issuable:change');
- let total = Number($('.issue_counter').text().replace(/[^\d]/, ''));
- if (isClose) {
- $('a.btn-close').addClass('hidden');
- $('a.btn-reopen').removeClass('hidden');
- $('div.status-box-closed').removeClass('hidden');
- $('div.status-box-open').addClass('hidden');
- total -= 1;
- } else {
- $('a.btn-reopen').addClass('hidden');
- $('a.btn-close').removeClass('hidden');
- $('div.status-box-closed').addClass('hidden');
- $('div.status-box-open').removeClass('hidden');
- total += 1;
- }
- $('.issue_counter').text(gl.text.addDelimiter(total));
- } else {
- new Flash(issueFailMessage, 'alert');
- }
- return $this.prop('disabled', false);
+ url: url
+ }).fail(function(jqXHR, textStatus, errorThrown) {
+ new Flash(issueFailMessage);
+ Issue.initCanCreateBranch();
+ }).done(function(data, textStatus, jqXHR) {
+ if ('id' in data) {
+ $(document).trigger('issuable:change');
+
+ const isClosed = $this.hasClass('btn-close');
+ closeButtons.toggleClass('hidden', isClosed);
+ reopenButtons.toggleClass('hidden', !isClosed);
+ isClosedBadge.toggleClass('hidden', !isClosed);
+ isOpenBadge.toggleClass('hidden', isClosed);
+
+ let numProjectIssues = Number(projectIssuesCounter.text().replace(/[^\d]/, ''));
+ numProjectIssues = isClosed ? numProjectIssues - 1 : numProjectIssues + 1;
+ projectIssuesCounter.text(gl.text.addDelimiter(numProjectIssues));
+ } else {
+ new Flash(issueFailMessage);
}
+
+ $this.prop('disabled', false);
+ Issue.initCanCreateBranch();
});
});
}
@@ -86,9 +89,9 @@ class Issue {
static initMergeRequests() {
var $container;
$container = $('#merge-requests');
- return $.getJSON($container.data('url')).error(function() {
- return new Flash('Failed to load referenced merge requests', 'alert');
- }).success(function(data) {
+ return $.getJSON($container.data('url')).fail(function() {
+ return new Flash('Failed to load referenced merge requests');
+ }).done(function(data) {
if ('html' in data) {
return $container.html(data.html);
}
@@ -98,9 +101,9 @@ class Issue {
static initRelatedBranches() {
var $container;
$container = $('#related-branches');
- return $.getJSON($container.data('url')).error(function() {
- return new Flash('Failed to load related branches', 'alert');
- }).success(function(data) {
+ return $.getJSON($container.data('url')).fail(function() {
+ return new Flash('Failed to load related branches');
+ }).done(function(data) {
if ('html' in data) {
return $container.html(data.html);
}
@@ -108,24 +111,27 @@ class Issue {
}
static initCanCreateBranch() {
- var $container;
- $container = $('#new-branch');
// If the user doesn't have the required permissions the container isn't
// rendered at all.
- if ($container.length === 0) {
+ if (Issue.$btnNewBranch.length === 0) {
return;
}
- return $.getJSON($container.data('path')).error(function() {
- $container.find('.unavailable').show();
- return new Flash('Failed to check if a new branch can be created.', 'alert');
- }).success(function(data) {
- if (data.can_create_branch) {
- $container.find('.available').show();
- } else {
- return $container.find('.unavailable').show();
- }
+ return $.getJSON(Issue.$btnNewBranch.data('path')).fail(function() {
+ Issue.setNewBranchButtonState(false, false);
+ new Flash('Failed to check if a new branch can be created.');
+ }).done(function(data) {
+ Issue.setNewBranchButtonState(false, data.can_create_branch);
});
}
+
+ static setNewBranchButtonState(isPending, canCreate) {
+ if (Issue.$btnNewBranch.length === 0) {
+ return;
+ }
+
+ Issue.$btnNewBranch.find('.available').toggle(!isPending && canCreate);
+ Issue.$btnNewBranch.find('.unavailable').toggle(!isPending && !canCreate);
+ }
}
export default Issue;
diff --git a/app/assets/javascripts/lib/utils/text_utility.js b/app/assets/javascripts/lib/utils/text_utility.js
index 2e5f8a09fc1..fecd531328d 100644
--- a/app/assets/javascripts/lib/utils/text_utility.js
+++ b/app/assets/javascripts/lib/utils/text_utility.js
@@ -1,192 +1,188 @@
-/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, quotes, one-var, one-var-declaration-per-line, operator-assignment, no-else-return, prefer-template, prefer-arrow-callback, no-empty, max-len, consistent-return, no-unused-vars, no-return-assign, max-len */
-
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, quotes, one-var, one-var-declaration-per-line, operator-assignment, no-else-return, prefer-template, prefer-arrow-callback, no-empty, max-len, consistent-return, no-unused-vars, no-return-assign, max-len, vars-on-top */
require('vendor/latinise');
-(function() {
- (function(w) {
- var base;
- if (w.gl == null) {
- w.gl = {};
- }
- if ((base = w.gl).text == null) {
- base.text = {};
+var base;
+var w = window;
+if (w.gl == null) {
+ w.gl = {};
+}
+if ((base = w.gl).text == null) {
+ base.text = {};
+}
+gl.text.addDelimiter = function(text) {
+ return text ? text.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") : text;
+};
+gl.text.highCountTrim = function(count) {
+ return count > 99 ? '99+' : count;
+};
+gl.text.randomString = function() {
+ return Math.random().toString(36).substring(7);
+};
+gl.text.replaceRange = function(s, start, end, substitute) {
+ return s.substring(0, start) + substitute + s.substring(end);
+};
+gl.text.getTextWidth = function(text, font) {
+ /**
+ * Uses canvas.measureText to compute and return the width of the given text of given font in pixels.
+ *
+ * @param {String} text The text to be rendered.
+ * @param {String} font The css font descriptor that text is to be rendered with (e.g. "bold 14px verdana").
+ *
+ * @see http://stackoverflow.com/questions/118241/calculate-text-width-with-javascript/21015393#21015393
+ */
+ // re-use canvas object for better performance
+ var canvas = gl.text.getTextWidth.canvas || (gl.text.getTextWidth.canvas = document.createElement('canvas'));
+ var context = canvas.getContext('2d');
+ context.font = font;
+ return context.measureText(text).width;
+};
+gl.text.selectedText = function(text, textarea) {
+ return text.substring(textarea.selectionStart, textarea.selectionEnd);
+};
+gl.text.lineBefore = function(text, textarea) {
+ var split;
+ split = text.substring(0, textarea.selectionStart).trim().split('\n');
+ return split[split.length - 1];
+};
+gl.text.lineAfter = function(text, textarea) {
+ return text.substring(textarea.selectionEnd).trim().split('\n')[0];
+};
+gl.text.blockTagText = function(text, textArea, blockTag, selected) {
+ var lineAfter, lineBefore;
+ lineBefore = this.lineBefore(text, textArea);
+ lineAfter = this.lineAfter(text, textArea);
+ if (lineBefore === blockTag && lineAfter === blockTag) {
+ // To remove the block tag we have to select the line before & after
+ if (blockTag != null) {
+ textArea.selectionStart = textArea.selectionStart - (blockTag.length + 1);
+ textArea.selectionEnd = textArea.selectionEnd + (blockTag.length + 1);
}
- gl.text.addDelimiter = function(text) {
- return text ? text.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") : text;
- };
- gl.text.highCountTrim = function(count) {
- return count > 99 ? '99+' : count;
- };
- gl.text.randomString = function() {
- return Math.random().toString(36).substring(7);
- };
- gl.text.replaceRange = function(s, start, end, substitute) {
- return s.substring(0, start) + substitute + s.substring(end);
- };
- gl.text.getTextWidth = function(text, font) {
- /**
- * Uses canvas.measureText to compute and return the width of the given text of given font in pixels.
- *
- * @param {String} text The text to be rendered.
- * @param {String} font The css font descriptor that text is to be rendered with (e.g. "bold 14px verdana").
- *
- * @see http://stackoverflow.com/questions/118241/calculate-text-width-with-javascript/21015393#21015393
- */
- // re-use canvas object for better performance
- var canvas = gl.text.getTextWidth.canvas || (gl.text.getTextWidth.canvas = document.createElement('canvas'));
- var context = canvas.getContext('2d');
- context.font = font;
- return context.measureText(text).width;
- };
- gl.text.selectedText = function(text, textarea) {
- return text.substring(textarea.selectionStart, textarea.selectionEnd);
- };
- gl.text.lineBefore = function(text, textarea) {
- var split;
- split = text.substring(0, textarea.selectionStart).trim().split('\n');
- return split[split.length - 1];
- };
- gl.text.lineAfter = function(text, textarea) {
- return text.substring(textarea.selectionEnd).trim().split('\n')[0];
- };
- gl.text.blockTagText = function(text, textArea, blockTag, selected) {
- var lineAfter, lineBefore;
- lineBefore = this.lineBefore(text, textArea);
- lineAfter = this.lineAfter(text, textArea);
- if (lineBefore === blockTag && lineAfter === blockTag) {
- // To remove the block tag we have to select the line before & after
- if (blockTag != null) {
- textArea.selectionStart = textArea.selectionStart - (blockTag.length + 1);
- textArea.selectionEnd = textArea.selectionEnd + (blockTag.length + 1);
- }
- return selected;
- } else {
- return blockTag + "\n" + selected + "\n" + blockTag;
- }
- };
- gl.text.insertText = function(textArea, text, tag, blockTag, selected, wrap) {
- var insertText, inserted, selectedSplit, startChar, removedLastNewLine, removedFirstNewLine, currentLineEmpty, lastNewLine;
- removedLastNewLine = false;
- removedFirstNewLine = false;
- currentLineEmpty = false;
+ return selected;
+ } else {
+ return blockTag + "\n" + selected + "\n" + blockTag;
+ }
+};
+gl.text.insertText = function(textArea, text, tag, blockTag, selected, wrap) {
+ var insertText, inserted, selectedSplit, startChar, removedLastNewLine, removedFirstNewLine, currentLineEmpty, lastNewLine;
+ removedLastNewLine = false;
+ removedFirstNewLine = false;
+ currentLineEmpty = false;
- // Remove the first newline
- if (selected.indexOf('\n') === 0) {
- removedFirstNewLine = true;
- selected = selected.replace(/\n+/, '');
- }
+ // Remove the first newline
+ if (selected.indexOf('\n') === 0) {
+ removedFirstNewLine = true;
+ selected = selected.replace(/\n+/, '');
+ }
- // Remove the last newline
- if (textArea.selectionEnd - textArea.selectionStart > selected.replace(/\n$/, '').length) {
- removedLastNewLine = true;
- selected = selected.replace(/\n$/, '');
- }
+ // Remove the last newline
+ if (textArea.selectionEnd - textArea.selectionStart > selected.replace(/\n$/, '').length) {
+ removedLastNewLine = true;
+ selected = selected.replace(/\n$/, '');
+ }
- selectedSplit = selected.split('\n');
+ selectedSplit = selected.split('\n');
- if (!wrap) {
- lastNewLine = textArea.value.substr(0, textArea.selectionStart).lastIndexOf('\n');
+ if (!wrap) {
+ lastNewLine = textArea.value.substr(0, textArea.selectionStart).lastIndexOf('\n');
- // Check whether the current line is empty or consists only of spaces(=handle as empty)
- if (/^\s*$/.test(textArea.value.substring(lastNewLine, textArea.selectionStart))) {
- currentLineEmpty = true;
- }
- }
+ // Check whether the current line is empty or consists only of spaces(=handle as empty)
+ if (/^\s*$/.test(textArea.value.substring(lastNewLine, textArea.selectionStart))) {
+ currentLineEmpty = true;
+ }
+ }
- startChar = !wrap && !currentLineEmpty && textArea.selectionStart > 0 ? '\n' : '';
+ startChar = !wrap && !currentLineEmpty && textArea.selectionStart > 0 ? '\n' : '';
- if (selectedSplit.length > 1 && (!wrap || (blockTag != null))) {
- if (blockTag != null) {
- insertText = this.blockTagText(text, textArea, blockTag, selected);
+ if (selectedSplit.length > 1 && (!wrap || (blockTag != null))) {
+ if (blockTag != null) {
+ insertText = this.blockTagText(text, textArea, blockTag, selected);
+ } else {
+ insertText = selectedSplit.map(function(val) {
+ if (val.indexOf(tag) === 0) {
+ return "" + (val.replace(tag, ''));
} else {
- insertText = selectedSplit.map(function(val) {
- if (val.indexOf(tag) === 0) {
- return "" + (val.replace(tag, ''));
- } else {
- return "" + tag + val;
- }
- }).join('\n');
+ return "" + tag + val;
}
- } else {
- insertText = "" + startChar + tag + selected + (wrap ? tag : ' ');
- }
+ }).join('\n');
+ }
+ } else {
+ insertText = "" + startChar + tag + selected + (wrap ? tag : ' ');
+ }
- if (removedFirstNewLine) {
- insertText = '\n' + insertText;
- }
+ if (removedFirstNewLine) {
+ insertText = '\n' + insertText;
+ }
- if (removedLastNewLine) {
- insertText += '\n';
- }
+ if (removedLastNewLine) {
+ insertText += '\n';
+ }
- if (document.queryCommandSupported('insertText')) {
- inserted = document.execCommand('insertText', false, insertText);
- }
- if (!inserted) {
- try {
- document.execCommand("ms-beginUndoUnit");
- } catch (error) {}
- textArea.value = this.replaceRange(text, textArea.selectionStart, textArea.selectionEnd, insertText);
- try {
- document.execCommand("ms-endUndoUnit");
- } catch (error) {}
- }
- return this.moveCursor(textArea, tag, wrap, removedLastNewLine);
- };
- gl.text.moveCursor = function(textArea, tag, wrapped, removedLastNewLine) {
- var pos;
- if (!textArea.setSelectionRange) {
- return;
- }
- if (textArea.selectionStart === textArea.selectionEnd) {
- if (wrapped) {
- pos = textArea.selectionStart - tag.length;
- } else {
- pos = textArea.selectionStart;
- }
+ if (document.queryCommandSupported('insertText')) {
+ inserted = document.execCommand('insertText', false, insertText);
+ }
+ if (!inserted) {
+ try {
+ document.execCommand("ms-beginUndoUnit");
+ } catch (error) {}
+ textArea.value = this.replaceRange(text, textArea.selectionStart, textArea.selectionEnd, insertText);
+ try {
+ document.execCommand("ms-endUndoUnit");
+ } catch (error) {}
+ }
+ return this.moveCursor(textArea, tag, wrap, removedLastNewLine);
+};
+gl.text.moveCursor = function(textArea, tag, wrapped, removedLastNewLine) {
+ var pos;
+ if (!textArea.setSelectionRange) {
+ return;
+ }
+ if (textArea.selectionStart === textArea.selectionEnd) {
+ if (wrapped) {
+ pos = textArea.selectionStart - tag.length;
+ } else {
+ pos = textArea.selectionStart;
+ }
- if (removedLastNewLine) {
- pos -= 1;
- }
+ if (removedLastNewLine) {
+ pos -= 1;
+ }
- return textArea.setSelectionRange(pos, pos);
- }
- };
- gl.text.updateText = function(textArea, tag, blockTag, wrap) {
- var $textArea, selected, text;
- $textArea = $(textArea);
- textArea = $textArea.get(0);
- text = $textArea.val();
- selected = this.selectedText(text, textArea);
- $textArea.focus();
- return this.insertText(textArea, text, tag, blockTag, selected, wrap);
- };
- gl.text.init = function(form) {
- var self;
- self = this;
- return $('.js-md', form).off('click').on('click', function() {
- var $this;
- $this = $(this);
- return self.updateText($this.closest('.md-area').find('textarea'), $this.data('md-tag'), $this.data('md-block'), !$this.data('md-prepend'));
- });
- };
- gl.text.removeListeners = function(form) {
- return $('.js-md', form).off();
- };
- gl.text.humanize = function(string) {
- return string.charAt(0).toUpperCase() + string.replace(/_/g, ' ').slice(1);
- };
- gl.text.pluralize = function(str, count) {
- return str + (count > 1 || count === 0 ? 's' : '');
- };
- gl.text.truncate = function(string, maxLength) {
- return string.substr(0, (maxLength - 3)) + '...';
- };
- gl.text.dasherize = function(str) {
- return str.replace(/[_\s]+/g, '-');
- };
- gl.text.slugify = function(str) {
- return str.trim().toLowerCase().latinise();
- };
- })(window);
-}).call(window);
+ return textArea.setSelectionRange(pos, pos);
+ }
+};
+gl.text.updateText = function(textArea, tag, blockTag, wrap) {
+ var $textArea, selected, text;
+ $textArea = $(textArea);
+ textArea = $textArea.get(0);
+ text = $textArea.val();
+ selected = this.selectedText(text, textArea);
+ $textArea.focus();
+ return this.insertText(textArea, text, tag, blockTag, selected, wrap);
+};
+gl.text.init = function(form) {
+ var self;
+ self = this;
+ return $('.js-md', form).off('click').on('click', function() {
+ var $this;
+ $this = $(this);
+ return self.updateText($this.closest('.md-area').find('textarea'), $this.data('md-tag'), $this.data('md-block'), !$this.data('md-prepend'));
+ });
+};
+gl.text.removeListeners = function(form) {
+ return $('.js-md', form).off();
+};
+gl.text.humanize = function(string) {
+ return string.charAt(0).toUpperCase() + string.replace(/_/g, ' ').slice(1);
+};
+gl.text.pluralize = function(str, count) {
+ return str + (count > 1 || count === 0 ? 's' : '');
+};
+gl.text.truncate = function(string, maxLength) {
+ return string.substr(0, (maxLength - 3)) + '...';
+};
+gl.text.dasherize = function(str) {
+ return str.replace(/[_\s]+/g, '-');
+};
+gl.text.slugify = function(str) {
+ return str.trim().toLowerCase().latinise();
+};
diff --git a/app/assets/javascripts/lib/utils/url_utility.js b/app/assets/javascripts/lib/utils/url_utility.js
index 09c4261b318..b9d2fc25c39 100644
--- a/app/assets/javascripts/lib/utils/url_utility.js
+++ b/app/assets/javascripts/lib/utils/url_utility.js
@@ -1,93 +1,90 @@
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, one-var, one-var-declaration-per-line, no-void, guard-for-in, no-restricted-syntax, prefer-template, quotes, max-len */
-(function() {
- (function(w) {
- var base;
- if (w.gl == null) {
- w.gl = {};
+var base;
+var w = window;
+if (w.gl == null) {
+ w.gl = {};
+}
+if ((base = w.gl).utils == null) {
+ base.utils = {};
+}
+// Returns an array containing the value(s) of the
+// of the key passed as an argument
+w.gl.utils.getParameterValues = function(sParam) {
+ var i, sPageURL, sParameterName, sURLVariables, values;
+ sPageURL = decodeURIComponent(window.location.search.substring(1));
+ sURLVariables = sPageURL.split('&');
+ sParameterName = void 0;
+ values = [];
+ i = 0;
+ while (i < sURLVariables.length) {
+ sParameterName = sURLVariables[i].split('=');
+ if (sParameterName[0] === sParam) {
+ values.push(sParameterName[1].replace(/\+/g, ' '));
}
- if ((base = w.gl).utils == null) {
- base.utils = {};
+ i += 1;
+ }
+ return values;
+};
+// @param {Object} params - url keys and value to merge
+// @param {String} url
+w.gl.utils.mergeUrlParams = function(params, url) {
+ var lastChar, newUrl, paramName, paramValue, pattern;
+ newUrl = decodeURIComponent(url);
+ for (paramName in params) {
+ paramValue = params[paramName];
+ pattern = new RegExp("\\b(" + paramName + "=).*?(&|$)");
+ if (paramValue == null) {
+ newUrl = newUrl.replace(pattern, '');
+ } else if (url.search(pattern) !== -1) {
+ newUrl = newUrl.replace(pattern, "$1" + paramValue + "$2");
+ } else {
+ newUrl = "" + newUrl + (newUrl.indexOf('?') > 0 ? '&' : '?') + paramName + "=" + paramValue;
}
- // Returns an array containing the value(s) of the
- // of the key passed as an argument
- w.gl.utils.getParameterValues = function(sParam) {
- var i, sPageURL, sParameterName, sURLVariables, values;
- sPageURL = decodeURIComponent(window.location.search.substring(1));
- sURLVariables = sPageURL.split('&');
- sParameterName = void 0;
- values = [];
- i = 0;
- while (i < sURLVariables.length) {
- sParameterName = sURLVariables[i].split('=');
- if (sParameterName[0] === sParam) {
- values.push(sParameterName[1].replace(/\+/g, ' '));
- }
- i += 1;
+ }
+ // Remove a trailing ampersand
+ lastChar = newUrl[newUrl.length - 1];
+ if (lastChar === '&') {
+ newUrl = newUrl.slice(0, -1);
+ }
+ return newUrl;
+};
+// removes parameter query string from url. returns the modified url
+w.gl.utils.removeParamQueryString = function(url, param) {
+ var urlVariables, variables;
+ url = decodeURIComponent(url);
+ urlVariables = url.split('&');
+ return ((function() {
+ var j, len, results;
+ results = [];
+ for (j = 0, len = urlVariables.length; j < len; j += 1) {
+ variables = urlVariables[j];
+ if (variables.indexOf(param) === -1) {
+ results.push(variables);
}
- return values;
- };
- // @param {Object} params - url keys and value to merge
- // @param {String} url
- w.gl.utils.mergeUrlParams = function(params, url) {
- var lastChar, newUrl, paramName, paramValue, pattern;
- newUrl = decodeURIComponent(url);
- for (paramName in params) {
- paramValue = params[paramName];
- pattern = new RegExp("\\b(" + paramName + "=).*?(&|$)");
- if (paramValue == null) {
- newUrl = newUrl.replace(pattern, '');
- } else if (url.search(pattern) !== -1) {
- newUrl = newUrl.replace(pattern, "$1" + paramValue + "$2");
- } else {
- newUrl = "" + newUrl + (newUrl.indexOf('?') > 0 ? '&' : '?') + paramName + "=" + paramValue;
- }
- }
- // Remove a trailing ampersand
- lastChar = newUrl[newUrl.length - 1];
- if (lastChar === '&') {
- newUrl = newUrl.slice(0, -1);
- }
- return newUrl;
- };
- // removes parameter query string from url. returns the modified url
- w.gl.utils.removeParamQueryString = function(url, param) {
- var urlVariables, variables;
- url = decodeURIComponent(url);
- urlVariables = url.split('&');
- return ((function() {
- var j, len, results;
- results = [];
- for (j = 0, len = urlVariables.length; j < len; j += 1) {
- variables = urlVariables[j];
- if (variables.indexOf(param) === -1) {
- results.push(variables);
- }
- }
- return results;
- })()).join('&');
- };
- w.gl.utils.removeParams = (params) => {
- const url = new URL(window.location.href);
- params.forEach((param) => {
- url.search = w.gl.utils.removeParamQueryString(url.search, param);
- });
- return url.href;
- };
- w.gl.utils.getLocationHash = function(url) {
- var hashIndex;
- if (typeof url === 'undefined') {
- // Note: We can't use window.location.hash here because it's
- // not consistent across browsers - Firefox will pre-decode it
- url = window.location.href;
- }
- hashIndex = url.indexOf('#');
- return hashIndex === -1 ? null : url.substring(hashIndex + 1);
- };
+ }
+ return results;
+ })()).join('&');
+};
+w.gl.utils.removeParams = (params) => {
+ const url = new URL(window.location.href);
+ params.forEach((param) => {
+ url.search = w.gl.utils.removeParamQueryString(url.search, param);
+ });
+ return url.href;
+};
+w.gl.utils.getLocationHash = function(url) {
+ var hashIndex;
+ if (typeof url === 'undefined') {
+ // Note: We can't use window.location.hash here because it's
+ // not consistent across browsers - Firefox will pre-decode it
+ url = window.location.href;
+ }
+ hashIndex = url.indexOf('#');
+ return hashIndex === -1 ? null : url.substring(hashIndex + 1);
+};
- w.gl.utils.refreshCurrentPage = () => gl.utils.visitUrl(document.location.href);
+w.gl.utils.refreshCurrentPage = () => gl.utils.visitUrl(document.location.href);
- w.gl.utils.visitUrl = (url) => {
- document.location.href = url;
- };
- })(window);
-}).call(window);
+w.gl.utils.visitUrl = (url) => {
+ document.location.href = url;
+};
diff --git a/app/assets/javascripts/vue_pipelines_index/components/async_button.js b/app/assets/javascripts/vue_pipelines_index/components/async_button.vue
index 58b8db4d519..11da6e908b7 100644
--- a/app/assets/javascripts/vue_pipelines_index/components/async_button.js
+++ b/app/assets/javascripts/vue_pipelines_index/components/async_button.vue
@@ -1,3 +1,4 @@
+<script>
/* eslint-disable no-new, no-alert */
/* global Flash */
import '~/flash';
@@ -65,29 +66,31 @@ export default {
this.isLoading = true;
this.service.postAction(this.endpoint)
- .then(() => {
- this.isLoading = false;
- eventHub.$emit('refreshPipelines');
- })
- .catch(() => {
- this.isLoading = false;
- new Flash('An error occured while making the request.');
- });
+ .then(() => {
+ this.isLoading = false;
+ eventHub.$emit('refreshPipelines');
+ })
+ .catch(() => {
+ this.isLoading = false;
+ new Flash('An error occured while making the request.');
+ });
},
},
-
- template: `
- <button
- type="button"
- @click="onClick"
- :class="buttonClass"
- :title="title"
- :aria-label="title"
- data-container="body"
- data-placement="top"
- :disabled="isLoading">
- <i :class="iconClass" aria-hidden="true"/>
- <i class="fa fa-spinner fa-spin" aria-hidden="true" v-if="isLoading" />
- </button>
- `,
};
+</script>
+
+<template>
+ <button
+ type="button"
+ @click="onClick"
+ :class="buttonClass"
+ :title="title"
+ :aria-label="title"
+ data-container="body"
+ data-placement="top"
+ :disabled="isLoading"
+ >
+ <i :class="iconClass" aria-hidden="true"></i>
+ <i class="fa fa-spinner fa-spin" aria-hidden="true" v-if="isLoading"></i>
+ </button>
+</template>
diff --git a/app/assets/javascripts/vue_pipelines_index/components/empty_state.js b/app/assets/javascripts/vue_pipelines_index/components/empty_state.js
deleted file mode 100644
index 56b4858f4b4..00000000000
--- a/app/assets/javascripts/vue_pipelines_index/components/empty_state.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import pipelinesEmptyStateSVG from 'empty_states/icons/_pipelines_empty.svg';
-
-export default {
- props: {
- helpPagePath: {
- type: String,
- required: true,
- },
- },
-
- template: `
- <div class="row empty-state">
- <div class="col-xs-12">
- <div class="svg-content">
- ${pipelinesEmptyStateSVG}
- </div>
- </div>
-
- <div class="col-xs-12 text-center">
- <div class="text-content">
- <h4>Build with confidence</h4>
- <p>
- Continous Integration can help catch bugs by running your tests automatically,
- while Continuous Deployment can help you deliver code to your product environment.
- </p>
- <a :href="helpPagePath" class="btn btn-info">
- Get started with Pipelines
- </a>
- </div>
- </div>
- </div>
- `,
-};
diff --git a/app/assets/javascripts/vue_pipelines_index/components/empty_state.vue b/app/assets/javascripts/vue_pipelines_index/components/empty_state.vue
new file mode 100644
index 00000000000..ba158bc4a1e
--- /dev/null
+++ b/app/assets/javascripts/vue_pipelines_index/components/empty_state.vue
@@ -0,0 +1,34 @@
+<script>
+import pipelinesEmptyStateSVG from 'empty_states/icons/_pipelines_empty.svg';
+
+export default {
+ props: {
+ helpPagePath: {
+ type: String,
+ required: true,
+ },
+ },
+ data: () => ({ pipelinesEmptyStateSVG }),
+};
+</script>
+
+<template>
+ <div class="row empty-state">
+ <div class="col-xs-12">
+ <div class="svg-content" v-html="pipelinesEmptyStateSVG" />
+ </div>
+
+ <div class="col-xs-12 text-center">
+ <div class="text-content">
+ <h4>Build with confidence</h4>
+ <p>
+ Continous Integration can help catch bugs by running your tests automatically,
+ while Continuous Deployment can help you deliver code to your product environment.
+ </p>
+ <a :href="helpPagePath" class="btn btn-info">
+ Get started with Pipelines
+ </a>
+ </div>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_pipelines_index/components/error_state.js b/app/assets/javascripts/vue_pipelines_index/components/error_state.js
deleted file mode 100644
index e5d228bddf8..00000000000
--- a/app/assets/javascripts/vue_pipelines_index/components/error_state.js
+++ /dev/null
@@ -1,19 +0,0 @@
-import pipelinesErrorStateSVG from 'empty_states/icons/_pipelines_failed.svg';
-
-export default {
- template: `
- <div class="row empty-state js-pipelines-error-state">
- <div class="col-xs-12">
- <div class="svg-content">
- ${pipelinesErrorStateSVG}
- </div>
- </div>
-
- <div class="col-xs-12 text-center">
- <div class="text-content">
- <h4>The API failed to fetch the pipelines.</h4>
- </div>
- </div>
- </div>
- `,
-};
diff --git a/app/assets/javascripts/vue_pipelines_index/components/error_state.vue b/app/assets/javascripts/vue_pipelines_index/components/error_state.vue
new file mode 100644
index 00000000000..90cee68163e
--- /dev/null
+++ b/app/assets/javascripts/vue_pipelines_index/components/error_state.vue
@@ -0,0 +1,21 @@
+<script>
+import pipelinesErrorStateSVG from 'empty_states/icons/_pipelines_failed.svg';
+
+export default {
+ data: () => ({ pipelinesErrorStateSVG }),
+};
+</script>
+
+<template>
+ <div class="row empty-state js-pipelines-error-state">
+ <div class="col-xs-12">
+ <div class="svg-content" v-html="pipelinesErrorStateSVG" />
+ </div>
+
+ <div class="col-xs-12 text-center">
+ <div class="text-content">
+ <h4>The API failed to fetch the pipelines.</h4>
+ </div>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_pipelines_index/pipelines.js b/app/assets/javascripts/vue_pipelines_index/pipelines.js
index 5575aa72d5e..6eea4812f33 100644
--- a/app/assets/javascripts/vue_pipelines_index/pipelines.js
+++ b/app/assets/javascripts/vue_pipelines_index/pipelines.js
@@ -4,8 +4,8 @@ import PipelinesService from './services/pipelines_service';
import eventHub from './event_hub';
import PipelinesTableComponent from '../vue_shared/components/pipelines_table';
import TablePaginationComponent from '../vue_shared/components/table_pagination';
-import EmptyState from './components/empty_state';
-import ErrorState from './components/error_state';
+import EmptyState from './components/empty_state.vue';
+import ErrorState from './components/error_state.vue';
import NavigationTabs from './components/navigation_tabs';
import NavigationControls from './components/nav_controls';
import Poll from '../lib/utils/poll';
diff --git a/app/assets/javascripts/vue_shared/components/pipelines_table_row.js b/app/assets/javascripts/vue_shared/components/pipelines_table_row.js
index f5b3cb9214e..8ebe12cb1c5 100644
--- a/app/assets/javascripts/vue_shared/components/pipelines_table_row.js
+++ b/app/assets/javascripts/vue_shared/components/pipelines_table_row.js
@@ -1,6 +1,6 @@
/* eslint-disable no-param-reassign */
-import AsyncButtonComponent from '../../vue_pipelines_index/components/async_button';
+import AsyncButtonComponent from '../../vue_pipelines_index/components/async_button.vue';
import PipelinesActionsComponent from '../../vue_pipelines_index/components/pipelines_actions';
import PipelinesArtifactsComponent from '../../vue_pipelines_index/components/pipelines_artifacts';
import PipelinesStatusComponent from '../../vue_pipelines_index/components/status';
diff --git a/app/assets/stylesheets/framework/calendar.scss b/app/assets/stylesheets/framework/calendar.scss
index 9a0f7a14e57..759401a7806 100644
--- a/app/assets/stylesheets/framework/calendar.scss
+++ b/app/assets/stylesheets/framework/calendar.scss
@@ -5,7 +5,7 @@
direction: rtl;
@media (min-width: $screen-sm-min) and (max-width: $screen-md-max) {
- overflow-x: scroll;
+ overflow-x: auto;
}
}
diff --git a/app/assets/stylesheets/framework/filters.scss b/app/assets/stylesheets/framework/filters.scss
index 12465d4a70b..11d44df4867 100644
--- a/app/assets/stylesheets/framework/filters.scss
+++ b/app/assets/stylesheets/framework/filters.scss
@@ -82,7 +82,7 @@
.input-token:last-child {
flex: 1;
-webkit-flex: 1;
- max-width: initial;
+ max-width: inherit;
}
}
@@ -246,17 +246,17 @@
}
}
-.filtered-search-history-dropdown-toggle-button {
+.filtered-search-history-dropdown-wrapper {
+ position: static;
display: flex;
- align-items: center;
+ flex-direction: column;
+}
+
+.filtered-search-history-dropdown-toggle-button {
+ flex: 1;
width: auto;
- height: 100%;
- padding-top: 0;
- padding-left: 0.75em;
- padding-bottom: 0;
- padding-right: 0.5em;
+ padding-right: 10px;
- background-color: transparent;
border-radius: 0;
border-top: 0;
border-left: 0;
@@ -264,6 +264,7 @@
border-right: 1px solid $border-color;
color: $gl-text-color-secondary;
+ line-height: 1;
transition: color 0.1s linear;
@@ -275,24 +276,21 @@
}
.dropdown-toggle-text {
+ display: inline-block;
color: inherit;
.fa {
+ vertical-align: middle;
color: inherit;
}
}
.fa {
- position: initial;
+ position: static;
}
}
-.filtered-search-history-dropdown-wrapper {
- position: initial;
- flex-shrink: 0;
-}
-
.filtered-search-history-dropdown {
width: 40%;
diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss
index 03fddaeb163..144adbcdaef 100644
--- a/app/assets/stylesheets/pages/builds.scss
+++ b/app/assets/stylesheets/pages/builds.scss
@@ -39,7 +39,7 @@
overflow-y: hidden;
font-size: 12px;
- .fa-refresh {
+ .fa-spinner {
font-size: 24px;
margin-left: 20px;
}
@@ -219,7 +219,7 @@
font-size: 12px;
position: relative;
- .fa-refresh {
+ .fa-spinner {
font-size: 24px;
}
@@ -366,7 +366,7 @@
background-color: $row-hover;
}
- .fa-refresh {
+ .fa-spinner {
font-size: 13px;
margin-left: 3px;
}
diff --git a/app/assets/stylesheets/pages/events.scss b/app/assets/stylesheets/pages/events.scss
index 79da490675a..5b723f7c722 100644
--- a/app/assets/stylesheets/pages/events.scss
+++ b/app/assets/stylesheets/pages/events.scss
@@ -10,10 +10,14 @@
position: relative;
&.event-inline {
- .profile-icon {
+ .system-note-image {
top: 20px;
}
+ .user-avatar {
+ top: 14px;
+ }
+
.event-title,
.event-item-timestamp {
line-height: 40px;
@@ -24,7 +28,7 @@
color: $gl-text-color;
}
- .profile-icon {
+ .system-note-image {
position: absolute;
left: 0;
top: 14px;
@@ -35,15 +39,18 @@
fill: $gl-text-color-secondary;
}
- &.open-icon svg {
- fill: $green-300;
+ &.opened-icon,
+ &.created-icon {
+ svg {
+ fill: $green-300;
+ }
}
&.closed-icon svg {
fill: $red-300;
}
- &.fork-icon svg {
+ &.accepted-icon svg {
fill: $blue-300;
}
}
@@ -128,8 +135,7 @@
li {
&.commit {
background: transparent;
- padding: 3px;
- padding-left: 0;
+ padding: 0;
border: none;
.commit-row-title {
@@ -183,7 +189,7 @@
max-width: 100%;
}
- .profile-icon {
+ .system-note-image {
display: none;
}
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 2f946ab2f59..6a419384a34 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -523,11 +523,12 @@
}
.content-block {
- border-top: 1px solid $border-color;
padding: $gl-padding-top $gl-padding;
}
.comments-disabled-notif {
+ line-height: 28px;
+
.btn {
margin-left: 5px;
}
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index 94ea4c5c8c6..ad0f2f6efbb 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -18,12 +18,12 @@ ul.notes {
float: left;
svg {
- width: 18px;
- height: 18px;
+ width: 16px;
+ height: 16px;
fill: $gray-darkest;
position: absolute;
- left: 30px;
- top: 15px;
+ left: 0;
+ top: 16px;
}
}
@@ -144,6 +144,10 @@ ul.notes {
padding: 0;
clear: both;
+ @media (min-width: $screen-sm-min) {
+ margin-left: 65px;
+ }
+
&.timeline-entry::after {
clear: none;
}
@@ -172,6 +176,10 @@ ul.notes {
.timeline-content {
padding: 14px 10px;
+
+ @media (min-width: $screen-sm-min) {
+ margin-left: 20px;
+ }
}
.note-header {
diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb
index c6651254d70..008d2f5815f 100644
--- a/app/controllers/projects/compare_controller.rb
+++ b/app/controllers/projects/compare_controller.rb
@@ -61,7 +61,6 @@ class Projects::CompareController < Projects::ApplicationController
@environment = EnvironmentsFinder.new(@project, current_user, environment_params).execute.last
@diff_notes_disabled = true
- @grouped_diff_discussions = {}
end
end
diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb
index b668a9331e7..1e41f980f31 100644
--- a/app/controllers/projects/hooks_controller.rb
+++ b/app/controllers/projects/hooks_controller.rb
@@ -10,7 +10,7 @@ class Projects::HooksController < Projects::ApplicationController
@hook = @project.hooks.new(hook_params)
@hook.save
- unless @hook.valid?
+ unless @hook.valid?
@hooks = @project.hooks.select(&:persisted?)
flash[:alert] = @hook.errors.full_messages.join.html_safe
end
@@ -49,7 +49,7 @@ class Projects::HooksController < Projects::ApplicationController
def hook_params
params.require(:hook).permit(
- :build_events,
+ :job_events,
:pipeline_events,
:enable_ssl_verification,
:issues_events,
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 5c1f7e69ee8..09dc8b38229 100755
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -16,7 +16,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController
before_action :define_show_vars, only: [:show, :diffs, :commits, :conflicts, :conflict_for_path, :builds, :pipelines]
before_action :define_widget_vars, only: [:merge, :cancel_merge_when_pipeline_succeeds, :merge_check]
before_action :define_commit_vars, only: [:diffs]
- before_action :define_diff_comment_vars, only: [:diffs]
before_action :ensure_ref_fetched, only: [:show, :diffs, :commits, :builds, :conflicts, :conflict_for_path, :pipelines]
before_action :close_merge_request_without_source_project, only: [:show, :diffs, :commits, :builds, :pipelines]
before_action :apply_diff_view_cookie!, only: [:new_diffs]
@@ -39,7 +38,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@collection_type = "MergeRequest"
@merge_requests = merge_requests_collection
@merge_requests = @merge_requests.page(params[:page])
- @merge_requests = @merge_requests.includes(merge_request_diff: :merge_request)
+ @merge_requests = @merge_requests.preload(merge_request_diff: :merge_request)
@issuable_meta_data = issuable_meta_data(@merge_requests, @collection_type)
if @merge_requests.out_of_range? && @merge_requests.total_pages != 0
@@ -101,34 +100,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
respond_to do |format|
format.html { define_discussion_vars }
format.json do
- @merge_request_diff =
- if params[:diff_id]
- @merge_request.merge_request_diffs.viewable.find(params[:diff_id])
- else
- @merge_request.merge_request_diff
- end
-
- @merge_request_diffs = @merge_request.merge_request_diffs.viewable.select_without_diff
- @comparable_diffs = @merge_request_diffs.select { |diff| diff.id < @merge_request_diff.id }
-
- if params[:start_sha].present?
- @start_sha = params[:start_sha]
- @start_version = @comparable_diffs.find { |diff| diff.head_commit_sha == @start_sha }
-
- unless @start_version
- @start_sha = @merge_request_diff.head_commit_sha
- @start_version = @merge_request_diff
- end
- end
+ define_diff_vars
+ define_diff_comment_vars
@environment = @merge_request.environments_for(current_user).last
- if @start_sha
- compared_diff_version
- else
- original_diff_version
- end
-
render json: { html: view_to_html_string("projects/merge_requests/show/_diffs") }
end
end
@@ -140,16 +116,17 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def diff_for_path
if params[:id]
merge_request
+ define_diff_vars
define_diff_comment_vars
else
build_merge_request
+ @diffs = @merge_request.diffs(diff_options)
@diff_notes_disabled = true
- @grouped_diff_discussions = {}
end
define_commit_vars
- render_diff_for_path(@merge_request.diffs(diff_options))
+ render_diff_for_path(@diffs)
end
def commits
@@ -586,15 +563,46 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@base_commit = @merge_request.diff_base_commit || @merge_request.likely_diff_base_commit
end
+ def define_diff_vars
+ @merge_request_diff =
+ if params[:diff_id]
+ @merge_request.merge_request_diffs.viewable.find(params[:diff_id])
+ else
+ @merge_request.merge_request_diff
+ end
+
+ @merge_request_diffs = @merge_request.merge_request_diffs.viewable.select_without_diff
+ @comparable_diffs = @merge_request_diffs.select { |diff| diff.id < @merge_request_diff.id }
+
+ if params[:start_sha].present?
+ @start_sha = params[:start_sha]
+ @start_version = @comparable_diffs.find { |diff| diff.head_commit_sha == @start_sha }
+
+ unless @start_version
+ @start_sha = @merge_request_diff.head_commit_sha
+ @start_version = @merge_request_diff
+ end
+ end
+
+ @diffs =
+ if @start_sha
+ @merge_request_diff.compare_with(@start_sha).diffs(diff_options)
+ else
+ @merge_request_diff.diffs(diff_options)
+ end
+ end
+
def define_diff_comment_vars
@new_diff_note_attrs = {
noteable_type: 'MergeRequest',
noteable_id: @merge_request.id
}
+ @diff_notes_disabled = !@merge_request_diff.latest? || @start_sha
+
@use_legacy_diff_notes = !@merge_request.has_complete_diff_refs?
- @grouped_diff_discussions = @merge_request.grouped_diff_discussions
+ @grouped_diff_discussions = @merge_request.grouped_diff_discussions(@merge_request_diff.diff_refs)
@notes = prepare_notes_for_rendering(@grouped_diff_discussions.values.flatten.flat_map(&:notes))
end
@@ -678,16 +686,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request = MergeRequests::BuildService.new(project, current_user, merge_request_params.merge(diff_options: diff_options)).execute
end
- def compared_diff_version
- @diff_notes_disabled = true
- @diffs = @merge_request_diff.compare_with(@start_sha).diffs(diff_options)
- end
-
- def original_diff_version
- @diff_notes_disabled = !@merge_request_diff.latest?
- @diffs = @merge_request_diff.diffs(diff_options)
- end
-
def close_merge_request_without_source_project
if !@merge_request.source_project && @merge_request.open?
@merge_request.close
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 47f7e0b1b28..6807c37f972 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -345,7 +345,11 @@ class ProjectsController < Projects::ApplicationController
end
def project_view_files?
- current_user && current_user.project_view == 'files'
+ if current_user
+ current_user.project_view == 'files'
+ else
+ project_view_files_allowed?
+ end
end
# Override extract_ref from ExtractsPath, which returns the branch and file path
@@ -359,4 +363,8 @@ class ProjectsController < Projects::ApplicationController
def get_id
project.repository.root_ref
end
+
+ def project_view_files_allowed?
+ !project.empty_repo? && can?(current_user, :download_code, project)
+ end
end
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb
index b0ac1623fbe..6c3f3a61e0a 100644
--- a/app/helpers/blob_helper.rb
+++ b/app/helpers/blob_helper.rb
@@ -118,6 +118,10 @@ module BlobHelper
blob && blob.text? && !blob.lfs_pointer? && !blob.only_display_raw?
end
+ def blob_rendered_as_text?(blob)
+ blob_text_viewable?(blob) && blob.to_partial_path(@project) == 'text'
+ end
+
def blob_size(blob)
if blob.lfs_pointer?
blob.lfs_size
diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb
index 5e0886cc599..dc144906548 100644
--- a/app/helpers/diff_helper.rb
+++ b/app/helpers/diff_helper.rb
@@ -62,6 +62,8 @@ module DiffHelper
end
def parallel_diff_discussions(left, right, diff_file)
+ return unless @grouped_diff_discussions
+
discussions_left = discussions_right = nil
if left && (left.unchanged? || left.removed?)
diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb
index fb872a13f74..5f5c76d3722 100644
--- a/app/helpers/events_helper.rb
+++ b/app/helpers/events_helper.rb
@@ -1,4 +1,15 @@
module EventsHelper
+ ICON_NAMES_BY_EVENT_TYPE = {
+ 'pushed to' => 'icon_commit',
+ 'pushed new' => 'icon_commit',
+ 'created' => 'icon_status_open',
+ 'opened' => 'icon_status_open',
+ 'closed' => 'icon_status_closed',
+ 'accepted' => 'icon_code_fork',
+ 'commented on' => 'icon_comment_o',
+ 'deleted' => 'icon_trash_o'
+ }.freeze
+
def link_to_author(event)
author = event.author
@@ -183,4 +194,21 @@ module EventsHelper
"event-inline"
end
end
+
+ def icon_for_event(note)
+ icon_name = ICON_NAMES_BY_EVENT_TYPE[note]
+ custom_icon(icon_name) if icon_name
+ end
+
+ def icon_for_profile_event(event)
+ if current_path?('users#show')
+ content_tag :div, class: "system-note-image #{event.action_name.parameterize}-icon" do
+ icon_for_event(event.action_name)
+ end
+ else
+ content_tag :div, class: 'system-note-image user-avatar' do
+ author_avatar(event, size: 32)
+ end
+ end
+ end
end
diff --git a/app/helpers/javascript_helper.rb b/app/helpers/javascript_helper.rb
index 68c09c922a6..d5e77c7e271 100644
--- a/app/helpers/javascript_helper.rb
+++ b/app/helpers/javascript_helper.rb
@@ -3,7 +3,8 @@ module JavascriptHelper
javascript_include_tag asset_path(js)
end
- def page_specific_javascript_bundle_tag(js)
- javascript_include_tag(*webpack_asset_paths(js))
+ # deprecated; use webpack_bundle_tag directly instead
+ def page_specific_javascript_bundle_tag(bundle)
+ webpack_bundle_tag(bundle)
end
end
diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb
index 5f3d89cf6cb..eab0738a368 100644
--- a/app/helpers/notes_helper.rb
+++ b/app/helpers/notes_helper.rb
@@ -61,12 +61,23 @@ module NotesHelper
end
def discussion_diff_path(discussion)
- return unless discussion.diff_discussion?
+ if discussion.for_merge_request? && discussion.diff_discussion?
+ if discussion.active?
+ # Without a diff ID, the link always points to the latest diff version
+ diff_id = nil
+ elsif merge_request_diff = discussion.latest_merge_request_diff
+ diff_id = merge_request_diff.id
+ else
+ # If the discussion is not active, and we cannot find the latest
+ # merge request diff for this discussion, we return no path at all.
+ return
+ end
- if discussion.for_merge_request? && discussion.active?
- diffs_namespace_project_merge_request_path(discussion.project.namespace, discussion.project, discussion.noteable, anchor: discussion.line_code)
+ diffs_namespace_project_merge_request_path(discussion.project.namespace, discussion.project, discussion.noteable, diff_id: diff_id, anchor: discussion.line_code)
elsif discussion.for_commit?
- namespace_project_commit_path(discussion.project.namespace, discussion.project, discussion.noteable, anchor: discussion.line_code)
+ anchor = discussion.line_code if discussion.diff_discussion?
+
+ namespace_project_commit_path(discussion.project.namespace, discussion.project, discussion.noteable, anchor: anchor)
end
end
end
diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb
index 243ef39ef61..de959f13713 100644
--- a/app/helpers/preferences_helper.rb
+++ b/app/helpers/preferences_helper.rb
@@ -63,6 +63,10 @@ module PreferencesHelper
end
def anonymous_project_view
- @project.empty_repo? || !can?(current_user, :download_code, @project) ? 'activity' : 'readme'
+ if !@project.empty_repo? && can?(current_user, :download_code, @project)
+ 'files'
+ else
+ 'activity'
+ end
end
end
diff --git a/app/helpers/webpack_helper.rb b/app/helpers/webpack_helper.rb
new file mode 100644
index 00000000000..6bacda9fe75
--- /dev/null
+++ b/app/helpers/webpack_helper.rb
@@ -0,0 +1,30 @@
+require 'webpack/rails/manifest'
+
+module WebpackHelper
+ def webpack_bundle_tag(bundle)
+ javascript_include_tag(*gitlab_webpack_asset_paths(bundle))
+ end
+
+ # override webpack-rails gem helper until changes can make it upstream
+ def gitlab_webpack_asset_paths(source, extension: nil)
+ return "" unless source.present?
+
+ paths = Webpack::Rails::Manifest.asset_paths(source)
+ if extension
+ paths = paths.select { |p| p.ends_with? ".#{extension}" }
+ end
+
+ # include full webpack-dev-server url for rspec tests running locally
+ if Rails.env.test? && Rails.configuration.webpack.dev_server.enabled
+ host = Rails.configuration.webpack.dev_server.host
+ port = Rails.configuration.webpack.dev_server.port
+ protocol = Rails.configuration.webpack.dev_server.https ? 'https' : 'http'
+
+ paths.map! do |p|
+ "#{protocol}://#{host}:#{port}#{p}"
+ end
+ end
+
+ paths
+ end
+end
diff --git a/app/models/blob.rb b/app/models/blob.rb
index 801d3442803..55872acef51 100644
--- a/app/models/blob.rb
+++ b/app/models/blob.rb
@@ -42,12 +42,16 @@ class Blob < SimpleDelegator
size && truncated?
end
+ def extension
+ extname.downcase.delete('.')
+ end
+
def svg?
text? && language && language.name == 'SVG'
end
def pdf?
- name && File.extname(name) == '.pdf'
+ extension == 'pdf'
end
def ipython_notebook?
@@ -55,11 +59,15 @@ class Blob < SimpleDelegator
end
def sketch?
- binary? && extname.downcase.delete('.') == 'sketch'
+ binary? && extension == 'sketch'
end
def stl?
- extname.downcase.delete('.') == 'stl'
+ extension == 'stl'
+ end
+
+ def markup?
+ text? && Gitlab::MarkupHelper.markup?(name)
end
def size_within_svg_limits?
@@ -77,8 +85,10 @@ class Blob < SimpleDelegator
else
'text'
end
- elsif image? || svg?
+ elsif image?
'image'
+ elsif svg?
+ 'svg'
elsif pdf?
'pdf'
elsif ipython_notebook?
@@ -87,8 +97,18 @@ class Blob < SimpleDelegator
'sketch'
elsif stl?
'stl'
+ elsif markup?
+ if only_display_raw?
+ 'too_large'
+ else
+ 'markup'
+ end
elsif text?
- 'text'
+ if only_display_raw?
+ 'too_large'
+ else
+ 'text'
+ end
else
'download'
end
diff --git a/app/models/ci/trigger.rb b/app/models/ci/trigger.rb
index b59e235c425..2f64f70685a 100644
--- a/app/models/ci/trigger.rb
+++ b/app/models/ci/trigger.rb
@@ -7,7 +7,7 @@ module Ci
belongs_to :project
belongs_to :owner, class_name: "User"
- has_many :trigger_requests, dependent: :destroy
+ has_many :trigger_requests
has_one :trigger_schedule, dependent: :destroy
validates :token, presence: true, uniqueness: true
diff --git a/app/models/commit.rb b/app/models/commit.rb
index 5c452f78546..8b8b3f00202 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -326,14 +326,13 @@ class Commit
end
def raw_diffs(*args)
- use_gitaly = Gitlab::GitalyClient.feature_enabled?(:commit_raw_diffs)
- deltas_only = args.last.is_a?(Hash) && args.last[:deltas_only]
-
- if use_gitaly && !deltas_only
- Gitlab::GitalyClient::Commit.diff_from_parent(self, *args)
- else
- raw.diffs(*args)
- end
+ # NOTE: This feature is intentionally disabled until
+ # https://gitlab.com/gitlab-org/gitaly/issues/178 is resolved
+ # if Gitlab::GitalyClient.feature_enabled?(:commit_raw_diffs)
+ # Gitlab::GitalyClient::Commit.diff_from_parent(self, *args)
+ # else
+ raw.diffs(*args)
+ # end
end
def diffs(diff_options = nil)
diff --git a/app/models/concerns/discussion_on_diff.rb b/app/models/concerns/discussion_on_diff.rb
index 87db0c810c3..8ee42875670 100644
--- a/app/models/concerns/discussion_on_diff.rb
+++ b/app/models/concerns/discussion_on_diff.rb
@@ -2,11 +2,9 @@
module DiscussionOnDiff
extend ActiveSupport::Concern
- included do
- NUMBER_OF_TRUNCATED_DIFF_LINES = 16
-
- memoized_values << :active
+ NUMBER_OF_TRUNCATED_DIFF_LINES = 16
+ included do
delegate :line_code,
:original_line_code,
:diff_file,
@@ -29,12 +27,6 @@ module DiscussionOnDiff
true
end
- def active?
- return @active if @active.present?
-
- @active = first_note.active?
- end
-
# Returns an array of at most 16 highlighted lines above a diff note
def truncated_diff_lines(highlight: true)
lines = highlight ? highlighted_diff_lines : diff_lines
diff --git a/app/models/concerns/note_on_diff.rb b/app/models/concerns/note_on_diff.rb
index 1a5a7007a2b..6c27dd5aa5c 100644
--- a/app/models/concerns/note_on_diff.rb
+++ b/app/models/concerns/note_on_diff.rb
@@ -25,4 +25,18 @@ module NoteOnDiff
def diff_attributes
raise NotImplementedError
end
+
+ def active?(diff_refs = nil)
+ raise NotImplementedError
+ end
+
+ private
+
+ def noteable_diff_refs
+ if noteable.respond_to?(:diff_sha_refs)
+ noteable.diff_sha_refs
+ else
+ noteable.diff_refs
+ end
+ end
end
diff --git a/app/models/concerns/noteable.rb b/app/models/concerns/noteable.rb
index 772ff6a6d2f..dd1e6630642 100644
--- a/app/models/concerns/noteable.rb
+++ b/app/models/concerns/noteable.rb
@@ -36,10 +36,10 @@ module Noteable
.discussions(self)
end
- def grouped_diff_discussions
+ def grouped_diff_discussions(*args)
# Doesn't use `discussion_notes`, because this may include commit diff notes
# besides MR diff notes, that we do no want to display on the MR Changes tab.
- notes.inc_relations_for_view.grouped_diff_discussions
+ notes.inc_relations_for_view.grouped_diff_discussions(*args)
end
def resolvable_discussions
diff --git a/app/models/container_repository.rb b/app/models/container_repository.rb
index 9682df3a586..82f4182d59a 100644
--- a/app/models/container_repository.rb
+++ b/app/models/container_repository.rb
@@ -23,6 +23,10 @@ class ContainerRepository < ActiveRecord::Base
@path ||= [project.full_path, name].select(&:present?).join('/')
end
+ def location
+ File.join(registry.path, path)
+ end
+
def tag(tag)
ContainerRegistry::Tag.new(self, tag)
end
diff --git a/app/models/diff_discussion.rb b/app/models/diff_discussion.rb
index d9b7e484e0f..6a6466b493b 100644
--- a/app/models/diff_discussion.rb
+++ b/app/models/diff_discussion.rb
@@ -10,6 +10,7 @@ class DiffDiscussion < Discussion
delegate :position,
:original_position,
+ :latest_merge_request_diff,
to: :first_note
diff --git a/app/models/diff_note.rb b/app/models/diff_note.rb
index 1523244f8a8..abe4518d62a 100644
--- a/app/models/diff_note.rb
+++ b/app/models/diff_note.rb
@@ -65,20 +65,18 @@ class DiffNote < Note
self.position.diff_refs == diff_refs
end
+ def latest_merge_request_diff
+ return unless for_merge_request?
+
+ self.noteable.merge_request_diff_for(self.position.diff_refs)
+ end
+
private
def supported?
for_commit? || self.noteable.has_complete_diff_refs?
end
- def noteable_diff_refs
- if noteable.respond_to?(:diff_sha_refs)
- noteable.diff_sha_refs
- else
- noteable.diff_refs
- end
- end
-
def set_original_position
self.original_position = self.position.dup unless self.original_position&.complete?
end
diff --git a/app/models/issue.rb b/app/models/issue.rb
index d8d9db477d2..d39ae3a6c92 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -26,8 +26,6 @@ class Issue < ActiveRecord::Base
validates :project, presence: true
- scope :cared, ->(user) { where(assignee_id: user) }
- scope :open_for, ->(user) { opened.assigned_to(user) }
scope :in_projects, ->(project_ids) { where(project_id: project_ids) }
scope :without_due_date, -> { where(due_date: nil) }
diff --git a/app/models/label.rb b/app/models/label.rb
index 568fa6d44f5..d8b0e250732 100644
--- a/app/models/label.rb
+++ b/app/models/label.rb
@@ -21,6 +21,8 @@ class Label < ActiveRecord::Base
has_many :issues, through: :label_links, source: :target, source_type: 'Issue'
has_many :merge_requests, through: :label_links, source: :target, source_type: 'MergeRequest'
+ before_validation :strip_whitespace_from_title_and_color
+
validates :color, color: true, allow_blank: false
# Don't allow ',' for label titles
@@ -193,4 +195,8 @@ class Label < ActiveRecord::Base
def sanitize_title(value)
CGI.unescapeHTML(Sanitize.clean(value.to_s))
end
+
+ def strip_whitespace_from_title_and_color
+ %w(color title).each { |attr| self[attr] = self[attr]&.strip }
+ end
end
diff --git a/app/models/legacy_diff_discussion.rb b/app/models/legacy_diff_discussion.rb
index cb2651a03f8..e617ce36f56 100644
--- a/app/models/legacy_diff_discussion.rb
+++ b/app/models/legacy_diff_discussion.rb
@@ -7,6 +7,8 @@
class LegacyDiffDiscussion < Discussion
include DiscussionOnDiff
+ memoized_values << :active
+
def legacy_diff_discussion?
true
end
@@ -15,6 +17,12 @@ class LegacyDiffDiscussion < Discussion
LegacyDiffNote
end
+ def active?(*args)
+ return @active if @active.present?
+
+ @active = first_note.active?(*args)
+ end
+
def collapsed?
!active?
end
diff --git a/app/models/legacy_diff_note.rb b/app/models/legacy_diff_note.rb
index 9a77557ebcd..d7c627432d2 100644
--- a/app/models/legacy_diff_note.rb
+++ b/app/models/legacy_diff_note.rb
@@ -56,11 +56,12 @@ class LegacyDiffNote < Note
#
# If the note's current diff cannot be matched in the MergeRequest's current
# diff, it's considered inactive.
- def active?
+ def active?(diff_refs = nil)
return @active if defined?(@active)
return true if for_commit?
return true unless diff_line
return false unless noteable
+ return false if diff_refs && diff_refs != noteable_diff_refs
noteable_diff = find_noteable_diff
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index b71a9e17a93..1d4827375d7 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -104,7 +104,6 @@ class MergeRequest < ActiveRecord::Base
scope :by_source_or_target_branch, ->(branch_name) do
where("source_branch = :branch OR target_branch = :branch", branch: branch_name)
end
- scope :cared, ->(user) { where('assignee_id = :user OR author_id = :user', user: user.id) }
scope :by_milestone, ->(milestone) { where(milestone_id: milestone) }
scope :of_projects, ->(ids) { where(target_project_id: ids) }
scope :from_project, ->(project) { where(source_project_id: project.id) }
@@ -367,6 +366,14 @@ class MergeRequest < ActiveRecord::Base
merge_request_diff(true)
end
+ def merge_request_diff_for(diff_refs)
+ @merge_request_diffs_by_diff_refs ||= Hash.new do |h, diff_refs|
+ h[diff_refs] = merge_request_diffs.viewable.select_without_diff.find_by_diff_refs(diff_refs)
+ end
+
+ @merge_request_diffs_by_diff_refs[diff_refs]
+ end
+
def reload_diff_if_branch_changed
if source_branch_changed? || target_branch_changed?
reload_diff
diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb
index 6ad56b842b2..6604af2b47e 100644
--- a/app/models/merge_request_diff.rb
+++ b/app/models/merge_request_diff.rb
@@ -31,6 +31,10 @@ class MergeRequestDiff < ActiveRecord::Base
# It allows you to override variables like head_commit_sha before getting diff.
after_create :save_git_content, unless: :importing?
+ def self.find_by_diff_refs(diff_refs)
+ find_by(start_commit_sha: diff_refs.start_sha, head_commit_sha: diff_refs.head_sha, base_commit_sha: diff_refs.base_sha)
+ end
+
def self.select_without_diff
select(column_names - ['st_diffs'])
end
@@ -130,6 +134,12 @@ class MergeRequestDiff < ActiveRecord::Base
st_commits.map { |commit| commit[:id] }
end
+ def diff_refs=(new_diff_refs)
+ self.base_commit_sha = new_diff_refs&.base_sha
+ self.start_commit_sha = new_diff_refs&.start_sha
+ self.head_commit_sha = new_diff_refs&.head_sha
+ end
+
def diff_refs
return unless start_commit_sha || base_commit_sha
diff --git a/app/models/note.rb b/app/models/note.rb
index 1ea7b946061..630d0adbece 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -96,6 +96,7 @@ class Note < ActiveRecord::Base
before_validation :set_discussion_id, on: :create
after_save :keep_around_commit, unless: :for_personal_snippet?
after_save :expire_etag_cache
+ after_destroy :expire_etag_cache
class << self
def model_name
@@ -113,11 +114,11 @@ class Note < ActiveRecord::Base
Discussion.build(notes)
end
- def grouped_diff_discussions
+ def grouped_diff_discussions(diff_refs = nil)
diff_notes.
fresh.
discussions.
- select(&:active?).
+ select { |n| n.active?(diff_refs) }.
group_by(&:line_code)
end
@@ -140,6 +141,10 @@ class Note < ActiveRecord::Base
true
end
+ def latest_merge_request_diff
+ nil
+ end
+
def max_attachment_size
current_application_settings.max_attachment_size.megabytes.to_i
end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index f4c51cdfdf4..2b11ed6128e 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -407,8 +407,6 @@ class Repository
# Runs code after a repository has been forked/imported.
def after_import
expire_content_cache
- expire_tags_cache
- expire_branches_cache
end
# Runs code after a new commit has been pushed.
@@ -965,13 +963,15 @@ class Repository
end
def is_ancestor?(ancestor_id, descendant_id)
- Gitlab::GitalyClient.migrate(:is_ancestor) do |is_enabled|
- if is_enabled
- raw_repository.is_ancestor?(ancestor_id, descendant_id)
- else
- merge_base_commit(ancestor_id, descendant_id) == ancestor_id
- end
- end
+ # NOTE: This feature is intentionally disabled until
+ # https://gitlab.com/gitlab-org/gitlab-ce/issues/30586 is resolved
+ # Gitlab::GitalyClient.migrate(:is_ancestor) do |is_enabled|
+ # if is_enabled
+ # raw_repository.is_ancestor?(ancestor_id, descendant_id)
+ # else
+ merge_base_commit(ancestor_id, descendant_id) == ancestor_id
+ # end
+ # end
end
def empty_repo?
diff --git a/app/models/user.rb b/app/models/user.rb
index 31e975b8e53..457ba05fb04 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -587,10 +587,6 @@ class User < ActiveRecord::Base
name.split.first unless name.blank?
end
- def cared_merge_requests
- MergeRequest.cared(self)
- end
-
def projects_limit_left
projects_limit - personal_projects.count
end
diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb
index cb58c115d54..87398303c68 100644
--- a/app/policies/group_policy.rb
+++ b/app/policies/group_policy.rb
@@ -28,6 +28,7 @@ class GroupPolicy < BasePolicy
can! :admin_namespace
can! :admin_group_member
can! :change_visibility_level
+ can! :create_subgroup if @user.can_create_group
end
if globally_viewable && @subject.request_access_enabled && !member
diff --git a/app/uploaders/file_uploader.rb b/app/uploaders/file_uploader.rb
index d6ccf0dc92c..d2783ce5b2f 100644
--- a/app/uploaders/file_uploader.rb
+++ b/app/uploaders/file_uploader.rb
@@ -38,10 +38,6 @@ class FileUploader < GitlabUploader
File.join(dynamic_path_segment, @secret)
end
- def cache_dir
- File.join(base_dir, 'tmp', @project.path_with_namespace, @secret)
- end
-
def model
project
end
diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml
index 703f611bb45..f4ba44096d3 100644
--- a/app/views/admin/application_settings/_form.html.haml
+++ b/app/views/admin/application_settings/_form.html.haml
@@ -571,6 +571,7 @@
The multiplier can also have a decimal value.
The default value (1) is a reasonable choice for the majority of GitLab
installations. Set to 0 to completely disable polling.
+ = link_to icon('question-circle'), help_page_path('administration/polling')
.form-actions
= f.submit 'Save', class: 'btn btn-save'
diff --git a/app/views/admin/health_check/show.html.haml b/app/views/admin/health_check/show.html.haml
index e79303240f0..6a208d76a38 100644
--- a/app/views/admin/health_check/show.html.haml
+++ b/app/views/admin/health_check/show.html.haml
@@ -13,7 +13,7 @@
= button_to reset_health_check_token_admin_application_settings_path,
method: :put, class: 'btn btn-default',
data: { confirm: 'Are you sure you want to reset the health check token?' } do
- = icon('refresh')
+ = icon('spinner')
Reset health check access token
%p.light
Health information can be retrieved as plain text, JSON, or XML using:
diff --git a/app/views/admin/runners/index.html.haml b/app/views/admin/runners/index.html.haml
index 7d26864d0f3..f118804cace 100644
--- a/app/views/admin/runners/index.html.haml
+++ b/app/views/admin/runners/index.html.haml
@@ -21,7 +21,7 @@
= button_to reset_runners_token_admin_application_settings_path,
method: :put, class: 'btn btn-default',
data: { confirm: 'Are you sure you want to reset registration token?' } do
- = icon('refresh')
+ = icon('spinner')
Reset runners registration token
.bs-callout
diff --git a/app/views/discussions/_discussion.html.haml b/app/views/discussions/_discussion.html.haml
index e04958817e4..8440fb3d785 100644
--- a/app/views/discussions/_discussion.html.haml
+++ b/app/views/discussions/_discussion.html.haml
@@ -20,21 +20,22 @@
= discussion.author.to_reference
started a discussion
+ - url = discussion_diff_path(discussion)
- if discussion.for_commit? && @noteable != discussion.noteable
on
- commit = discussion.noteable
- if commit
commit
- - anchor = discussion.line_code if discussion.diff_discussion?
- = link_to commit.short_id, namespace_project_commit_path(discussion.project.namespace, discussion.project, discussion.noteable, anchor: anchor), class: 'monospace'
+ = link_to commit.short_id, url, class: 'monospace'
- else
a deleted commit
- elsif discussion.diff_discussion?
on
- - if discussion.active?
- = link_to 'the diff', discussion_diff_path(discussion)
- - else
- an outdated diff
+ = conditional_link_to url.present?, url do
+ - if discussion.active?
+ the diff
+ - else
+ an outdated diff
= time_ago_with_tooltip(discussion.created_at, placement: "bottom", html_class: "note-created-ago")
= render "discussions/headline", discussion: discussion
diff --git a/app/views/events/event/_common.html.haml b/app/views/events/event/_common.html.haml
index af97e9588a5..01e72862114 100644
--- a/app/views/events/event/_common.html.haml
+++ b/app/views/events/event/_common.html.haml
@@ -1,13 +1,4 @@
-- if event.target
- - if event.action_name == "opened"
- .profile-icon.open-icon
- = custom_icon("icon_status_open")
- - elsif event.action_name == "closed"
- .profile-icon.closed-icon
- = custom_icon("icon_status_closed")
- - else
- .profile-icon.fork-icon
- = custom_icon("icon_code_fork")
+= icon_for_profile_event(event)
.event-title
%span.author_name= link_to_author event
diff --git a/app/views/events/event/_created_project.html.haml b/app/views/events/event/_created_project.html.haml
index fee85c94277..d8e59be57bb 100644
--- a/app/views/events/event/_created_project.html.haml
+++ b/app/views/events/event/_created_project.html.haml
@@ -1,5 +1,4 @@
-.profile-icon.open-icon
- = custom_icon("icon_status_open")
+= icon_for_profile_event(event)
.event-title
%span.author_name= link_to_author event
diff --git a/app/views/events/event/_note.html.haml b/app/views/events/event/_note.html.haml
index 83709f5e4d0..df4b9562215 100644
--- a/app/views/events/event/_note.html.haml
+++ b/app/views/events/event/_note.html.haml
@@ -1,5 +1,4 @@
-.profile-icon
- = custom_icon("icon_comment_o")
+= icon_for_profile_event(event)
.event-title
%span.author_name= link_to_author event
diff --git a/app/views/events/event/_push.html.haml b/app/views/events/event/_push.html.haml
index efdc8764acf..c0943100ae3 100644
--- a/app/views/events/event/_push.html.haml
+++ b/app/views/events/event/_push.html.haml
@@ -1,10 +1,6 @@
- project = event.project
-.profile-icon
- - if event.action_name == "deleted"
- = custom_icon("trash_o")
- - else
- = custom_icon("icon_commit")
+= icon_for_profile_event(event)
.event-title
%span.author_name= link_to_author event
diff --git a/app/views/groups/subgroups.html.haml b/app/views/groups/subgroups.html.haml
index be809083139..8f0724c0677 100644
--- a/app/views/groups/subgroups.html.haml
+++ b/app/views/groups/subgroups.html.haml
@@ -9,7 +9,7 @@
.nav-controls
= form_tag request.path, method: :get do |f|
= search_field_tag :filter_groups, params[:filter_groups], placeholder: 'Filter by name', class: 'form-control', spellcheck: false
- - if can? current_user, :admin_group, @group
+ - if can?(current_user, :create_subgroup, @group)
= link_to new_group_path(parent_id: @group.id), class: 'btn btn-new pull-right' do
New Subgroup
diff --git a/app/views/help/ui.html.haml b/app/views/help/ui.html.haml
index 207f80bedfe..615dd56afbd 100644
--- a/app/views/help/ui.html.haml
+++ b/app/views/help/ui.html.haml
@@ -252,7 +252,7 @@
= icon('chevron-down')
.dropdown-menu.dropdown-select.dropdown-menu-selectable
.dropdown-title
- %span Dropdown Title
+ %span Dropdown title
%button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } }
= icon('times')
.dropdown-input
@@ -291,7 +291,7 @@
= icon('chevron-down')
.dropdown-menu.dropdown-select.dropdown-menu-selectable.is-loading
.dropdown-title
- %span Dropdown Title
+ %span Dropdown title
%button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } }
= icon('times')
.dropdown-input
@@ -335,7 +335,7 @@
= icon('chevron-down')
.dropdown-menu.dropdown-select.dropdown-menu-selectable.dropdown-menu-user
.dropdown-title
- %span Dropdown Title
+ %span Dropdown title
%button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } }
= icon('times')
.dropdown-input
@@ -362,7 +362,7 @@
.dropdown-title
%button.dropdown-title-button.dropdown-menu-back{ aria: { label: "Go back" } }
= icon('arrow-left')
- %span Dropdown Title
+ %span Dropdown title
%button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } }
= icon('times')
.dropdown-input
diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml
index a611481a0a4..19473b6ab27 100644
--- a/app/views/layouts/_head.html.haml
+++ b/app/views/layouts/_head.html.haml
@@ -28,9 +28,9 @@
= stylesheet_link_tag "application", media: "all"
= stylesheet_link_tag "print", media: "print"
- = javascript_include_tag(*webpack_asset_paths("runtime"))
- = javascript_include_tag(*webpack_asset_paths("common"))
- = javascript_include_tag(*webpack_asset_paths("main"))
+ = webpack_bundle_tag "runtime"
+ = webpack_bundle_tag "common"
+ = webpack_bundle_tag "main"
- if content_for?(:page_specific_javascripts)
= yield :page_specific_javascripts
diff --git a/app/views/layouts/nav/_explore.html.haml b/app/views/layouts/nav/_explore.html.haml
index 3a1fcd00e9c..0cb367452f7 100644
--- a/app/views/layouts/nav/_explore.html.haml
+++ b/app/views/layouts/nav/_explore.html.haml
@@ -1,16 +1,29 @@
%ul
= nav_link(path: ['dashboard#show', 'root#show', 'projects#trending', 'projects#starred', 'projects#index'], html_options: {class: 'home'}) do
- = link_to explore_root_path, title: 'Projects' do
+ = link_to explore_root_path, title: 'Projects', class: 'dashboard-shortcuts-projects' do
+ .shortcut-mappings
+ .key
+ = icon('arrow-up', 'aria-label' => 'hidden')
+ P
%span
Projects
= nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do
- = link_to explore_groups_path, title: 'Groups' do
+ = link_to explore_groups_path, title: 'Groups', class: 'dashboard-shortcuts-groups' do
+ .shortcut-mappings
+ .key
+ = icon('arrow-up', 'aria-label' => 'hidden')
+ G
%span
Groups
= nav_link(controller: :snippets) do
- = link_to explore_snippets_path, title: 'Snippets' do
+ = link_to explore_snippets_path, title: 'Snippets', class: 'dashboard-shortcuts-snippets' do
+ .shortcut-mappings
+ .key
+ = icon('arrow-up', 'aria-label' => 'hidden')
+ S
%span
Snippets
+ %li.divider
= nav_link(controller: :help) do
= link_to help_path, title: 'Help' do
%span
diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml
index 299dace3406..e34cddeb3e2 100644
--- a/app/views/layouts/nav/_project.html.haml
+++ b/app/views/layouts/nav/_project.html.haml
@@ -23,7 +23,7 @@
Registry
- if project_nav_tab? :issues
- = nav_link(controller: [:issues, :labels, :milestones, :boards]) do
+ = nav_link(controller: @project.default_issues_tracker? ? [:issues, :labels, :milestones, :boards] : :issues) do
= link_to namespace_project_issues_path(@project.namespace, @project), title: 'Issues', class: 'shortcuts-issues' do
%span
Issues
@@ -31,7 +31,7 @@
%span.badge.count.issue_counter= number_with_delimiter(IssuesFinder.new(current_user, project_id: @project.id).execute.opened.count)
- if project_nav_tab? :merge_requests
- = nav_link(controller: :merge_requests) do
+ = nav_link(controller: @project.default_issues_tracker? ? :merge_requests : [:merge_requests, :labels, :milestones]) do
= link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do
%span
Merge Requests
diff --git a/app/views/notify/project_was_exported_email.html.haml b/app/views/notify/project_was_exported_email.html.haml
index 76440926a2b..3def26342a1 100644
--- a/app/views/notify/project_was_exported_email.html.haml
+++ b/app/views/notify/project_was_exported_email.html.haml
@@ -2,7 +2,7 @@
Project #{@project.name} was exported successfully.
%p
The project export can be downloaded from:
- = link_to download_export_namespace_project_url(@project.namespace, @project), rel: 'nofollow', download: '', do
+ = link_to download_export_namespace_project_url(@project.namespace, @project), rel: 'nofollow', download: '' do
= @project.name_with_namespace + " export"
%p
The download link will expire in 24 hours.
diff --git a/app/views/projects/blob/_blob.html.haml b/app/views/projects/blob/_blob.html.haml
index aa9b852035e..9aafff343f0 100644
--- a/app/views/projects/blob/_blob.html.haml
+++ b/app/views/projects/blob/_blob.html.haml
@@ -25,11 +25,10 @@
#blob-content-holder.blob-content-holder
%article.file-holder
= render "projects/blob/header", blob: blob
- - if current_user
- .js-file-fork-suggestion-section.file-fork-suggestion.hidden
- %span.file-fork-suggestion-note
- You don't have permission to edit this file. Try forking this project to edit the file.
- = link_to 'Fork', fork_path, method: :post, class: 'btn btn-grouped btn-inverted btn-new'
- %button.js-cancel-fork-suggestion.btn.btn-grouped{ type: 'button' }
- Cancel
- = render blob.to_partial_path(@project), blob: blob
+
+ - if blob.empty?
+ .file-content.code
+ .nothing-here-block
+ Empty file
+ - else
+ = render blob.to_partial_path(@project), blob: blob
diff --git a/app/views/projects/blob/_header.html.haml b/app/views/projects/blob/_header.html.haml
index 6c7d389e707..7a4a293548c 100644
--- a/app/views/projects/blob/_header.html.haml
+++ b/app/views/projects/blob/_header.html.haml
@@ -13,7 +13,7 @@
.file-actions.hidden-xs
.btn-group{ role: "group" }<
- = copy_blob_content_button(blob) if !blame && blob_text_viewable?(blob)
+ = copy_blob_content_button(blob) if !blame && blob_rendered_as_text?(blob)
= open_raw_file_button(namespace_project_raw_path(@project.namespace, @project, @id))
= view_on_environment_button(@commit.sha, @path, @environment) if @environment
@@ -38,3 +38,10 @@
- if current_user
= replace_blob_link
= delete_blob_link
+- if current_user
+ .js-file-fork-suggestion-section.file-fork-suggestion.hidden
+ %span.file-fork-suggestion-note
+ You don't have permission to edit this file. Try forking this project to edit the file.
+ = link_to 'Fork', fork_path, method: :post, class: 'btn btn-grouped btn-inverted btn-new'
+ %button.js-cancel-fork-suggestion.btn.btn-grouped{ type: 'button' }
+ Cancel
diff --git a/app/views/projects/blob/_image.html.haml b/app/views/projects/blob/_image.html.haml
index ea3cecb86a9..73877d730f5 100644
--- a/app/views/projects/blob/_image.html.haml
+++ b/app/views/projects/blob/_image.html.haml
@@ -1,15 +1,2 @@
.file-content.image_file
- - if blob.svg?
- - if blob.size_within_svg_limits?
- -# We need to scrub SVG but we cannot do so in the RawController: it would
- -# be wrong/strange if RawController modified the data.
- - blob.load_all_data!(@repository)
- - blob = sanitize_svg(blob)
- %img{ src: "data:#{blob.mime_type};base64,#{Base64.encode64(blob.data)}", alt: "#{blob.name}" }
- - else
- .nothing-here-block
- The SVG could not be displayed as it is too large, you can
- #{link_to('view the raw file', namespace_project_raw_path(@project.namespace, @project, @id), target: '_blank', rel: 'noopener noreferrer')}
- instead.
- - else
- %img{ src: namespace_project_raw_path(@project.namespace, @project, tree_join(@commit.id, blob.path)), alt: "#{blob.name}" }
+ %img{ src: namespace_project_raw_path(@project.namespace, @project, @id), alt: blob.name }
diff --git a/app/views/projects/blob/_markup.html.haml b/app/views/projects/blob/_markup.html.haml
new file mode 100644
index 00000000000..4ee4b03ff04
--- /dev/null
+++ b/app/views/projects/blob/_markup.html.haml
@@ -0,0 +1,4 @@
+- blob.load_all_data!(@repository)
+
+.file-content.wiki
+ = render_markup(blob.name, blob.data)
diff --git a/app/views/projects/blob/_svg.html.haml b/app/views/projects/blob/_svg.html.haml
new file mode 100644
index 00000000000..93be58fc658
--- /dev/null
+++ b/app/views/projects/blob/_svg.html.haml
@@ -0,0 +1,9 @@
+- if blob.size_within_svg_limits?
+ -# We need to scrub SVG but we cannot do so in the RawController: it would
+ -# be wrong/strange if RawController modified the data.
+ - blob.load_all_data!(@repository)
+ - blob = sanitize_svg(blob)
+ .file-content.image_file
+ %img{ src: "data:#{blob.mime_type};base64,#{Base64.encode64(blob.data)}", alt: blob.name }
+- else
+ = render 'too_large'
diff --git a/app/views/projects/blob/_text.html.haml b/app/views/projects/blob/_text.html.haml
index 7b16d266982..20638f6961d 100644
--- a/app/views/projects/blob/_text.html.haml
+++ b/app/views/projects/blob/_text.html.haml
@@ -1,19 +1,2 @@
-- if blob.only_display_raw?
- .file-content.code
- .nothing-here-block
- File too large, you can
- = succeed '.' do
- = link_to 'view the raw file', namespace_project_raw_path(@project.namespace, @project, @id), target: '_blank', rel: 'noopener noreferrer'
-
-- else
- - blob.load_all_data!(@repository)
-
- - if blob.empty?
- .file-content.code
- .nothing-here-block Empty file
- - else
- - if markup?(blob.name)
- .file-content.wiki
- = render_markup(blob.name, blob.data)
- - else
- = render 'shared/file_highlight', blob: blob, repository: @repository
+- blob.load_all_data!(@repository)
+= render 'shared/file_highlight', blob: blob, repository: @repository
diff --git a/app/views/projects/blob/_too_large.html.haml b/app/views/projects/blob/_too_large.html.haml
new file mode 100644
index 00000000000..a505f87df40
--- /dev/null
+++ b/app/views/projects/blob/_too_large.html.haml
@@ -0,0 +1,5 @@
+.file-content.code
+ .nothing-here-block
+ The file could not be displayed as it is too large, you can
+ #{link_to('view the raw file', namespace_project_raw_path(@project.namespace, @project, @id), target: '_blank', rel: 'noopener noreferrer')}
+ instead.
diff --git a/app/views/projects/builds/_sidebar.html.haml b/app/views/projects/builds/_sidebar.html.haml
index f4a66398c85..c4159ce1a36 100644
--- a/app/views/projects/builds/_sidebar.html.haml
+++ b/app/views/projects/builds/_sidebar.html.haml
@@ -136,7 +136,7 @@
- else
= build.id
- if build.retried?
- %i.fa.fa-refresh.has-tooltip{ data: { container: 'body', placement: 'bottom' }, title: 'Job was retried' }
+ %i.fa.fa-spinner.has-tooltip{ data: { container: 'body', placement: 'bottom' }, title: 'Job was retried' }
:javascript
new Sidebar();
diff --git a/app/views/projects/ci/builds/_build.html.haml b/app/views/projects/ci/builds/_build.html.haml
index 4700b7a9a45..2c3fd1fcd4d 100644
--- a/app/views/projects/ci/builds/_build.html.haml
+++ b/app/views/projects/ci/builds/_build.html.haml
@@ -36,7 +36,7 @@
= icon('warning', class: 'text-warning has-tooltip', title: 'Job is stuck. Check runners.')
- if retried
- = icon('refresh', class: 'text-warning has-tooltip', title: 'Job was retried')
+ = icon('spinner', class: 'text-warning has-tooltip', title: 'Job was retried')
.label-container
- if job.tags.any?
diff --git a/app/views/projects/diffs/_parallel_view.html.haml b/app/views/projects/diffs/_parallel_view.html.haml
index f920f359de2..45c95f7ab6a 100644
--- a/app/views/projects/diffs/_parallel_view.html.haml
+++ b/app/views/projects/diffs/_parallel_view.html.haml
@@ -5,8 +5,7 @@
- left = line[:left]
- right = line[:right]
- last_line = right.new_pos if right
- - unless @diff_notes_disabled
- - discussions_left, discussions_right = parallel_diff_discussions(left, right, diff_file)
+ - discussions_left, discussions_right = parallel_diff_discussions(left, right, diff_file)
%tr.line_holder.parallel
- if left
- case left.type
diff --git a/app/views/projects/diffs/_text_file.html.haml b/app/views/projects/diffs/_text_file.html.haml
index ebd1a914ee7..5f3968b6709 100644
--- a/app/views/projects/diffs/_text_file.html.haml
+++ b/app/views/projects/diffs/_text_file.html.haml
@@ -4,11 +4,10 @@
%a.show-suppressed-diff.js-show-suppressed-diff Changes suppressed. Click to show.
%table.text-file.code.js-syntax-highlight{ data: diff_view_data, class: too_big ? 'hide' : '' }
- - discussions = @grouped_diff_discussions unless @diff_notes_disabled
= render partial: "projects/diffs/line",
collection: diff_file.highlighted_diff_lines,
as: :line,
- locals: { diff_file: diff_file, discussions: discussions }
+ locals: { diff_file: diff_file, discussions: @grouped_diff_discussions }
- if !diff_file.new_file && !diff_file.deleted_file && diff_file.highlighted_diff_lines.any?
- last_line = diff_file.highlighted_diff_lines.last
diff --git a/app/views/projects/labels/edit.html.haml b/app/views/projects/labels/edit.html.haml
index a80a07b52e6..7f0059cdcda 100644
--- a/app/views/projects/labels/edit.html.haml
+++ b/app/views/projects/labels/edit.html.haml
@@ -1,6 +1,6 @@
- @no_container = true
- page_title "Edit", @label.name, "Labels"
-= render "projects/issues/head"
+= render "shared/mr_head"
%div{ class: container_class }
%h3.page-title
diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml
index 29f861c09c6..fc72c4fb635 100644
--- a/app/views/projects/labels/index.html.haml
+++ b/app/views/projects/labels/index.html.haml
@@ -1,7 +1,7 @@
- @no_container = true
- page_title "Labels"
- hide_class = ''
-= render "projects/issues/head"
+= render "shared/mr_head"
- if @labels.exists? || @prioritized_labels.exists?
%div{ class: container_class }
diff --git a/app/views/projects/labels/new.html.haml b/app/views/projects/labels/new.html.haml
index f0d9be744d1..8f6c085a361 100644
--- a/app/views/projects/labels/new.html.haml
+++ b/app/views/projects/labels/new.html.haml
@@ -1,6 +1,6 @@
- @no_container = true
- page_title "New Label"
-= render "projects/issues/head"
+= render "shared/mr_head"
%div{ class: container_class }
%h3.page-title
diff --git a/app/views/projects/merge_requests/_head.html.haml b/app/views/projects/merge_requests/_head.html.haml
new file mode 100644
index 00000000000..b7f73fe5339
--- /dev/null
+++ b/app/views/projects/merge_requests/_head.html.haml
@@ -0,0 +1,21 @@
+= content_for :sub_nav do
+ .scrolling-tabs-container.sub-nav-scroll
+ = render 'shared/nav_scroll'
+ .nav-links.sub-nav.scrolling-tabs
+ %ul{ class: (container_class) }
+ = nav_link(controller: :merge_requests) do
+ = link_to namespace_project_merge_requests_path(@project.namespace, @project), title: 'Merge Requests' do
+ %span
+ List
+
+ - if project_nav_tab? :labels
+ = nav_link(controller: :labels) do
+ = link_to namespace_project_labels_path(@project.namespace, @project), title: 'Labels' do
+ %span
+ Labels
+
+ - if project_nav_tab? :milestones
+ = nav_link(controller: :milestones) do
+ = link_to namespace_project_milestones_path(@project.namespace, @project), title: 'Milestones' do
+ %span
+ Milestones
diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml
index 64f17ab34b1..6bf0035e051 100644
--- a/app/views/projects/merge_requests/index.html.haml
+++ b/app/views/projects/merge_requests/index.html.haml
@@ -2,6 +2,9 @@
- @bulk_edit = can?(current_user, :admin_merge_request, @project)
- page_title "Merge Requests"
+- unless @project.default_issues_tracker?
+ = content_for :sub_nav do
+ = render "projects/merge_requests/head"
= render 'projects/last_push'
- content_for :page_specific_javascripts do
diff --git a/app/views/projects/merge_requests/show/_versions.html.haml b/app/views/projects/merge_requests/show/_versions.html.haml
index 74a7b1dc498..547be78992e 100644
--- a/app/views/projects/merge_requests/show/_versions.html.haml
+++ b/app/views/projects/merge_requests/show/_versions.html.haml
@@ -72,13 +72,16 @@
= link_to namespace_project_compare_path(@project.namespace, @project, from: @start_version.base_commit_sha, to: @merge_request_diff.base_commit_sha) do
new commits
from
- %code= @merge_request.target_branch
+ = succeed '.' do
+ %code= @merge_request.target_branch
- - unless @merge_request_diff.latest? && !@start_sha
+ - if @diff_notes_disabled
.comments-disabled-notif.content-block
= icon('info-circle')
- if @start_sha
Comments are disabled because you're comparing two versions of this merge request.
- else
- Comments are disabled because you're viewing an old version of this merge request.
- = link_to 'Show latest version', diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'btn btn-sm'
+ Discussions on this version of the merge request are displayed but comment creation is disabled.
+
+ .pull-right
+ = link_to 'Show latest version', diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'btn btn-sm'
diff --git a/app/views/projects/merge_requests/widget/open/_accept.html.haml b/app/views/projects/merge_requests/widget/open/_accept.html.haml
index cb117d1908c..4cbd22150c7 100644
--- a/app/views/projects/merge_requests/widget/open/_accept.html.haml
+++ b/app/views/projects/merge_requests/widget/open/_accept.html.haml
@@ -18,7 +18,7 @@
Select merge moment
%ul.js-merge-dropdown.dropdown-menu.dropdown-menu-right{ role: 'menu' }
%li
- = link_to "#", class: "merge_when_pipeline_succeeds" do
+ = link_to "#", class: "merge-when-pipeline-succeeds" do
= icon('check fw')
Merge when pipeline succeeds
%li
diff --git a/app/views/projects/milestones/edit.html.haml b/app/views/projects/milestones/edit.html.haml
index 55b0b837c6d..e57a76dbfd2 100644
--- a/app/views/projects/milestones/edit.html.haml
+++ b/app/views/projects/milestones/edit.html.haml
@@ -1,6 +1,6 @@
- @no_container = true
- page_title "Edit", @milestone.title, "Milestones"
-= render "projects/issues/head"
+= render "shared/mr_head"
%div{ class: container_class }
diff --git a/app/views/projects/milestones/index.html.haml b/app/views/projects/milestones/index.html.haml
index 8e85b2e8a20..e1096bd1d67 100644
--- a/app/views/projects/milestones/index.html.haml
+++ b/app/views/projects/milestones/index.html.haml
@@ -1,6 +1,6 @@
- @no_container = true
- page_title 'Milestones'
-= render 'projects/issues/head'
+= render "shared/mr_head"
%div{ class: container_class }
.top-area
diff --git a/app/views/projects/milestones/new.html.haml b/app/views/projects/milestones/new.html.haml
index cda093ade81..586eb909afa 100644
--- a/app/views/projects/milestones/new.html.haml
+++ b/app/views/projects/milestones/new.html.haml
@@ -1,6 +1,6 @@
- @no_container = true
- page_title "New Milestone"
-= render "projects/issues/head"
+= render "shared/mr_head"
%div{ class: container_class }
%h3.page-title
diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml
index 8b62b156853..a173117984d 100644
--- a/app/views/projects/milestones/show.html.haml
+++ b/app/views/projects/milestones/show.html.haml
@@ -1,7 +1,7 @@
- @no_container = true
- page_title @milestone.title, "Milestones"
- page_description @milestone.description
-= render "projects/issues/head"
+= render "shared/mr_head"
%div{ class: container_class }
.detail-page-header.milestone-page-header
diff --git a/app/views/projects/registry/repositories/_image.html.haml b/app/views/projects/registry/repositories/_image.html.haml
index d183ce34a3a..8bc78f8d018 100644
--- a/app/views/projects/registry/repositories/_image.html.haml
+++ b/app/views/projects/registry/repositories/_image.html.haml
@@ -4,7 +4,7 @@
= icon('chevron-down', 'aria-hidden': 'true')
= escape_once(image.path)
- = clipboard_button(clipboard_text: "docker pull #{image.path}")
+ = clipboard_button(clipboard_text: "docker pull #{image.location}")
.controls.hidden-xs.pull-right
= link_to namespace_project_container_registry_path(@project.namespace, @project, image),
diff --git a/app/views/projects/registry/repositories/_tag.html.haml b/app/views/projects/registry/repositories/_tag.html.haml
index 854b7d0ebf7..378a23f07e6 100644
--- a/app/views/projects/registry/repositories/_tag.html.haml
+++ b/app/views/projects/registry/repositories/_tag.html.haml
@@ -1,7 +1,7 @@
%tr.tag
%td
= escape_once(tag.name)
- = clipboard_button(text: "docker pull #{tag.path}")
+ = clipboard_button(text: "docker pull #{tag.location}")
%td
- if tag.revision
%span.has-tooltip{ title: "#{tag.revision}" }
diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml
index edfe6da1816..de1229d58aa 100644
--- a/app/views/projects/show.html.haml
+++ b/app/views/projects/show.html.haml
@@ -13,7 +13,7 @@
= render "home_panel"
- if current_user && can?(current_user, :download_code, @project)
- %nav.project-stats.limit-container-width{ class: container_class }
+ %nav.project-stats{ class: container_class }
%ul.nav
%li
= link_to project_files_path(@project) do
@@ -74,11 +74,11 @@
Set up auto deploy
- if @repository.commit
- .limit-container-width{ class: container_class }
+ %div{ class: container_class }
.project-last-commit
= render 'projects/last_commit', commit: @repository.commit, ref: current_ref, project: @project
-.limit-container-width{ class: container_class }
+%div{ class: container_class }
- if @project.archived?
.text-warning.center.prepend-top-20
%p
diff --git a/app/views/shared/_mr_head.html.haml b/app/views/shared/_mr_head.html.haml
new file mode 100644
index 00000000000..4211ec6351d
--- /dev/null
+++ b/app/views/shared/_mr_head.html.haml
@@ -0,0 +1,4 @@
+- if @project.default_issues_tracker?
+ = render "projects/issues/head"
+- else
+ = render "projects/merge_requests/head"
diff --git a/app/views/shared/icons/_icon_merged.svg b/app/views/shared/icons/_icon_merged.svg
index d8f96558bea..43d591daefa 100644
--- a/app/views/shared/icons/_icon_merged.svg
+++ b/app/views/shared/icons/_icon_merged.svg
@@ -1 +1 @@
-<svg width="14" height="14" viewBox="0 0 14 14" xmlns="http://www.w3.org/2000/svg"><path d="M0 7c0-3.866 3.142-7 7-7 3.866 0 7 3.142 7 7 0 3.866-3.142 7-7 7-3.866 0-7-3.142-7-7z"/><path d="M1 7c0 3.309 2.69 6 6 6 3.309 0 6-2.69 6-6 0-3.309-2.69-6-6-6-3.309 0-6 2.69-6 6z" fill="#FFF"/><path d="M9.427 6.523a.932.932 0 0 0-.808.489v-.01c-.49-.01-1.059-.172-1.46-.489-.35-.278-.7-.772-.882-1.17a.964.964 0 0 0 .35-.744.943.943 0 0 0-.934-.959c-.518 0-.933.432-.933.964 0 .35.191.662.467.825v3.147a.97.97 0 0 0-.467.825c0 .532.415.959.933.959a.943.943 0 0 0 .934-.96.965.965 0 0 0-.467-.824V6.844c.313.336.672.61 1.073.81.402.202.948.303 1.386.308v-.01c.168.293.467.49.808.49a.943.943 0 0 0 .933-.96.943.943 0 0 0-.933-.96z"/></svg>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="m2 3c.552 0 1-.448 1-1 0-.552-.448-1-1-1-.552 0-1 .448-1 1 0 .552.448 1 1 1m.761.85c.154 2.556 1.987 4.692 4.45 5.255.328-.655 1.01-1.105 1.789-1.105 1.105 0 2 .895 2 2 0 1.105-.895 2-2 2-.89 0-1.645-.582-1.904-1.386-1.916-.376-3.548-1.5-4.596-3.044v4.493c.863.222 1.5 1.01 1.5 1.937 0 1.105-.895 2-2 2-1.105 0-2-.895-2-2 0-.74.402-1.387 1-1.732v-8.535c-.598-.346-1-.992-1-1.732 0-1.105.895-2 2-2 1.105 0 2 .895 2 2 0 .835-.512 1.551-1.239 1.85m6.239 7.15c.552 0 1-.448 1-1 0-.552-.448-1-1-1-.552 0-1 .448-1 1 0 .552.448 1 1 1m-7 4c.552 0 1-.448 1-1 0-.552-.448-1-1-1-.552 0-1 .448-1 1 0 .552.448 1 1 1" transform="translate(3)"/></svg>
diff --git a/app/views/shared/icons/_trash_o.svg b/app/views/shared/icons/_icon_trash_o.svg
index 0d7a91ab536..0d7a91ab536 100644
--- a/app/views/shared/icons/_trash_o.svg
+++ b/app/views/shared/icons/_icon_trash_o.svg
diff --git a/app/workers/build_coverage_worker.rb b/app/workers/build_coverage_worker.rb
index def0ab1dde1..f7ae996bb17 100644
--- a/app/workers/build_coverage_worker.rb
+++ b/app/workers/build_coverage_worker.rb
@@ -3,7 +3,6 @@ class BuildCoverageWorker
include BuildQueue
def perform(build_id)
- Ci::Build.find_by(id: build_id)
- .try(:update_coverage)
+ Ci::Build.find_by(id: build_id)&.update_coverage
end
end
diff --git a/changelogs/unreleased/2120-issues-search-bar-not-picking-up.yml b/changelogs/unreleased/2120-issues-search-bar-not-picking-up.yml
new file mode 100644
index 00000000000..706609b7baf
--- /dev/null
+++ b/changelogs/unreleased/2120-issues-search-bar-not-picking-up.yml
@@ -0,0 +1,4 @@
+---
+title: Fix filtered search input width for IE
+merge_request:
+author:
diff --git a/changelogs/unreleased/22714-update-all-instances-of-fa-refresh.yml b/changelogs/unreleased/22714-update-all-instances-of-fa-refresh.yml
new file mode 100644
index 00000000000..ad7c011933f
--- /dev/null
+++ b/changelogs/unreleased/22714-update-all-instances-of-fa-refresh.yml
@@ -0,0 +1,4 @@
+---
+title: Update all instances of the old loading icon
+merge_request: 10490
+author: Andrew Torres
diff --git a/changelogs/unreleased/27729-improve-webpack-dev-environment.yml b/changelogs/unreleased/27729-improve-webpack-dev-environment.yml
new file mode 100644
index 00000000000..d04ea70ab1c
--- /dev/null
+++ b/changelogs/unreleased/27729-improve-webpack-dev-environment.yml
@@ -0,0 +1,4 @@
+---
+title: Add webpack_bundle_tag helper to improve non-localhost GDK configurations
+merge_request: 10604
+author:
diff --git a/changelogs/unreleased/28017-separate-ce-params-on-api.yml b/changelogs/unreleased/28017-separate-ce-params-on-api.yml
new file mode 100644
index 00000000000..039a8d207b0
--- /dev/null
+++ b/changelogs/unreleased/28017-separate-ce-params-on-api.yml
@@ -0,0 +1,4 @@
+---
+title: Separate CE params on Grape API
+merge_request:
+author:
diff --git a/changelogs/unreleased/29056-backport-ee-cleanup-database-file.yml b/changelogs/unreleased/29056-backport-ee-cleanup-database-file.yml
new file mode 100644
index 00000000000..0ebb9d57611
--- /dev/null
+++ b/changelogs/unreleased/29056-backport-ee-cleanup-database-file.yml
@@ -0,0 +1,4 @@
+---
+title: Turns true value and false value database methods from instance to class methods
+merge_request: 10583
+author:
diff --git a/changelogs/unreleased/29866-navbar-counters.yml b/changelogs/unreleased/29866-navbar-counters.yml
deleted file mode 100644
index c67dff6cffa..00000000000
--- a/changelogs/unreleased/29866-navbar-counters.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add shortcuts and counters to MRs and issues in navbar
-merge_request:
-author:
diff --git a/changelogs/unreleased/30024-owner-can-t-initialize-git-repo-for-new-project-in-group.yml b/changelogs/unreleased/30024-owner-can-t-initialize-git-repo-for-new-project-in-group.yml
deleted file mode 100644
index c43d2732b9a..00000000000
--- a/changelogs/unreleased/30024-owner-can-t-initialize-git-repo-for-new-project-in-group.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Disable invalid service templates
-merge_request:
-author:
diff --git a/changelogs/unreleased/30457-expire-note-destroy.yml b/changelogs/unreleased/30457-expire-note-destroy.yml
new file mode 100644
index 00000000000..f5c89da68a9
--- /dev/null
+++ b/changelogs/unreleased/30457-expire-note-destroy.yml
@@ -0,0 +1,4 @@
+---
+title: Fix issue's note cache expiration after delete
+merge_request:
+author: mhasbini
diff --git a/changelogs/unreleased/30678-improve-dev-server-process.yml b/changelogs/unreleased/30678-improve-dev-server-process.yml
new file mode 100644
index 00000000000..efa2fc210e3
--- /dev/null
+++ b/changelogs/unreleased/30678-improve-dev-server-process.yml
@@ -0,0 +1,4 @@
+---
+title: Keep webpack-dev-server process functional across branch changes
+merge_request: 10581
+author:
diff --git a/changelogs/unreleased/30779-show-mr-subnav-issue-tracker.yml b/changelogs/unreleased/30779-show-mr-subnav-issue-tracker.yml
new file mode 100644
index 00000000000..59f8942911c
--- /dev/null
+++ b/changelogs/unreleased/30779-show-mr-subnav-issue-tracker.yml
@@ -0,0 +1,4 @@
+---
+title: Show sub-nav under Merge Requests when issue tracker is non-default.
+merge_request: 10658
+author:
diff --git a/changelogs/unreleased/add-vue-loader.yml b/changelogs/unreleased/add-vue-loader.yml
new file mode 100644
index 00000000000..382ef61ff21
--- /dev/null
+++ b/changelogs/unreleased/add-vue-loader.yml
@@ -0,0 +1,4 @@
+---
+title: add support for .vue templates
+merge_request: 10517
+author:
diff --git a/changelogs/unreleased/bb_save_trace.yml b/changelogs/unreleased/bb_save_trace.yml
new file mode 100644
index 00000000000..6ff31f4f111
--- /dev/null
+++ b/changelogs/unreleased/bb_save_trace.yml
@@ -0,0 +1,5 @@
+---
+title: "[BB Importer] Save the error trace and the whole raw document to debug problems
+ easier"
+merge_request:
+author:
diff --git a/changelogs/unreleased/boards-done-add-tooltip.yml b/changelogs/unreleased/boards-done-add-tooltip.yml
new file mode 100644
index 00000000000..139f1efc8ee
--- /dev/null
+++ b/changelogs/unreleased/boards-done-add-tooltip.yml
@@ -0,0 +1,4 @@
+---
+title: Add tooltip to header of Done board
+merge_request: 10574
+author: Andy Brown
diff --git a/changelogs/unreleased/clean_carrierwave_tempfiles.yml b/changelogs/unreleased/clean_carrierwave_tempfiles.yml
new file mode 100644
index 00000000000..53fa69700ff
--- /dev/null
+++ b/changelogs/unreleased/clean_carrierwave_tempfiles.yml
@@ -0,0 +1,4 @@
+---
+title: Periodically clean up temporary upload files to recover storage space
+merge_request: 9466
+author: blackst0ne
diff --git a/changelogs/unreleased/dm-link-discussion-to-outdated-diff.yml b/changelogs/unreleased/dm-link-discussion-to-outdated-diff.yml
new file mode 100644
index 00000000000..d489bada7ea
--- /dev/null
+++ b/changelogs/unreleased/dm-link-discussion-to-outdated-diff.yml
@@ -0,0 +1,4 @@
+---
+title: Link to outdated diff in older MR version from outdated diff discussion
+merge_request:
+author:
diff --git a/changelogs/unreleased/dz-fix-project-view.yml b/changelogs/unreleased/dz-fix-project-view.yml
new file mode 100644
index 00000000000..647a1c96bd9
--- /dev/null
+++ b/changelogs/unreleased/dz-fix-project-view.yml
@@ -0,0 +1,4 @@
+---
+title: Change project view default for existing users and anonymous visitors to files+readme
+merge_request: 10498
+author:
diff --git a/changelogs/unreleased/fix-missing-capitalisation-buttons.yml b/changelogs/unreleased/fix-missing-capitalisation-buttons.yml
new file mode 100644
index 00000000000..b2c40483475
--- /dev/null
+++ b/changelogs/unreleased/fix-missing-capitalisation-buttons.yml
@@ -0,0 +1,4 @@
+---
+title: Fix missing capitalisation on views
+merge_request:
+author:
diff --git a/changelogs/unreleased/fix-preloading-merge_request_diff.yml b/changelogs/unreleased/fix-preloading-merge_request_diff.yml
new file mode 100644
index 00000000000..d38b6b0a707
--- /dev/null
+++ b/changelogs/unreleased/fix-preloading-merge_request_diff.yml
@@ -0,0 +1,4 @@
+---
+title: Fix bad query for PostgreSQL showing merge requests list
+merge_request: 10666
+author:
diff --git a/changelogs/unreleased/fix_cache_expiration_in_repository.yml b/changelogs/unreleased/fix_cache_expiration_in_repository.yml
new file mode 100644
index 00000000000..5f34f2bd040
--- /dev/null
+++ b/changelogs/unreleased/fix_cache_expiration_in_repository.yml
@@ -0,0 +1,4 @@
+---
+title: Fix redundant cache expiration in Repository
+merge_request: 10575
+author: blackst0ne
diff --git a/changelogs/unreleased/fix_spaces_in_label_title.yml b/changelogs/unreleased/fix_spaces_in_label_title.yml
new file mode 100644
index 00000000000..51f07438edb
--- /dev/null
+++ b/changelogs/unreleased/fix_spaces_in_label_title.yml
@@ -0,0 +1,4 @@
+---
+title: Remove heading and trailing spaces from label's color and title
+merge_request: 10603
+author: blackst0ne
diff --git a/changelogs/unreleased/mr-widget-bug-fix.yml b/changelogs/unreleased/mr-widget-bug-fix.yml
new file mode 100644
index 00000000000..9af29d3927e
--- /dev/null
+++ b/changelogs/unreleased/mr-widget-bug-fix.yml
@@ -0,0 +1,5 @@
+---
+title: Fix MR widget bug that merged a MR when Merge when pipeline succeeds was clicked
+ via the dropdown
+merge_request: 10611
+author:
diff --git a/changelogs/unreleased/omnibus-gitlab-1993-check-shell-repositories-path-group-is-root.yml b/changelogs/unreleased/omnibus-gitlab-1993-check-shell-repositories-path-group-is-root.yml
new file mode 100644
index 00000000000..3b9284258cb
--- /dev/null
+++ b/changelogs/unreleased/omnibus-gitlab-1993-check-shell-repositories-path-group-is-root.yml
@@ -0,0 +1,4 @@
+---
+title: "Make the `gitlab:gitlab_shell:check` task check that the repositories storage path are owned by the `root` group"
+merge_request:
+author:
diff --git a/changelogs/unreleased/reset-new-branch-button.yml b/changelogs/unreleased/reset-new-branch-button.yml
new file mode 100644
index 00000000000..318ee46298f
--- /dev/null
+++ b/changelogs/unreleased/reset-new-branch-button.yml
@@ -0,0 +1,4 @@
+---
+title: Reset New branch button when issue state changes
+merge_request: 5962
+author: winniehell
diff --git a/changelogs/unreleased/sh-fix-ssh-keys-with-spaces.yml b/changelogs/unreleased/sh-fix-ssh-keys-with-spaces.yml
deleted file mode 100644
index fe75d7e1156..00000000000
--- a/changelogs/unreleased/sh-fix-ssh-keys-with-spaces.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Handle SSH keys that have multiple spaces between each marker
-merge_request:
-author:
diff --git a/changelogs/unreleased/siemens-gitlab-ce-fix-subgroup-hide-button.yml b/changelogs/unreleased/siemens-gitlab-ce-fix-subgroup-hide-button.yml
new file mode 100644
index 00000000000..716311c7582
--- /dev/null
+++ b/changelogs/unreleased/siemens-gitlab-ce-fix-subgroup-hide-button.yml
@@ -0,0 +1,4 @@
+---
+title: Hide new subgroup button if user has no permission to create one
+merge_request: 10627
+author:
diff --git a/changelogs/unreleased/spec_for_schema.yml b/changelogs/unreleased/spec_for_schema.yml
new file mode 100644
index 00000000000..7ea0b8672ce
--- /dev/null
+++ b/changelogs/unreleased/spec_for_schema.yml
@@ -0,0 +1,4 @@
+---
+title: Add spec for schema.rb
+merge_request: 10580
+author: blackst0ne
diff --git a/changelogs/unreleased/user-activity-scroll-bar.yml b/changelogs/unreleased/user-activity-scroll-bar.yml
new file mode 100644
index 00000000000..97cccee42cb
--- /dev/null
+++ b/changelogs/unreleased/user-activity-scroll-bar.yml
@@ -0,0 +1,4 @@
+---
+title: Fix preemptive scroll bar on user activity calendar.
+merge_request: !10636
+author:
diff --git a/changelogs/unreleased/zj-api-fix-build-events.yml b/changelogs/unreleased/zj-api-fix-build-events.yml
new file mode 100644
index 00000000000..7700d8dcd22
--- /dev/null
+++ b/changelogs/unreleased/zj-api-fix-build-events.yml
@@ -0,0 +1,4 @@
+---
+title: "Bugfix: POST /projects/:id/hooks and PUT /projects/:id/hook/:hook_id no longer ignore the the job_events param in the V4 API"
+merge_request: 10586
+author:
diff --git a/changelogs/unreleased/zj-fk-ci-triggers.yml b/changelogs/unreleased/zj-fk-ci-triggers.yml
new file mode 100644
index 00000000000..9fe708b25c0
--- /dev/null
+++ b/changelogs/unreleased/zj-fk-ci-triggers.yml
@@ -0,0 +1,4 @@
+---
+title: Add foreign key for ci_trigger_requests on ci_triggers
+merge_request: 10537
+author:
diff --git a/config/dependency_decisions.yml b/config/dependency_decisions.yml
index fdba1f6541e..59c7050a14d 100644
--- a/config/dependency_decisions.yml
+++ b/config/dependency_decisions.yml
@@ -344,3 +344,57 @@
:why: https://github.com/nodeca/pako/blob/master/LICENSE
:versions: []
:when: 2017-04-05 10:43:45.897720000 Z
+- - :approve
+ - caniuse-db
+ - :who: Mike Greiling
+ :why: https://github.com/Fyrd/caniuse/blob/master/LICENSE
+ :versions: []
+ :when: 2017-04-07 16:05:14.185549000 Z
+- - :approve
+ - domelementtype
+ - :who: Mike Greiling
+ :why: https://github.com/fb55/domelementtype/blob/master/LICENSE
+ :versions: []
+ :when: 2017-04-07 16:19:17.992640000 Z
+- - :approve
+ - domhandler
+ - :who: Mike Greiling
+ :why: https://github.com/fb55/domhandler/blob/master/LICENSE
+ :versions: []
+ :when: 2017-04-07 16:19:19.628953000 Z
+- - :approve
+ - domutils
+ - :who: Mike Greiling
+ :why: https://github.com/fb55/domutils/blob/master/LICENSE
+ :versions: []
+ :when: 2017-04-07 16:19:21.159356000 Z
+- - :approve
+ - entities
+ - :who: Mike Greiling
+ :why: https://github.com/fb55/entities/blob/master/LICENSE
+ :versions: []
+ :when: 2017-04-07 16:19:23.900571000 Z
+- - :approve
+ - ansi-html
+ - :who: Mike Greiling
+ :why: https://github.com/Tjatse/ansi-html/blob/master/LICENSE
+ :versions: []
+ :when: 2017-04-10 05:42:12.898178000 Z
+- - :approve
+ - map-stream
+ - :who: Mike Greiling
+ :why: https://github.com/dominictarr/map-stream/blob/master/LICENCE
+ :versions: []
+ :when: 2017-04-10 06:27:52.269085000 Z
+- - :approve
+ - pause-stream
+ - :who: Mike Greiling
+ :why: https://github.com/dominictarr/pause-stream/blob/master/LICENSE
+ :versions: []
+ :when: 2017-04-10 06:28:39.825894000 Z
+- - :approve
+ - undefsafe
+ - :who: Mike Greiling
+ :why: https://github.com/remy/undefsafe/blob/master/LICENSE
+ :versions: []
+ :when: 2017-04-10 06:30:00.002555000 Z
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index 3c70f35b9d0..06c9f734c2a 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -579,9 +579,9 @@ test:
storages:
default:
path: tmp/tests/repositories/
- gitaly_address: unix:<%= Rails.root.join('tmp/sockets/private/gitaly.socket') %>
+ gitaly_address: unix:tmp/tests/gitaly/gitaly.socket
gitaly:
- enabled: false
+ enabled: true
backup:
path: tmp/tests/backups
gitlab_shell:
diff --git a/config/webpack.config.js b/config/webpack.config.js
index e3bc939d578..ffb16190093 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -6,10 +6,12 @@ var webpack = require('webpack');
var StatsPlugin = require('stats-webpack-plugin');
var CompressionPlugin = require('compression-webpack-plugin');
var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
+var WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin');
var ROOT_PATH = path.resolve(__dirname, '..');
var IS_PRODUCTION = process.env.NODE_ENV === 'production';
var IS_DEV_SERVER = process.argv[1].indexOf('webpack-dev-server') !== -1;
+var DEV_SERVER_HOST = process.env.DEV_SERVER_HOST || 'localhost';
var DEV_SERVER_PORT = parseInt(process.env.DEV_SERVER_PORT, 10) || 3808;
var DEV_SERVER_LIVERELOAD = process.env.DEV_SERVER_LIVERELOAD !== 'false';
var WEBPACK_REPORT = process.env.WEBPACK_REPORT;
@@ -65,13 +67,18 @@ var config = {
{
test: /\.js$/,
exclude: /(node_modules|vendor\/assets)/,
- loader: 'babel-loader'
+ loader: 'babel-loader',
+ },
+ {
+ test: /\.vue$/,
+ loader: 'vue-loader',
},
{
test: /\.svg$/,
- use: 'raw-loader'
- }, {
- test: /\.(worker.js|pdf)$/,
+ loader: 'raw-loader',
+ },
+ {
+ test: /\.(worker\.js|pdf)$/,
exclude: /node_modules/,
loader: 'file-loader',
},
@@ -176,12 +183,17 @@ if (IS_PRODUCTION) {
if (IS_DEV_SERVER) {
config.devtool = 'cheap-module-eval-source-map';
config.devServer = {
+ host: DEV_SERVER_HOST,
port: DEV_SERVER_PORT,
headers: { 'Access-Control-Allow-Origin': '*' },
stats: 'errors-only',
inline: DEV_SERVER_LIVERELOAD
};
- config.output.publicPath = '//localhost:' + DEV_SERVER_PORT + config.output.publicPath;
+ config.output.publicPath = '//' + DEV_SERVER_HOST + ':' + DEV_SERVER_PORT + config.output.publicPath;
+ config.plugins.push(
+ // watch node_modules for changes if we encounter a missing module compile error
+ new WatchMissingNodeModulesPlugin(path.join(ROOT_PATH, 'node_modules'))
+ );
}
if (WEBPACK_REPORT) {
diff --git a/db/fixtures/development/17_cycle_analytics.rb b/db/fixtures/development/17_cycle_analytics.rb
index 4bc735916c1..0d7eb1a7c93 100644
--- a/db/fixtures/development/17_cycle_analytics.rb
+++ b/db/fixtures/development/17_cycle_analytics.rb
@@ -223,7 +223,9 @@ class Gitlab::Seeder::CycleAnalytics
end
Gitlab::Seeder.quiet do
- if ENV['SEED_CYCLE_ANALYTICS']
+ flag = 'SEED_CYCLE_ANALYTICS'
+
+ if ENV[flag]
Project.all.each do |project|
seeder = Gitlab::Seeder::CycleAnalytics.new(project)
seeder.seed!
@@ -235,6 +237,6 @@ Gitlab::Seeder.quiet do
seeder = Gitlab::Seeder::CycleAnalytics.new(Project.order(:id).first, perf: true)
seeder.seed_metrics!
else
- puts "Not running the cycle analytics seed file. Use the `SEED_CYCLE_ANALYTICS` environment variable to enable it."
+ puts "Skipped. Use the `#{flag}` environment variable to enable."
end
end
diff --git a/db/fixtures/development/20_nested_groups.rb b/db/fixtures/development/20_nested_groups.rb
index d8dddc3fee9..2bc78e120a5 100644
--- a/db/fixtures/development/20_nested_groups.rb
+++ b/db/fixtures/development/20_nested_groups.rb
@@ -27,43 +27,49 @@ end
Sidekiq::Testing.inline! do
Gitlab::Seeder.quiet do
- project_urls = [
- 'https://android.googlesource.com/platform/hardware/broadcom/libbt.git',
- 'https://android.googlesource.com/platform/hardware/broadcom/wlan.git',
- 'https://android.googlesource.com/platform/hardware/bsp/bootloader/intel/edison-u-boot.git',
- 'https://android.googlesource.com/platform/hardware/bsp/broadcom.git',
- 'https://android.googlesource.com/platform/hardware/bsp/freescale.git',
- 'https://android.googlesource.com/platform/hardware/bsp/imagination.git',
- 'https://android.googlesource.com/platform/hardware/bsp/intel.git',
- 'https://android.googlesource.com/platform/hardware/bsp/kernel/common/v4.1.git',
- 'https://android.googlesource.com/platform/hardware/bsp/kernel/common/v4.4.git'
- ]
+ flag = 'SEED_NESTED_GROUPS'
- user = User.admins.first
+ if ENV[flag]
+ project_urls = [
+ 'https://android.googlesource.com/platform/hardware/broadcom/libbt.git',
+ 'https://android.googlesource.com/platform/hardware/broadcom/wlan.git',
+ 'https://android.googlesource.com/platform/hardware/bsp/bootloader/intel/edison-u-boot.git',
+ 'https://android.googlesource.com/platform/hardware/bsp/broadcom.git',
+ 'https://android.googlesource.com/platform/hardware/bsp/freescale.git',
+ 'https://android.googlesource.com/platform/hardware/bsp/imagination.git',
+ 'https://android.googlesource.com/platform/hardware/bsp/intel.git',
+ 'https://android.googlesource.com/platform/hardware/bsp/kernel/common/v4.1.git',
+ 'https://android.googlesource.com/platform/hardware/bsp/kernel/common/v4.4.git'
+ ]
- project_urls.each_with_index do |url, i|
- full_path = url.sub('https://android.googlesource.com/', '')
- full_path = full_path.sub(/\.git\z/, '')
- full_path, _, project_path = full_path.rpartition('/')
- group = Group.find_by_full_path(full_path) || create_group_with_parents(user, full_path)
+ user = User.admins.first
- params = {
- import_url: url,
- namespace_id: group.id,
- path: project_path,
- name: project_path,
- description: FFaker::Lorem.sentence,
- visibility_level: Gitlab::VisibilityLevel.values.sample
- }
+ project_urls.each_with_index do |url, i|
+ full_path = url.sub('https://android.googlesource.com/', '')
+ full_path = full_path.sub(/\.git\z/, '')
+ full_path, _, project_path = full_path.rpartition('/')
+ group = Group.find_by_full_path(full_path) || create_group_with_parents(user, full_path)
- project = Projects::CreateService.new(user, params).execute
- project.send(:_run_after_commit_queue)
+ params = {
+ import_url: url,
+ namespace_id: group.id,
+ path: project_path,
+ name: project_path,
+ description: FFaker::Lorem.sentence,
+ visibility_level: Gitlab::VisibilityLevel.values.sample
+ }
- if project.valid?
- print '.'
- else
- print 'F'
+ project = Projects::CreateService.new(user, params).execute
+ project.send(:_run_after_commit_queue)
+
+ if project.valid?
+ print '.'
+ else
+ print 'F'
+ end
end
+ else
+ puts "Skipped. Use the `#{flag}` environment variable to enable."
end
end
end
diff --git a/db/migrate/20130218141258_convert_closed_to_state_in_issue.rb b/db/migrate/20130218141258_convert_closed_to_state_in_issue.rb
index 94c0a6845d5..67a0d3b53eb 100644
--- a/db/migrate/20130218141258_convert_closed_to_state_in_issue.rb
+++ b/db/migrate/20130218141258_convert_closed_to_state_in_issue.rb
@@ -1,6 +1,6 @@
# rubocop:disable all
class ConvertClosedToStateInIssue < ActiveRecord::Migration
- include Gitlab::Database
+ include Gitlab::Database::MigrationHelpers
def up
execute "UPDATE #{table_name} SET state = 'closed' WHERE closed = #{true_value}"
diff --git a/db/migrate/20130218141327_convert_closed_to_state_in_merge_request.rb b/db/migrate/20130218141327_convert_closed_to_state_in_merge_request.rb
index 64a9c761352..307fc6a023d 100644
--- a/db/migrate/20130218141327_convert_closed_to_state_in_merge_request.rb
+++ b/db/migrate/20130218141327_convert_closed_to_state_in_merge_request.rb
@@ -1,6 +1,6 @@
# rubocop:disable all
class ConvertClosedToStateInMergeRequest < ActiveRecord::Migration
- include Gitlab::Database
+ include Gitlab::Database::MigrationHelpers
def up
execute "UPDATE #{table_name} SET state = 'merged' WHERE closed = #{true_value} AND merged = #{true_value}"
diff --git a/db/migrate/20130218141344_convert_closed_to_state_in_milestone.rb b/db/migrate/20130218141344_convert_closed_to_state_in_milestone.rb
index 41508c2dc95..d12703cf3b2 100644
--- a/db/migrate/20130218141344_convert_closed_to_state_in_milestone.rb
+++ b/db/migrate/20130218141344_convert_closed_to_state_in_milestone.rb
@@ -1,6 +1,6 @@
# rubocop:disable all
class ConvertClosedToStateInMilestone < ActiveRecord::Migration
- include Gitlab::Database
+ include Gitlab::Database::MigrationHelpers
def up
execute "UPDATE #{table_name} SET state = 'closed' WHERE closed = #{true_value}"
diff --git a/db/migrate/20130315124931_user_color_scheme.rb b/db/migrate/20130315124931_user_color_scheme.rb
index 06e28a49d9d..09af928fde7 100644
--- a/db/migrate/20130315124931_user_color_scheme.rb
+++ b/db/migrate/20130315124931_user_color_scheme.rb
@@ -1,6 +1,6 @@
# rubocop:disable all
class UserColorScheme < ActiveRecord::Migration
- include Gitlab::Database
+ include Gitlab::Database::MigrationHelpers
def up
add_column :users, :color_scheme_id, :integer, null: false, default: 1
diff --git a/db/migrate/20131112220935_add_visibility_level_to_projects.rb b/db/migrate/20131112220935_add_visibility_level_to_projects.rb
index 5efc17b228e..86d73753adc 100644
--- a/db/migrate/20131112220935_add_visibility_level_to_projects.rb
+++ b/db/migrate/20131112220935_add_visibility_level_to_projects.rb
@@ -1,6 +1,6 @@
# rubocop:disable all
class AddVisibilityLevelToProjects < ActiveRecord::Migration
- include Gitlab::Database
+ include Gitlab::Database::MigrationHelpers
def self.up
add_column :projects, :visibility_level, :integer, :default => 0, :null => false
diff --git a/db/migrate/20140313092127_migrate_already_imported_projects.rb b/db/migrate/20140313092127_migrate_already_imported_projects.rb
index f2e91fe1b40..0afc26b8764 100644
--- a/db/migrate/20140313092127_migrate_already_imported_projects.rb
+++ b/db/migrate/20140313092127_migrate_already_imported_projects.rb
@@ -1,6 +1,6 @@
# rubocop:disable all
class MigrateAlreadyImportedProjects < ActiveRecord::Migration
- include Gitlab::Database
+ include Gitlab::Database::MigrationHelpers
def up
execute("UPDATE projects SET import_status = 'finished' WHERE imported = #{true_value}")
diff --git a/db/migrate/20141007100818_add_visibility_level_to_snippet.rb b/db/migrate/20141007100818_add_visibility_level_to_snippet.rb
index 688d8578478..0c14f75c154 100644
--- a/db/migrate/20141007100818_add_visibility_level_to_snippet.rb
+++ b/db/migrate/20141007100818_add_visibility_level_to_snippet.rb
@@ -1,6 +1,6 @@
# rubocop:disable all
class AddVisibilityLevelToSnippet < ActiveRecord::Migration
- include Gitlab::Database
+ include Gitlab::Database::MigrationHelpers
def up
add_column :snippets, :visibility_level, :integer, :default => 0, :null => false
diff --git a/db/migrate/20151209144329_migrate_ci_web_hooks.rb b/db/migrate/20151209144329_migrate_ci_web_hooks.rb
index cb1e556623a..62a6d334f04 100644
--- a/db/migrate/20151209144329_migrate_ci_web_hooks.rb
+++ b/db/migrate/20151209144329_migrate_ci_web_hooks.rb
@@ -1,6 +1,6 @@
# rubocop:disable all
class MigrateCiWebHooks < ActiveRecord::Migration
- include Gitlab::Database
+ include Gitlab::Database::MigrationHelpers
def up
execute(
diff --git a/db/migrate/20151209145909_migrate_ci_emails.rb b/db/migrate/20151209145909_migrate_ci_emails.rb
index 6b7a106814d..5de7b205fb1 100644
--- a/db/migrate/20151209145909_migrate_ci_emails.rb
+++ b/db/migrate/20151209145909_migrate_ci_emails.rb
@@ -1,6 +1,6 @@
# rubocop:disable all
class MigrateCiEmails < ActiveRecord::Migration
- include Gitlab::Database
+ include Gitlab::Database::MigrationHelpers
def up
# This inserts a new service: BuildsEmailService
diff --git a/db/migrate/20151210125232_migrate_ci_slack_service.rb b/db/migrate/20151210125232_migrate_ci_slack_service.rb
index 633d5148d97..fff130b7b10 100644
--- a/db/migrate/20151210125232_migrate_ci_slack_service.rb
+++ b/db/migrate/20151210125232_migrate_ci_slack_service.rb
@@ -1,6 +1,6 @@
# rubocop:disable all
class MigrateCiSlackService < ActiveRecord::Migration
- include Gitlab::Database
+ include Gitlab::Database::MigrationHelpers
def up
properties_query = 'SELECT properties FROM ci_services ' \
diff --git a/db/migrate/20151210125927_migrate_ci_hip_chat_service.rb b/db/migrate/20151210125927_migrate_ci_hip_chat_service.rb
index dae084ce180..824f6f84195 100644
--- a/db/migrate/20151210125927_migrate_ci_hip_chat_service.rb
+++ b/db/migrate/20151210125927_migrate_ci_hip_chat_service.rb
@@ -1,6 +1,6 @@
# rubocop:disable all
class MigrateCiHipChatService < ActiveRecord::Migration
- include Gitlab::Database
+ include Gitlab::Database::MigrationHelpers
def up
# From properties strip `hipchat_` key
diff --git a/db/migrate/20170407135259_add_foreigh_key_trigger_requests_trigger.rb b/db/migrate/20170407135259_add_foreigh_key_trigger_requests_trigger.rb
new file mode 100644
index 00000000000..81761c65a9f
--- /dev/null
+++ b/db/migrate/20170407135259_add_foreigh_key_trigger_requests_trigger.rb
@@ -0,0 +1,15 @@
+class AddForeighKeyTriggerRequestsTrigger < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_foreign_key(:ci_trigger_requests, :ci_triggers, column: :trigger_id)
+ end
+
+ def down
+ remove_foreign_key(:ci_trigger_requests, column: :trigger_id)
+ end
+end
diff --git a/db/post_migrate/20170301205640_migrate_build_events_to_pipeline_events.rb b/db/post_migrate/20170301205640_migrate_build_events_to_pipeline_events.rb
index 2dd14ee5a78..04bf89c9687 100644
--- a/db/post_migrate/20170301205640_migrate_build_events_to_pipeline_events.rb
+++ b/db/post_migrate/20170301205640_migrate_build_events_to_pipeline_events.rb
@@ -1,6 +1,5 @@
class MigrateBuildEventsToPipelineEvents < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
- include Gitlab::Database
DOWNTIME = false
diff --git a/db/post_migrate/20170406142253_migrate_user_project_view.rb b/db/post_migrate/20170406142253_migrate_user_project_view.rb
new file mode 100644
index 00000000000..22f0f2ac200
--- /dev/null
+++ b/db/post_migrate/20170406142253_migrate_user_project_view.rb
@@ -0,0 +1,19 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class MigrateUserProjectView < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+
+ def up
+ update_column_in_batches(:users, :project_view, 2) do |table, query|
+ query.where(table[:project_view].eq(0))
+ end
+ end
+
+ def down
+ # Nothing can be done to restore old values
+ end
+end
diff --git a/db/post_migrate/20170408033905_remove_old_cache_directories.rb b/db/post_migrate/20170408033905_remove_old_cache_directories.rb
new file mode 100644
index 00000000000..b23b52896b9
--- /dev/null
+++ b/db/post_migrate/20170408033905_remove_old_cache_directories.rb
@@ -0,0 +1,23 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+# Remove all files from old custom carrierwave's cache directories.
+# See https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9466
+
+class RemoveOldCacheDirectories < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ # FileUploader cache.
+ FileUtils.rm_rf(Dir[Rails.root.join('public', 'uploads', 'tmp', '*')])
+ end
+
+ def down
+ # Old cache is not supposed to be recoverable.
+ # So the down method is empty.
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index d400c823387..5689f7331dc 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,8 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20170407140450) do
+ActiveRecord::Schema.define(version: 20170408033905) do
+
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
enable_extension "pg_trgm"
@@ -1356,6 +1357,7 @@ ActiveRecord::Schema.define(version: 20170407140450) do
add_foreign_key "chat_teams", "namespaces", on_delete: :cascade
add_foreign_key "ci_builds", "ci_pipelines", column: "auto_canceled_by_id", name: "fk_a2141b1522", on_delete: :nullify
add_foreign_key "ci_pipelines", "ci_pipelines", column: "auto_canceled_by_id", name: "fk_262d4c2d19", on_delete: :nullify
+ add_foreign_key "ci_trigger_requests", "ci_triggers", column: "trigger_id", name: "fk_b8ec8b7245", on_delete: :cascade
add_foreign_key "ci_trigger_schedules", "ci_triggers", column: "trigger_id", name: "fk_90a406cc94", on_delete: :cascade
add_foreign_key "ci_triggers", "users", column: "owner_id", name: "fk_e8e10d1964", on_delete: :cascade
add_foreign_key "container_repositories", "projects"
diff --git a/doc/README.md b/doc/README.md
index b0b3d2156a7..b6790b4d008 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -62,6 +62,7 @@ All technical content published by GitLab lives in the documentation, including:
- [Migrate GitLab CI to CE/EE](migrate_ci_to_ce/README.md) Follow this guide to migrate your existing GitLab CI data to GitLab CE/EE.
- [Monitoring uptime](user/admin_area/monitoring/health_check.md) Check the server status using the health check endpoint.
- [Operations](administration/operations.md) Keeping GitLab up and running.
+- [Polling](administration/polling.md) Configure how often the GitLab UI polls for updates
- [Raketasks](raketasks/README.md) Backups, maintenance, automatic webhook setup and the importing of projects.
- [Reply by email](administration/reply_by_email.md) Allow users to comment on issues and merge requests by replying to notification emails.
- [Repository checks](administration/repository_checks.md) Periodic Git repository checks.
diff --git a/doc/administration/polling.md b/doc/administration/polling.md
new file mode 100644
index 00000000000..35aaa20df2c
--- /dev/null
+++ b/doc/administration/polling.md
@@ -0,0 +1,24 @@
+# Polling configuration
+
+The GitLab UI polls for updates for different resources (issue notes, issue
+titles, pipeline statuses, etc.) on a schedule appropriate to the resource.
+
+In "Application settings -> Real-time features" you can configure "Polling
+interval multiplier". This multiplier is applied to all resources at once,
+and decimal values are supported. For the sake of the examples below, we will
+say that issue notes poll every 2 seconds, and issue titles poll every 5
+seconds; these are _not_ the actual values.
+
+- 1 is the default, and recommended for most installations. (Issue notes poll
+every 2 seconds, and issue titles poll every 5 seconds.)
+- 0 will disable UI polling completely. (On the next poll, clients will stop
+polling for updates.)
+- A value greater than 1 will slow polling down. If you see issues with
+database load from lots of clients polling for updates, increasing the
+multiplier from 1 can be a good compromise, rather than disabling polling
+completely. (For example: If this is set to 2, then issue notes poll every 4
+seconds, and issue titles poll every 10 seconds.)
+- A value between 0 and 1 will make the UI poll more frequently (so updates
+will show in other sessions faster), but is **not recommended**. 1 should be
+fast enough. (For example, if this is set to 0.5, then issue notes poll every
+1 second, and issue titles poll every 2.5 seconds.)
diff --git a/doc/api/README.md b/doc/api/README.md
index e627b6f2ee8..d444ce94573 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -303,6 +303,17 @@ Additional pagination headers are also sent back.
| `X-Next-Page` | The index of the next page |
| `X-Prev-Page` | The index of the previous page |
+## Namespaced path encoding
+
+If using namespaced API calls, make sure that the `NAMESPACE/PROJECT_NAME` is
+URL-encoded.
+
+For example, `/` is represented by `%2F`:
+
+```
+/api/v4/projects/diaspora%2Fdiaspora
+```
+
## `id` vs `iid`
When you work with the API, you may notice two similar fields in API entities:
@@ -398,7 +409,6 @@ Content-Type: application/json
}
```
-
## Clients
There are many unofficial GitLab API Clients for most of the popular
diff --git a/doc/api/access_requests.md b/doc/api/access_requests.md
index 96b8d654c58..21de7d18632 100644
--- a/doc/api/access_requests.md
+++ b/doc/api/access_requests.md
@@ -25,7 +25,7 @@ GET /projects/:id/access_requests
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The group/project ID or path |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
```bash
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/groups/:id/access_requests
@@ -66,7 +66,7 @@ POST /projects/:id/access_requests
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The group/project ID or path |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
```bash
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/groups/:id/access_requests
@@ -97,7 +97,7 @@ PUT /projects/:id/access_requests/:user_id/approve
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The group/project ID or path |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `user_id` | integer | yes | The user ID of the access requester |
| `access_level` | integer | no | A valid access level (defaults: `30`, developer access level) |
@@ -130,7 +130,7 @@ DELETE /projects/:id/access_requests/:user_id
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The group/project ID or path |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `user_id` | integer | yes | The user ID of the access requester |
```bash
diff --git a/doc/api/award_emoji.md b/doc/api/award_emoji.md
index f57928d3c93..5f3adcc397a 100644
--- a/doc/api/award_emoji.md
+++ b/doc/api/award_emoji.md
@@ -23,7 +23,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `awardable_id` | integer | yes | The ID (`iid` for merge requests/issues, `id` for snippets) of an awardable |
```bash
@@ -83,7 +83,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `awardable_id` | integer | yes | The ID (`iid` for merge requests/issues, `id` for snippets) of an awardable |
| `award_id` | integer | yes | The ID of the award emoji |
@@ -126,7 +126,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `awardable_id` | integer | yes | The ID (`iid` for merge requests/issues, `id` for snippets) of an awardable |
| `name` | string | yes | The name of the emoji, without colons |
@@ -152,7 +152,7 @@ Example Response:
"updated_at": "2016-06-17T17:47:29.266Z",
"awardable_id": 80,
"awardable_type": "Issue"
-}
+}
```
### Delete an award emoji
@@ -170,7 +170,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `issue_iid` | integer | yes | The internal ID of an issue |
| `award_id` | integer | yes | The ID of a award_emoji |
@@ -195,7 +195,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `issue_iid` | integer | yes | The internal ID of an issue |
| `note_id` | integer | yes | The ID of an note |
@@ -237,7 +237,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `issue_iid` | integer | yes | The internal ID of an issue |
| `note_id` | integer | yes | The ID of a note |
| `award_id` | integer | yes | The ID of the award emoji |
@@ -277,7 +277,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `issue_iid` | integer | yes | The internal ID of an issue |
| `note_id` | integer | yes | The ID of a note |
| `name` | string | yes | The name of the emoji, without colons |
@@ -320,7 +320,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `issue_iid` | integer | yes | The internal ID of an issue |
| `note_id` | integer | yes | The ID of a note |
| `award_id` | integer | yes | The ID of a award_emoji |
diff --git a/doc/api/boards.md b/doc/api/boards.md
index b2106463639..17d2be0ee16 100644
--- a/doc/api/boards.md
+++ b/doc/api/boards.md
@@ -15,7 +15,7 @@ GET /projects/:id/boards
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
```bash
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/:id/boards
@@ -71,7 +71,7 @@ GET /projects/:id/boards/:board_id/lists
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `board_id` | integer | yes | The ID of a board |
```bash
@@ -122,7 +122,7 @@ GET /projects/:id/boards/:board_id/lists/:list_id
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `board_id` | integer | yes | The ID of a board |
| `list_id`| integer | yes | The ID of a board's list |
@@ -154,7 +154,7 @@ POST /projects/:id/boards/:board_id/lists
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `board_id` | integer | yes | The ID of a board |
| `label_id` | integer | yes | The ID of a label |
@@ -186,7 +186,7 @@ PUT /projects/:id/boards/:board_id/lists/:list_id
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `board_id` | integer | yes | The ID of a board |
| `list_id` | integer | yes | The ID of a board's list |
| `position` | integer | yes | The position of the list |
@@ -219,7 +219,7 @@ DELETE /projects/:id/boards/:board_id/lists/:list_id
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `board_id` | integer | yes | The ID of a board |
| `list_id` | integer | yes | The ID of a board's list |
diff --git a/doc/api/branches.md b/doc/api/branches.md
index 815aabda8e3..5717215deb6 100644
--- a/doc/api/branches.md
+++ b/doc/api/branches.md
@@ -12,7 +12,7 @@ GET /projects/:id/repository/branches
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
```bash
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/5/repository/branches
@@ -59,7 +59,7 @@ GET /projects/:id/repository/branches/:branch
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `branch` | string | yes | The name of the branch |
```bash
@@ -109,7 +109,7 @@ curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `branch` | string | yes | The name of the branch |
| `developers_can_push` | boolean | no | Flag if developers can push to the branch |
| `developers_can_merge` | boolean | no | Flag if developers can merge to the branch |
@@ -157,7 +157,7 @@ curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `branch` | string | yes | The name of the branch |
Example response:
@@ -195,7 +195,7 @@ POST /projects/:id/repository/branches
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of a project |
+| `id` | integer | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `branch` | string | yes | The name of the branch |
| `ref` | string | yes | The branch name or commit SHA to create branch from |
@@ -238,7 +238,7 @@ DELETE /projects/:id/repository/branches/:branch
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `branch` | string | yes | The name of the branch |
In case of an error, an explaining message is provided.
@@ -257,7 +257,7 @@ DELETE /projects/:id/repository/merged_branches
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
```bash
diff --git a/doc/api/build_variables.md b/doc/api/build_variables.md
index 1c26e9b33ab..9218902e84a 100644
--- a/doc/api/build_variables.md
+++ b/doc/api/build_variables.md
@@ -10,7 +10,7 @@ GET /projects/:id/variables
| Attribute | Type | required | Description |
|-----------|---------|----------|---------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID of a project or [urlencoded NAMESPACE/PROJECT_NAME of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
```
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/variables"
@@ -39,7 +39,7 @@ GET /projects/:id/variables/:key
| Attribute | Type | required | Description |
|-----------|---------|----------|-----------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID of a project or [urlencoded NAMESPACE/PROJECT_NAME of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `key` | string | yes | The `key` of a variable |
```
@@ -63,7 +63,7 @@ POST /projects/:id/variables
| Attribute | Type | required | Description |
|-----------|---------|----------|-----------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID of a project or [urlencoded NAMESPACE/PROJECT_NAME of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `key` | string | yes | The `key` of a variable; must have no more than 255 characters; only `A-Z`, `a-z`, `0-9`, and `_` are allowed |
| `value` | string | yes | The `value` of a variable |
@@ -88,7 +88,7 @@ PUT /projects/:id/variables/:key
| Attribute | Type | required | Description |
|-----------|---------|----------|-------------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID of a project or [urlencoded NAMESPACE/PROJECT_NAME of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `key` | string | yes | The `key` of a variable |
| `value` | string | yes | The `value` of a variable |
@@ -113,7 +113,7 @@ DELETE /projects/:id/variables/:key
| Attribute | Type | required | Description |
|-----------|---------|----------|-------------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID of a project or [urlencoded NAMESPACE/PROJECT_NAME of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `key` | string | yes | The `key` of a variable |
```
diff --git a/doc/api/commits.md b/doc/api/commits.md
index 24c402346b1..9cb58dd3ae9 100644
--- a/doc/api/commits.md
+++ b/doc/api/commits.md
@@ -10,7 +10,7 @@ GET /projects/:id/repository/commits
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID of a project or NAMESPACE/PROJECT_NAME owned by the authenticated user
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
| `ref_name` | string | no | The name of a repository branch or tag or if not given the default branch |
| `since` | string | no | Only commits after or on this date will be returned in ISO 8601 format YYYY-MM-DDTHH:MM:SSZ |
| `until` | string | no | Only commits before or on this date will be returned in ISO 8601 format YYYY-MM-DDTHH:MM:SSZ |
@@ -68,7 +68,7 @@ POST /projects/:id/repository/commits
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID of a project or NAMESPACE/PROJECT_NAME |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
| `branch` | string | yes | The name of a branch |
| `commit_message` | string | yes | Commit message |
| `actions[]` | array | yes | An array of action hashes to commit as a batch. See the next table for what attributes it can take. |
@@ -155,7 +155,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID of a project or NAMESPACE/PROJECT_NAME owned by the authenticated user
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
| `sha` | string | yes | The commit hash or name of a repository branch or tag |
```bash
@@ -203,7 +203,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID of a project or NAMESPACE/PROJECT_NAME owned by the authenticated user
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
| `sha` | string | yes | The commit hash |
| `branch` | string | yes | The name of the branch |
@@ -245,7 +245,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID of a project or NAMESPACE/PROJECT_NAME owned by the authenticated user
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
| `sha` | string | yes | The commit hash or name of a repository branch or tag |
```bash
@@ -281,7 +281,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID of a project or NAMESPACE/PROJECT_NAME owned by the authenticated user
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
| `sha` | string | yes | The commit hash or name of a repository branch or tag |
```bash
@@ -330,7 +330,7 @@ POST /projects/:id/repository/commits/:sha/comments
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID of a project or NAMESPACE/PROJECT_NAME owned by the authenticated user
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
| `sha` | string | yes | The commit SHA or name of a repository branch or tag |
| `note` | string | yes | The text of the comment |
| `path` | string | no | The file path relative to the repository |
@@ -375,7 +375,7 @@ GET /projects/:id/repository/commits/:sha/statuses
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID of a project or NAMESPACE/PROJECT_NAME owned by the authenticated user
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
| `sha` | string | yes | The commit SHA
| `ref` | string | no | The name of a repository branch or tag or, if not given, the default branch
| `stage` | string | no | Filter by [build stage](../ci/yaml/README.md#stages), e.g., `test`
@@ -449,7 +449,7 @@ POST /projects/:id/statuses/:sha
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID of a project or NAMESPACE/PROJECT_NAME owned by the authenticated user
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
| `sha` | string | yes | The commit SHA
| `state` | string | yes | The state of the status. Can be one of the following: `pending`, `running`, `success`, `failed`, `canceled`
| `ref` | string | no | The `ref` (branch or tag) to which the status refers
diff --git a/doc/api/deploy_keys.md b/doc/api/deploy_keys.md
index f051f55ac3e..c3fe7f84ef2 100644
--- a/doc/api/deploy_keys.md
+++ b/doc/api/deploy_keys.md
@@ -43,7 +43,7 @@ GET /projects/:id/deploy_keys
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of the project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
```bash
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/5/deploy_keys"
@@ -82,7 +82,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of the project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `key_id` | integer | yes | The ID of the deploy key |
```bash
@@ -114,7 +114,7 @@ POST /projects/:id/deploy_keys
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of the project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `title` | string | yes | New deploy key's title |
| `key` | string | yes | New deploy key |
| `can_push` | boolean | no | Can deploy key push to the project's repository |
@@ -145,7 +145,7 @@ DELETE /projects/:id/deploy_keys/:key_id
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of the project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `key_id` | integer | yes | The ID of the deploy key |
```bash
@@ -162,7 +162,7 @@ curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitla
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of the project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `key_id` | integer | yes | The ID of the deploy key |
Example response:
diff --git a/doc/api/deployments.md b/doc/api/deployments.md
index 76e18c8a9bd..0273c819614 100644
--- a/doc/api/deployments.md
+++ b/doc/api/deployments.md
@@ -10,7 +10,7 @@ GET /projects/:id/deployments
| Attribute | Type | Required | Description |
|-----------|---------|----------|---------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
```bash
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/deployments"
@@ -147,7 +147,7 @@ GET /projects/:id/deployments/:deployment_id
| Attribute | Type | Required | Description |
|-----------|---------|----------|---------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `deployment_id` | integer | yes | The ID of the deployment |
```bash
diff --git a/doc/api/enviroments.md b/doc/api/enviroments.md
index 3f0a8d989f9..49930f01945 100644
--- a/doc/api/enviroments.md
+++ b/doc/api/enviroments.md
@@ -10,7 +10,7 @@ GET /projects/:id/environments
| Attribute | Type | Required | Description |
| --------- | ------- | -------- | --------------------- |
-| `id` | integer | yes | The ID of the project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
```bash
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/1/environments
@@ -41,7 +41,7 @@ POST /projects/:id/environment
| Attribute | Type | Required | Description |
| ------------- | ------- | -------- | ---------------------------- |
-| `id` | integer | yes | The ID of the project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `name` | string | yes | The name of the environment |
| `external_url` | string | no | Place to link to for this environment |
@@ -72,7 +72,7 @@ PUT /projects/:id/environments/:environments_id
| Attribute | Type | Required | Description |
| --------------- | ------- | --------------------------------- | ------------------------------- |
-| `id` | integer | yes | The ID of the project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `environment_id` | integer | yes | The ID of the environment | The ID of the environment |
| `name` | string | no | The new name of the environment |
| `external_url` | string | no | The new external_url |
@@ -102,7 +102,7 @@ DELETE /projects/:id/environments/:environment_id
| Attribute | Type | Required | Description |
| --------- | ------- | -------- | --------------------- |
-| `id` | integer | yes | The ID of the project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `environment_id` | integer | yes | The ID of the environment |
```bash
@@ -119,7 +119,7 @@ POST /projects/:id/environments/:environment_id/stop
| Attribute | Type | Required | Description |
| --------- | ------- | -------- | --------------------- |
-| `id` | integer | yes | The ID of the project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `environment_id` | integer | yes | The ID of the environment |
```bash
diff --git a/doc/api/groups.md b/doc/api/groups.md
index dfc6b80bfd9..bc61bfec9b9 100644
--- a/doc/api/groups.md
+++ b/doc/api/groups.md
@@ -53,7 +53,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or path of a group |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
| `archived` | boolean | no | Limit by archived status |
| `visibility` | string | no | Limit by visibility `public`, `internal`, or `private` |
| `order_by` | string | no | Return projects ordered by `id`, `name`, `path`, `created_at`, `updated_at`, or `last_activity_at` fields. Default is `created_at` |
@@ -119,7 +119,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or path of a group |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
```bash
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/groups/4
@@ -299,7 +299,7 @@ POST /groups/:id/projects/:project_id
Parameters:
-- `id` (required) - The ID or path of a group
+- `id` (required) - The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user
- `project_id` (required) - The ID or path of a project
## Update group
diff --git a/doc/api/issues.md b/doc/api/issues.md
index 54c099d4bf8..5f01fcdd396 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -111,7 +111,7 @@ GET /groups/:id/issues?search=issue+title+or+description
| Attribute | Type | Required | Description |
|-------------|----------------|----------|-----------------------------------------------------------------------------------------------------------------------------|
-| `id` | integer | yes | The ID of a group |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
| `state` | string | no | Return all issues or just those that are `opened` or `closed` |
| `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned. `No+Label` lists all issues with no labels |
| `iids` | Array[integer] | no | Return only the issues having the given `iid` |
@@ -193,7 +193,7 @@ GET /projects/:id/issues?search=issue+title+or+description
| Attribute | Type | Required | Description |
|-------------|----------------|----------|-----------------------------------------------------------------------------------------------------------------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `iids` | Array[integer] | no | Return only the milestone having the given `iid` |
| `state` | string | no | Return all issues or just those that are `opened` or `closed` |
| `labels` | string | no | Comma-separated list of label names, issues must have all labels to be returned. `No+Label` lists all issues with no labels |
@@ -266,7 +266,7 @@ GET /projects/:id/issues/:issue_iid
| Attribute | Type | Required | Description |
|-------------|---------|----------|--------------------------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `issue_iid` | integer | yes | The internal ID of a project's issue |
```bash
@@ -331,7 +331,7 @@ POST /projects/:id/issues
| Attribute | Type | Required | Description |
|-------------------------------------------|---------|----------|--------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `title` | string | yes | The title of an issue |
| `description` | string | no | The description of an issue |
| `confidential` | boolean | no | Set an issue to be confidential. Default is `false`. |
@@ -391,7 +391,7 @@ PUT /projects/:id/issues/:issue_iid
| Attribute | Type | Required | Description |
|----------------|---------|----------|------------------------------------------------------------------------------------------------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `issue_iid` | integer | yes | The internal ID of a project's issue |
| `title` | string | no | The title of an issue |
| `description` | string | no | The description of an issue |
@@ -450,7 +450,7 @@ DELETE /projects/:id/issues/:issue_iid
| Attribute | Type | Required | Description |
|-------------|---------|----------|--------------------------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `issue_iid` | integer | yes | The internal ID of a project's issue |
```bash
@@ -472,7 +472,7 @@ POST /projects/:id/issues/:issue_iid/move
| Attribute | Type | Required | Description |
|-----------------|---------|----------|--------------------------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `issue_iid` | integer | yes | The internal ID of a project's issue |
| `to_project_id` | integer | yes | The ID of the new project |
@@ -528,7 +528,7 @@ POST /projects/:id/issues/:issue_iid/subscribe
| Attribute | Type | Required | Description |
|-------------|---------|----------|--------------------------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `issue_iid` | integer | yes | The internal ID of a project's issue |
```bash
@@ -583,7 +583,7 @@ POST /projects/:id/issues/:issue_iid/unsubscribe
| Attribute | Type | Required | Description |
|-------------|---------|----------|--------------------------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `issue_iid` | integer | yes | The internal ID of a project's issue |
```bash
@@ -602,7 +602,7 @@ POST /projects/:id/issues/:issue_iid/todo
| Attribute | Type | Required | Description |
|-------------|---------|----------|--------------------------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `issue_iid` | integer | yes | The internal ID of a project's issue |
```bash
@@ -693,7 +693,7 @@ POST /projects/:id/issues/:issue_iid/time_estimate
| Attribute | Type | Required | Description |
|-------------|---------|----------|------------------------------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `issue_iid` | integer | yes | The internal ID of a project's issue |
| `duration` | string | yes | The duration in human format. e.g: 3h30m |
@@ -722,7 +722,7 @@ POST /projects/:id/issues/:issue_iid/reset_time_estimate
| Attribute | Type | Required | Description |
|-------------|---------|----------|--------------------------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `issue_iid` | integer | yes | The internal ID of a project's issue |
```bash
@@ -750,7 +750,7 @@ POST /projects/:id/issues/:issue_iid/add_spent_time
| Attribute | Type | Required | Description |
|-------------|---------|----------|------------------------------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `issue_iid` | integer | yes | The internal ID of a project's issue |
| `duration` | string | yes | The duration in human format. e.g: 3h30m |
@@ -779,7 +779,7 @@ POST /projects/:id/issues/:issue_iid/reset_spent_time
| Attribute | Type | Required | Description |
|-------------|---------|----------|--------------------------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `issue_iid` | integer | yes | The internal ID of a project's issue |
```bash
@@ -805,7 +805,7 @@ GET /projects/:id/issues/:issue_iid/time_stats
| Attribute | Type | Required | Description |
|-------------|---------|----------|--------------------------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `issue_iid` | integer | yes | The internal ID of a project's issue |
```bash
diff --git a/doc/api/jobs.md b/doc/api/jobs.md
index 7340123e09d..bea2b96c97a 100644
--- a/doc/api/jobs.md
+++ b/doc/api/jobs.md
@@ -10,7 +10,7 @@ GET /projects/:id/jobs
| Attribute | Type | Required | Description |
|-----------|---------|----------|---------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `scope` | string **or** array of strings | no | The scope of jobs to show, one or array of: `created`, `pending`, `running`, `failed`, `success`, `canceled`, `skipped`; showing all jobs if none provided |
```
@@ -125,7 +125,7 @@ GET /projects/:id/pipeline/:pipeline_id/jobs
| Attribute | Type | Required | Description |
|---------------|--------------------------------|----------|----------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `pipeline_id` | integer | yes | The ID of a pipeline |
| `scope` | string **or** array of strings | no | The scope of jobs to show, one or array of: `created`, `pending`, `running`, `failed`, `success`, `canceled`, `skipped`; showing all jobs if none provided |
@@ -241,7 +241,7 @@ GET /projects/:id/jobs/:job_id
| Attribute | Type | Required | Description |
|------------|---------|----------|---------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `job_id` | integer | yes | The ID of a job |
```
@@ -309,7 +309,7 @@ GET /projects/:id/jobs/:job_id/artifacts
| Attribute | Type | Required | Description |
|------------|---------|----------|---------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `job_id` | integer | yes | The ID of a job |
```
@@ -340,7 +340,7 @@ Parameters
| Attribute | Type | Required | Description |
|-------------|---------|----------|-------------------------- |
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `ref_name` | string | yes | The ref from a repository |
| `job` | string | yes | The name of the job |
@@ -369,7 +369,7 @@ GET /projects/:id/jobs/:job_id/trace
| Attribute | Type | Required | Description |
|------------|---------|----------|---------------------|
-| id | integer | yes | The ID of a project |
+| id | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| job_id | integer | yes | The ID of a job |
```
@@ -393,7 +393,7 @@ POST /projects/:id/jobs/:job_id/cancel
| Attribute | Type | Required | Description |
|------------|---------|----------|---------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `job_id` | integer | yes | The ID of a job |
```
@@ -439,7 +439,7 @@ POST /projects/:id/jobs/:job_id/retry
| Attribute | Type | Required | Description |
|------------|---------|----------|---------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `job_id` | integer | yes | The ID of a job |
```
@@ -487,7 +487,7 @@ Parameters
| Attribute | Type | Required | Description |
|-------------|---------|----------|---------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `job_id` | integer | yes | The ID of a job |
Example of request
@@ -537,7 +537,7 @@ Parameters
| Attribute | Type | Required | Description |
|-------------|---------|----------|---------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `job_id` | integer | yes | The ID of a job |
Example request:
@@ -585,7 +585,7 @@ POST /projects/:id/jobs/:job_id/play
| Attribute | Type | Required | Description |
|-----------|---------|----------|---------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `job_id` | integer | yes | The ID of a job |
```
diff --git a/doc/api/labels.md b/doc/api/labels.md
index 839000a4f48..778348ea371 100644
--- a/doc/api/labels.md
+++ b/doc/api/labels.md
@@ -10,7 +10,7 @@ GET /projects/:id/labels
| Attribute | Type | Required | Description |
| --------- | ------- | -------- | --------------------- |
-| `id` | integer | yes | The ID of the project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
```bash
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/1/labels
@@ -88,7 +88,7 @@ POST /projects/:id/labels
| Attribute | Type | Required | Description |
| ------------- | ------- | -------- | ---------------------------- |
-| `id` | integer | yes | The ID of the project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `name` | string | yes | The name of the label |
| `color` | string | yes | The color of the label given in 6-digit hex notation with leading '#' sign (e.g. #FFAABB) or one of the [CSS color names](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#Color_keywords) |
| `description` | string | no | The description of the label |
@@ -124,7 +124,7 @@ DELETE /projects/:id/labels
| Attribute | Type | Required | Description |
| --------- | ------- | -------- | --------------------- |
-| `id` | integer | yes | The ID of the project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `name` | string | yes | The name of the label |
```bash
@@ -142,7 +142,7 @@ PUT /projects/:id/labels
| Attribute | Type | Required | Description |
| --------------- | ------- | --------------------------------- | ------------------------------- |
-| `id` | integer | yes | The ID of the project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `name` | string | yes | The name of the existing label |
| `new_name` | string | yes if `color` is not provided | The new name of the label |
| `color` | string | yes if `new_name` is not provided | The color of the label given in 6-digit hex notation with leading '#' sign (e.g. #FFAABB) or one of the [CSS color names](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value#Color_keywords) |
@@ -182,7 +182,7 @@ POST /projects/:id/labels/:label_id/subscribe
| Attribute | Type | Required | Description |
| ---------- | ----------------- | -------- | ------------------------------------ |
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `label_id` | integer or string | yes | The ID or title of a project's label |
```bash
@@ -217,7 +217,7 @@ POST /projects/:id/labels/:label_id/unsubscribe
| Attribute | Type | Required | Description |
| ---------- | ----------------- | -------- | ------------------------------------ |
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `label_id` | integer or string | yes | The ID or title of a project's label |
```bash
diff --git a/doc/api/members.md b/doc/api/members.md
index fe46f8f84bc..3c661284f11 100644
--- a/doc/api/members.md
+++ b/doc/api/members.md
@@ -23,7 +23,7 @@ GET /projects/:id/members
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The group/project ID or path |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project or group](README.md#namespaced-path-encoding) owned by the authenticated user |
| `query` | string | no | A query string to search for members |
```bash
@@ -65,7 +65,7 @@ GET /projects/:id/members/:user_id
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The group/project ID or path |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project or group](README.md#namespaced-path-encoding) owned by the authenticated user |
| `user_id` | integer | yes | The user ID of the member |
```bash
@@ -98,7 +98,7 @@ POST /projects/:id/members
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The group/project ID or path |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project or group](README.md#namespaced-path-encoding) owned by the authenticated user |
| `user_id` | integer | yes | The user ID of the new member |
| `access_level` | integer | yes | A valid access level |
| `expires_at` | string | no | A date string in the format YEAR-MONTH-DAY |
@@ -132,7 +132,7 @@ PUT /projects/:id/members/:user_id
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The group/project ID or path |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project or group](README.md#namespaced-path-encoding) owned by the authenticated user |
| `user_id` | integer | yes | The user ID of the member |
| `access_level` | integer | yes | A valid access level |
| `expires_at` | string | no | A date string in the format YEAR-MONTH-DAY |
@@ -166,7 +166,7 @@ DELETE /projects/:id/members/:user_id
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The group/project ID or path |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project or group](README.md#namespaced-path-encoding) owned by the authenticated user |
| `user_id` | integer | yes | The user ID of the member |
```bash
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index 2e0545da1c4..ff956add348 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -15,7 +15,7 @@ GET /projects/:id/merge_requests?iids[]=42&iids[]=43
Parameters:
-- `id` (required) - The ID of a project
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `iid` (optional) - Return the request having the given `iid`
- `state` (optional) - Return `all` requests or just those that are `merged`, `opened` or `closed`
- `order_by` (optional) - Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at`
@@ -87,7 +87,7 @@ GET /projects/:id/merge_requests/:merge_request_iid
Parameters:
-- `id` (required) - The ID of a project
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `merge_request_iid` (required) - The internal ID of the merge request
```json
@@ -155,7 +155,7 @@ GET /projects/:id/merge_requests/:merge_request_iid/commits
Parameters:
-- `id` (required) - The ID of a project
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `merge_request_iid` (required) - The internal ID of the merge request
@@ -192,7 +192,7 @@ GET /projects/:id/merge_requests/:merge_request_iid/changes
Parameters:
-- `id` (required) - The ID of a project
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `merge_request_iid` (required) - The internal ID of the merge request
```json
@@ -271,7 +271,7 @@ POST /projects/:id/merge_requests
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | string | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `source_branch` | string | yes | The source branch |
| `target_branch` | string | yes | The target branch |
| `title` | string | yes | Title of MR |
@@ -347,7 +347,7 @@ PUT /projects/:id/merge_requests/:merge_request_iid
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | string | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `merge_request_iid` | integer | yes | The ID of a merge request |
| `target_branch` | string | no | The target branch |
| `title` | string | no | Title of MR |
@@ -422,9 +422,9 @@ Only for admins and project owners. Soft deletes the merge request in question.
DELETE /projects/:id/merge_requests/:merge_request_iid
```
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of a project |
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `merge_request_iid` | integer | yes | The internal ID of the merge request |
```bash
@@ -450,7 +450,7 @@ PUT /projects/:id/merge_requests/:merge_request_iid/merge
Parameters:
-- `id` (required) - The ID of a project
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `merge_request_iid` (required) - Internal ID of MR
- `merge_commit_message` (optional) - Custom merge commit message
- `should_remove_source_branch` (optional) - if `true` removes the source branch
@@ -524,7 +524,7 @@ PUT /projects/:id/merge_requests/:merge_request_iid/cancel_merge_when_pipeline_s
```
Parameters:
-- `id` (required) - The ID of a project
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `merge_request_iid` (required) - Internal ID of MR
```json
@@ -596,7 +596,7 @@ GET /projects/:id/merge_requests/:merge_request_iid/closes_issues
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `merge_request_iid` | integer | yes | The internal ID of the merge request |
```bash
@@ -671,7 +671,7 @@ POST /projects/:id/merge_requests/:merge_request_iid/subscribe
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `merge_request_iid` | integer | yes | The internal ID of the merge request |
```bash
@@ -745,7 +745,7 @@ POST /projects/:id/merge_requests/:merge_request_iid/unsubscribe
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `merge_request_iid` | integer | yes | The internal ID of the merge request |
```bash
@@ -819,7 +819,7 @@ POST /projects/:id/merge_requests/:merge_request_iid/todo
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `merge_request_iid` | integer | yes | The internal ID of the merge request |
```bash
@@ -1027,7 +1027,7 @@ POST /projects/:id/merge_requests/:merge_request_iid/time_estimate
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `merge_request_iid` | integer | yes | The internal ID of the merge request |
| `duration` | string | yes | The duration in human format. e.g: 3h30m |
@@ -1056,7 +1056,7 @@ POST /projects/:id/merge_requests/:merge_request_iid/reset_time_estimate
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `merge_request_iid` | integer | yes | The internal ID of a project's merge_request |
```bash
@@ -1084,7 +1084,7 @@ POST /projects/:id/merge_requests/:merge_request_iid/add_spent_time
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `merge_request_iid` | integer | yes | The internal ID of the merge request |
| `duration` | string | yes | The duration in human format. e.g: 3h30m |
@@ -1113,7 +1113,7 @@ POST /projects/:id/merge_requests/:merge_request_iid/reset_spent_time
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `merge_request_iid` | integer | yes | The internal ID of a project's merge_request |
```bash
@@ -1139,7 +1139,7 @@ GET /projects/:id/merge_requests/:merge_request_iid/time_stats
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `merge_request_iid` | integer | yes | The internal ID of the merge request |
```bash
diff --git a/doc/api/milestones.md b/doc/api/milestones.md
index 3c86357a6c3..7640eeb8d00 100644
--- a/doc/api/milestones.md
+++ b/doc/api/milestones.md
@@ -17,7 +17,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `iids` | Array[integer] | optional | Return only the milestones having the given `iids` |
| `state` | string | optional | Return only `active` or `closed` milestones` |
| `search` | string | optional | Return only milestones with a title or description matching the provided string |
@@ -56,8 +56,8 @@ GET /projects/:id/milestones/:milestone_id
Parameters:
-- `id` (required) - The ID of a project
-- `milestone_id` (required) - The ID of a project milestone
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
+- `milestone_id` (required) - The ID of the project's milestone
## Create new milestone
@@ -69,7 +69,7 @@ POST /projects/:id/milestones
Parameters:
-- `id` (required) - The ID of a project
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `title` (required) - The title of an milestone
- `description` (optional) - The description of the milestone
- `due_date` (optional) - The due date of the milestone
@@ -85,7 +85,7 @@ PUT /projects/:id/milestones/:milestone_id
Parameters:
-- `id` (required) - The ID of a project
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `milestone_id` (required) - The ID of a project milestone
- `title` (optional) - The title of a milestone
- `description` (optional) - The description of a milestone
@@ -103,7 +103,7 @@ GET /projects/:id/milestones/:milestone_id/issues
Parameters:
-- `id` (required) - The ID of a project
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `milestone_id` (required) - The ID of a project milestone
## Get all merge requests assigned to a single milestone
@@ -116,5 +116,5 @@ GET /projects/:id/milestones/:milestone_id/merge_requests
Parameters:
-- `id` (required) - The ID of a project
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `milestone_id` (required) - The ID of a project milestone
diff --git a/doc/api/notes.md b/doc/api/notes.md
index 5e927143714..b71fea5fc9f 100644
--- a/doc/api/notes.md
+++ b/doc/api/notes.md
@@ -14,7 +14,7 @@ GET /projects/:id/issues/:issue_iid/notes
Parameters:
-- `id` (required) - The ID of a project
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `issue_iid` (required) - The IID of an issue
```json
@@ -68,7 +68,7 @@ GET /projects/:id/issues/:issue_iid/notes/:note_id
Parameters:
-- `id` (required) - The ID of a project
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `issue_iid` (required) - The IID of a project issue
- `note_id` (required) - The ID of an issue note
@@ -83,7 +83,7 @@ POST /projects/:id/issues/:issue_iid/notes
Parameters:
-- `id` (required) - The ID of a project
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `issue_id` (required) - The IID of an issue
- `body` (required) - The content of a note
- `created_at` (optional) - Date time string, ISO 8601 formatted, e.g. 2016-03-11T03:45:40Z
@@ -98,7 +98,7 @@ PUT /projects/:id/issues/:issue_iid/notes/:note_id
Parameters:
-- `id` (required) - The ID of a project
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `issue_iid` (required) - The IID of an issue
- `note_id` (required) - The ID of a note
- `body` (required) - The content of a note
@@ -115,7 +115,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `issue_iid` | integer | yes | The IID of an issue |
| `note_id` | integer | yes | The ID of a note |
@@ -135,7 +135,7 @@ GET /projects/:id/snippets/:snippet_id/notes
Parameters:
-- `id` (required) - The ID of a project
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `snippet_id` (required) - The ID of a project snippet
### Get single snippet note
@@ -148,7 +148,7 @@ GET /projects/:id/snippets/:snippet_id/notes/:note_id
Parameters:
-- `id` (required) - The ID of a project
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `snippet_id` (required) - The ID of a project snippet
- `note_id` (required) - The ID of an snippet note
@@ -182,7 +182,7 @@ POST /projects/:id/snippets/:snippet_id/notes
Parameters:
-- `id` (required) - The ID of a project
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `snippet_id` (required) - The ID of a snippet
- `body` (required) - The content of a note
@@ -196,7 +196,7 @@ PUT /projects/:id/snippets/:snippet_id/notes/:note_id
Parameters:
-- `id` (required) - The ID of a project
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `snippet_id` (required) - The ID of a snippet
- `note_id` (required) - The ID of a note
- `body` (required) - The content of a note
@@ -213,7 +213,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `snippet_id` | integer | yes | The ID of a snippet |
| `note_id` | integer | yes | The ID of a note |
@@ -233,7 +233,7 @@ GET /projects/:id/merge_requests/:merge_request_iid/notes
Parameters:
-- `id` (required) - The ID of a project
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `merge_request_iid` (required) - The IID of a project merge request
### Get single merge request note
@@ -246,7 +246,7 @@ GET /projects/:id/merge_requests/:merge_request_iid/notes/:note_id
Parameters:
-- `id` (required) - The ID of a project
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `merge_request_iid` (required) - The IID of a project merge request
- `note_id` (required) - The ID of a merge request note
@@ -283,7 +283,7 @@ POST /projects/:id/merge_requests/:merge_request_iid/notes
Parameters:
-- `id` (required) - The ID of a project
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `merge_request_iid` (required) - The IID of a merge request
- `body` (required) - The content of a note
@@ -297,7 +297,7 @@ PUT /projects/:id/merge_requests/:merge_request_iid/notes/:note_id
Parameters:
-- `id` (required) - The ID of a project
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `merge_request_iid` (required) - The IID of a merge request
- `note_id` (required) - The ID of a note
- `body` (required) - The content of a note
@@ -314,7 +314,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `merge_request_iid` | integer | yes | The IID of a merge request |
| `note_id` | integer | yes | The ID of a note |
diff --git a/doc/api/pipeline_triggers.md b/doc/api/pipeline_triggers.md
index 50fc19f0e08..d639e8a0991 100644
--- a/doc/api/pipeline_triggers.md
+++ b/doc/api/pipeline_triggers.md
@@ -12,7 +12,7 @@ GET /projects/:id/triggers
| Attribute | Type | required | Description |
|-----------|---------|----------|---------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
```
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/triggers"
@@ -43,7 +43,7 @@ GET /projects/:id/triggers/:trigger_id
| Attribute | Type | required | Description |
|--------------|---------|----------|--------------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `trigger_id` | integer | yes | The trigger id |
```
@@ -73,7 +73,7 @@ POST /projects/:id/triggers
| Attribute | Type | required | Description |
|---------------|---------|----------|--------------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `description` | string | yes | The trigger name |
```
@@ -103,7 +103,7 @@ PUT /projects/:id/triggers/:trigger_id
| Attribute | Type | required | Description |
|---------------|---------|----------|--------------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `trigger_id` | integer | yes | The trigger id |
| `description` | string | no | The trigger name |
@@ -134,7 +134,7 @@ POST /projects/:id/triggers/:trigger_id/take_ownership
| Attribute | Type | required | Description |
|---------------|---------|----------|--------------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `trigger_id` | integer | yes | The trigger id |
```
@@ -164,7 +164,7 @@ DELETE /projects/:id/triggers/:trigger_id
| Attribute | Type | required | Description |
|----------------|---------|----------|--------------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `trigger_id` | integer | yes | The trigger id |
```
diff --git a/doc/api/pipelines.md b/doc/api/pipelines.md
index 574a8bacb25..732ad8da4ac 100644
--- a/doc/api/pipelines.md
+++ b/doc/api/pipelines.md
@@ -10,7 +10,7 @@ GET /projects/:id/pipelines
| Attribute | Type | Required | Description |
|-----------|---------|----------|---------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
```
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/pipelines"
@@ -45,7 +45,7 @@ GET /projects/:id/pipelines/:pipeline_id
| Attribute | Type | Required | Description |
|------------|---------|----------|---------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `pipeline_id` | integer | yes | The ID of a pipeline |
```
@@ -91,7 +91,7 @@ POST /projects/:id/pipeline
| Attribute | Type | Required | Description |
|------------|---------|----------|---------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `ref` | string | yes | Reference to commit |
```
@@ -137,7 +137,7 @@ POST /projects/:id/pipelines/:pipeline_id/retry
| Attribute | Type | Required | Description |
|------------|---------|----------|---------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `pipeline_id` | integer | yes | The ID of a pipeline |
```
@@ -173,7 +173,7 @@ Response:
}
```
-## Cancel a pipelines jobs
+## Cancel a pipelines jobs
> [Introduced][ce-5837] in GitLab 8.11
@@ -183,7 +183,7 @@ POST /projects/:id/pipelines/:pipeline_id/cancel
| Attribute | Type | Required | Description |
|------------|---------|----------|---------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `pipeline_id` | integer | yes | The ID of a pipeline |
```
diff --git a/doc/api/project_snippets.md b/doc/api/project_snippets.md
index 4f6f561b83e..ff379473961 100644
--- a/doc/api/project_snippets.md
+++ b/doc/api/project_snippets.md
@@ -23,7 +23,7 @@ GET /projects/:id/snippets
Parameters:
-- `id` (required) - The ID of a project
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
## Single snippet
@@ -35,7 +35,7 @@ GET /projects/:id/snippets/:snippet_id
Parameters:
-- `id` (required) - The ID of a project
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `snippet_id` (required) - The ID of a project's snippet
```json
@@ -67,7 +67,7 @@ POST /projects/:id/snippets
Parameters:
-- `id` (required) - The ID of a project
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `title` (required) - The title of a snippet
- `file_name` (required) - The name of a snippet file
- `code` (required) - The content of a snippet
@@ -83,7 +83,7 @@ PUT /projects/:id/snippets/:snippet_id
Parameters:
-- `id` (required) - The ID of a project
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `snippet_id` (required) - The ID of a project's snippet
- `title` (optional) - The title of a snippet
- `file_name` (optional) - The name of a snippet file
@@ -101,7 +101,7 @@ DELETE /projects/:id/snippets/:snippet_id
Parameters:
-- `id` (required) - The ID of a project
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `snippet_id` (required) - The ID of a project's snippet
## Snippet content
@@ -114,5 +114,5 @@ GET /projects/:id/snippets/:snippet_id/raw
Parameters:
-- `id` (required) - The ID of a project
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `snippet_id` (required) - The ID of a project's snippet
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 686f3dba35d..63f88a464f5 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -18,6 +18,7 @@ Constants for project visibility levels are next:
The project can be cloned without any authentication.
+
## List projects
Get a list of visible projects for authenticated user. When being accessed without authentication, all public projects are returned.
@@ -157,8 +158,7 @@ Parameters:
### Get single project
-Get a specific project, identified by project ID or NAMESPACE/PROJECT_NAME, which is owned by the authenticated user.
-If using namespaced projects call make sure that the NAMESPACE/PROJECT_NAME is URL-encoded, eg. `/api/v3/projects/diaspora%2Fdiaspora` (where `/` is represented by `%2F`). This endpoint can be accessed without authentication if
+Get a specific project. This endpoint can be accessed without authentication if
the project is publicly accessible.
```
@@ -169,7 +169,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or NAMESPACE/PROJECT_NAME of the project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
```json
{
@@ -295,7 +295,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or NAMESPACE/PROJECT_NAME of the project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
```json
[
@@ -497,7 +497,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or NAMESPACE/PROJECT_NAME of the project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
| `name` | string | yes | The name of the project |
| `path` | string | no | Custom repository name for the project. By default generated based on name |
| `default_branch` | string | no | `master` by default |
@@ -529,7 +529,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or NAMESPACE/PROJECT_NAME of the project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
| `namespace` | integer/string | yes | The ID or path of the namespace that the project will be forked to |
### Star a project
@@ -544,7 +544,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID or NAMESPACE/PROJECT_NAME of the project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
```bash
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/5/star"
@@ -609,7 +609,7 @@ POST /projects/:id/unstar
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID of the project or NAMESPACE/PROJECT_NAME |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
```bash
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/5/unstar"
@@ -675,7 +675,7 @@ POST /projects/:id/archive
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID of the project or NAMESPACE/PROJECT_NAME |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
```bash
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/5/archive"
@@ -757,7 +757,7 @@ POST /projects/:id/unarchive
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID of the project or NAMESPACE/PROJECT_NAME |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
```bash
curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/5/unarchive"
@@ -840,7 +840,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID of the project or NAMESPACE/PROJECT_NAME |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
## Uploads
@@ -856,7 +856,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID of the project or NAMESPACE/PROJECT_NAME |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
| `file` | string | yes | The file to be uploaded |
```json
@@ -887,7 +887,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID of the project or NAMESPACE/PROJECT_NAME |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
| `group_id` | integer | yes | The ID of the group to share with |
| `group_access` | integer | yes | The permissions level to grant the group |
| `expires_at` | string | no | Share expiration date in ISO 8601 format: 2016-09-26 |
@@ -904,7 +904,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID of the project or NAMESPACE/PROJECT_NAME |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
| `group_id` | integer | yes | The ID of the group |
```bash
@@ -928,7 +928,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID of the project or NAMESPACE/PROJECT_NAME |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
### Get project hook
@@ -942,7 +942,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID of the project or NAMESPACE/PROJECT_NAME |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
| `hook_id` | integer | yes | The ID of a project hook |
```json
@@ -975,7 +975,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID of the project or NAMESPACE/PROJECT_NAME |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
| `url` | string | yes | The hook URL |
| `push_events` | boolean | no | Trigger hook on push events |
| `issues_events` | boolean | no | Trigger hook on issues events |
@@ -1000,7 +1000,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID of the project or NAMESPACE/PROJECT_NAME |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
| `hook_id` | integer | yes | The ID of the project hook |
| `url` | string | yes | The hook URL |
| `push_events` | boolean | no | Trigger hook on push events |
@@ -1027,7 +1027,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID of the project or NAMESPACE/PROJECT_NAME |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
| `hook_id` | integer | yes | The ID of the project hook |
Note the JSON response differs if the hook is available or not. If the project hook
@@ -1049,7 +1049,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID of the project or NAMESPACE/PROJECT_NAME |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
```json
[
@@ -1106,7 +1106,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID of the project or NAMESPACE/PROJECT_NAME |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
| `branch` | string | yes | The name of the branch |
| `developers_can_push` | boolean | no | Flag if developers can push to the branch |
| `developers_can_merge` | boolean | no | Flag if developers can merge to the branch |
@@ -1123,7 +1123,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID of the project or NAMESPACE/PROJECT_NAME |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
| `branch` | string | yes | The name of the branch |
### Unprotect single branch
@@ -1138,7 +1138,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID of the project or NAMESPACE/PROJECT_NAME |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
| `branch` | string | yes | The name of the branch |
## Admin fork relation
@@ -1155,7 +1155,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID of the project or NAMESPACE/PROJECT_NAME |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
| `forked_from_id` | ID | yes | The ID of the project that was forked from |
### Delete an existing forked from relationship
@@ -1168,7 +1168,7 @@ Parameter:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | The ID of the project or NAMESPACE/PROJECT_NAME |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
## Search for projects by name
diff --git a/doc/api/repositories.md b/doc/api/repositories.md
index b1bf9ca07cc..859cbd63831 100644
--- a/doc/api/repositories.md
+++ b/doc/api/repositories.md
@@ -13,7 +13,7 @@ GET /projects/:id/repository/tree
Parameters:
-- `id` (required) - The ID of a project
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `path` (optional) - The path inside repository. Used to get contend of subdirectories
- `ref` (optional) - The name of a repository branch or tag or if not given the default branch
- `recursive` (optional) - Boolean value used to get a recursive tree (false by default)
@@ -84,7 +84,7 @@ GET /projects/:id/repository/blobs/:sha
Parameters:
-- `id` (required) - The ID of a project
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `sha` (required) - The commit or branch name
## Raw blob content
@@ -98,7 +98,7 @@ GET /projects/:id/repository/blobs/:sha/raw
Parameters:
-- `id` (required) - The ID of a project
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `sha` (required) - The blob SHA
## Get file archive
@@ -112,7 +112,7 @@ GET /projects/:id/repository/archive
Parameters:
-- `id` (required) - The ID of a project
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `sha` (optional) - The commit SHA to download defaults to the tip of the default branch
## Compare branches, tags or commits
@@ -126,7 +126,7 @@ GET /projects/:id/repository/compare
Parameters:
-- `id` (required) - The ID of a project
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `from` (required) - the commit SHA or branch name
- `to` (required) - the commit SHA or branch name
@@ -181,7 +181,7 @@ GET /projects/:id/repository/contributors
Parameters:
-- `id` (required) - The ID of a project
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
Response:
diff --git a/doc/api/runners.md b/doc/api/runners.md
index 46f882ce937..16d362a3530 100644
--- a/doc/api/runners.md
+++ b/doc/api/runners.md
@@ -222,7 +222,7 @@ GET /projects/:id/runners
| Attribute | Type | Required | Description |
|-----------|---------|----------|---------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
```
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/9/runners"
@@ -259,7 +259,7 @@ POST /projects/:id/runners
| Attribute | Type | Required | Description |
|-------------|---------|----------|---------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `runner_id` | integer | yes | The ID of a runner |
```
@@ -290,7 +290,7 @@ DELETE /projects/:id/runners/:runner_id
| Attribute | Type | Required | Description |
|-------------|---------|----------|---------------------|
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `runner_id` | integer | yes | The ID of a runner |
```
diff --git a/doc/api/tags.md b/doc/api/tags.md
index bf350f024f5..0f6c4e6794e 100644
--- a/doc/api/tags.md
+++ b/doc/api/tags.md
@@ -12,7 +12,7 @@ GET /projects/:id/repository/tags
Parameters:
-- `id` (required) - The ID of a project
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
```json
[
@@ -53,7 +53,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
-| `id` | integer | yes | The ID of a project |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `tag_name` | string | yes | The name of the tag |
```bash
@@ -93,7 +93,7 @@ POST /projects/:id/repository/tags
Parameters:
-- `id` (required) - The ID of a project
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `tag_name` (required) - The name of a tag
- `ref` (required) - Create tag using commit SHA, another tag name, or branch name.
- `message` (optional) - Creates annotated tag.
@@ -138,7 +138,7 @@ DELETE /projects/:id/repository/tags/:tag_name
Parameters:
-- `id` (required) - The ID of a project
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `tag_name` (required) - The name of a tag
@@ -153,7 +153,7 @@ POST /projects/:id/repository/tags/:tag_name/release
Parameters:
-- `id` (required) - The ID of a project
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `tag_name` (required) - The name of a tag
- `description` (required) - Release notes with markdown support
@@ -174,7 +174,7 @@ PUT /projects/:id/repository/tags/:tag_name/release
Parameters:
-- `id` (required) - The ID of a project
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `tag_name` (required) - The name of a tag
- `description` (required) - Release notes with markdown support
diff --git a/doc/ci/README.md b/doc/ci/README.md
index b3780a08828..c4f9a3cb573 100644
--- a/doc/ci/README.md
+++ b/doc/ci/README.md
@@ -110,9 +110,8 @@ Here is an collection of tutorials and guides on setting up your CI pipeline.
- [Run PHP Composer & NPM scripts then deploy them to a staging server](examples/deployment/composer-npm-deploy.md)
- **Blog posts**
- [Automated Debian packaging](https://about.gitlab.com/2016/10/12/automated-debian-package-build-with-gitlab-ci/)
- - [Spring boot application with GitLab CI and Kubernetes](https://about.gitlab.com/2016/11/30/setting-up-gitlab-ci-for-android-projects/)
- - [Setting up CI for iOS projects](https://about.gitlab.com/2016/12/14/continuous-delivery-of-a-spring-boot-application-with-gitlab-ci-and-kubernetes/)
- - [Using GitLab CI for iOS projects](https://about.gitlab.com/2016/03/10/setting-up-gitlab-ci-for-ios-projects/)
+ - [Spring boot application with GitLab CI and Kubernetes](https://about.gitlab.com/2016/12/14/continuous-delivery-of-a-spring-boot-application-with-gitlab-ci-and-kubernetes/)
+ - [Setting up GitLab CI for iOS projects](https://about.gitlab.com/2016/03/10/setting-up-gitlab-ci-for-ios-projects/)
- [Setting up GitLab CI for Android projects](https://about.gitlab.com/2016/11/30/setting-up-gitlab-ci-for-android-projects/)
- [Building a new GitLab Docs site with Nanoc, GitLab CI, and GitLab Pages](https://about.gitlab.com/2016/12/07/building-a-new-gitlab-docs-site-with-nanoc-gitlab-ci-and-gitlab-pages/)
- [CI/CD with GitLab in action](https://about.gitlab.com/2017/03/13/ci-cd-demo/)
diff --git a/doc/ci/img/pipelines.png b/doc/ci/img/pipelines.png
index 5937e9d99c8..a604fcb2587 100644
--- a/doc/ci/img/pipelines.png
+++ b/doc/ci/img/pipelines.png
Binary files differ
diff --git a/doc/development/fe_guide/performance.md b/doc/development/fe_guide/performance.md
index 9437a5f7a6e..2ddcbe13afa 100644
--- a/doc/development/fe_guide/performance.md
+++ b/doc/development/fe_guide/performance.md
@@ -12,8 +12,8 @@ Thus, we must strike a balance between sending requests and the feeling of realt
Use the following rules when creating realtime solutions.
1. The server will tell you how much to poll by sending `Poll-Interval` in the header.
-Use that as your polling interval. This way it is easy for system administrators to change the
-polling rate.
+Use that as your polling interval. This way it is [easy for system administrators to change the
+polling rate](../../administration/polling.md).
A `Poll-Interval: -1` means you should disable polling, and this must be implemented.
1. A response with HTTP status `4XX` or `5XX` should disable polling as well.
1. Use a common library for polling.
@@ -48,8 +48,8 @@ Steps to split page-specific JavaScript from the main `main.js`:
```haml
- content_for :page_specific_javascripts do
- = page_specific_javascript_bundle_tag('lib_chart')
- = page_specific_javascript_bundle_tag('graphs')
+ = webpack_bundle_tag 'lib_chart'
+ = webpack_bundle_tag 'graphs'
```
The above loads `chart.js` and `graphs_bundle.js` for this page only. `chart.js`
diff --git a/doc/development/fe_guide/testing.md b/doc/development/fe_guide/testing.md
index 8d3513d3566..a4631fd0073 100644
--- a/doc/development/fe_guide/testing.md
+++ b/doc/development/fe_guide/testing.md
@@ -13,10 +13,19 @@ for more information on general testing practices at GitLab.
## Karma test suite
GitLab uses the [Karma][karma] test runner with [Jasmine][jasmine] as its test
-framework for our JavaScript unit tests. For tests that rely on DOM
+framework for our JavaScript unit tests. For tests that rely on DOM
manipulation we use fixtures which are pre-compiled from HAML source files and
served during testing by the [jasmine-jquery][jasmine-jquery] plugin.
+JavaScript tests live in `spec/javascripts/`, matching the folder structure
+of `app/assets/javascripts/`: `app/assets/javascripts/behaviors/autosize.js`
+has a corresponding `spec/javascripts/behaviors/autosize_spec.js` file.
+
+Keep in mind that in a CI environment, these tests are run in a headless
+browser and you will not have access to certain APIs, such as
+[`Notification`](https://developer.mozilla.org/en-US/docs/Web/API/notification),
+which will have to be stubbed.
+
### Running frontend tests
`rake karma` runs the frontend-only (JavaScript) tests.
@@ -80,24 +89,23 @@ If an integration test depends on JavaScript to run correctly, you need to make
sure the spec is configured to enable JavaScript when the tests are run. If you
don't do this you'll see vague error messages from the spec runner.
-To enable a JavaScript driver in an `rspec` test, add `js: true` to the
+To enable a JavaScript driver in an `rspec` test, add `:js` to the
individual spec or the context block containing multiple specs that need
JavaScript enabled:
```ruby
-
# For one spec
-it 'presents information about abuse report', js: true do
- # assertions...
+it 'presents information about abuse report', :js do
+ # assertions...
end
-describe "Admin::AbuseReports", js: true do
- it 'presents information about abuse report' do
- # assertions...
- end
- it 'shows buttons for adding to abuse report' do
- # assertions...
- end
+describe "Admin::AbuseReports", :js do
+ it 'presents information about abuse report' do
+ # assertions...
+ end
+ it 'shows buttons for adding to abuse report' do
+ # assertions...
+ end
end
```
@@ -113,13 +121,12 @@ file for the failing spec, add the `@javascript` flag above the Scenario:
```
@javascript
Scenario: Developer can approve merge request
- Given I am a "Shop" developer
- And I visit project "Shop" merge requests page
- And merge request 'Bug NS-04' must be approved
- And I click link "Bug NS-04"
- When I click link "Approve"
- Then I should see approved merge request "Bug NS-04"
-
+ Given I am a "Shop" developer
+ And I visit project "Shop" merge requests page
+ And merge request 'Bug NS-04' must be approved
+ And I click link "Bug NS-04"
+ When I click link "Approve"
+ Then I should see approved merge request "Bug NS-04"
```
[capybara]: http://teamcapybara.github.io/capybara/
diff --git a/doc/development/migration_style_guide.md b/doc/development/migration_style_guide.md
index 587922d0136..3e8b709c18f 100644
--- a/doc/development/migration_style_guide.md
+++ b/doc/development/migration_style_guide.md
@@ -4,28 +4,53 @@ When writing migrations for GitLab, you have to take into account that
these will be ran by hundreds of thousands of organizations of all sizes, some with
many years of data in their database.
-In addition, having to take a server offline for a an upgrade small or big is
-a big burden for most organizations. For this reason it is important that your
-migrations are written carefully, can be applied online and adhere to the style guide below.
+In addition, having to take a server offline for a a upgrade small or big is a
+big burden for most organizations. For this reason it is important that your
+migrations are written carefully, can be applied online and adhere to the style
+guide below.
-Migrations should not require GitLab installations to be taken offline unless
-_absolutely_ necessary - see the ["What Requires Downtime?"](what_requires_downtime.md)
-page. If a migration requires downtime, this should be clearly mentioned during
-the review process, as well as being documented in the monthly release post. For
-more information, see the "Downtime Tagging" section below.
+Migrations are **not** allowed to require GitLab installations to be taken
+offline unless _absolutely necessary_. Downtime assumptions should be based on
+the behaviour of a migration when performed using PostgreSQL, as various
+operations in MySQL may require downtime without there being alternatives.
+
+When downtime is necessary the migration has to be approved by:
+
+1. The VP of Engineering
+1. A Backend Lead
+1. A Database Specialist
+
+An up-to-date list of people holding these titles can be found at
+<https://about.gitlab.com/team/>.
+
+The document ["What Requires Downtime?"](what_requires_downtime.md) specifies
+various database operations, whether they require downtime and how to
+work around that whenever possible.
When writing your migrations, also consider that databases might have stale data
-or inconsistencies and guard for that. Try to make as little assumptions as possible
-about the state of the database.
+or inconsistencies and guard for that. Try to make as few assumptions as
+possible about the state of the database.
+
+Please don't depend on GitLab-specific code since it can change in future
+versions. If needed copy-paste GitLab code into the migration to make it forward
+compatible.
+
+## Commit Guidelines
-Please don't depend on GitLab specific code since it can change in future versions.
-If needed copy-paste GitLab code into the migration to make it forward compatible.
+Each migration **must** be added in its own commit with a descriptive commit
+message. If a commit adds a migration it _should only_ include the migration and
+any corresponding changes to `db/schema.rb`. This makes it easy to revert a
+database migration without accidentally reverting other changes.
## Downtime Tagging
Every migration must specify if it requires downtime or not, and if it should
-require downtime it must also specify a reason for this. To do so, add the
-following two constants to the migration class' body:
+require downtime it must also specify a reason for this. This is required even
+if 99% of the migrations won't require downtime as this makes it easier to find
+the migrations that _do_ require downtime.
+
+To tag a migration, add the following two constants to the migration class'
+body:
* `DOWNTIME`: a boolean that when set to `true` indicates the migration requires
downtime.
@@ -50,12 +75,53 @@ from a migration class.
## Reversibility
-Your migration should be reversible. This is very important, as it should
+Your migration **must be** reversible. This is very important, as it should
be possible to downgrade in case of a vulnerability or bugs.
In your migration, add a comment describing how the reversibility of the
migration was tested.
+## Multi Threading
+
+Sometimes a migration might need to use multiple Ruby threads to speed up a
+migration. For this to work your migration needs to include the module
+`Gitlab::Database::MultiThreadedMigration`:
+
+```ruby
+class MyMigration < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+ include Gitlab::Database::MultiThreadedMigration
+end
+```
+
+You can then use the method `with_multiple_threads` to perform work in separate
+threads. For example:
+
+```ruby
+class MyMigration < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+ include Gitlab::Database::MultiThreadedMigration
+
+ def up
+ with_multiple_threads(4) do
+ disable_statement_timeout
+
+ # ...
+ end
+ end
+end
+```
+
+Here the call to `disable_statement_timeout` will use the connection local to
+the `with_multiple_threads` block, instead of re-using the global connection
+pool. This ensures each thread has its own connection object, and won't time
+out when trying to obtain one.
+
+**NOTE:** PostgreSQL has a maximum amount of connections that it allows. This
+limit can vary from installation to installation. As a result it's recommended
+you do not use more than 32 threads in a single migration. Usually 4-8 threads
+should be more than enough.
+
## Removing indices
When removing an index make sure to use the method `remove_concurrent_index` instead
@@ -78,7 +144,10 @@ end
## Adding indices
-If you need to add an unique index please keep in mind there is possibility of existing duplicates. If it is possible write a separate migration for handling this situation. It can be just removing or removing with overwriting all references to these duplicates depend on situation.
+If you need to add a unique index please keep in mind there is the possibility
+of existing duplicates being present in the database. This means that should
+always _first_ add a migration that removes any duplicates, before adding the
+unique index.
When adding an index make sure to use the method `add_concurrent_index` instead
of the regular `add_index` method. The `add_concurrent_index` method
@@ -90,17 +159,22 @@ so:
```ruby
class MyMigration < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
+
disable_ddl_transaction!
- def change
+ def up
+ add_concurrent_index :table, :column
+ end
+ def down
+ remove_index :table, :column if index_exists?(:table, :column)
end
end
```
## Adding Columns With Default Values
-When adding columns with default values you should use the method
+When adding columns with default values you must use the method
`add_column_with_default`. This method ensures the table is updated without
requiring downtime. This method is not reversible so you must manually define
the `up` and `down` methods in your migration class.
@@ -123,6 +197,9 @@ class MyMigration < ActiveRecord::Migration
end
```
+Keep in mind that this operation can easily take 10-15 minutes to complete on
+larger installations (e.g. GitLab.com). As a result you should only add default
+values if absolutely necessary.
## Integer column type
@@ -147,13 +224,15 @@ 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.
+Make sure that your migration works with MySQL and PostgreSQL with data. An
+empty database does not guarantee that your migration is correct.
Make sure your migration can be reversed.
## Data migration
-Please prefer Arel and plain SQL over usual ActiveRecord syntax. In case of using plain SQL you need to quote all input manually with `quote_string` helper.
+Please prefer Arel and plain SQL over usual ActiveRecord syntax. In case of
+using plain SQL you need to quote all input manually with `quote_string` helper.
Example with Arel:
@@ -177,3 +256,17 @@ select_all("SELECT name, COUNT(id) as cnt FROM tags GROUP BY name HAVING COUNT(i
execute("DELETE FROM tags WHERE id IN(#{duplicate_ids.join(",")})")
end
```
+
+If you need more complex logic you can define and use models local to a
+migration. For example:
+
+```ruby
+class MyMigration < ActiveRecord::Migration
+ class Project < ActiveRecord::Base
+ self.table_name = 'projects'
+ end
+end
+```
+
+When doing so be sure to explicitly set the model's table name so it's not
+derived from the class name or namespace.
diff --git a/doc/development/polling.md b/doc/development/polling.md
index 05e19f0c515..3b34f985cd4 100644
--- a/doc/development/polling.md
+++ b/doc/development/polling.md
@@ -51,5 +51,6 @@ request path. By doing this we avoid query parameter ordering problems and make
route matching easier.
For more information see:
+- [`Poll-Interval` header](fe_guide/performance.md#realtime-components)
- [RFC 7232](https://tools.ietf.org/html/rfc7232)
- [ETag proposal](https://gitlab.com/gitlab-org/gitlab-ce/issues/26926)
diff --git a/doc/development/testing.md b/doc/development/testing.md
index 5bc958f5a96..ad540ec13db 100644
--- a/doc/development/testing.md
+++ b/doc/development/testing.md
@@ -9,52 +9,179 @@ this guide defines a rule that contradicts the thoughtbot guide, this guide
takes precedence. Some guidelines may be repeated verbatim to stress their
importance.
-## Factories
+## Definitions
+
+### Unit tests
+
+Formal definition: https://en.wikipedia.org/wiki/Unit_testing
+
+These kind of tests ensure that a single unit of code (a method) works as
+expected (given an input, it has a predictable output). These tests should be
+isolated as much as possible. For example, model methods that don't do anything
+with the database shouldn't need a DB record. Classes that don't need database
+records should use stubs/doubles as much as possible.
+
+| Code path | Tests path | Testing engine | Notes |
+| --------- | ---------- | -------------- | ----- |
+| `app/finders/` | `spec/finders/` | RSpec | |
+| `app/helpers/` | `spec/helpers/` | RSpec | |
+| `app/db/{post_,}migrate/` | `spec/migrations/` | RSpec | |
+| `app/policies/` | `spec/policies/` | RSpec | |
+| `app/presenters/` | `spec/presenters/` | RSpec | |
+| `app/routing/` | `spec/routing/` | RSpec | |
+| `app/serializers/` | `spec/serializers/` | RSpec | |
+| `app/services/` | `spec/services/` | RSpec | |
+| `app/tasks/` | `spec/tasks/` | RSpec | |
+| `app/uploaders/` | `spec/uploaders/` | RSpec | |
+| `app/views/` | `spec/views/` | RSpec | |
+| `app/workers/` | `spec/workers/` | RSpec | |
+| `app/assets/javascripts/` | `spec/javascripts/` | Karma | More details in the [JavaScript](#javascript) section. |
+
+### Integration tests
+
+Formal definition: https://en.wikipedia.org/wiki/Integration_testing
+
+These kind of tests ensure that individual parts of the application work well together, without the overhead of the actual app environment (i.e. the browser). These tests should assert at the request/response level: status code, headers, body. They're useful to test permissions, redirections, what view is rendered etc.
+
+| Code path | Tests path | Testing engine | Notes |
+| --------- | ---------- | -------------- | ----- |
+| `app/controllers/` | `spec/controllers/` | RSpec | |
+| `app/mailers/` | `spec/mailers/` | RSpec | |
+| `lib/api/` | `spec/requests/api/` | RSpec | |
+| `lib/ci/api/` | `spec/requests/ci/api/` | RSpec | |
+| `app/assets/javascripts/` | `spec/javascripts/` | Karma | More details in the [JavaScript](#javascript) section. |
+
+#### About controller tests
+
+In an ideal world, controllers should be thin. However, when this is not the
+case, it's acceptable to write a system/feature test without JavaScript instead
+of a controller test. The reason is that testing a fat controller usually
+involves a lot of stubbing, things like:
-GitLab uses [factory_girl] as a test fixture replacement.
-
-- Factory definitions live in `spec/factories/`, named using the pluralization
- of their corresponding model (`User` factories are defined in `users.rb`).
-- There should be only one top-level factory definition per file.
-- FactoryGirl methods are mixed in to all RSpec groups. This means you can (and
- should) call `create(...)` instead of `FactoryGirl.create(...)`.
-- Make use of [traits] to clean up definitions and usages.
-- When defining a factory, don't define attributes that are not required for the
- resulting record to pass validation.
-- When instantiating from a factory, don't supply attributes that aren't
- required by the test.
-- Factories don't have to be limited to `ActiveRecord` objects.
- [See example](https://gitlab.com/gitlab-org/gitlab-ce/commit/0b8cefd3b2385a21cfed779bd659978c0402766d).
-
-[factory_girl]: https://github.com/thoughtbot/factory_girl
-[traits]: http://www.rubydoc.info/gems/factory_girl/file/GETTING_STARTED.md#Traits
-
-## JavaScript
-
-GitLab uses [Karma] to run its [Jasmine] JavaScript specs. They can be run on
-the command line via `bundle exec karma`.
-
-- JavaScript tests live in `spec/javascripts/`, matching the folder structure
- of `app/assets/javascripts/`: `app/assets/javascripts/behaviors/autosize.js`
- has a corresponding `spec/javascripts/behaviors/autosize_spec.js` file.
-- Haml fixtures required for JavaScript tests live in
- `spec/javascripts/fixtures`. They should contain the bare minimum amount of
- markup necessary for the test.
-
- > **Warning:** Keep in mind that a Rails view may change and
- invalidate your test, but everything will still pass because your fixture
- doesn't reflect the latest view. Because of this we encourage you to
- generate fixtures from actual rails views whenever possible.
-
-- Keep in mind that in a CI environment, these tests are run in a headless
- browser and you will not have access to certain APIs, such as
- [`Notification`](https://developer.mozilla.org/en-US/docs/Web/API/notification),
- which will have to be stubbed.
-
-[Karma]: https://github.com/karma-runner/karma
-[Jasmine]: https://github.com/jasmine/jasmine
+```ruby
+controller.instance_variable_set(:@user, user)
+```
-For more information, see the [frontend testing guide](fe_guide/testing.md).
+and use methods which are deprecated in Rails 5 ([#23768]).
+
+[#23768]: https://gitlab.com/gitlab-org/gitlab-ce/issues/23768
+
+#### About Karma
+
+As you may have noticed, Karma is both in the Unit tests and the Integration
+tests category. That's because Karma is a tool that provides an environment to
+run JavaScript tests, so you can either run unit tests (e.g. test a single
+JavaScript method), or integration tests (e.g. test a component that is composed
+of multiple components).
+
+### System tests or Feature tests
+
+Formal definition: https://en.wikipedia.org/wiki/System_testing.
+
+These kind of tests ensure the application works as expected from a user point
+of view (aka black-box testing). These tests should test a happy path for a
+given page or set of pages, and a test case should be added for any regression
+that couldn't have been caught at lower levels with better tests (i.e. if a
+regression is found, regression tests should be added at the lowest-level
+possible).
+
+| Tests path | Testing engine | Notes |
+| ---------- | -------------- | ----- |
+| `spec/features/` | [Capybara] + [RSpec] | If your spec has the `:js` metadata, the browser driver will be [Poltergeist], otherwise it's using [RackTest]. |
+| `features/` | Spinach | Spinach tests are deprecated, [you shouldn't add new Spinach tests](#spinach-feature-tests). |
+
+[Capybara]: https://github.com/teamcapybara/capybara
+[RSpec]: https://github.com/rspec/rspec-rails#feature-specs
+[Poltergeist]: https://github.com/teamcapybara/capybara#poltergeist
+[RackTest]: https://github.com/teamcapybara/capybara#racktest
+
+#### Best practices
+
+- Create only the necessary records in the database
+- Test a happy path and a less happy path but that's it
+- Every other possible path should be tested with Unit or Integration tests
+- Test what's displayed on the page, not the internals of ActiveRecord models.
+ For instance, if you want to verify that a record was created, add
+ expectations that its attributes are displayed on the page, not that
+ `Model.count` increased by one.
+- It's ok to look for DOM elements but don't abuse it since it makes the tests
+ more brittle
+
+If we're confident that the low-level components work well (and we should be if
+we have enough Unit & Integration tests), we shouldn't need to duplicate their
+thorough testing at the System test level.
+
+It's very easy to add tests, but a lot harder to remove or improve tests, so one
+should take care of not introducing too many (slow and duplicated) specs.
+
+The reasons why we should follow these best practices are as follows:
+
+- System tests are slow to run since they spin up the entire application stack
+ in a headless browser, and even slower when they integrate a JS driver
+- When system tests run with a JavaScript driver, the tests are run in a
+ different thread than the application. This means it does not share a
+ database connection and your test will have to commit the transactions in
+ order for the running application to see the data (and vice-versa). In that
+ case we need to truncate the database after each spec instead of simply
+ rolling back a transaction (the faster strategy that's in use for other kind
+ of tests). This is slower than transactions, however, so we want to use
+ truncation only when necessary.
+
+### Black-box tests or End-to-end tests
+
+GitLab consists of [multiple pieces] such as [GitLab Shell], [GitLab Workhorse],
+[Gitaly], [GitLab Pages], [GitLab Runner], and GitLab Rails. All theses pieces
+are configured and packaged by [GitLab Omnibus].
+
+[GitLab QA] is a tool that allows to test that all these pieces integrate well
+together by building a Docker image for a given version of GitLab Rails and
+running feature tests (i.e. using Capybara) against it.
+
+The actual test scenarios and steps are [part of GitLab Rails] so that they're
+always in-sync with the codebase.
+
+[multiple pieces]: ./architecture.md#components
+[GitLab Shell]: https://gitlab.com/gitlab-org/gitlab-shell
+[GitLab Workhorse]: https://gitlab.com/gitlab-org/gitlab-workhorse
+[Gitaly]: https://gitlab.com/gitlab-org/gitaly
+[GitLab Pages]: https://gitlab.com/gitlab-org/gitlab-pages
+[GitLab Runner]: https://gitlab.com/gitlab-org/gitlab-ci-multi-runner
+[GitLab Omnibus]: https://gitlab.com/gitlab-org/omnibus-gitlab
+[GitLab QA]: https://gitlab.com/gitlab-org/gitlab-qa
+[part of GitLab Rails]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/qa
+
+## How to test at the correct level?
+
+As many things in life, deciding what to test at each level of testing is a
+trade-off:
+
+- Unit tests are usually cheap, and you should consider them like the basement
+ of your house: you need them to be confident that your code is behaving
+ correctly. However if you run only unit tests without integration / system tests, you might [miss] the [big] [picture]!
+- Integration tests are a bit more expensive, but don't abuse them. A feature test
+ is often better than an integration test that is stubbing a lot of internals.
+- System tests are expensive (compared to unit tests), even more if they require
+ a JavaScript driver. Make sure to follow the guidelines in the [Speed](#test-speed)
+ section.
+
+Another way to see it is to think about the "cost of tests", this is well
+explained [in this article][tests-cost] and the basic idea is that the cost of a
+test includes:
+
+- The time it takes to write the test
+- The time it takes to run the test every time the suite runs
+- The time it takes to understand the test
+- The time it takes to fix the test if it breaks and the underlying code is OK
+- Maybe, the time it takes to change the code to make the code testable.
+
+[miss]: https://twitter.com/ThePracticalDev/status/850748070698651649
+[big]: https://twitter.com/timbray/status/822470746773409794
+[picture]: https://twitter.com/withzombies/status/829716565834752000
+[tests-cost]: https://medium.com/table-xi/high-cost-tests-and-high-value-tests-a86e27a54df#.2ulyh3a4e
+
+## Frontend testing
+
+Please consult the [dedicated "Frontend testing" guide](./fe_guide/testing.md).
## RSpec
@@ -117,53 +244,124 @@ it 'is overdue' do
end
```
-### Test speed
+### System / Feature tests
-GitLab has a massive test suite that, without parallelization, can take more
-than an hour to run. It's important that we make an effort to write tests that
-are accurate and effective _as well as_ fast.
+- Feature specs should be named `ROLE_ACTION_spec.rb`, such as
+ `user_changes_password_spec.rb`.
+- Use only one `feature` block per feature spec file.
+- Use scenario titles that describe the success and failure paths.
+- Avoid scenario titles that add no information, such as "successfully".
+- Avoid scenario titles that repeat the feature title.
-Here are some things to keep in mind regarding test performance:
+### Matchers
-- `double` and `spy` are faster than `FactoryGirl.build(...)`
-- `FactoryGirl.build(...)` and `.build_stubbed` are faster than `.create`.
-- Don't `create` an object when `build`, `build_stubbed`, `attributes_for`,
- `spy`, or `double` will do. Database persistence is slow!
-- Use `create(:empty_project)` instead of `create(:project)` when you don't need
- the underlying Git repository. Filesystem operations are slow!
-- Don't mark a feature as requiring JavaScript (through `@javascript` in
- Spinach or `js: true` in RSpec) unless it's _actually_ required for the test
- to be valid. Headless browser testing is slow!
+Custom matchers should be created to clarify the intent and/or hide the
+complexity of RSpec expectations.They should be placed under
+`spec/support/matchers/`. Matchers can be placed in subfolder if they apply to
+a certain type of specs only (e.g. features, requests etc.) but shouldn't be if
+they apply to multiple type of specs.
-### Features / Integration
+### Shared contexts
-GitLab uses [rspec-rails feature specs] to test features in a browser
-environment. These are [capybara] specs running on the headless [poltergeist]
-driver.
+All shared contexts should be be placed under `spec/support/shared_contexts/`.
+Shared contexts can be placed in subfolder if they apply to a certain type of
+specs only (e.g. features, requests etc.) but shouldn't be if they apply to
+multiple type of specs.
-- Feature specs live in `spec/features/` and should be named
- `ROLE_ACTION_spec.rb`, such as `user_changes_password_spec.rb`.
-- Use only one `feature` block per feature spec file.
-- Use scenario titles that describe the success and failure paths.
-- Avoid scenario titles that add no information, such as "successfully."
-- Avoid scenario titles that repeat the feature title.
+Each file should include only one context and have a descriptive name, e.g.
+`spec/support/shared_contexts/controllers/githubish_import_controller_shared_context.rb`.
-[rspec-rails feature specs]: https://github.com/rspec/rspec-rails#feature-specs
-[capybara]: https://github.com/teamcapybara/capybara
-[poltergeist]: https://github.com/teampoltergeist/poltergeist
+### Shared examples
-## Spinach (feature) tests
+All shared examples should be be placed under `spec/support/shared_examples/`.
+Shared examples can be placed in subfolder if they apply to a certain type of
+specs only (e.g. features, requests etc.) but shouldn't be if they apply to
+multiple type of specs.
-GitLab [moved from Cucumber to Spinach](https://github.com/gitlabhq/gitlabhq/pull/1426)
-for its feature/integration tests in September 2012.
+Each file should include only one context and have a descriptive name, e.g.
+`spec/support/shared_examples/controllers/githubish_import_controller_shared_example.rb`.
-As of March 2016, we are [trying to avoid adding new Spinach
-tests](https://gitlab.com/gitlab-org/gitlab-ce/issues/14121) going forward,
-opting for [RSpec feature](#features-integration) specs.
+### Helpers
-Adding new Spinach scenarios is acceptable _only if_ the new scenario requires
-no more than one new `step` definition. If more than that is required, the
-test should be re-implemented using RSpec instead.
+Helpers are usually modules that provide some methods to hide the complexity of
+specific RSpec examples. You can define helpers in RSpec files if they're not
+intended to be shared with other specs. Otherwise, they should be be placed
+under `spec/support/helpers/`. Helpers can be placed in subfolder if they apply
+to a certain type of specs only (e.g. features, requests etc.) but shouldn't be
+if they apply to multiple type of specs.
+
+Helpers should follow the Rails naming / namespacing convention. For instance
+`spec/support/helpers/cycle_analytics_helpers.rb` should define:
+
+```ruby
+module Spec
+ module Support
+ module Helpers
+ module CycleAnalyticsHelpers
+ def create_commit_referencing_issue(issue, branch_name: random_git_name)
+ project.repository.add_branch(user, branch_name, 'master')
+ create_commit("Commit for ##{issue.iid}", issue.project, user, branch_name)
+ end
+ end
+ end
+ end
+end
+```
+
+Helpers should not change the RSpec config. For instance, the helpers module
+described above should not include:
+
+```ruby
+RSpec.configure do |config|
+ config.include Spec::Support::Helpers::CycleAnalyticsHelpers
+end
+```
+
+### Factories
+
+GitLab uses [factory_girl] as a test fixture replacement.
+
+- Factory definitions live in `spec/factories/`, named using the pluralization
+ of their corresponding model (`User` factories are defined in `users.rb`).
+- There should be only one top-level factory definition per file.
+- FactoryGirl methods are mixed in to all RSpec groups. This means you can (and
+ should) call `create(...)` instead of `FactoryGirl.create(...)`.
+- Make use of [traits] to clean up definitions and usages.
+- When defining a factory, don't define attributes that are not required for the
+ resulting record to pass validation.
+- When instantiating from a factory, don't supply attributes that aren't
+ required by the test.
+- Factories don't have to be limited to `ActiveRecord` objects.
+ [See example](https://gitlab.com/gitlab-org/gitlab-ce/commit/0b8cefd3b2385a21cfed779bd659978c0402766d).
+
+[factory_girl]: https://github.com/thoughtbot/factory_girl
+[traits]: http://www.rubydoc.info/gems/factory_girl/file/GETTING_STARTED.md#Traits
+
+### Fixtures
+
+All fixtures should be be placed under `spec/fixtures/`.
+
+### Config
+
+RSpec config files are files that change the RSpec config (i.e.
+`RSpec.configure do |config|` blocks). They should be placed under
+`spec/support/config/`.
+
+Each file should be related to a specific domain, e.g.
+`spec/support/config/capybara.rb`, `spec/support/config/carrierwave.rb`, etc.
+
+Helpers can be included in the `spec/support/config/rspec.rb` file. If a
+helpers module applies only to a certain kind of specs, it should add modifiers
+to the `config.include` call. For instance if
+`spec/support/helpers/cycle_analytics_helpers.rb` applies to `:lib` and
+`type: :model` specs only, you would write the following:
+
+```ruby
+RSpec.configure do |config|
+ config.include Spec::Support::Helpers::CycleAnalyticsHelpers, :lib
+ config.include Spec::Support::Helpers::CycleAnalyticsHelpers, type: :model
+end
+```
## Testing Rake Tasks
@@ -201,6 +399,77 @@ describe 'gitlab:shell rake tasks' do
end
```
+## Test speed
+
+GitLab has a massive test suite that, without [parallelization], can take hours
+to run. It's important that we make an effort to write tests that are accurate
+and effective _as well as_ fast.
+
+Here are some things to keep in mind regarding test performance:
+
+- `double` and `spy` are faster than `FactoryGirl.build(...)`
+- `FactoryGirl.build(...)` and `.build_stubbed` are faster than `.create`.
+- Don't `create` an object when `build`, `build_stubbed`, `attributes_for`,
+ `spy`, or `double` will do. Database persistence is slow!
+- Use `create(:empty_project)` instead of `create(:project)` when you don't need
+ the underlying Git repository. Filesystem operations are slow!
+- Don't mark a feature as requiring JavaScript (through `@javascript` in
+ Spinach or `:js` in RSpec) unless it's _actually_ required for the test
+ to be valid. Headless browser testing is slow!
+
+[parallelization]: #test-suite-parallelization-on-the-ci
+
+### Test suite parallelization on the CI
+
+Our current CI parallelization setup is as follows:
+
+1. The `knapsack` job in the prepare stage that is supposed to ensure we have a
+ `knapsack/${CI_PROJECT_NAME}/rspec_report-master.json` file:
+ - The `knapsack/${CI_PROJECT_NAME}/rspec_report-master.json` file is fetched
+ from S3, if it's not here we initialize the file with `{}`.
+1. Each `rspec x y` job are run with `knapsack rspec` and should have an evenly
+ distributed share of tests:
+ - It works because the jobs have access to the
+ `knapsack/${CI_PROJECT_NAME}/rspec_report-master.json` since the "artifacts
+ from all previous stages are passed by default". [^1]
+ - the jobs set their own report path to
+ `KNAPSACK_REPORT_PATH=knapsack/${CI_PROJECT_NAME}/${JOB_NAME[0]}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json`.
+ - if knapsack is doing its job, test files that are run should be listed under
+ `Report specs`, not under `Leftover specs`.
+1. The `update-knapsack` job takes all the
+ `knapsack/${CI_PROJECT_NAME}/${JOB_NAME[0]}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json`
+ files from the `rspec x y` jobs and merge them all together into a single
+ `knapsack/${CI_PROJECT_NAME}/rspec_report-master.json` file that is then
+ uploaded to S3.
+
+After that, the next pipeline will use the up-to-date
+`knapsack/${CI_PROJECT_NAME}/rspec_report-master.json` file. The same strategy
+is used for Spinach tests as well.
+
+### Monitoring
+
+The GitLab test suite is [monitored] and a [public dashboard] is available for
+everyone to see. Feel free to look at the slowest test files and try to improve
+them.
+
+[monitored]: ./performance.md#rspec-profiling
+[public dashboard]: https://redash.gitlab.com/public/dashboards/l1WhHXaxrCWM5Ai9D7YDqHKehq6OU3bx5gssaiWe?org_slug=default
+
+## Spinach (feature) tests
+
+GitLab [moved from Cucumber to Spinach](https://github.com/gitlabhq/gitlabhq/pull/1426)
+for its feature/integration tests in September 2012.
+
+As of March 2016, we are [trying to avoid adding new Spinach
+tests](https://gitlab.com/gitlab-org/gitlab-ce/issues/14121) going forward,
+opting for [RSpec feature](#features-integration) specs.
+
+Adding new Spinach scenarios is acceptable _only if_ the new scenario requires
+no more than one new `step` definition. If more than that is required, the
+test should be re-implemented using RSpec instead.
+
---
[Return to Development documentation](README.md)
+
+[^1]: /ci/yaml/README.html#dependencies
diff --git a/doc/development/what_requires_downtime.md b/doc/development/what_requires_downtime.md
index bbcd26477f3..8da6ad684f5 100644
--- a/doc/development/what_requires_downtime.md
+++ b/doc/development/what_requires_downtime.md
@@ -2,7 +2,8 @@
When working with a database certain operations can be performed without taking
GitLab offline, others do require a downtime period. This guide describes
-various operations and their impact.
+various operations, their impact, and how to perform them without requiring
+downtime.
## Adding Columns
@@ -41,50 +42,156 @@ information on how to use this method.
## Dropping Columns
-On PostgreSQL you can safely remove an existing column without the need for
-downtime. When you drop a column in PostgreSQL it's not immediately removed,
-instead it is simply disabled. The data is removed on the next vacuum run.
+Removing columns is tricky because running GitLab processes may still be using
+the columns. To work around this you will need two separate merge requests and
+releases: one to ignore and then remove the column, and one to remove the ignore
+rule.
-On MySQL this operation requires downtime.
+### Step 1: Ignoring The Column
-While database wise dropping a column may be fine on PostgreSQL this operation
-still requires downtime because the application code may still be using the
-column that was removed. For example, consider the following migration:
+The first step is to ignore the column in the application code. This is
+necessary because Rails caches the columns and re-uses this cache in various
+places. This can be done by including the `IgnorableColumn` module into the
+model, followed by defining the columns to ignore. For example, to ignore
+`updated_at` in the User model you'd use the following:
```ruby
-class MyMigration < ActiveRecord::Migration
- def change
- remove_column :projects, :dummy
- end
+class User < ActiveRecord::Base
+ include IgnorableColumn
+
+ ignore_column :updated_at
end
```
-Now imagine that the GitLab instance is running and actively uses the `dummy`
-column. If we were to run the migration this would result in the GitLab instance
-producing errors whenever it tries to use the `dummy` column.
+Once added you should create a _post-deployment_ migration that removes the
+column. Both these changes should be submitted in the same merge request.
-As a result of the above downtime _is_ required when removing a column, even
-when using PostgreSQL.
+### Step 2: Removing The Ignore Rule
+
+Once the changes from step 1 have been released & deployed you can set up a
+separate merge request that removes the ignore rule. This merge request can
+simply remove the `ignore_column` line, and the `include IgnorableColumn` line
+if no other `ignore_column` calls remain.
## Renaming Columns
-Renaming columns requires downtime as running GitLab instances will continue
-using the old column name until a new version is deployed. This can result
-in the instance producing errors, which in turn can impact the user experience.
+Renaming columns the normal way requires downtime as an application may continue
+using the old column name during/after a database migration. To rename a column
+without requiring downtime we need two migrations: a regular migration, and a
+post-deployment migration. Both these migration can go in the same release.
-## Changing Column Constraints
+### Step 1: Add The Regular Migration
+
+First we need to create the regular migration. This migration should use
+`Gitlab::Database::MigrationHelpers#rename_column_concurrently` to perform the
+renaming. For example
+
+```ruby
+# A regular migration in db/migrate
+class RenameUsersUpdatedAtToUpdatedAtTimestamp < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ disable_ddl_transaction!
+
+ def up
+ rename_column_concurrently :users, :updated_at, :updated_at_timestamp
+ end
+
+ def down
+ cleanup_concurrent_column_rename :users, :updated_at_timestamp, :updated_at
+ end
+end
+```
+
+This will take care of renaming the column, ensuring data stays in sync, copying
+over indexes and foreign keys, etc.
+
+**NOTE:** if a column contains 1 or more indexes that do not contain the name of
+the original column, the above procedure will fail. In this case you will first
+need to rename these indexes.
-Generally changing column constraints requires checking all rows in the table to
-see if they meet the new constraint, unless a constraint is _removed_. For
-example, changing a column that previously allowed NULL values to not allow NULL
-values requires the database to verify all existing rows.
+### Step 2: Add A Post-Deployment Migration
-The specific behaviour varies a bit between databases but in general the safest
-approach is to assume changing constraints requires downtime.
+The renaming procedure requires some cleaning up in a post-deployment migration.
+We can perform this cleanup using
+`Gitlab::Database::MigrationHelpers#cleanup_concurrent_column_rename`:
+
+```ruby
+# A post-deployment migration in db/post_migrate
+class CleanupUsersUpdatedAtRename < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ disable_ddl_transaction!
+
+ def up
+ cleanup_concurrent_column_rename :users, :updated_at, :updated_at_timestamp
+ end
+
+ def down
+ rename_column_concurrently :users, :updated_at_timestamp, :updated_at
+ end
+end
+```
+
+## Changing Column Constraints
+
+Adding or removing a NOT NULL clause (or another constraint) can typically be
+done without requiring downtime. However, this does require that any application
+changes are deployed _first_. Thus, changing the constraints of a column should
+happen in a post-deployment migration.
## Changing Column Types
-This operation requires downtime.
+Changing the type of a column can be done using
+`Gitlab::Database::MigrationHelpers#change_column_type_concurrently`. This
+method works similarly to `rename_column_concurrently`. For example, let's say
+we want to change the type of `users.username` from `string` to `text`.
+
+### Step 1: Create A Regular Migration
+
+A regular migration is used to create a new column with a temporary name along
+with setting up some triggers to keep data in sync. Such a migration would look
+as follows:
+
+```ruby
+# A regular migration in db/migrate
+class ChangeUsersUsernameStringToText < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ disable_ddl_transaction!
+
+ def up
+ change_column_type_concurrently :users, :username, :text
+ end
+
+ def down
+ cleanup_concurrent_column_type_change :users, :username
+ end
+end
+```
+
+### Step 2: Create A Post Deployment Migration
+
+Next we need to clean up our changes using a post-deployment migration:
+
+```ruby
+# A post-deployment migration in db/post_migrate
+class ChangeUsersUsernameStringToTextCleanup < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ disable_ddl_transaction!
+
+ def up
+ cleanup_concurrent_column_type_change :users
+ end
+
+ def down
+ change_column_type_concurrently :users, :username, :string
+ end
+end
+```
+
+And that's it, we're done!
## Adding Indexes
@@ -101,12 +208,19 @@ Migrations can take advantage of this by using the method
```ruby
class MyMigration < ActiveRecord::Migration
- def change
+ def up
add_concurrent_index :projects, :column_name
end
+
+ def down
+ remove_index(:projects, :column_name) if index_exists?(:projects, :column_name)
+ end
end
```
+Note that `add_concurrent_index` can not be reversed automatically, thus you
+need to manually define `up` and `down`.
+
When running this on PostgreSQL the `CONCURRENTLY` option mentioned above is
used. On MySQL this method produces a regular `CREATE INDEX` query.
@@ -125,43 +239,54 @@ This operation is safe as there's no code using the table just yet.
## Dropping Tables
-This operation requires downtime as application code may still be using the
-table.
+Dropping tables can be done safely using a post-deployment migration, but only
+if the application no longer uses the table.
## Adding Foreign Keys
-Adding foreign keys acquires an exclusive lock on both the source and target
-tables in PostgreSQL. This requires downtime as otherwise the entire application
-grinds to a halt for the duration of the operation.
+Adding foreign keys usually works in 3 steps:
+
+1. Start a transaction
+1. Run `ALTER TABLE` to add the constraint(s)
+1. Check all existing data
-On MySQL this operation also requires downtime _unless_ foreign key checks are
-disabled. Because this means checks aren't enforced this is not ideal, as such
-one should assume MySQL also requires downtime.
+Because `ALTER TABLE` typically acquires an exclusive lock until the end of a
+transaction this means this approach would require downtime.
+
+GitLab allows you to work around this by using
+`Gitlab::Database::MigrationHelpers#add_concurrent_foreign_key`. This method
+ensures that when PostgreSQL is used no downtime is needed.
## Removing Foreign Keys
-This operation should not require downtime on both PostgreSQL and MySQL.
+This operation does not require downtime.
-## Updating Data
+## Data Migrations
-Updating data should generally be safe. The exception to this is data that's
-being migrated from one version to another while the application still produces
-data in the old version.
+Data migrations can be tricky. The usual approach to migrate data is to take a 3
+step approach:
-For example, imagine the application writes the string `'dog'` to a column but
-it really is meant to write `'cat'` instead. One might think that the following
-migration is all that is needed to solve this problem:
+1. Migrate the initial batch of data
+1. Deploy the application code
+1. Migrate any remaining data
-```ruby
-class MyMigration < ActiveRecord::Migration
- def up
- execute("UPDATE some_table SET column = 'cat' WHERE column = 'dog';")
- end
-end
-```
+Usually this works, but not always. For example, if a field's format is to be
+changed from JSON to something else we have a bit of a problem. If we were to
+change existing data before deploying application code we'll most likely run
+into errors. On the other hand, if we were to migrate after deploying the
+application code we could run into the same problems.
+
+If you merely need to correct some invalid data, then a post-deployment
+migration is usually enough. If you need to change the format of data (e.g. from
+JSON to something else) it's typically best to add a new column for the new data
+format, and have the application use that. In such a case the procedure would
+be:
-Unfortunately this is not enough. Because the application is still running and
-using the old value this may result in the table still containing rows where
-`column` is set to `dog`, even after the migration finished.
+1. Add a new column in the new format
+1. Copy over existing data to this new column
+1. Deploy the application code
+1. In a post-deployment migration, copy over any remaining data
-In these cases downtime _is_ required, even for rarely updated tables.
+In general there is no one-size-fits-all solution, therefore it's best to
+discuss these kind of migrations in a merge request to make sure they are
+implemented in the best way possible.
diff --git a/doc/install/README.md b/doc/install/README.md
index d35709266e4..58cc7d312fd 100644
--- a/doc/install/README.md
+++ b/doc/install/README.md
@@ -20,8 +20,8 @@ the hardware requirements.
- [Docker](https://docs.gitlab.com/omnibus/docker/) - Install GitLab using Docker.
- [Installation on Google Cloud Platform](google_cloud_platform/index.md) - Install
GitLab on Google Cloud Platform using our official image.
-- [Digital Ocean and Docker](digitaloceandocker.md) - Install GitLab quickly
- on DigitalOcean using Docker.
+- Testing only! [DigitalOcean and Docker Machine](digitaloceandocker.md) -
+ Quickly test any version of GitLab on DigitalOcean using Docker Machine.
## Database
diff --git a/doc/install/digitaloceandocker.md b/doc/install/digitaloceandocker.md
index 820060a489b..8efc0530b8a 100644
--- a/doc/install/digitaloceandocker.md
+++ b/doc/install/digitaloceandocker.md
@@ -1,4 +1,7 @@
-# Digital Ocean and Docker
+# Digital Ocean and Docker Machine test environment
+
+## Warning. This guide is for quickly testing different versions of GitLab and
+## not recommended for ease of future upgrades or keeping the data you create.
## Initial setup
diff --git a/doc/install/installation.md b/doc/install/installation.md
index 5b72c2cce07..1f61a4f67bb 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -289,9 +289,9 @@ sudo usermod -aG redis git
### Clone the Source
# Clone GitLab repository
- sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 9-0-stable gitlab
+ sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 9-1-stable gitlab
-**Note:** You can change `9-0-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
+**Note:** You can change `9-1-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
### Configure It
@@ -475,7 +475,7 @@ with setting up Gitaly until you upgrade to GitLab 9.2 or later.
sudo -u git cp config.toml.example config.toml
# If you are using non-default settings you need to update config.toml
sudo -u git -H editor config.toml
-
+
# Enable Gitaly in the init script
echo 'gitaly_enabled=true' | sudo tee -a /etc/default/gitlab
diff --git a/doc/topics/authentication/index.md b/doc/topics/authentication/index.md
new file mode 100644
index 00000000000..eafd2fd9d04
--- /dev/null
+++ b/doc/topics/authentication/index.md
@@ -0,0 +1,46 @@
+# Authentication
+
+This page gathers all the resources for the topic **Authentication** within GitLab.
+
+## GitLab users
+
+- [SSH](../../ssh/README.md)
+- [Two-Factor Authentication (2FA)](../../user/profile/account/two_factor_authentication.md#two-factor-authentication)
+- **Articles:**
+ - [Support for Universal 2nd Factor Authentication - YubiKeys](https://about.gitlab.com/2016/06/22/gitlab-adds-support-for-u2f/)
+ - [Security Webcast with Yubico](https://about.gitlab.com/2016/08/31/gitlab-and-yubico-security-webcast/)
+- **Integrations:**
+ - [GitLab as OAuth2 authentication service provider](../../integration/oauth_provider.md#introduction-to-oauth)
+
+## GitLab administrators
+
+- [LDAP (Community Edition)](../../administration/auth/ldap.md)
+- [LDAP (Enterprise Edition)](https://docs.gitlab.com/ee/administration/auth/ldap-ee.html)
+- [Enforce Two-factor Authentication (2FA)](../../security/two_factor_authentication.md#enforce-two-factor-authentication-2fa)
+- **Articles:**
+ - [Feature Highlight: LDAP Integration](https://about.gitlab.com/2014/07/10/feature-highlight-ldap-sync/)
+ - [Debugging LDAP](https://about.gitlab.com/handbook/support/workflows/ldap/debugging_ldap.html)
+- **Integrations:**
+ - [OmniAuth](../../integration/omniauth.md)
+ - [Authentiq OmniAuth Provider](../../administration/auth/authentiq.md#authentiq-omniauth-provider)
+ - [Atlassian Crowd OmniAuth Provider](../../administration/auth/crowd.md)
+ - [CAS OmniAuth Provider](../../integration/cas.md)
+ - [SAML OmniAuth Provider](../../integration/saml.md)
+ - [Okta SSO provider](../../administration/auth/okta.md)
+ - [Kerberos integration (GitLab EE)](https://docs.gitlab.com/ee/integration/kerberos.html)
+
+## API
+
+- [OAuth 2 Tokens](../../api/README.md#oauth-2-tokens)
+- [Private Tokens](../../api/README.md#private-tokens)
+- [Impersonation tokens](../../api/README.md#impersonation-tokens)
+- [GitLab as an OAuth2 provider](../../api/oauth2.md#gitlab-as-an-oauth2-provider)
+- [GitLab Runner API - Authentication](../../api/ci/runners.md#authentication)
+
+## Third-party resources
+
+- [Kanboard Plugin GitLab Authentication](https://kanboard.net/plugin/gitlab-auth)
+- [Jenkins GitLab OAuth Plugin](https://wiki.jenkins-ci.org/display/JENKINS/GitLab+OAuth+Plugin)
+- [Setup Gitlab CE with Active Directory authentication](https://www.caseylabs.com/setup-gitlab-ce-with-active-directory-authentication/)
+- [How to customize GitLab to support OpenID authentication](http://eric.van-der-vlist.com/blog/2013/11/23/how-to-customize-gitlab-to-support-openid-authentication/)
+- [Openshift - Configuring Authentication and User Agent](https://docs.openshift.org/latest/install_config/configuring_authentication.html#GitLab)
diff --git a/doc/topics/git/index.md b/doc/topics/git/index.md
new file mode 100644
index 00000000000..b99ba317a43
--- /dev/null
+++ b/doc/topics/git/index.md
@@ -0,0 +1,61 @@
+# Git documentation
+
+Git is a [free and open source](https://git-scm.com/about/free-and-open-source)
+distributed version control system designed to handle everything from small to
+very large projects with speed and efficiency.
+
+[GitLab](https://about.gitlab.com) is a Git-based fully integrated platform for
+software development. Besides Git's functionalities, GitLab has a lot of
+powerful [features](https://about.gitlab.com/features/) to enhance your
+[workflow](https://about.gitlab.com/2016/10/25/gitlab-workflow-an-overview/).
+
+We've gathered some resources to help you to get the best from Git with GitLab.
+
+## Getting started
+
+- [Git concepts](../../university/training/user_training.md#git-concepts)
+- [Start using Git on the command line](../../gitlab-basics/start-using-git.md)
+- [Command Line basic commands](../../gitlab-basics/command-line-commands.md)
+- [GitLab Git Cheat Sheet (download)](https://gitlab.com/gitlab-com/marketing/raw/master/design/print/git-cheatsheet/print-pdf/git-cheatsheet.pdf)
+- **Articles:**
+ - [Git Tips & Tricks](https://about.gitlab.com/2016/12/08/git-tips-and-tricks/)
+ - [Eight Tips to help you work better with Git](https://about.gitlab.com/2015/02/19/8-tips-to-help-you-work-better-with-git/)
+- **Presentations:**
+ - [GLU Course: About Version Control](https://docs.google.com/presentation/d/16sX7hUrCZyOFbpvnrAFrg6tVO5_yT98IgdAqOmXwBho/edit?usp=sharing)
+- **Third-party resources:**
+ - What is [Git](https://git-scm.com)
+ - [Version control](https://git-scm.com/book/en/v2/Getting-Started-About-Version-Control)
+ - [Getting Started - Git Basics](https://git-scm.com/book/en/v2/Getting-Started-Git-Basics)
+ - [Getting Started - Installing Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
+ - [Git on the Server - GitLab](https://git-scm.com/book/en/v2/Git-on-the-Server-GitLab)
+
+## Branching strategies
+
+- **Articles:**
+ - [GitLab Flow](https://about.gitlab.com/2014/09/29/gitlab-flow/)
+- **Third-party resources:**
+ - [Git Branching - Branches in a Nutshell](https://git-scm.com/book/en/v2/Git-Branching-Branches-in-a-Nutshell)
+ - [Git Branching - Branching Workflows](https://git-scm.com/book/en/v2/Git-Branching-Branching-Workflows)
+
+## Advanced use
+
+- [Custom Git Hooks](../../administration/custom_hooks.md)
+- [Git Attributes](../../user/project/git_attributes.md)
+- Git Submodules: [Using Git submodules with GitLab CI](../../ci/git_submodules.md#using-git-submodules-with-gitlab-ci)
+
+## API
+
+- [Gitignore templates](../../api/templates/gitignores.md)
+
+## Git LFS
+
+- [Git LFS](../../workflow/lfs/manage_large_binaries_with_git_lfs.md)
+- [Git-Annex to Git-LFS migration guide](https://docs.gitlab.com/ee/workflow/lfs/migrate_from_git_annex_to_git_lfs.html)
+- **Articles:**
+ - [Getting Started with Git LFS](https://about.gitlab.com/2017/01/30/getting-started-with-git-lfs-tutorial/)
+ - [Towards a production quality open source Git LFS server](https://about.gitlab.com/2015/08/13/towards-a-production-quality-open-source-git-lfs-server/)
+
+## General information
+
+- **Articles:**
+ - [The future of SaaS hosted Git repository pricing](https://about.gitlab.com/2016/05/11/git-repository-pricing/)
diff --git a/doc/topics/index.md b/doc/topics/index.md
index 6de13d79554..ad388dff822 100644
--- a/doc/topics/index.md
+++ b/doc/topics/index.md
@@ -7,10 +7,10 @@ you through better understanding GitLab's concepts
through our regular docs, and, when available, through articles (guides,
tutorials, technical overviews, blog posts) and videos.
-- [GitLab Installation](../install/README.md)
+- [Authentication](authentication/index.md)
- [Continuous Integration (GitLab CI)](../ci/README.md)
+- [Git](git/index.md)
+- [GitLab Installation](../install/README.md)
- [GitLab Pages](../user/project/pages/index.md)
->**Note:**
-Non-linked topics are currently under development and subjected to change.
-More topics will be available soon.
+>**Note:** More topics will be available soon.
diff --git a/doc/update/9.0-to-9.1.md b/doc/update/9.0-to-9.1.md
index ae983dea384..1191662ee14 100644
--- a/doc/update/9.0-to-9.1.md
+++ b/doc/update/9.0-to-9.1.md
@@ -1,9 +1,5 @@
# From 9.0 to 9.1
-** TODO: **
-
-# TODO clean out 9.0-specific stuff
-
Make sure you view this update guide from the tag (version) of GitLab you would
like to install. In most cases this should be the highest numbered production
tag (without rc in it). You can select the tag in the version dropdown at the
diff --git a/doc/update/README.md b/doc/update/README.md
index 837b31abb97..7921d03d611 100644
--- a/doc/update/README.md
+++ b/doc/update/README.md
@@ -48,6 +48,23 @@ GitLab provides official Docker images for both Community and Enterprise
editions. They are based on the Omnibus package and instructions on how to
update them are in [a separate document][omnidocker].
+## Upgrading without downtime
+
+Starting with GitLab 9.1.0 it's possible to upgrade to a newer version of GitLab
+without having to take your GitLab instance offline. However, for this to work
+there are the following requirements:
+
+1. You can only upgrade 1 release at a time. For example, if 9.1.15 is the last
+ release of 9.1 then you can safely upgrade from that version to 9.2.0.
+ However, if you are running 9.1.14 you first need to upgrade to 9.1.15.
+2. You have to use [post-deployment
+ migrations](../development/post_deployment_migrations.md).
+3. You are using PostgreSQL. If you are using MySQL you will still need downtime
+ when upgrading.
+
+This applies to major, minor, and patch releases unless stated otherwise in a
+release post.
+
## Upgrading between editions
GitLab comes in two flavors: [Community Edition][ce] which is MIT licensed,
diff --git a/doc/update/patch_versions.md b/doc/update/patch_versions.md
index 1c493599cf8..f69d567eeb7 100644
--- a/doc/update/patch_versions.md
+++ b/doc/update/patch_versions.md
@@ -57,7 +57,7 @@ sudo -u git -H bundle clean
sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
# Clean up assets and cache
-sudo -u git -H bundle exec rake yarn:install gitlab:assets:clean gitlab:assets:compile cache:clear RAILS_ENV=production
+sudo -u git -H bundle exec rake yarn:install gitlab:assets:clean gitlab:assets:compile cache:clear RAILS_ENV=production NODE_ENV=production
```
### 4. Update gitlab-workhorse to the corresponding version
diff --git a/doc/user/profile/account/two_factor_authentication.md b/doc/user/profile/account/two_factor_authentication.md
index 63a3d3c472e..fb69d934ae1 100644
--- a/doc/user/profile/account/two_factor_authentication.md
+++ b/doc/user/profile/account/two_factor_authentication.md
@@ -143,7 +143,7 @@ into the password field.
To disable two-factor authentication on your account (for example, if you
have lost your code generation device) you can:
* [Use a saved recovery code](#use-a-saved-recovery-code)
-* [Generate new recovery codes using SSH](#generate-new-recovery-codes-using-SSH)
+* [Generate new recovery codes using SSH](#generate-new-recovery-codes-using-ssh)
* [Ask a GitLab administrator to disable two-factor authentication on your account](#ask-a-gitlab-administrator-to-disable-two-factor-authentication-on-your-account)
### Use a saved recovery code
diff --git a/doc/user/project/cycle_analytics.md b/doc/user/project/cycle_analytics.md
index 62afd8cf247..8f6b530c033 100644
--- a/doc/user/project/cycle_analytics.md
+++ b/doc/user/project/cycle_analytics.md
@@ -5,10 +5,10 @@
Cycle Analytics measures the time it takes to go from an [idea to production] for
each project you have. This is achieved by not only indicating the total time it
-takes to reach at that point, but the total time is broken down into the
+takes to reach that point, but the total time is broken down into the
multiple stages an idea has to pass through to be shipped.
-Cycle Analytics is that it is tightly coupled with the [GitLab flow] and
+Cycle Analytics is tightly coupled with the [GitLab flow] and
calculates a separate median for each stage.
## Overview
diff --git a/doc/user/project/integrations/kubernetes.md b/doc/user/project/integrations/kubernetes.md
index 2a890acde4d..f7d5e3a8ab2 100644
--- a/doc/user/project/integrations/kubernetes.md
+++ b/doc/user/project/integrations/kubernetes.md
@@ -60,7 +60,7 @@ to use terminals. Support is currently limited to the first container in the
first pod of your environment.
When enabled, the Kubernetes service adds [web terminal](../../../ci/environments.md#web-terminals)
-support to your environments. This is based on the `exec` functionality found in
+support to your [environments](../../../ci/environments.md). This is based on the `exec` functionality found in
Docker and Kubernetes, so you get a new shell session within your existing
containers. To use this integration, you should deploy to Kubernetes using
the deployment variables above, ensuring any pods you create are labelled with
diff --git a/doc/workflow/gitlab_flow.md b/doc/workflow/gitlab_flow.md
index d12c0c6d0c4..1b172b21f3d 100644
--- a/doc/workflow/gitlab_flow.md
+++ b/doc/workflow/gitlab_flow.md
@@ -313,5 +313,4 @@ Merging only when needed prevents creating merge commits in your feature branch
### References
-- [Sketch file](https://www.dropbox.com/s/58dvsj5votbwrzv/git_flows.sketch?dl=0) with vectors of images in this article
- [Git Flow by Vincent Driessen](http://nvie.com/posts/a-successful-git-branching-model/)
diff --git a/doc/workflow/todos.md b/doc/workflow/todos.md
index 4b0fba842e9..3d8d3ce8f13 100644
--- a/doc/workflow/todos.md
+++ b/doc/workflow/todos.md
@@ -111,7 +111,7 @@ There are four kinds of filters you can use on your Todos dashboard.
| Type | Filter by issue or merge request |
| Action | Filter by the action that triggered the Todo |
-You can also filter by more than one of these at the same time.
+You can also filter by more than one of these at the same time. The possible Actions are `Any Action`, `Assigned`, `Mentioned`, `Added`, `Pipelines`, and `Directly Addressed`, [as described above](#what-triggers-a-todo).
[ce-2817]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/2817
[ce-7926]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7926
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index 32bbf956d7f..09d105f6b4c 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -5,11 +5,16 @@ module API
before { authenticate! }
helpers do
- params :optional_params do
+ params :optional_params_ce do
optional :description, type: String, desc: 'The description of the group'
optional :visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The visibility of the group'
optional :lfs_enabled, type: Boolean, desc: 'Enable/disable LFS for the projects in this group'
optional :request_access_enabled, type: Boolean, desc: 'Allow users to request member access'
+ optional :share_with_group_lock, type: Boolean, desc: 'Prevent sharing a project with another group within this group'
+ end
+
+ params :optional_params do
+ use :optional_params_ce
end
params :statistics_params do
diff --git a/lib/api/helpers/internal_helpers.rb b/lib/api/helpers/internal_helpers.rb
index 2135a787b11..810e5063996 100644
--- a/lib/api/helpers/internal_helpers.rb
+++ b/lib/api/helpers/internal_helpers.rb
@@ -53,12 +53,12 @@ module API
]
end
- def parse_allowed_environment_variables
- return if params[:env].blank?
+ def parse_env
+ return {} if params[:env].blank?
JSON.parse(params[:env])
-
rescue JSON::ParserError
+ {}
end
end
end
diff --git a/lib/api/internal.rb b/lib/api/internal.rb
index 70d0d57204d..215bc03d0e9 100644
--- a/lib/api/internal.rb
+++ b/lib/api/internal.rb
@@ -11,14 +11,16 @@ module API
# Params:
# key_id - ssh key id for Git over SSH
# user_id - user id for Git over HTTP
+ # protocol - Git access protocol being used, e.g. HTTP or SSH
# project - project path with namespace
# action - git action (git-upload-pack or git-receive-pack)
- # ref - branch name
- # forced_push - forced_push
- # protocol - Git access protocol being used, e.g. HTTP or SSH
+ # changes - changes as "oldrev newrev ref", see Gitlab::ChangesList
post "/allowed" do
status 200
+ # Stores some Git-specific env thread-safely
+ Gitlab::Git::Env.set(parse_env)
+
actor =
if params[:key_id]
Key.find_by(id: params[:key_id])
@@ -30,18 +32,10 @@ module API
actor.update_last_used_at if actor.is_a?(Key)
- access =
- if wiki?
- Gitlab::GitAccessWiki.new(actor, project, protocol, authentication_abilities: ssh_authentication_abilities)
- else
- Gitlab::GitAccess.new(actor,
- project,
- protocol,
- authentication_abilities: ssh_authentication_abilities,
- env: parse_allowed_environment_variables)
- end
-
- access_status = access.check(params[:action], params[:changes])
+ access_checker = wiki? ? Gitlab::GitAccessWiki : Gitlab::GitAccess
+ access_status = access_checker
+ .new(actor, project, protocol, authentication_abilities: ssh_authentication_abilities)
+ .check(params[:action], params[:changes])
response = { status: access_status.status, message: access_status.message }
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index 09053e615cb..05423c17449 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -30,7 +30,7 @@ module API
use :pagination
end
- params :issue_params do
+ params :issue_params_ce do
optional :description, type: String, desc: 'The description of an issue'
optional :assignee_id, type: Integer, desc: 'The ID of a user to assign issue'
optional :milestone_id, type: Integer, desc: 'The ID of a milestone to assign issue'
@@ -38,6 +38,10 @@ module API
optional :due_date, type: String, desc: 'Date time string in the format YEAR-MONTH-DAY'
optional :confidential, type: Boolean, desc: 'Boolean parameter if the issue should be confidential'
end
+
+ params :issue_params do
+ use :issue_params_ce
+ end
end
resource :issues do
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index c8033664133..cb7aec47cf0 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -33,13 +33,17 @@ module API
end
end
- params :optional_params do
+ params :optional_params_ce do
optional :description, type: String, desc: 'The description of the merge request'
optional :assignee_id, type: Integer, desc: 'The ID of a user to assign the merge request'
optional :milestone_id, type: Integer, desc: 'The ID of a milestone to assign the merge request'
optional :labels, type: String, desc: 'Comma-separated list of label names'
optional :remove_source_branch, type: Boolean, desc: 'Remove source branch when merging'
end
+
+ params :optional_params do
+ use :optional_params_ce
+ end
end
desc 'List merge requests' do
@@ -145,14 +149,24 @@ module API
success Entities::MergeRequest
end
params do
+ # CE
+ at_least_one_of_ce = [
+ :assignee_id,
+ :description,
+ :labels,
+ :milestone_id,
+ :remove_source_branch,
+ :state_event,
+ :target_branch,
+ :title
+ ]
optional :title, type: String, allow_blank: false, desc: 'The title of the merge request'
optional :target_branch, type: String, allow_blank: false, desc: 'The target branch'
optional :state_event, type: String, values: %w[close reopen],
desc: 'Status of the merge request'
+
use :optional_params
- at_least_one_of :title, :target_branch, :description, :assignee_id,
- :milestone_id, :labels, :state_event,
- :remove_source_branch
+ at_least_one_of(*at_least_one_of_ce)
end
put ':id/merge_requests/:merge_request_iid' do
merge_request = find_merge_request_with_access(params.delete(:merge_request_iid), :update_merge_request)
@@ -173,6 +187,7 @@ module API
success Entities::MergeRequest
end
params do
+ # CE
optional :merge_commit_message, type: String, desc: 'Custom merge commit message'
optional :should_remove_source_branch, type: Boolean,
desc: 'When true, the source branch will be deleted if possible'
diff --git a/lib/api/project_hooks.rb b/lib/api/project_hooks.rb
index 53791166c33..87dfd1573a4 100644
--- a/lib/api/project_hooks.rb
+++ b/lib/api/project_hooks.rb
@@ -13,7 +13,7 @@ module API
optional :merge_requests_events, type: Boolean, desc: "Trigger hook on merge request events"
optional :tag_push_events, type: Boolean, desc: "Trigger hook on tag push events"
optional :note_events, type: Boolean, desc: "Trigger hook on note(comment) events"
- optional :build_events, type: Boolean, desc: "Trigger hook on build events"
+ optional :job_events, type: Boolean, desc: "Trigger hook on job events"
optional :pipeline_events, type: Boolean, desc: "Trigger hook on pipeline events"
optional :wiki_page_events, type: Boolean, desc: "Trigger hook on wiki events"
optional :enable_ssl_verification, type: Boolean, desc: "Do SSL verification when triggering the hook"
@@ -53,7 +53,10 @@ module API
use :project_hook_properties
end
post ":id/hooks" do
- hook = user_project.hooks.new(declared_params(include_missing: false))
+ hook_params = declared_params(include_missing: false)
+ hook_params[:build_events] = hook_params.delete(:job_events) { false }
+
+ hook = user_project.hooks.new(hook_params)
if hook.save
present hook, with: Entities::ProjectHook
@@ -74,7 +77,10 @@ module API
put ":id/hooks/:hook_id" do
hook = user_project.hooks.find(params.delete(:hook_id))
- if hook.update_attributes(declared_params(include_missing: false))
+ update_params = declared_params(include_missing: false)
+ update_params[:build_events] = update_params.delete(:job_events) if update_params[:job_events]
+
+ if hook.update_attributes(update_params)
present hook, with: Entities::ProjectHook
else
error!("Invalid url given", 422) if hook.errors[:url].present?
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 766fbea53e6..50842370947 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -6,7 +6,7 @@ module API
before { authenticate_non_get! }
helpers do
- params :optional_params do
+ params :optional_params_ce do
optional :description, type: String, desc: 'The description of the project'
optional :issues_enabled, type: Boolean, desc: 'Flag indication if the issue tracker is enabled'
optional :merge_requests_enabled, type: Boolean, desc: 'Flag indication if merge requests are enabled'
@@ -22,6 +22,10 @@ module API
optional :only_allow_merge_if_pipeline_succeeds, type: Boolean, desc: 'Only allow to merge if builds succeed'
optional :only_allow_merge_if_all_discussions_are_resolved, type: Boolean, desc: 'Only allow to merge if all discussions are resolved'
end
+
+ params :optional_params do
+ use :optional_params_ce
+ end
end
resource :projects do
@@ -198,17 +202,33 @@ module API
success Entities::Project
end
params do
+ # CE
+ at_least_one_of_ce =
+ [
+ :builds_enabled,
+ :container_registry_enabled,
+ :default_branch,
+ :description,
+ :issues_enabled,
+ :lfs_enabled,
+ :merge_requests_enabled,
+ :name,
+ :only_allow_merge_if_all_discussions_are_resolved,
+ :only_allow_merge_if_pipeline_succeeds,
+ :path,
+ :public_builds,
+ :request_access_enabled,
+ :shared_runners_enabled,
+ :snippets_enabled,
+ :visibility,
+ :wiki_enabled,
+ ]
optional :name, type: String, desc: 'The name of the project'
optional :default_branch, type: String, desc: 'The default branch of the project'
optional :path, type: String, desc: 'The path of the repository'
+
use :optional_params
- at_least_one_of :name, :description, :issues_enabled, :merge_requests_enabled,
- :wiki_enabled, :builds_enabled, :snippets_enabled,
- :shared_runners_enabled, :container_registry_enabled,
- :lfs_enabled, :visibility, :public_builds,
- :request_access_enabled, :only_allow_merge_if_pipeline_succeeds,
- :only_allow_merge_if_all_discussions_are_resolved, :path,
- :default_branch
+ at_least_one_of(*at_least_one_of_ce)
end
put ':id' do
authorize_admin_project
diff --git a/lib/api/settings.rb b/lib/api/settings.rb
index c7f97ad2aab..d01c7f2703b 100644
--- a/lib/api/settings.rb
+++ b/lib/api/settings.rb
@@ -20,6 +20,55 @@ module API
success Entities::ApplicationSetting
end
params do
+ # CE
+ at_least_one_of_ce = [
+ :admin_notification_email,
+ :after_sign_out_path,
+ :after_sign_up_text,
+ :akismet_enabled,
+ :container_registry_token_expire_delay,
+ :default_artifacts_expire_in,
+ :default_branch_protection,
+ :default_group_visibility,
+ :default_project_visibility,
+ :default_projects_limit,
+ :default_snippet_visibility,
+ :disabled_oauth_sign_in_sources,
+ :domain_blacklist_enabled,
+ :domain_whitelist,
+ :email_author_in_body,
+ :enabled_git_access_protocol,
+ :gravatar_enabled,
+ :help_page_text,
+ :home_page_url,
+ :housekeeping_enabled,
+ :html_emails_enabled,
+ :import_sources,
+ :koding_enabled,
+ :max_artifacts_size,
+ :max_attachment_size,
+ :max_pages_size,
+ :metrics_enabled,
+ :plantuml_enabled,
+ :polling_interval_multiplier,
+ :recaptcha_enabled,
+ :repository_checks_enabled,
+ :repository_storage,
+ :require_two_factor_authentication,
+ :restricted_visibility_levels,
+ :send_user_confirmation_email,
+ :sentry_enabled,
+ :session_expire_delay,
+ :shared_runners_enabled,
+ :sidekiq_throttling_enabled,
+ :sign_in_text,
+ :signin_enabled,
+ :signup_enabled,
+ :terminal_max_session_time,
+ :user_default_external,
+ :user_oauth_applications,
+ :version_check_enabled
+ ]
optional :default_branch_protection, type: Integer, values: [0, 1, 2], desc: 'Determine if developers can push to master'
optional :default_project_visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The default project visibility'
optional :default_snippet_visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The default snippet visibility'
@@ -111,22 +160,8 @@ module API
end
optional :terminal_max_session_time, type: Integer, desc: 'Maximum time for web terminal websocket connection (in seconds). Set to 0 for unlimited time.'
optional :polling_interval_multiplier, type: BigDecimal, desc: 'Interval multiplier used by endpoints that perform polling. Set to 0 to disable polling.'
- at_least_one_of :default_branch_protection, :default_project_visibility, :default_snippet_visibility,
- :default_group_visibility, :restricted_visibility_levels, :import_sources,
- :enabled_git_access_protocol, :gravatar_enabled, :default_projects_limit,
- :max_attachment_size, :session_expire_delay, :disabled_oauth_sign_in_sources,
- :user_oauth_applications, :user_default_external, :signup_enabled,
- :send_user_confirmation_email, :domain_whitelist, :domain_blacklist_enabled,
- :after_sign_up_text, :signin_enabled, :require_two_factor_authentication,
- :home_page_url, :after_sign_out_path, :sign_in_text, :help_page_text,
- :shared_runners_enabled, :max_artifacts_size,
- :default_artifacts_expire_in, :max_pages_size,
- :container_registry_token_expire_delay,
- :metrics_enabled, :sidekiq_throttling_enabled, :recaptcha_enabled,
- :akismet_enabled, :admin_notification_email, :sentry_enabled,
- :repository_storage, :repository_checks_enabled, :koding_enabled, :plantuml_enabled,
- :version_check_enabled, :email_author_in_body, :html_emails_enabled,
- :housekeeping_enabled, :terminal_max_session_time, :polling_interval_multiplier
+
+ at_least_one_of(*at_least_one_of_ce)
end
put "application/settings" do
attrs = declared_params(include_missing: false)
diff --git a/lib/api/users.rb b/lib/api/users.rb
index 6f40f92240a..eedc59f8636 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -37,11 +37,13 @@ module API
success Entities::UserBasic
end
params do
+ # CE
optional :username, type: String, desc: 'Get a single user with a specific username'
optional :search, type: String, desc: 'Search for a username'
optional :active, type: Boolean, default: false, desc: 'Filters only active users'
optional :external, type: Boolean, default: false, desc: 'Filters only external users'
optional :blocked, type: Boolean, default: false, desc: 'Filters only blocked users'
+
use :pagination
end
get do
diff --git a/lib/banzai/reference_parser/merge_request_parser.rb b/lib/banzai/reference_parser/merge_request_parser.rb
index 7d7dcce017e..84a28b33d7c 100644
--- a/lib/banzai/reference_parser/merge_request_parser.rb
+++ b/lib/banzai/reference_parser/merge_request_parser.rb
@@ -3,23 +3,42 @@ module Banzai
class MergeRequestParser < BaseParser
self.reference_type = :merge_request
+ def nodes_visible_to_user(user, nodes)
+ merge_requests = merge_requests_for_nodes(nodes)
+
+ nodes.select do |node|
+ merge_request = merge_requests[node]
+
+ merge_request && can?(user, :read_merge_request, merge_request.project)
+ end
+ end
+
+ def referenced_by(nodes)
+ merge_requests = merge_requests_for_nodes(nodes)
+
+ nodes.map { |node| merge_requests[node] }.compact.uniq
+ end
+
def merge_requests_for_nodes(nodes)
@merge_requests_for_nodes ||= grouped_objects_for_nodes(
nodes,
- MergeRequest.all,
+ MergeRequest.includes(
+ :author,
+ :assignee,
+ {
+ # These associations are primarily used for checking permissions.
+ # Eager loading these ensures we don't end up running dozens of
+ # queries in this process.
+ target_project: [
+ { namespace: :owner },
+ { group: [:owners, :group_members] },
+ :invited_groups,
+ :project_members
+ ]
+ }),
self.class.data_attribute
)
end
-
- def references_relation
- MergeRequest.includes(:author, :assignee, :target_project)
- end
-
- private
-
- def can_read_reference?(user, ref_project)
- can?(user, :read_merge_request, ref_project)
- end
end
end
end
diff --git a/lib/bitbucket/representation/base.rb b/lib/bitbucket/representation/base.rb
index 94adaacc9b5..800d5a075c6 100644
--- a/lib/bitbucket/representation/base.rb
+++ b/lib/bitbucket/representation/base.rb
@@ -1,6 +1,8 @@
module Bitbucket
module Representation
class Base
+ attr_reader :raw
+
def initialize(raw)
@raw = raw
end
@@ -8,10 +10,6 @@ module Bitbucket
def self.decorate(entries)
entries.map { |entry| new(entry)}
end
-
- private
-
- attr_reader :raw
end
end
end
diff --git a/lib/container_registry/tag.rb b/lib/container_registry/tag.rb
index d00e6191e7e..728deea224f 100644
--- a/lib/container_registry/tag.rb
+++ b/lib/container_registry/tag.rb
@@ -29,6 +29,10 @@ module ContainerRegistry
"#{repository.path}:#{name}"
end
+ def location
+ "#{repository.location}:#{name}"
+ end
+
def [](key)
return unless manifest
diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb
index f4efa20374a..5a6d9ae99a0 100644
--- a/lib/gitlab/bitbucket_import/importer.rb
+++ b/lib/gitlab/bitbucket_import/importer.rb
@@ -149,7 +149,7 @@ module Gitlab
description += @formatter.author_line(pull_request.author) unless find_user_id(pull_request.author)
description += pull_request.description
- merge_request = project.merge_requests.create(
+ merge_request = project.merge_requests.create!(
iid: pull_request.iid,
title: pull_request.title,
description: description,
@@ -168,7 +168,7 @@ module Gitlab
import_pull_request_comments(pull_request, merge_request) if merge_request.persisted?
rescue StandardError => e
- errors << { type: :pull_request, iid: pull_request.iid, errors: e.message }
+ errors << { type: :pull_request, iid: pull_request.iid, errors: e.message, trace: e.backtrace.join("\n"), raw_response: pull_request.raw }
end
end
end
diff --git a/lib/gitlab/checks/change_access.rb b/lib/gitlab/checks/change_access.rb
index eb2f2e144fd..8793b20aa35 100644
--- a/lib/gitlab/checks/change_access.rb
+++ b/lib/gitlab/checks/change_access.rb
@@ -5,7 +5,7 @@ module Gitlab
attr_reader :user_access, :project, :skip_authorization, :protocol
def initialize(
- change, user_access:, project:, env: {}, skip_authorization: false,
+ change, user_access:, project:, skip_authorization: false,
protocol:
)
@oldrev, @newrev, @ref = change.values_at(:oldrev, :newrev, :ref)
@@ -13,7 +13,6 @@ module Gitlab
@tag_name = Gitlab::Git.tag_name(@ref)
@user_access = user_access
@project = project
- @env = env
@skip_authorization = skip_authorization
@protocol = protocol
end
@@ -97,7 +96,7 @@ module Gitlab
end
def forced_push?
- Gitlab::Checks::ForcePush.force_push?(@project, @oldrev, @newrev, env: @env)
+ Gitlab::Checks::ForcePush.force_push?(@project, @oldrev, @newrev)
end
def update?
diff --git a/lib/gitlab/checks/force_push.rb b/lib/gitlab/checks/force_push.rb
index de0c9049ebf..1e73f89158d 100644
--- a/lib/gitlab/checks/force_push.rb
+++ b/lib/gitlab/checks/force_push.rb
@@ -1,20 +1,16 @@
module Gitlab
module Checks
class ForcePush
- def self.force_push?(project, oldrev, newrev, env: {})
+ def self.force_push?(project, oldrev, newrev)
return false if project.empty_repo?
# Created or deleted branch
if Gitlab::Git.blank_ref?(oldrev) || Gitlab::Git.blank_ref?(newrev)
false
else
- missed_ref, exit_status = Gitlab::Git::RevList.new(oldrev, newrev, project: project, env: env).execute
-
- if exit_status == 0
- missed_ref.present?
- else
- raise "Got a non-zero exit code while calling out to `git rev-list` in the force-push check."
- end
+ Gitlab::Git::RevList.new(
+ path_to_repo: project.repository.path_to_repo,
+ oldrev: oldrev, newrev: newrev).missed_ref.present?
end
end
end
diff --git a/lib/gitlab/ci/trace/stream.rb b/lib/gitlab/ci/trace/stream.rb
index 2af94e2c60e..41dcf846fed 100644
--- a/lib/gitlab/ci/trace/stream.rb
+++ b/lib/gitlab/ci/trace/stream.rb
@@ -76,11 +76,14 @@ module Gitlab
stream.each_line do |line|
matches = line.scan(regex)
next unless matches.is_a?(Array)
+ next if matches.empty?
match = matches.flatten.last
coverage = match.gsub(/\d+(\.\d+)?/).first
- return coverage.to_f if coverage.present?
+ return coverage if coverage.present?
end
+
+ nil
rescue
# if bad regex or something goes wrong we dont want to interrupt transition
# so we just silentrly ignore error for now
diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb
index 63b8d0d3b9d..d0bd1299671 100644
--- a/lib/gitlab/database.rb
+++ b/lib/gitlab/database.rb
@@ -57,16 +57,16 @@ module Gitlab
postgresql? ? "RANDOM()" : "RAND()"
end
- def true_value
- if Gitlab::Database.postgresql?
+ def self.true_value
+ if postgresql?
"'t'"
else
1
end
end
- def false_value
- if Gitlab::Database.postgresql?
+ def self.false_value
+ if postgresql?
"'f'"
else
0
diff --git a/lib/gitlab/database/migration_helpers.rb b/lib/gitlab/database/migration_helpers.rb
index 525aa920328..6dabbe0264c 100644
--- a/lib/gitlab/database/migration_helpers.rb
+++ b/lib/gitlab/database/migration_helpers.rb
@@ -89,7 +89,8 @@ module Gitlab
ADD CONSTRAINT #{key_name}
FOREIGN KEY (#{column})
REFERENCES #{target} (id)
- ON DELETE #{on_delete} NOT VALID;
+ #{on_delete ? "ON DELETE #{on_delete}" : ''}
+ NOT VALID;
EOF
# Validate the existing constraint. This can potentially take a very
@@ -114,6 +115,14 @@ module Gitlab
execute('SET statement_timeout TO 0') if Database.postgresql?
end
+ def true_value
+ Database.true_value
+ end
+
+ def false_value
+ Database.false_value
+ end
+
# Updates the value of a column in batches.
#
# This method updates the table in batches of 5% of the total row count.
@@ -250,6 +259,245 @@ module Gitlab
raise error
end
end
+
+ # Renames a column without requiring downtime.
+ #
+ # Concurrent renames work by using database triggers to ensure both the
+ # old and new column are in sync. However, this method will _not_ remove
+ # the triggers or the old column automatically; this needs to be done
+ # manually in a post-deployment migration. This can be done using the
+ # method `cleanup_concurrent_column_rename`.
+ #
+ # table - The name of the database table containing the column.
+ # old - The old column name.
+ # new - The new column name.
+ # type - The type of the new column. If no type is given the old column's
+ # type is used.
+ def rename_column_concurrently(table, old, new, type: nil)
+ if transaction_open?
+ raise 'rename_column_concurrently can not be run inside a transaction'
+ end
+
+ trigger_name = rename_trigger_name(table, old, new)
+ quoted_table = quote_table_name(table)
+ quoted_old = quote_column_name(old)
+ quoted_new = quote_column_name(new)
+
+ if Database.postgresql?
+ install_rename_triggers_for_postgresql(trigger_name, quoted_table,
+ quoted_old, quoted_new)
+ else
+ install_rename_triggers_for_mysql(trigger_name, quoted_table,
+ quoted_old, quoted_new)
+ end
+
+ old_col = column_for(table, old)
+ new_type = type || old_col.type
+
+ add_column(table, new, new_type,
+ limit: old_col.limit,
+ default: old_col.default,
+ null: old_col.null,
+ precision: old_col.precision,
+ scale: old_col.scale)
+
+ update_column_in_batches(table, new, Arel::Table.new(table)[old])
+
+ copy_indexes(table, old, new)
+ copy_foreign_keys(table, old, new)
+ end
+
+ # Changes the type of a column concurrently.
+ #
+ # table - The table containing the column.
+ # column - The name of the column to change.
+ # new_type - The new column type.
+ def change_column_type_concurrently(table, column, new_type)
+ temp_column = "#{column}_for_type_change"
+
+ rename_column_concurrently(table, column, temp_column, type: new_type)
+ end
+
+ # Performs cleanup of a concurrent type change.
+ #
+ # table - The table containing the column.
+ # column - The name of the column to change.
+ # new_type - The new column type.
+ def cleanup_concurrent_column_type_change(table, column)
+ temp_column = "#{column}_for_type_change"
+
+ transaction do
+ # This has to be performed in a transaction as otherwise we might have
+ # inconsistent data.
+ cleanup_concurrent_column_rename(table, column, temp_column)
+ rename_column(table, temp_column, column)
+ end
+ end
+
+ # Cleans up a concurrent column name.
+ #
+ # This method takes care of removing previously installed triggers as well
+ # as removing the old column.
+ #
+ # table - The name of the database table.
+ # old - The name of the old column.
+ # new - The name of the new column.
+ def cleanup_concurrent_column_rename(table, old, new)
+ trigger_name = rename_trigger_name(table, old, new)
+
+ if Database.postgresql?
+ remove_rename_triggers_for_postgresql(table, trigger_name)
+ else
+ remove_rename_triggers_for_mysql(trigger_name)
+ end
+
+ remove_column(table, old)
+ end
+
+ # Performs a concurrent column rename when using PostgreSQL.
+ def install_rename_triggers_for_postgresql(trigger, table, old, new)
+ execute <<-EOF.strip_heredoc
+ CREATE OR REPLACE FUNCTION #{trigger}()
+ RETURNS trigger AS
+ $BODY$
+ BEGIN
+ NEW.#{new} := NEW.#{old};
+ RETURN NEW;
+ END;
+ $BODY$
+ LANGUAGE 'plpgsql'
+ VOLATILE
+ EOF
+
+ execute <<-EOF.strip_heredoc
+ CREATE TRIGGER #{trigger}
+ BEFORE INSERT OR UPDATE
+ ON #{table}
+ FOR EACH ROW
+ EXECUTE PROCEDURE #{trigger}()
+ EOF
+ end
+
+ # Installs the triggers necessary to perform a concurrent column rename on
+ # MySQL.
+ def install_rename_triggers_for_mysql(trigger, table, old, new)
+ execute <<-EOF.strip_heredoc
+ CREATE TRIGGER #{trigger}_insert
+ BEFORE INSERT
+ ON #{table}
+ FOR EACH ROW
+ SET NEW.#{new} = NEW.#{old}
+ EOF
+
+ execute <<-EOF.strip_heredoc
+ CREATE TRIGGER #{trigger}_update
+ BEFORE UPDATE
+ ON #{table}
+ FOR EACH ROW
+ SET NEW.#{new} = NEW.#{old}
+ EOF
+ end
+
+ # Removes the triggers used for renaming a PostgreSQL column concurrently.
+ def remove_rename_triggers_for_postgresql(table, trigger)
+ execute("DROP TRIGGER #{trigger} ON #{table}")
+ execute("DROP FUNCTION #{trigger}()")
+ end
+
+ # Removes the triggers used for renaming a MySQL column concurrently.
+ def remove_rename_triggers_for_mysql(trigger)
+ execute("DROP TRIGGER #{trigger}_insert")
+ execute("DROP TRIGGER #{trigger}_update")
+ end
+
+ # Returns the (base) name to use for triggers when renaming columns.
+ def rename_trigger_name(table, old, new)
+ 'trigger_' + Digest::SHA256.hexdigest("#{table}_#{old}_#{new}").first(12)
+ end
+
+ # Returns an Array containing the indexes for the given column
+ def indexes_for(table, column)
+ column = column.to_s
+
+ indexes(table).select { |index| index.columns.include?(column) }
+ end
+
+ # Returns an Array containing the foreign keys for the given column.
+ def foreign_keys_for(table, column)
+ column = column.to_s
+
+ foreign_keys(table).select { |fk| fk.column == column }
+ end
+
+ # Copies all indexes for the old column to a new column.
+ #
+ # table - The table containing the columns and indexes.
+ # old - The old column.
+ # new - The new column.
+ def copy_indexes(table, old, new)
+ old = old.to_s
+ new = new.to_s
+
+ indexes_for(table, old).each do |index|
+ new_columns = index.columns.map do |column|
+ column == old ? new : column
+ end
+
+ # This is necessary as we can't properly rename indexes such as
+ # "ci_taggings_idx".
+ unless index.name.include?(old)
+ raise "The index #{index.name} can not be copied as it does not "\
+ "mention the old column. You have to rename this index manually first."
+ end
+
+ name = index.name.gsub(old, new)
+
+ options = {
+ unique: index.unique,
+ name: name,
+ length: index.lengths,
+ order: index.orders
+ }
+
+ # These options are not supported by MySQL, so we only add them if
+ # they were previously set.
+ options[:using] = index.using if index.using
+ options[:where] = index.where if index.where
+
+ unless index.opclasses.blank?
+ opclasses = index.opclasses.dup
+
+ # Copy the operator classes for the old column (if any) to the new
+ # column.
+ opclasses[new] = opclasses.delete(old) if opclasses[old]
+
+ options[:opclasses] = opclasses
+ end
+
+ add_concurrent_index(table, new_columns, options)
+ end
+ end
+
+ # Copies all foreign keys for the old column to the new column.
+ #
+ # table - The table containing the columns and indexes.
+ # old - The old column.
+ # new - The new column.
+ def copy_foreign_keys(table, old, new)
+ foreign_keys_for(table, old).each do |fk|
+ add_concurrent_foreign_key(fk.from_table,
+ fk.to_table,
+ column: new,
+ on_delete: fk.on_delete)
+ end
+ end
+
+ # Returns the column for the given table and column name.
+ def column_for(table, name)
+ name = name.to_s
+
+ columns(table).find { |column| column.name == name }
+ end
end
end
end
diff --git a/lib/gitlab/database/multi_threaded_migration.rb b/lib/gitlab/database/multi_threaded_migration.rb
new file mode 100644
index 00000000000..7ae5a4c17c8
--- /dev/null
+++ b/lib/gitlab/database/multi_threaded_migration.rb
@@ -0,0 +1,52 @@
+module Gitlab
+ module Database
+ module MultiThreadedMigration
+ MULTI_THREAD_AR_CONNECTION = :thread_local_ar_connection
+
+ # This overwrites the default connection method so that every thread can
+ # use a thread-local connection, while still supporting all of Rails'
+ # migration methods.
+ def connection
+ Thread.current[MULTI_THREAD_AR_CONNECTION] ||
+ ActiveRecord::Base.connection
+ end
+
+ # Starts a thread-pool for N threads, along with N threads each using a
+ # single connection. The provided block is yielded from inside each
+ # thread.
+ #
+ # Example:
+ #
+ # with_multiple_threads(4) do
+ # execute('SELECT ...')
+ # end
+ #
+ # thread_count - The number of threads to start.
+ #
+ # join - When set to true this method will join the threads, blocking the
+ # caller until all threads have finished running.
+ #
+ # Returns an Array containing the started threads.
+ def with_multiple_threads(thread_count, join: true)
+ pool = Gitlab::Database.create_connection_pool(thread_count)
+
+ threads = Array.new(thread_count) do
+ Thread.new do
+ pool.with_connection do |connection|
+ begin
+ Thread.current[MULTI_THREAD_AR_CONNECTION] = connection
+ yield
+ ensure
+ Thread.current[MULTI_THREAD_AR_CONNECTION] = nil
+ end
+ end
+ end
+ end
+
+ threads.each(&:join) if join
+
+ threads
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/diff/diff_refs.rb b/lib/gitlab/diff/diff_refs.rb
index 8406ca4269c..7948782aecc 100644
--- a/lib/gitlab/diff/diff_refs.rb
+++ b/lib/gitlab/diff/diff_refs.rb
@@ -18,6 +18,12 @@ module Gitlab
head_sha == other.head_sha
end
+ alias_method :eql?, :==
+
+ def hash
+ [base_sha, start_sha, head_sha].hash
+ end
+
# There is only one case in which we will have `start_sha` and `head_sha`,
# but not `base_sha`, which is when a diff is generated between an
# orphaned branch and another branch, which means there _is_ no base, but
diff --git a/lib/gitlab/etag_caching/middleware.rb b/lib/gitlab/etag_caching/middleware.rb
index 11167632e07..270d67dd50c 100644
--- a/lib/gitlab/etag_caching/middleware.rb
+++ b/lib/gitlab/etag_caching/middleware.rb
@@ -1,40 +1,12 @@
module Gitlab
module EtagCaching
class Middleware
- RESERVED_WORDS = NamespaceValidator::WILDCARD_ROUTES.map { |word| "/#{word}/" }.join('|')
- ROUTES = [
- {
- regexp: %r(^(?!.*(#{RESERVED_WORDS})).*/noteable/issue/\d+/notes\z),
- name: 'issue_notes'
- },
- {
- regexp: %r(^(?!.*(#{RESERVED_WORDS})).*/issues/\d+/rendered_title\z),
- name: 'issue_title'
- },
- {
- regexp: %r(^(?!.*(#{RESERVED_WORDS})).*/pipelines\.json\z),
- name: 'project_pipelines'
- },
- {
- regexp: %r(^(?!.*(#{RESERVED_WORDS})).*/commit/\s+/pipelines\.json\z),
- name: 'commit_pipelines'
- },
- {
- regexp: %r(^(?!.*(#{RESERVED_WORDS})).*/merge_requests/new\.json\z),
- name: 'new_merge_request_pipelines'
- },
- {
- regexp: %r(^(?!.*(#{RESERVED_WORDS})).*/merge_requests/\d+/pipelines\.json\z),
- name: 'merge_request_pipelines'
- }
- ].freeze
-
def initialize(app)
@app = app
end
def call(env)
- route = match_current_route(env)
+ route = Gitlab::EtagCaching::Router.match(env)
return @app.call(env) unless route
track_event(:etag_caching_middleware_used, route)
@@ -55,10 +27,6 @@ module Gitlab
private
- def match_current_route(env)
- ROUTES.find { |route| route[:regexp].match(env['PATH_INFO']) }
- end
-
def get_etag(env)
cache_key = env['PATH_INFO']
store = Gitlab::EtagCaching::Store.new
@@ -95,7 +63,7 @@ module Gitlab
end
def track_event(name, route)
- Gitlab::Metrics.add_event(name, endpoint: route[:name])
+ Gitlab::Metrics.add_event(name, endpoint: route.name)
end
end
end
diff --git a/lib/gitlab/etag_caching/router.rb b/lib/gitlab/etag_caching/router.rb
new file mode 100644
index 00000000000..f6e4f279c06
--- /dev/null
+++ b/lib/gitlab/etag_caching/router.rb
@@ -0,0 +1,39 @@
+module Gitlab
+ module EtagCaching
+ class Router
+ Route = Struct.new(:regexp, :name)
+
+ RESERVED_WORDS = NamespaceValidator::WILDCARD_ROUTES.map { |word| "/#{word}/" }.join('|')
+ ROUTES = [
+ Gitlab::EtagCaching::Router::Route.new(
+ %r(^(?!.*(#{RESERVED_WORDS})).*/noteable/issue/\d+/notes\z),
+ 'issue_notes'
+ ),
+ Gitlab::EtagCaching::Router::Route.new(
+ %r(^(?!.*(#{RESERVED_WORDS})).*/issues/\d+/rendered_title\z),
+ 'issue_title'
+ ),
+ Gitlab::EtagCaching::Router::Route.new(
+ %r(^(?!.*(#{RESERVED_WORDS})).*/commit/\S+/pipelines\.json\z),
+ 'commit_pipelines'
+ ),
+ Gitlab::EtagCaching::Router::Route.new(
+ %r(^(?!.*(#{RESERVED_WORDS})).*/merge_requests/new\.json\z),
+ 'new_merge_request_pipelines'
+ ),
+ Gitlab::EtagCaching::Router::Route.new(
+ %r(^(?!.*(#{RESERVED_WORDS})).*/merge_requests/\d+/pipelines\.json\z),
+ 'merge_request_pipelines'
+ ),
+ Gitlab::EtagCaching::Router::Route.new(
+ %r(^(?!.*(#{RESERVED_WORDS})).*/pipelines\.json\z),
+ 'project_pipelines'
+ )
+ ].freeze
+
+ def self.match(env)
+ ROUTES.find { |route| route.regexp.match(env['PATH_INFO']) }
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/git/env.rb b/lib/gitlab/git/env.rb
new file mode 100644
index 00000000000..0fdc57ec954
--- /dev/null
+++ b/lib/gitlab/git/env.rb
@@ -0,0 +1,38 @@
+module Gitlab
+ module Git
+ # Ephemeral (per request) storage for environment variables that some Git
+ # commands may need.
+ #
+ # For example, in pre-receive hooks, new objects are put in a temporary
+ # $GIT_OBJECT_DIRECTORY. Without it set, the new objects cannot be retrieved
+ # (this would break push rules for instance).
+ #
+ # This class is thread-safe via RequestStore.
+ class Env
+ WHITELISTED_GIT_VARIABLES = %w[
+ GIT_OBJECT_DIRECTORY
+ GIT_ALTERNATE_OBJECT_DIRECTORIES
+ ].freeze
+
+ def self.set(env)
+ return unless RequestStore.active?
+
+ RequestStore.store[:gitlab_git_env] = whitelist_git_env(env)
+ end
+
+ def self.all
+ return {} unless RequestStore.active?
+
+ RequestStore.fetch(:gitlab_git_env) { {} }
+ end
+
+ def self.[](key)
+ all[key]
+ end
+
+ def self.whitelist_git_env(env)
+ env.select { |key, _| WHITELISTED_GIT_VARIABLES.include?(key.to_s) }.with_indifferent_access
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index fc473b2c21e..d7dac9f6149 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -8,6 +8,10 @@ module Gitlab
class Repository
include Gitlab::Git::Popen
+ ALLOWED_OBJECT_DIRECTORIES_VARIABLES = %w[
+ GIT_OBJECT_DIRECTORY
+ GIT_ALTERNATE_OBJECT_DIRECTORIES
+ ].freeze
SEARCH_CONTEXT_LINES = 3
NoRepository = Class.new(StandardError)
@@ -41,13 +45,15 @@ module Gitlab
# Default branch in the repository
def root_ref
- @root_ref ||= Gitlab::GitalyClient.migrate(:root_ref) do |is_enabled|
- if is_enabled
- gitaly_ref_client.default_branch_name
- else
- discover_default_branch
- end
- end
+ # NOTE: This feature is intentionally disabled until
+ # https://gitlab.com/gitlab-org/gitaly/issues/179 is resolved
+ # @root_ref ||= Gitlab::GitalyClient.migrate(:root_ref) do |is_enabled|
+ # if is_enabled
+ # gitaly_ref_client.default_branch_name
+ # else
+ @root_ref ||= discover_default_branch
+ # end
+ # end
rescue GRPC::BadStatus => e
raise CommandError.new(e)
end
@@ -58,7 +64,7 @@ module Gitlab
end
def rugged
- @rugged ||= Rugged::Repository.new(path)
+ @rugged ||= Rugged::Repository.new(path, alternates: alternate_object_directories)
rescue Rugged::RepositoryError, Rugged::OSError
raise NoRepository.new('no repository for such path')
end
@@ -66,13 +72,15 @@ module Gitlab
# Returns an Array of branch names
# sorted by name ASC
def branch_names
- Gitlab::GitalyClient.migrate(:branch_names) do |is_enabled|
- if is_enabled
- gitaly_ref_client.branch_names
- else
- branches.map(&:name)
- end
- end
+ # Gitlab::GitalyClient.migrate(:branch_names) do |is_enabled|
+ # NOTE: This feature is intentionally disabled until
+ # https://gitlab.com/gitlab-org/gitaly/issues/179 is resolved
+ # if is_enabled
+ # gitaly_ref_client.branch_names
+ # else
+ branches.map(&:name)
+ # end
+ # end
rescue GRPC::BadStatus => e
raise CommandError.new(e)
end
@@ -127,13 +135,15 @@ module Gitlab
# Returns an Array of tag names
def tag_names
- Gitlab::GitalyClient.migrate(:tag_names) do |is_enabled|
- if is_enabled
- gitaly_ref_client.tag_names
- else
- rugged.tags.map { |t| t.name }
- end
- end
+ # Gitlab::GitalyClient.migrate(:tag_names) do |is_enabled|
+ # NOTE: This feature is intentionally disabled until
+ # https://gitlab.com/gitlab-org/gitaly/issues/179 is resolved
+ # if is_enabled
+ # gitaly_ref_client.tag_names
+ # else
+ rugged.tags.map { |t| t.name }
+ # end
+ # end
rescue GRPC::BadStatus => e
raise CommandError.new(e)
end
@@ -454,17 +464,19 @@ module Gitlab
# Returns a RefName for a given SHA
def ref_name_for_sha(ref_path, sha)
- Gitlab::GitalyClient.migrate(:find_ref_name) do |is_enabled|
- if is_enabled
- gitaly_ref_client.find_ref_name(sha, ref_path)
- else
- args = %W(#{Gitlab.config.git.bin_path} for-each-ref --count=1 #{ref_path} --contains #{sha})
+ # NOTE: This feature is intentionally disabled until
+ # https://gitlab.com/gitlab-org/gitaly/issues/180 is resolved
+ # Gitlab::GitalyClient.migrate(:find_ref_name) do |is_enabled|
+ # if is_enabled
+ # gitaly_ref_client.find_ref_name(sha, ref_path)
+ # else
+ args = %W(#{Gitlab.config.git.bin_path} for-each-ref --count=1 #{ref_path} --contains #{sha})
- # Not found -> ["", 0]
- # Found -> ["b8d95eb4969eefacb0a58f6a28f6803f8070e7b9 commit\trefs/environments/production/77\n", 0]
- Gitlab::Popen.popen(args, @path).first.split.last
- end
- end
+ # Not found -> ["", 0]
+ # Found -> ["b8d95eb4969eefacb0a58f6a28f6803f8070e7b9 commit\trefs/environments/production/77\n", 0]
+ Gitlab::Popen.popen(args, @path).first.split.last
+ # end
+ # end
end
# Returns commits collection
@@ -978,6 +990,10 @@ module Gitlab
private
+ def alternate_object_directories
+ Gitlab::Git::Env.all.values_at(*ALLOWED_OBJECT_DIRECTORIES_VARIABLES).compact
+ end
+
# Get the content of a blob for a given commit. If the blob is a commit
# (for submodules) then return the blob's OID.
def blob_content(commit, blob_name)
diff --git a/lib/gitlab/git/rev_list.rb b/lib/gitlab/git/rev_list.rb
index 79dd0cf7df2..a16b0ed76f4 100644
--- a/lib/gitlab/git/rev_list.rb
+++ b/lib/gitlab/git/rev_list.rb
@@ -1,41 +1,42 @@
module Gitlab
module Git
class RevList
- attr_reader :project, :env
-
- ALLOWED_VARIABLES = %w[GIT_OBJECT_DIRECTORY GIT_ALTERNATE_OBJECT_DIRECTORIES].freeze
-
- def initialize(oldrev, newrev, project:, env: nil)
- @project = project
- @env = env.presence || {}
- @args = [Gitlab.config.git.bin_path,
- "--git-dir=#{project.repository.path_to_repo}",
- "rev-list",
- "--max-count=1",
- oldrev,
- "^#{newrev}"]
+ attr_reader :oldrev, :newrev, :path_to_repo
+
+ def initialize(path_to_repo:, newrev:, oldrev: nil)
+ @oldrev = oldrev
+ @newrev = newrev
+ @path_to_repo = path_to_repo
end
- def execute
- Gitlab::Popen.popen(@args, nil, parse_environment_variables)
+ # This method returns an array of new references
+ def new_refs
+ execute([*base_args, newrev, '--not', '--all'])
end
- def valid?
- environment_variables.all? do |(name, value)|
- value.to_s.start_with?(project.repository.path_to_repo)
- end
+ # This methods returns an array of missed references
+ def missed_ref
+ execute([*base_args, '--max-count=1', oldrev, "^#{newrev}"])
end
private
- def parse_environment_variables
- return {} unless valid?
+ def execute(args)
+ output, status = Gitlab::Popen.popen(args, nil, Gitlab::Git::Env.all.stringify_keys)
+
+ unless status.zero?
+ raise "Got a non-zero exit code while calling out `#{args.join(' ')}`."
+ end
- environment_variables
+ output.split("\n")
end
- def environment_variables
- @environment_variables ||= env.slice(*ALLOWED_VARIABLES).compact
+ def base_args
+ [
+ Gitlab.config.git.bin_path,
+ "--git-dir=#{path_to_repo}",
+ 'rev-list'
+ ]
end
end
end
diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb
index eea2f206902..99724db8da2 100644
--- a/lib/gitlab/git_access.rb
+++ b/lib/gitlab/git_access.rb
@@ -18,13 +18,12 @@ module Gitlab
attr_reader :actor, :project, :protocol, :user_access, :authentication_abilities
- def initialize(actor, project, protocol, authentication_abilities:, env: {})
+ def initialize(actor, project, protocol, authentication_abilities:)
@actor = actor
@project = project
@protocol = protocol
@authentication_abilities = authentication_abilities
@user_access = UserAccess.new(user, project: project)
- @env = env
end
def check(cmd, changes)
@@ -152,7 +151,6 @@ module Gitlab
change,
user_access: user_access,
project: project,
- env: @env,
skip_authorization: deploy_key?,
protocol: protocol
).exec
diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake
index a9a48f7188f..f41c73154f5 100644
--- a/lib/tasks/gitlab/check.rake
+++ b/lib/tasks/gitlab/check.rake
@@ -431,8 +431,7 @@ namespace :gitlab do
def check_repo_base_user_and_group
gitlab_shell_ssh_user = Gitlab.config.gitlab_shell.ssh_user
- gitlab_shell_owner_group = Gitlab.config.gitlab_shell.owner_group
- puts "Repo paths owned by #{gitlab_shell_ssh_user}:#{gitlab_shell_owner_group}?"
+ puts "Repo paths owned by #{gitlab_shell_ssh_user}:root, or #{gitlab_shell_ssh_user}:#{Gitlab.config.gitlab_shell.owner_group}?"
Gitlab.config.repositories.storages.each do |name, repository_storage|
repo_base_path = repository_storage['path']
@@ -443,15 +442,16 @@ namespace :gitlab do
break
end
- uid = uid_for(gitlab_shell_ssh_user)
- gid = gid_for(gitlab_shell_owner_group)
- if File.stat(repo_base_path).uid == uid && File.stat(repo_base_path).gid == gid
+ user_id = uid_for(gitlab_shell_ssh_user)
+ root_group_id = gid_for('root')
+ group_ids = [root_group_id, gid_for(Gitlab.config.gitlab_shell.owner_group)]
+ if File.stat(repo_base_path).uid == user_id && group_ids.include?(File.stat(repo_base_path).gid)
puts "yes".color(:green)
else
puts "no".color(:red)
- puts " User id for #{gitlab_shell_ssh_user}: #{uid}. Groupd id for #{gitlab_shell_owner_group}: #{gid}".color(:blue)
+ puts " User id for #{gitlab_shell_ssh_user}: #{user_id}. Groupd id for root: #{root_group_id}".color(:blue)
try_fixing_it(
- "sudo chown -R #{gitlab_shell_ssh_user}:#{gitlab_shell_owner_group} #{repo_base_path}"
+ "sudo chown -R #{gitlab_shell_ssh_user}:root #{repo_base_path}"
)
for_more_information(
see_installation_guide_section "GitLab Shell"
diff --git a/lib/tasks/gitlab/update_templates.rake b/lib/tasks/gitlab/update_templates.rake
index dbdfb335a5c..cb2adc81c9d 100644
--- a/lib/tasks/gitlab/update_templates.rake
+++ b/lib/tasks/gitlab/update_templates.rake
@@ -44,7 +44,7 @@ namespace :gitlab do
),
Template.new(
"https://gitlab.com/gitlab-org/gitlab-ci-yml.git",
- /(\.{1,2}|LICENSE|Pages|autodeploy|\.gitlab-ci.yml)\z/
+ /(\.{1,2}|LICENSE|CONTRIBUTING.md|Pages|autodeploy|\.gitlab-ci.yml)\z/
)
].freeze
diff --git a/package.json b/package.json
index 6d4f99e33b3..a17399ddb8f 100644
--- a/package.json
+++ b/package.json
@@ -1,10 +1,10 @@
{
"private": true,
"scripts": {
- "dev-server": "webpack-dev-server --config config/webpack.config.js",
- "eslint": "eslint --max-warnings 0 --ext .js .",
- "eslint-fix": "eslint --max-warnings 0 --ext .js --fix .",
- "eslint-report": "eslint --max-warnings 0 --ext .js --format html --output-file ./eslint-report.html .",
+ "dev-server": "nodemon --watch config/webpack.config.js -- ./node_modules/.bin/webpack-dev-server --config config/webpack.config.js",
+ "eslint": "eslint --max-warnings 0 --ext .js,.vue .",
+ "eslint-fix": "eslint --max-warnings 0 --ext .js,.vue --fix .",
+ "eslint-report": "eslint --max-warnings 0 --ext .js,.vue --format html --output-file ./eslint-report.html .",
"karma": "karma start config/karma.config.js --single-run",
"karma-coverage": "BABEL_ENV=coverage karma start config/karma.config.js --single-run",
"karma-start": "karma start config/karma.config.js",
@@ -20,10 +20,12 @@
"bootstrap-sass": "^3.3.6",
"compression-webpack-plugin": "^0.3.2",
"core-js": "^2.4.1",
+ "css-loader": "^0.28.0",
"d3": "^3.5.11",
"document-register-element": "^1.3.0",
"dropzone": "^4.2.0",
"emoji-unicode-version": "^0.2.1",
+ "eslint-plugin-html": "^2.0.1",
"file-loader": "^0.11.1",
"jquery": "^2.2.1",
"jquery-ujs": "^1.2.1",
@@ -34,6 +36,7 @@
"pikaday": "^1.5.1",
"raphael": "^2.2.7",
"raw-loader": "^0.5.1",
+ "react-dev-utils": "^0.5.2",
"select2": "3.5.2-browserify",
"stats-webpack-plugin": "^0.4.3",
"three": "^0.84.0",
@@ -42,8 +45,10 @@
"timeago.js": "^2.0.5",
"underscore": "^1.8.3",
"visibilityjs": "^1.2.4",
- "vue": "^2.2.4",
+ "vue": "^2.2.6",
+ "vue-loader": "^11.3.4",
"vue-resource": "^0.9.3",
+ "vue-template-compiler": "^2.2.6",
"webpack": "^2.3.3",
"webpack-bundle-analyzer": "^2.3.0"
},
@@ -65,6 +70,7 @@
"karma-phantomjs-launcher": "^1.0.2",
"karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "^2.0.2",
+ "nodemon": "^1.11.0",
"webpack-dev-server": "^2.4.2"
}
}
diff --git a/qa/qa/page/main/menu.rb b/qa/qa/page/main/menu.rb
index 45db7a92fa4..7ce4e9009f5 100644
--- a/qa/qa/page/main/menu.rb
+++ b/qa/qa/page/main/menu.rb
@@ -11,7 +11,7 @@ module QA
end
def go_to_admin_area
- within_user_menu { click_link 'Admin Area' }
+ within_user_menu { click_link 'Admin area' }
end
def sign_out
diff --git a/scripts/prepare_build.sh b/scripts/prepare_build.sh
index 6e3f76b8399..6cacb81b8bc 100755
--- a/scripts/prepare_build.sh
+++ b/scripts/prepare_build.sh
@@ -15,21 +15,12 @@ retry() {
return 1
}
-if [ -f /.dockerenv ] || [ -f ./dockerinit ]; then
- cp config/database.yml.mysql config/database.yml
- sed -i 's/username:.*/username: root/g' config/database.yml
- sed -i 's/password:.*/password:/g' config/database.yml
- sed -i 's/# socket:.*/host: mysql/g' config/database.yml
+cp config/database.yml.mysql config/database.yml
+sed -i 's/username:.*/username: root/g' config/database.yml
+sed -i 's/password:.*/password:/g' config/database.yml
+sed -i 's/# socket:.*/host: mysql/g' config/database.yml
- cp config/resque.yml.example config/resque.yml
- sed -i 's/localhost/redis/g' config/resque.yml
+cp config/resque.yml.example config/resque.yml
+sed -i 's/localhost/redis/g' config/resque.yml
- export FLAGS="--path vendor --retry 3 --quiet"
-else
- rnd=$(awk 'BEGIN { srand() ; printf("%d\n",rand()*5) }')
- export PATH="$HOME/bin:/usr/local/bin:/usr/bin:/bin"
- cp config/database.yml.mysql config/database.yml
- sed "s/username\:.*$/username\: runner/" -i config/database.yml
- sed "s/password\:.*$/password\: 'password'/" -i config/database.yml
- sed "s/gitlabhq_test/gitlabhq_test_$rnd/" -i config/database.yml
-fi
+export FLAGS="--path vendor --retry 3 --quiet"
diff --git a/spec/factories/notes.rb b/spec/factories/notes.rb
index 90c35e2c7f8..93f4903119c 100644
--- a/spec/factories/notes.rb
+++ b/spec/factories/notes.rb
@@ -40,6 +40,7 @@ FactoryGirl.define do
transient do
line_number 14
+ diff_refs { noteable.try(:diff_refs) }
end
position do
@@ -48,7 +49,7 @@ FactoryGirl.define do
new_path: "files/ruby/popen.rb",
old_line: nil,
new_line: line_number,
- diff_refs: noteable.try(:diff_refs)
+ diff_refs: diff_refs
)
end
diff --git a/spec/features/dashboard/shortcuts_spec.rb b/spec/features/dashboard/shortcuts_spec.rb
index fa5524e18d8..4c9adcabe34 100644
--- a/spec/features/dashboard/shortcuts_spec.rb
+++ b/spec/features/dashboard/shortcuts_spec.rb
@@ -1,27 +1,49 @@
require 'spec_helper'
feature 'Dashboard shortcuts', feature: true, js: true do
- before do
- login_as :user
- visit root_dashboard_path
- end
+ context 'logged in' do
+ before do
+ login_as :user
+ visit root_dashboard_path
+ end
+
+ scenario 'Navigate to tabs' do
+ find('body').native.send_keys([:shift, 'P'])
+
+ check_page_title('Projects')
+
+ find('body').native.send_key([:shift, 'I'])
+
+ check_page_title('Issues')
- scenario 'Navigate to tabs' do
- find('body').native.send_keys([:shift, 'P'])
+ find('body').native.send_key([:shift, 'M'])
+
+ check_page_title('Merge Requests')
+
+ find('body').native.send_keys([:shift, 'T'])
+
+ check_page_title('Todos')
+ end
+ end
- check_page_title('Projects')
+ context 'logged out' do
+ before do
+ visit explore_root_path
+ end
- find('body').native.send_key([:shift, 'I'])
+ scenario 'Navigate to tabs' do
+ find('body').native.send_keys([:shift, 'P'])
- check_page_title('Issues')
+ expect(page).to have_content('No projects found')
- find('body').native.send_key([:shift, 'M'])
+ find('body').native.send_keys([:shift, 'G'])
- check_page_title('Merge Requests')
+ expect(page).to have_content('No public groups')
- find('body').native.send_keys([:shift, 'T'])
+ find('body').native.send_keys([:shift, 'S'])
- check_page_title('Todos')
+ expect(page).to have_selector('.snippets-list-holder')
+ end
end
def check_page_title(title)
diff --git a/spec/features/discussion_comments/commit_spec.rb b/spec/features/discussion_comments/commit_spec.rb
new file mode 100644
index 00000000000..96e0b78f6b9
--- /dev/null
+++ b/spec/features/discussion_comments/commit_spec.rb
@@ -0,0 +1,18 @@
+require 'spec_helper'
+
+describe 'Discussion Comments Merge Request', :feature, :js do
+ include RepoHelpers
+
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+ let(:merge_request) { create(:merge_request, source_project: project) }
+
+ before do
+ project.add_master(user)
+ login_as(user)
+
+ visit namespace_project_commit_path(project.namespace, project, sample_commit.id)
+ end
+
+ it_behaves_like 'discussion comments', 'commit'
+end
diff --git a/spec/features/discussion_comments/issue_spec.rb b/spec/features/discussion_comments/issue_spec.rb
new file mode 100644
index 00000000000..ccc9efccd18
--- /dev/null
+++ b/spec/features/discussion_comments/issue_spec.rb
@@ -0,0 +1,16 @@
+require 'spec_helper'
+
+describe 'Discussion Comments Issue', :feature, :js do
+ let(:user) { create(:user) }
+ let(:project) { create(:empty_project) }
+ let(:issue) { create(:issue, project: project) }
+
+ before do
+ project.add_master(user)
+ login_as(user)
+
+ visit namespace_project_issue_path(project.namespace, project, issue)
+ end
+
+ it_behaves_like 'discussion comments', 'issue'
+end
diff --git a/spec/features/discussion_comments/merge_request_spec.rb b/spec/features/discussion_comments/merge_request_spec.rb
new file mode 100644
index 00000000000..f99ebeb9cd9
--- /dev/null
+++ b/spec/features/discussion_comments/merge_request_spec.rb
@@ -0,0 +1,16 @@
+require 'spec_helper'
+
+describe 'Discussion Comments Merge Request', :feature, :js do
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+ let(:merge_request) { create(:merge_request, source_project: project) }
+
+ before do
+ project.add_master(user)
+ login_as(user)
+
+ visit namespace_project_merge_request_path(project.namespace, project, merge_request)
+ end
+
+ it_behaves_like 'discussion comments', 'merge request'
+end
diff --git a/spec/features/discussion_comments/snippets_spec.rb b/spec/features/discussion_comments/snippets_spec.rb
new file mode 100644
index 00000000000..19a306511b2
--- /dev/null
+++ b/spec/features/discussion_comments/snippets_spec.rb
@@ -0,0 +1,16 @@
+require 'spec_helper'
+
+describe 'Discussion Comments Issue', :feature, :js do
+ let(:user) { create(:user) }
+ let(:project) { create(:empty_project) }
+ let(:snippet) { create(:project_snippet, :private, project: project, author: user) }
+
+ before do
+ project.add_master(user)
+ login_as(user)
+
+ visit namespace_project_snippet_path(project.namespace, project, snippet)
+ end
+
+ it_behaves_like 'discussion comments', 'snippet'
+end
diff --git a/spec/features/merge_requests/discussion_spec.rb b/spec/features/merge_requests/discussion_spec.rb
new file mode 100644
index 00000000000..f59d0faa274
--- /dev/null
+++ b/spec/features/merge_requests/discussion_spec.rb
@@ -0,0 +1,51 @@
+require 'spec_helper'
+
+feature 'Merge Request Discussions', feature: true do
+ before do
+ login_as :admin
+ end
+
+ context "Diff discussions" do
+ let(:merge_request) { create(:merge_request, importing: true) }
+ let(:project) { merge_request.source_project }
+ let!(:old_merge_request_diff) { merge_request.merge_request_diffs.create(diff_refs: outdated_diff_refs) }
+ let!(:new_merge_request_diff) { merge_request.merge_request_diffs.create }
+
+ let!(:outdated_discussion) { create(:diff_note_on_merge_request, project: project, noteable: merge_request, position: outdated_position).to_discussion }
+ let!(:active_discussion) { create(:diff_note_on_merge_request, noteable: merge_request, project: project).to_discussion }
+
+ let(:outdated_position) do
+ Gitlab::Diff::Position.new(
+ old_path: "files/ruby/popen.rb",
+ new_path: "files/ruby/popen.rb",
+ old_line: nil,
+ new_line: 9,
+ diff_refs: outdated_diff_refs
+ )
+ end
+
+ let(:outdated_diff_refs) { project.commit("874797c3a73b60d2187ed6e2fcabd289ff75171e").diff_refs }
+
+ before(:each) do
+ visit namespace_project_merge_request_path(project.namespace, project, merge_request)
+ end
+
+ context 'active discussions' do
+ it 'shows a link to the diff' do
+ within(".discussion[data-discussion-id='#{active_discussion.id}']") do
+ path = diffs_namespace_project_merge_request_path(project.namespace, project, merge_request, anchor: active_discussion.line_code)
+ expect(page).to have_link('the diff', href: path)
+ end
+ end
+ end
+
+ context 'outdated discussions' do
+ it 'shows a link to the outdated diff' do
+ within(".discussion[data-discussion-id='#{outdated_discussion.id}']") do
+ path = diffs_namespace_project_merge_request_path(project.namespace, project, merge_request, diff_id: old_merge_request_diff.id, anchor: outdated_discussion.line_code)
+ expect(page).to have_link('an outdated diff', href: path)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb b/spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb
index 646e7bab265..cd540ca113a 100644
--- a/spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb
+++ b/spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb
@@ -89,6 +89,19 @@ feature 'Merge When Pipeline Succeeds', :feature, :js do
it_behaves_like 'Merge when pipeline succeeds activator'
end
end
+
+ describe 'enabling Merge when pipeline succeeds via dropdown' do
+ it 'activates the Merge when pipeline succeeds feature' do
+ click_button 'Select merge moment'
+ within('.js-merge-dropdown') do
+ click_link 'Merge when pipeline succeeds'
+ end
+
+ expect(page).to have_content "Set by #{user.name} to be merged automatically when the pipeline succeeds."
+ expect(page).to have_content "The source branch will not be removed."
+ expect(page).to have_link "Cancel automatic merge"
+ end
+ end
end
context 'when merge when pipeline succeeds is enabled' do
diff --git a/spec/features/merge_requests/merge_request_versions_spec.rb b/spec/features/merge_requests/versions_spec.rb
index 04e85ed3f73..68a68f5d3f3 100644
--- a/spec/features/merge_requests/merge_request_versions_spec.rb
+++ b/spec/features/merge_requests/versions_spec.rb
@@ -36,8 +36,23 @@ feature 'Merge Request versions', js: true, feature: true do
expect(page).to have_content '5 changed files'
end
- it 'show the message about disabled comments' do
- expect(page).to have_content 'Comments are disabled'
+ it 'show the message about disabled comment creation' do
+ expect(page).to have_content 'comment creation is disabled'
+ end
+
+ it 'shows comments that were last relevant at that version' do
+ position = Gitlab::Diff::Position.new(
+ old_path: ".gitmodules",
+ new_path: ".gitmodules",
+ old_line: nil,
+ new_line: 4,
+ diff_refs: merge_request_diff1.diff_refs
+ )
+ outdated_diff_note = create(:diff_note_on_merge_request, project: project, noteable: merge_request, position: position)
+ outdated_diff_note.position = outdated_diff_note.original_position
+ outdated_diff_note.save!
+
+ expect(page).to have_css(".diffs .notes[data-discussion-id='#{outdated_diff_note.discussion_id}']")
end
end
diff --git a/spec/helpers/preferences_helper_spec.rb b/spec/helpers/preferences_helper_spec.rb
index f3e79cc7290..2c0e9975f73 100644
--- a/spec/helpers/preferences_helper_spec.rb
+++ b/spec/helpers/preferences_helper_spec.rb
@@ -86,10 +86,10 @@ describe PreferencesHelper do
context 'when repository is not empty' do
let(:project) { create(:project, :public, :repository) }
- it 'returns readme if user has repository access' do
+ it 'returns files and readme if user has repository access' do
allow(helper).to receive(:can?).with(nil, :download_code, project).and_return(true)
- expect(helper.default_project_view).to eq('readme')
+ expect(helper.default_project_view).to eq('files')
end
it 'returns activity if user does not have repository access' do
diff --git a/spec/javascripts/build_spec.js b/spec/javascripts/build_spec.js
index edd4b3c1440..7174bf1e041 100644
--- a/spec/javascripts/build_spec.js
+++ b/spec/javascripts/build_spec.js
@@ -98,9 +98,9 @@ describe('Build', () => {
jasmine.clock().tick(4001);
- expect($.ajax.calls.count()).toBe(2);
+ expect($.ajax.calls.count()).toBe(3);
- args = $.ajax.calls.argsFor(1)[0];
+ args = $.ajax.calls.argsFor(2)[0];
expect(args.url).toBe(`${BUILD_URL}/trace.json`);
expect(args.dataType).toBe('json');
expect(args.data.state).toBe('newstate');
@@ -133,7 +133,7 @@ describe('Build', () => {
expect($('#build-trace .js-build-output').text()).toMatch(/Update/);
jasmine.clock().tick(4001);
- args = $.ajax.calls.argsFor(1)[0];
+ args = $.ajax.calls.argsFor(2)[0];
args.success.call($, {
html: '<span>Different</span>',
status: 'running',
diff --git a/spec/javascripts/environments/environment_spec.js b/spec/javascripts/environments/environment_spec.js
index 4431baa4b96..9762688af1a 100644
--- a/spec/javascripts/environments/environment_spec.js
+++ b/spec/javascripts/environments/environment_spec.js
@@ -83,9 +83,10 @@ describe('Environment', () => {
it('should render a table with environments', (done) => {
setTimeout(() => {
+ expect(component.$el.querySelectorAll('table')).toBeDefined();
expect(
- component.$el.querySelectorAll('table tbody tr').length,
- ).toEqual(1);
+ component.$el.querySelector('.environment-name').textContent.trim(),
+ ).toEqual(environment.name);
done();
}, 0);
});
diff --git a/spec/javascripts/environments/folder/environments_folder_view_spec.js b/spec/javascripts/environments/folder/environments_folder_view_spec.js
index 43a217a67f5..72f3db29a66 100644
--- a/spec/javascripts/environments/folder/environments_folder_view_spec.js
+++ b/spec/javascripts/environments/folder/environments_folder_view_spec.js
@@ -47,9 +47,10 @@ describe('Environments Folder View', () => {
it('should render a table with environments', (done) => {
setTimeout(() => {
+ expect(component.$el.querySelectorAll('table')).toBeDefined();
expect(
- component.$el.querySelectorAll('table tbody tr').length,
- ).toEqual(2);
+ component.$el.querySelector('.environment-name').textContent.trim(),
+ ).toEqual(environmentsList[0].name);
done();
}, 0);
});
diff --git a/spec/javascripts/issue_spec.js b/spec/javascripts/issue_spec.js
index aabc8bea12f..9a2570ef7e9 100644
--- a/spec/javascripts/issue_spec.js
+++ b/spec/javascripts/issue_spec.js
@@ -1,18 +1,17 @@
-/* eslint-disable space-before-function-paren, no-var, one-var, one-var-declaration-per-line, no-use-before-define, comma-dangle, max-len */
+/* eslint-disable space-before-function-paren, one-var, one-var-declaration-per-line, no-use-before-define, comma-dangle, max-len */
import Issue from '~/issue';
require('~/lib/utils/text_utility');
describe('Issue', function() {
- var INVALID_URL = 'http://goesnowhere.nothing/whereami';
- var $boxClosed, $boxOpen, $btnClose, $btnReopen;
+ let $boxClosed, $boxOpen, $btnClose, $btnReopen;
preloadFixtures('issues/closed-issue.html.raw');
preloadFixtures('issues/issue-with-task-list.html.raw');
preloadFixtures('issues/open-issue.html.raw');
function expectErrorMessage() {
- var $flashMessage = $('div.flash-alert');
+ const $flashMessage = $('div.flash-alert');
expect($flashMessage).toExist();
expect($flashMessage).toBeVisible();
expect($flashMessage).toHaveText('Unable to update this issue at this time.');
@@ -26,10 +25,28 @@ describe('Issue', function() {
expectVisibility($btnReopen, !isIssueOpen);
}
- function expectPendingRequest(req, $triggeredButton) {
- expect(req.type).toBe('PUT');
- expect(req.url).toBe($triggeredButton.attr('href'));
- expect($triggeredButton).toHaveProp('disabled', true);
+ function expectNewBranchButtonState(isPending, canCreate) {
+ if (Issue.$btnNewBranch.length === 0) {
+ return;
+ }
+
+ const $available = Issue.$btnNewBranch.find('.available');
+ expect($available).toHaveText('New branch');
+
+ if (!isPending && canCreate) {
+ expect($available).toBeVisible();
+ } else {
+ expect($available).toBeHidden();
+ }
+
+ const $unavailable = Issue.$btnNewBranch.find('.unavailable');
+ expect($unavailable).toHaveText('New branch unavailable');
+
+ if (!isPending && !canCreate) {
+ expect($unavailable).toBeVisible();
+ } else {
+ expect($unavailable).toBeHidden();
+ }
}
function expectVisibility($element, shouldBeVisible) {
@@ -81,100 +98,107 @@ describe('Issue', function() {
});
});
- describe('close issue', function() {
- beforeEach(function() {
- loadFixtures('issues/open-issue.html.raw');
- findElements();
- this.issue = new Issue();
-
- expectIssueState(true);
- });
+ [true, false].forEach((isIssueInitiallyOpen) => {
+ describe(`with ${isIssueInitiallyOpen ? 'open' : 'closed'} issue`, function() {
+ const action = isIssueInitiallyOpen ? 'close' : 'reopen';
+
+ function ajaxSpy(req) {
+ if (req.url === this.$triggeredButton.attr('href')) {
+ expect(req.type).toBe('PUT');
+ expect(this.$triggeredButton).toHaveProp('disabled', true);
+ expectNewBranchButtonState(true, false);
+ return this.issueStateDeferred;
+ } else if (req.url === Issue.$btnNewBranch.data('path')) {
+ expect(req.type).toBe('get');
+ expectNewBranchButtonState(true, false);
+ return this.canCreateBranchDeferred;
+ }
+
+ expect(req.url).toBe('unexpected');
+ return null;
+ }
+
+ beforeEach(function() {
+ if (isIssueInitiallyOpen) {
+ loadFixtures('issues/open-issue.html.raw');
+ } else {
+ loadFixtures('issues/closed-issue.html.raw');
+ }
+
+ findElements();
+ this.issue = new Issue();
+ expectIssueState(isIssueInitiallyOpen);
+ this.$triggeredButton = isIssueInitiallyOpen ? $btnClose : $btnReopen;
+
+ this.$projectIssuesCounter = $('.issue_counter');
+ this.$projectIssuesCounter.text('1,001');
+
+ this.issueStateDeferred = new jQuery.Deferred();
+ this.canCreateBranchDeferred = new jQuery.Deferred();
+
+ spyOn(jQuery, 'ajax').and.callFake(ajaxSpy.bind(this));
+ });
- it('closes an issue', function() {
- spyOn(jQuery, 'ajax').and.callFake(function(req) {
- expectPendingRequest(req, $btnClose);
- req.success({
+ it(`${action}s the issue`, function() {
+ this.$triggeredButton.trigger('click');
+ this.issueStateDeferred.resolve({
id: 34
});
- });
-
- $btnClose.trigger('click');
+ this.canCreateBranchDeferred.resolve({
+ can_create_branch: !isIssueInitiallyOpen
+ });
- expectIssueState(false);
- expect($btnClose).toHaveProp('disabled', false);
- expect($('.issue_counter')).toHaveText(0);
- });
+ expectIssueState(!isIssueInitiallyOpen);
+ expect(this.$triggeredButton).toHaveProp('disabled', false);
+ expect(this.$projectIssuesCounter.text()).toBe(isIssueInitiallyOpen ? '1,000' : '1,002');
+ expectNewBranchButtonState(false, !isIssueInitiallyOpen);
+ });
- it('fails to close an issue with success:false', function() {
- spyOn(jQuery, 'ajax').and.callFake(function(req) {
- expectPendingRequest(req, $btnClose);
- req.success({
+ it(`fails to ${action} the issue if saved:false`, function() {
+ this.$triggeredButton.trigger('click');
+ this.issueStateDeferred.resolve({
saved: false
});
- });
-
- $btnClose.attr('href', INVALID_URL);
- $btnClose.trigger('click');
-
- expectIssueState(true);
- expect($btnClose).toHaveProp('disabled', false);
- expectErrorMessage();
- expect($('.issue_counter')).toHaveText(1);
- });
+ this.canCreateBranchDeferred.resolve({
+ can_create_branch: isIssueInitiallyOpen
+ });
- it('fails to closes an issue with HTTP error', function() {
- spyOn(jQuery, 'ajax').and.callFake(function(req) {
- expectPendingRequest(req, $btnClose);
- req.error();
+ expectIssueState(isIssueInitiallyOpen);
+ expect(this.$triggeredButton).toHaveProp('disabled', false);
+ expectErrorMessage();
+ expect(this.$projectIssuesCounter.text()).toBe('1,001');
+ expectNewBranchButtonState(false, isIssueInitiallyOpen);
});
- $btnClose.attr('href', INVALID_URL);
- $btnClose.trigger('click');
-
- expectIssueState(true);
- expect($btnClose).toHaveProp('disabled', true);
- expectErrorMessage();
- expect($('.issue_counter')).toHaveText(1);
- });
-
- it('updates counter', () => {
- spyOn(jQuery, 'ajax').and.callFake(function(req) {
- expectPendingRequest(req, $btnClose);
- req.success({
- id: 34
+ it(`fails to ${action} the issue if HTTP error occurs`, function() {
+ this.$triggeredButton.trigger('click');
+ this.issueStateDeferred.reject();
+ this.canCreateBranchDeferred.resolve({
+ can_create_branch: isIssueInitiallyOpen
});
- });
- expect($('.issue_counter')).toHaveText(1);
- $('.issue_counter').text('1,001');
- expect($('.issue_counter').text()).toEqual('1,001');
- $btnClose.trigger('click');
- expect($('.issue_counter').text()).toEqual('1,000');
- });
- });
+ expectIssueState(isIssueInitiallyOpen);
+ expect(this.$triggeredButton).toHaveProp('disabled', true);
+ expectErrorMessage();
+ expect(this.$projectIssuesCounter.text()).toBe('1,001');
+ expectNewBranchButtonState(false, isIssueInitiallyOpen);
+ });
- describe('reopen issue', function() {
- beforeEach(function() {
- loadFixtures('issues/closed-issue.html.raw');
- findElements();
- this.issue = new Issue();
+ it('disables the new branch button if Ajax call fails', function() {
+ this.$triggeredButton.trigger('click');
+ this.issueStateDeferred.reject();
+ this.canCreateBranchDeferred.reject();
- expectIssueState(false);
- });
-
- it('reopens an issue', function() {
- spyOn(jQuery, 'ajax').and.callFake(function(req) {
- expectPendingRequest(req, $btnReopen);
- req.success({
- id: 34
- });
+ expectNewBranchButtonState(false, false);
});
- $btnReopen.trigger('click');
+ it('does not trigger Ajax call if new branch button is missing', function() {
+ Issue.$btnNewBranch = $();
+ this.canCreateBranchDeferred = null;
- expectIssueState(true);
- expect($btnReopen).toHaveProp('disabled', false);
- expect($('.issue_counter')).toHaveText(1);
+ this.$triggeredButton.trigger('click');
+ this.issueStateDeferred.reject();
+ });
});
});
});
diff --git a/spec/javascripts/lib/utils/poll_spec.js b/spec/javascripts/lib/utils/poll_spec.js
index e3429c2a1cb..918b6d32c43 100644
--- a/spec/javascripts/lib/utils/poll_spec.js
+++ b/spec/javascripts/lib/utils/poll_spec.js
@@ -4,6 +4,20 @@ import Poll from '~/lib/utils/poll';
Vue.use(VueResource);
+const waitForAllCallsToFinish = (service, waitForCount, successCallback) => {
+ const timer = () => {
+ setTimeout(() => {
+ if (service.fetch.calls.count() === waitForCount) {
+ successCallback();
+ } else {
+ timer();
+ }
+ }, 5);
+ };
+
+ timer();
+};
+
class ServiceMock {
constructor(endpoint) {
this.service = Vue.resource(endpoint);
@@ -16,6 +30,7 @@ class ServiceMock {
describe('Poll', () => {
let callbacks;
+ let service;
beforeEach(() => {
callbacks = {
@@ -23,8 +38,11 @@ describe('Poll', () => {
error: () => {},
};
+ service = new ServiceMock('endpoint');
+
spyOn(callbacks, 'success');
spyOn(callbacks, 'error');
+ spyOn(service, 'fetch').and.callThrough();
});
it('calls the success callback when no header for interval is provided', (done) => {
@@ -35,19 +53,20 @@ describe('Poll', () => {
Vue.http.interceptors.push(successInterceptor);
new Poll({
- resource: new ServiceMock('endpoint'),
+ resource: service,
method: 'fetch',
successCallback: callbacks.success,
errorCallback: callbacks.error,
}).makeRequest();
- setTimeout(() => {
+ waitForAllCallsToFinish(service, 1, () => {
expect(callbacks.success).toHaveBeenCalled();
expect(callbacks.error).not.toHaveBeenCalled();
+
+ Vue.http.interceptors = _.without(Vue.http.interceptors, successInterceptor);
+
done();
}, 0);
-
- Vue.http.interceptors = _.without(Vue.http.interceptors, successInterceptor);
});
it('calls the error callback whe the http request returns an error', (done) => {
@@ -58,19 +77,19 @@ describe('Poll', () => {
Vue.http.interceptors.push(errorInterceptor);
new Poll({
- resource: new ServiceMock('endpoint'),
+ resource: service,
method: 'fetch',
successCallback: callbacks.success,
errorCallback: callbacks.error,
}).makeRequest();
- setTimeout(() => {
+ waitForAllCallsToFinish(service, 1, () => {
expect(callbacks.success).not.toHaveBeenCalled();
expect(callbacks.error).toHaveBeenCalled();
- done();
- }, 0);
+ Vue.http.interceptors = _.without(Vue.http.interceptors, errorInterceptor);
- Vue.http.interceptors = _.without(Vue.http.interceptors, errorInterceptor);
+ done();
+ });
});
it('should call the success callback when the interval header is -1', (done) => {
@@ -81,7 +100,7 @@ describe('Poll', () => {
Vue.http.interceptors.push(intervalInterceptor);
new Poll({
- resource: new ServiceMock('endpoint'),
+ resource: service,
method: 'fetch',
successCallback: callbacks.success,
errorCallback: callbacks.error,
@@ -90,10 +109,11 @@ describe('Poll', () => {
setTimeout(() => {
expect(callbacks.success).toHaveBeenCalled();
expect(callbacks.error).not.toHaveBeenCalled();
+
+ Vue.http.interceptors = _.without(Vue.http.interceptors, intervalInterceptor);
+
done();
}, 0);
-
- Vue.http.interceptors = _.without(Vue.http.interceptors, intervalInterceptor);
});
it('starts polling when http status is 200 and interval header is provided', (done) => {
@@ -103,26 +123,28 @@ describe('Poll', () => {
Vue.http.interceptors.push(pollInterceptor);
- const service = new ServiceMock('endpoint');
- spyOn(service, 'fetch').and.callThrough();
-
- new Poll({
+ const Polling = new Poll({
resource: service,
method: 'fetch',
data: { page: 1 },
successCallback: callbacks.success,
errorCallback: callbacks.error,
- }).makeRequest();
+ });
+
+ Polling.makeRequest();
+
+ waitForAllCallsToFinish(service, 2, () => {
+ Polling.stop();
- setTimeout(() => {
expect(service.fetch.calls.count()).toEqual(2);
expect(service.fetch).toHaveBeenCalledWith({ page: 1 });
expect(callbacks.success).toHaveBeenCalled();
expect(callbacks.error).not.toHaveBeenCalled();
- done();
- }, 5);
- Vue.http.interceptors = _.without(Vue.http.interceptors, pollInterceptor);
+ Vue.http.interceptors = _.without(Vue.http.interceptors, pollInterceptor);
+
+ done();
+ });
});
describe('stop', () => {
@@ -133,9 +155,6 @@ describe('Poll', () => {
Vue.http.interceptors.push(pollInterceptor);
- const service = new ServiceMock('endpoint');
- spyOn(service, 'fetch').and.callThrough();
-
const Polling = new Poll({
resource: service,
method: 'fetch',
@@ -150,14 +169,15 @@ describe('Poll', () => {
Polling.makeRequest();
- setTimeout(() => {
+ waitForAllCallsToFinish(service, 1, () => {
expect(service.fetch.calls.count()).toEqual(1);
expect(service.fetch).toHaveBeenCalledWith({ page: 1 });
expect(Polling.stop).toHaveBeenCalled();
- done();
- }, 100);
- Vue.http.interceptors = _.without(Vue.http.interceptors, pollInterceptor);
+ Vue.http.interceptors = _.without(Vue.http.interceptors, pollInterceptor);
+
+ done();
+ });
});
});
@@ -169,10 +189,6 @@ describe('Poll', () => {
Vue.http.interceptors.push(pollInterceptor);
- const service = new ServiceMock('endpoint');
-
- spyOn(service, 'fetch').and.callThrough();
-
const Polling = new Poll({
resource: service,
method: 'fetch',
@@ -187,17 +203,22 @@ describe('Poll', () => {
});
spyOn(Polling, 'stop').and.callThrough();
+ spyOn(Polling, 'restart').and.callThrough();
Polling.makeRequest();
- setTimeout(() => {
+ waitForAllCallsToFinish(service, 2, () => {
+ Polling.stop();
+
expect(service.fetch.calls.count()).toEqual(2);
expect(service.fetch).toHaveBeenCalledWith({ page: 1 });
expect(Polling.stop).toHaveBeenCalled();
- done();
- }, 10);
+ expect(Polling.restart).toHaveBeenCalled();
- Vue.http.interceptors = _.without(Vue.http.interceptors, pollInterceptor);
+ Vue.http.interceptors = _.without(Vue.http.interceptors, pollInterceptor);
+
+ done();
+ });
});
});
});
diff --git a/spec/javascripts/lib/utils/text_utility_spec.js b/spec/javascripts/lib/utils/text_utility_spec.js
index 4200e943121..daef9b93fa5 100644
--- a/spec/javascripts/lib/utils/text_utility_spec.js
+++ b/spec/javascripts/lib/utils/text_utility_spec.js
@@ -1,110 +1,108 @@
require('~/lib/utils/text_utility');
-(() => {
- describe('text_utility', () => {
- describe('gl.text.getTextWidth', () => {
- it('returns zero width when no text is passed', () => {
- expect(gl.text.getTextWidth('')).toBe(0);
- });
+describe('text_utility', () => {
+ describe('gl.text.getTextWidth', () => {
+ it('returns zero width when no text is passed', () => {
+ expect(gl.text.getTextWidth('')).toBe(0);
+ });
- it('returns zero width when no text is passed and font is passed', () => {
- expect(gl.text.getTextWidth('', '100px sans-serif')).toBe(0);
- });
+ it('returns zero width when no text is passed and font is passed', () => {
+ expect(gl.text.getTextWidth('', '100px sans-serif')).toBe(0);
+ });
- it('returns width when text is passed', () => {
- expect(gl.text.getTextWidth('foo') > 0).toBe(true);
- });
+ it('returns width when text is passed', () => {
+ expect(gl.text.getTextWidth('foo') > 0).toBe(true);
+ });
- it('returns bigger width when font is larger', () => {
- const largeFont = gl.text.getTextWidth('foo', '100px sans-serif');
- const regular = gl.text.getTextWidth('foo', '10px sans-serif');
- expect(largeFont > regular).toBe(true);
- });
+ it('returns bigger width when font is larger', () => {
+ const largeFont = gl.text.getTextWidth('foo', '100px sans-serif');
+ const regular = gl.text.getTextWidth('foo', '10px sans-serif');
+ expect(largeFont > regular).toBe(true);
});
+ });
- describe('gl.text.pluralize', () => {
- it('returns pluralized', () => {
- expect(gl.text.pluralize('test', 2)).toBe('tests');
- });
+ describe('gl.text.pluralize', () => {
+ it('returns pluralized', () => {
+ expect(gl.text.pluralize('test', 2)).toBe('tests');
+ });
- it('returns pluralized when count is 0', () => {
- expect(gl.text.pluralize('test', 0)).toBe('tests');
- });
+ it('returns pluralized when count is 0', () => {
+ expect(gl.text.pluralize('test', 0)).toBe('tests');
+ });
- it('does not return pluralized', () => {
- expect(gl.text.pluralize('test', 1)).toBe('test');
- });
+ it('does not return pluralized', () => {
+ expect(gl.text.pluralize('test', 1)).toBe('test');
});
+ });
- describe('gl.text.highCountTrim', () => {
- it('returns 99+ for count >= 100', () => {
- expect(gl.text.highCountTrim(105)).toBe('99+');
- expect(gl.text.highCountTrim(100)).toBe('99+');
- });
+ describe('gl.text.highCountTrim', () => {
+ it('returns 99+ for count >= 100', () => {
+ expect(gl.text.highCountTrim(105)).toBe('99+');
+ expect(gl.text.highCountTrim(100)).toBe('99+');
+ });
- it('returns exact number for count < 100', () => {
- expect(gl.text.highCountTrim(45)).toBe(45);
- });
+ it('returns exact number for count < 100', () => {
+ expect(gl.text.highCountTrim(45)).toBe(45);
});
+ });
- describe('gl.text.insertText', () => {
- let textArea;
+ describe('gl.text.insertText', () => {
+ let textArea;
- beforeAll(() => {
- textArea = document.createElement('textarea');
- document.querySelector('body').appendChild(textArea);
- });
+ beforeAll(() => {
+ textArea = document.createElement('textarea');
+ document.querySelector('body').appendChild(textArea);
+ });
- afterAll(() => {
- textArea.parentNode.removeChild(textArea);
- });
+ afterAll(() => {
+ textArea.parentNode.removeChild(textArea);
+ });
- describe('without selection', () => {
- it('inserts the tag on an empty line', () => {
- const initialValue = '';
+ describe('without selection', () => {
+ it('inserts the tag on an empty line', () => {
+ const initialValue = '';
- textArea.value = initialValue;
- textArea.selectionStart = 0;
- textArea.selectionEnd = 0;
+ textArea.value = initialValue;
+ textArea.selectionStart = 0;
+ textArea.selectionEnd = 0;
- gl.text.insertText(textArea, textArea.value, '*', null, '', false);
+ gl.text.insertText(textArea, textArea.value, '*', null, '', false);
- expect(textArea.value).toEqual(`${initialValue}* `);
- });
+ expect(textArea.value).toEqual(`${initialValue}* `);
+ });
- it('inserts the tag on a new line if the current one is not empty', () => {
- const initialValue = 'some text';
+ it('inserts the tag on a new line if the current one is not empty', () => {
+ const initialValue = 'some text';
- textArea.value = initialValue;
- textArea.setSelectionRange(initialValue.length, initialValue.length);
+ textArea.value = initialValue;
+ textArea.setSelectionRange(initialValue.length, initialValue.length);
- gl.text.insertText(textArea, textArea.value, '*', null, '', false);
+ gl.text.insertText(textArea, textArea.value, '*', null, '', false);
- expect(textArea.value).toEqual(`${initialValue}\n* `);
- });
+ expect(textArea.value).toEqual(`${initialValue}\n* `);
+ });
- it('inserts the tag on the same line if the current line only contains spaces', () => {
- const initialValue = ' ';
+ it('inserts the tag on the same line if the current line only contains spaces', () => {
+ const initialValue = ' ';
- textArea.value = initialValue;
- textArea.setSelectionRange(initialValue.length, initialValue.length);
+ textArea.value = initialValue;
+ textArea.setSelectionRange(initialValue.length, initialValue.length);
- gl.text.insertText(textArea, textArea.value, '*', null, '', false);
+ gl.text.insertText(textArea, textArea.value, '*', null, '', false);
- expect(textArea.value).toEqual(`${initialValue}* `);
- });
+ expect(textArea.value).toEqual(`${initialValue}* `);
+ });
- it('inserts the tag on the same line if the current line only contains tabs', () => {
- const initialValue = '\t\t\t';
+ it('inserts the tag on the same line if the current line only contains tabs', () => {
+ const initialValue = '\t\t\t';
- textArea.value = initialValue;
- textArea.setSelectionRange(initialValue.length, initialValue.length);
+ textArea.value = initialValue;
+ textArea.setSelectionRange(initialValue.length, initialValue.length);
- gl.text.insertText(textArea, textArea.value, '*', null, '', false);
+ gl.text.insertText(textArea, textArea.value, '*', null, '', false);
- expect(textArea.value).toEqual(`${initialValue}* `);
- });
+ expect(textArea.value).toEqual(`${initialValue}* `);
});
});
});
-})();
+});
diff --git a/spec/javascripts/vue_pipelines_index/async_button_spec.js b/spec/javascripts/vue_pipelines_index/async_button_spec.js
index bc8e504c413..6e910d2dc71 100644
--- a/spec/javascripts/vue_pipelines_index/async_button_spec.js
+++ b/spec/javascripts/vue_pipelines_index/async_button_spec.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import asyncButtonComp from '~/vue_pipelines_index/components/async_button';
+import asyncButtonComp from '~/vue_pipelines_index/components/async_button.vue';
describe('Pipelines Async Button', () => {
let component;
diff --git a/spec/javascripts/vue_pipelines_index/empty_state_spec.js b/spec/javascripts/vue_pipelines_index/empty_state_spec.js
index 733337168dc..2b10d54babe 100644
--- a/spec/javascripts/vue_pipelines_index/empty_state_spec.js
+++ b/spec/javascripts/vue_pipelines_index/empty_state_spec.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import emptyStateComp from '~/vue_pipelines_index/components/empty_state';
+import emptyStateComp from '~/vue_pipelines_index/components/empty_state.vue';
describe('Pipelines Empty State', () => {
let component;
diff --git a/spec/javascripts/vue_pipelines_index/error_state_spec.js b/spec/javascripts/vue_pipelines_index/error_state_spec.js
index 524e018b1fa..7999c15c18d 100644
--- a/spec/javascripts/vue_pipelines_index/error_state_spec.js
+++ b/spec/javascripts/vue_pipelines_index/error_state_spec.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import errorStateComp from '~/vue_pipelines_index/components/error_state';
+import errorStateComp from '~/vue_pipelines_index/components/error_state.vue';
describe('Pipelines Error State', () => {
let component;
diff --git a/spec/lib/container_registry/tag_spec.rb b/spec/lib/container_registry/tag_spec.rb
index bc1912d8e6c..f8fffbdca41 100644
--- a/spec/lib/container_registry/tag_spec.rb
+++ b/spec/lib/container_registry/tag_spec.rb
@@ -50,6 +50,13 @@ describe ContainerRegistry::Tag do
end
end
+ describe '#location' do
+ it 'returns a full location of the tag' do
+ expect(tag.location)
+ .to eq 'registry.gitlab/group/test:tag'
+ end
+ end
+
context 'manifest processing' do
context 'schema v1' do
before do
diff --git a/spec/lib/gitlab/ci/trace/stream_spec.rb b/spec/lib/gitlab/ci/trace/stream_spec.rb
index f1a1a71c528..2e57ccef182 100644
--- a/spec/lib/gitlab/ci/trace/stream_spec.rb
+++ b/spec/lib/gitlab/ci/trace/stream_spec.rb
@@ -167,7 +167,7 @@ describe Gitlab::Ci::Trace::Stream do
let(:data) { 'Coverage 1033 / 1051 LOC (98.29%) covered' }
let(:regex) { '\(\d+.\d+\%\) covered' }
- it { is_expected.to eq(98.29) }
+ it { is_expected.to eq("98.29") }
end
context 'valid content & bad regex' do
@@ -188,14 +188,14 @@ describe Gitlab::Ci::Trace::Stream do
let(:data) { ' (98.39%) covered. (98.29%) covered' }
let(:regex) { '\(\d+.\d+\%\) covered' }
- it { is_expected.to eq(98.29) }
+ it { is_expected.to eq("98.29") }
end
context 'using a regex capture' do
let(:data) { 'TOTAL 9926 3489 65%' }
let(:regex) { 'TOTAL\s+\d+\s+\d+\s+(\d{1,3}\%)' }
- it { is_expected.to eq(65) }
+ it { is_expected.to eq("65") }
end
end
end
diff --git a/spec/lib/gitlab/ci/trace_spec.rb b/spec/lib/gitlab/ci/trace_spec.rb
index 69e8dc9220d..9cb0b62590a 100644
--- a/spec/lib/gitlab/ci/trace_spec.rb
+++ b/spec/lib/gitlab/ci/trace_spec.rb
@@ -40,12 +40,24 @@ describe Gitlab::Ci::Trace do
describe '#extract_coverage' do
let(:regex) { '\(\d+.\d+\%\) covered' }
- before do
- trace.set('Coverage 1033 / 1051 LOC (98.29%) covered')
+ context 'matching coverage' do
+ before do
+ trace.set('Coverage 1033 / 1051 LOC (98.29%) covered')
+ end
+
+ it "returns valid coverage" do
+ expect(trace.extract_coverage(regex)).to eq("98.29")
+ end
end
- it "returns valid coverage" do
- expect(trace.extract_coverage(regex)).to eq(98.29)
+ context 'no coverage' do
+ before do
+ trace.set('No coverage')
+ end
+
+ it 'returs nil' do
+ expect(trace.extract_coverage(regex)).to be_nil
+ end
end
end
diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb
index 4ac79454647..a044b871730 100644
--- a/spec/lib/gitlab/database/migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/migration_helpers_spec.rb
@@ -175,6 +175,50 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
end
end
+ describe '#true_value' do
+ context 'using PostgreSQL' do
+ before do
+ expect(Gitlab::Database).to receive(:postgresql?).and_return(true)
+ end
+
+ it 'returns the appropriate value' do
+ expect(model.true_value).to eq("'t'")
+ end
+ end
+
+ context 'using MySQL' do
+ before do
+ expect(Gitlab::Database).to receive(:postgresql?).and_return(false)
+ end
+
+ it 'returns the appropriate value' do
+ expect(model.true_value).to eq(1)
+ end
+ end
+ end
+
+ describe '#false_value' do
+ context 'using PostgreSQL' do
+ before do
+ expect(Gitlab::Database).to receive(:postgresql?).and_return(true)
+ end
+
+ it 'returns the appropriate value' do
+ expect(model.false_value).to eq("'f'")
+ end
+ end
+
+ context 'using MySQL' do
+ before do
+ expect(Gitlab::Database).to receive(:postgresql?).and_return(false)
+ end
+
+ it 'returns the appropriate value' do
+ expect(model.false_value).to eq(0)
+ end
+ end
+ end
+
describe '#update_column_in_batches' do
before do
create_list(:empty_project, 5)
@@ -294,4 +338,392 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
end
end
end
+
+ describe '#rename_column_concurrently' do
+ context 'in a transaction' do
+ it 'raises RuntimeError' do
+ allow(model).to receive(:transaction_open?).and_return(true)
+
+ expect { model.rename_column_concurrently(:users, :old, :new) }.
+ to raise_error(RuntimeError)
+ end
+ end
+
+ context 'outside a transaction' do
+ let(:old_column) do
+ double(:column,
+ type: :integer,
+ limit: 8,
+ default: 0,
+ null: false,
+ precision: 5,
+ scale: 1)
+ end
+
+ let(:trigger_name) { model.rename_trigger_name(:users, :old, :new) }
+
+ before do
+ allow(model).to receive(:transaction_open?).and_return(false)
+ allow(model).to receive(:column_for).and_return(old_column)
+
+ # Since MySQL and PostgreSQL use different quoting styles we'll just
+ # stub the methods used for this to make testing easier.
+ allow(model).to receive(:quote_column_name) { |name| name.to_s }
+ allow(model).to receive(:quote_table_name) { |name| name.to_s }
+ end
+
+ context 'using MySQL' do
+ it 'renames a column concurrently' do
+ allow(Gitlab::Database).to receive(:postgresql?).and_return(false)
+
+ expect(model).to receive(:install_rename_triggers_for_mysql).
+ with(trigger_name, 'users', 'old', 'new')
+
+ expect(model).to receive(:add_column).
+ with(:users, :new, :integer,
+ limit: old_column.limit,
+ default: old_column.default,
+ null: old_column.null,
+ precision: old_column.precision,
+ scale: old_column.scale)
+
+ expect(model).to receive(:update_column_in_batches)
+
+ expect(model).to receive(:copy_indexes).with(:users, :old, :new)
+ expect(model).to receive(:copy_foreign_keys).with(:users, :old, :new)
+
+ model.rename_column_concurrently(:users, :old, :new)
+ end
+ end
+
+ context 'using PostgreSQL' do
+ it 'renames a column concurrently' do
+ allow(Gitlab::Database).to receive(:postgresql?).and_return(true)
+
+ expect(model).to receive(:install_rename_triggers_for_postgresql).
+ with(trigger_name, 'users', 'old', 'new')
+
+ expect(model).to receive(:add_column).
+ with(:users, :new, :integer,
+ limit: old_column.limit,
+ default: old_column.default,
+ null: old_column.null,
+ precision: old_column.precision,
+ scale: old_column.scale)
+
+ expect(model).to receive(:update_column_in_batches)
+
+ expect(model).to receive(:copy_indexes).with(:users, :old, :new)
+ expect(model).to receive(:copy_foreign_keys).with(:users, :old, :new)
+
+ model.rename_column_concurrently(:users, :old, :new)
+ end
+ end
+ end
+ end
+
+ describe '#cleanup_concurrent_column_rename' do
+ it 'cleans up the renaming procedure for PostgreSQL' do
+ allow(Gitlab::Database).to receive(:postgresql?).and_return(true)
+
+ expect(model).to receive(:remove_rename_triggers_for_postgresql).
+ with(:users, /trigger_.{12}/)
+
+ expect(model).to receive(:remove_column).with(:users, :old)
+
+ model.cleanup_concurrent_column_rename(:users, :old, :new)
+ end
+
+ it 'cleans up the renaming procedure for MySQL' do
+ allow(Gitlab::Database).to receive(:postgresql?).and_return(false)
+
+ expect(model).to receive(:remove_rename_triggers_for_mysql).
+ with(/trigger_.{12}/)
+
+ expect(model).to receive(:remove_column).with(:users, :old)
+
+ model.cleanup_concurrent_column_rename(:users, :old, :new)
+ end
+ end
+
+ describe '#change_column_type_concurrently' do
+ it 'changes the column type' do
+ expect(model).to receive(:rename_column_concurrently).
+ with('users', 'username', 'username_for_type_change', type: :text)
+
+ model.change_column_type_concurrently('users', 'username', :text)
+ end
+ end
+
+ describe '#cleanup_concurrent_column_type_change' do
+ it 'cleans up the type changing procedure' do
+ expect(model).to receive(:cleanup_concurrent_column_rename).
+ with('users', 'username', 'username_for_type_change')
+
+ expect(model).to receive(:rename_column).
+ with('users', 'username_for_type_change', 'username')
+
+ model.cleanup_concurrent_column_type_change('users', 'username')
+ end
+ end
+
+ describe '#install_rename_triggers_for_postgresql' do
+ it 'installs the triggers for PostgreSQL' do
+ expect(model).to receive(:execute).
+ with(/CREATE OR REPLACE FUNCTION foo()/m)
+
+ expect(model).to receive(:execute).
+ with(/CREATE TRIGGER foo/m)
+
+ model.install_rename_triggers_for_postgresql('foo', :users, :old, :new)
+ end
+ end
+
+ describe '#install_rename_triggers_for_mysql' do
+ it 'installs the triggers for MySQL' do
+ expect(model).to receive(:execute).
+ with(/CREATE TRIGGER foo_insert.+ON users/m)
+
+ expect(model).to receive(:execute).
+ with(/CREATE TRIGGER foo_update.+ON users/m)
+
+ model.install_rename_triggers_for_mysql('foo', :users, :old, :new)
+ end
+ end
+
+ describe '#remove_rename_triggers_for_postgresql' do
+ it 'removes the function and trigger' do
+ expect(model).to receive(:execute).with('DROP TRIGGER foo ON bar')
+ expect(model).to receive(:execute).with('DROP FUNCTION foo()')
+
+ model.remove_rename_triggers_for_postgresql('bar', 'foo')
+ end
+ end
+
+ describe '#remove_rename_triggers_for_mysql' do
+ it 'removes the triggers' do
+ expect(model).to receive(:execute).with('DROP TRIGGER foo_insert')
+ expect(model).to receive(:execute).with('DROP TRIGGER foo_update')
+
+ model.remove_rename_triggers_for_mysql('foo')
+ end
+ end
+
+ describe '#rename_trigger_name' do
+ it 'returns a String' do
+ expect(model.rename_trigger_name(:users, :foo, :bar)).
+ to match(/trigger_.{12}/)
+ end
+ end
+
+ describe '#indexes_for' do
+ it 'returns the indexes for a column' do
+ idx1 = double(:idx, columns: %w(project_id))
+ idx2 = double(:idx, columns: %w(user_id))
+
+ allow(model).to receive(:indexes).with('table').and_return([idx1, idx2])
+
+ expect(model.indexes_for('table', :user_id)).to eq([idx2])
+ end
+ end
+
+ describe '#foreign_keys_for' do
+ it 'returns the foreign keys for a column' do
+ fk1 = double(:fk, column: 'project_id')
+ fk2 = double(:fk, column: 'user_id')
+
+ allow(model).to receive(:foreign_keys).with('table').and_return([fk1, fk2])
+
+ expect(model.foreign_keys_for('table', :user_id)).to eq([fk2])
+ end
+ end
+
+ describe '#copy_indexes' do
+ context 'using a regular index using a single column' do
+ it 'copies the index' do
+ index = double(:index,
+ columns: %w(project_id),
+ name: 'index_on_issues_project_id',
+ using: nil,
+ where: nil,
+ opclasses: {},
+ unique: false,
+ lengths: [],
+ orders: [])
+
+ allow(model).to receive(:indexes_for).with(:issues, 'project_id').
+ and_return([index])
+
+ expect(model).to receive(:add_concurrent_index).
+ with(:issues,
+ %w(gl_project_id),
+ unique: false,
+ name: 'index_on_issues_gl_project_id',
+ length: [],
+ order: [])
+
+ model.copy_indexes(:issues, :project_id, :gl_project_id)
+ end
+ end
+
+ context 'using a regular index with multiple columns' do
+ it 'copies the index' do
+ index = double(:index,
+ columns: %w(project_id foobar),
+ name: 'index_on_issues_project_id_foobar',
+ using: nil,
+ where: nil,
+ opclasses: {},
+ unique: false,
+ lengths: [],
+ orders: [])
+
+ allow(model).to receive(:indexes_for).with(:issues, 'project_id').
+ and_return([index])
+
+ expect(model).to receive(:add_concurrent_index).
+ with(:issues,
+ %w(gl_project_id foobar),
+ unique: false,
+ name: 'index_on_issues_gl_project_id_foobar',
+ length: [],
+ order: [])
+
+ model.copy_indexes(:issues, :project_id, :gl_project_id)
+ end
+ end
+
+ context 'using an index with a WHERE clause' do
+ it 'copies the index' do
+ index = double(:index,
+ columns: %w(project_id),
+ name: 'index_on_issues_project_id',
+ using: nil,
+ where: 'foo',
+ opclasses: {},
+ unique: false,
+ lengths: [],
+ orders: [])
+
+ allow(model).to receive(:indexes_for).with(:issues, 'project_id').
+ and_return([index])
+
+ expect(model).to receive(:add_concurrent_index).
+ with(:issues,
+ %w(gl_project_id),
+ unique: false,
+ name: 'index_on_issues_gl_project_id',
+ length: [],
+ order: [],
+ where: 'foo')
+
+ model.copy_indexes(:issues, :project_id, :gl_project_id)
+ end
+ end
+
+ context 'using an index with a USING clause' do
+ it 'copies the index' do
+ index = double(:index,
+ columns: %w(project_id),
+ name: 'index_on_issues_project_id',
+ where: nil,
+ using: 'foo',
+ opclasses: {},
+ unique: false,
+ lengths: [],
+ orders: [])
+
+ allow(model).to receive(:indexes_for).with(:issues, 'project_id').
+ and_return([index])
+
+ expect(model).to receive(:add_concurrent_index).
+ with(:issues,
+ %w(gl_project_id),
+ unique: false,
+ name: 'index_on_issues_gl_project_id',
+ length: [],
+ order: [],
+ using: 'foo')
+
+ model.copy_indexes(:issues, :project_id, :gl_project_id)
+ end
+ end
+
+ context 'using an index with custom operator classes' do
+ it 'copies the index' do
+ index = double(:index,
+ columns: %w(project_id),
+ name: 'index_on_issues_project_id',
+ using: nil,
+ where: nil,
+ opclasses: { 'project_id' => 'bar' },
+ unique: false,
+ lengths: [],
+ orders: [])
+
+ allow(model).to receive(:indexes_for).with(:issues, 'project_id').
+ and_return([index])
+
+ expect(model).to receive(:add_concurrent_index).
+ with(:issues,
+ %w(gl_project_id),
+ unique: false,
+ name: 'index_on_issues_gl_project_id',
+ length: [],
+ order: [],
+ opclasses: { 'gl_project_id' => 'bar' })
+
+ model.copy_indexes(:issues, :project_id, :gl_project_id)
+ end
+ end
+
+ describe 'using an index of which the name does not contain the source column' do
+ it 'raises RuntimeError' do
+ index = double(:index,
+ columns: %w(project_id),
+ name: 'index_foobar_index',
+ using: nil,
+ where: nil,
+ opclasses: {},
+ unique: false,
+ lengths: [],
+ orders: [])
+
+ allow(model).to receive(:indexes_for).with(:issues, 'project_id').
+ and_return([index])
+
+ expect { model.copy_indexes(:issues, :project_id, :gl_project_id) }.
+ to raise_error(RuntimeError)
+ end
+ end
+ end
+
+ describe '#copy_foreign_keys' do
+ it 'copies foreign keys from one column to another' do
+ fk = double(:fk,
+ from_table: 'issues',
+ to_table: 'projects',
+ on_delete: :cascade)
+
+ allow(model).to receive(:foreign_keys_for).with(:issues, :project_id).
+ and_return([fk])
+
+ expect(model).to receive(:add_concurrent_foreign_key).
+ with('issues', 'projects', column: :gl_project_id, on_delete: :cascade)
+
+ model.copy_foreign_keys(:issues, :project_id, :gl_project_id)
+ end
+ end
+
+ describe '#column_for' do
+ it 'returns a column object for an existing column' do
+ column = model.column_for(:users, :id)
+
+ expect(column.name).to eq('id')
+ end
+
+ it 'returns nil when a column does not exist' do
+ expect(model.column_for(:users, :kittens)).to be_nil
+ end
+ end
end
diff --git a/spec/lib/gitlab/database/multi_threaded_migration_spec.rb b/spec/lib/gitlab/database/multi_threaded_migration_spec.rb
new file mode 100644
index 00000000000..6c45f13bb5a
--- /dev/null
+++ b/spec/lib/gitlab/database/multi_threaded_migration_spec.rb
@@ -0,0 +1,41 @@
+require 'spec_helper'
+
+describe Gitlab::Database::MultiThreadedMigration do
+ let(:migration) do
+ Class.new { include Gitlab::Database::MultiThreadedMigration }.new
+ end
+
+ describe '#connection' do
+ after do
+ Thread.current[described_class::MULTI_THREAD_AR_CONNECTION] = nil
+ end
+
+ it 'returns the thread-local connection if present' do
+ Thread.current[described_class::MULTI_THREAD_AR_CONNECTION] = 10
+
+ expect(migration.connection).to eq(10)
+ end
+
+ it 'returns the global connection if no thread-local connection was set' do
+ expect(migration.connection).to eq(ActiveRecord::Base.connection)
+ end
+ end
+
+ describe '#with_multiple_threads' do
+ it 'starts multiple threads and yields the supplied block in every thread' do
+ output = Queue.new
+
+ migration.with_multiple_threads(2) do
+ output << migration.connection.execute('SELECT 1')
+ end
+
+ expect(output.size).to eq(2)
+ end
+
+ it 'joins the threads when the join parameter is set' do
+ expect_any_instance_of(Thread).to receive(:join).and_call_original
+
+ migration.with_multiple_threads(1) { }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb
index 4ce4e6e1034..9b1d66a1b1c 100644
--- a/spec/lib/gitlab/database_spec.rb
+++ b/spec/lib/gitlab/database_spec.rb
@@ -150,13 +150,13 @@ describe Gitlab::Database, lib: true do
it 'returns correct value for PostgreSQL' do
expect(described_class).to receive(:postgresql?).and_return(true)
- expect(MigrationTest.new.true_value).to eq "'t'"
+ expect(described_class.true_value).to eq "'t'"
end
it 'returns correct value for MySQL' do
expect(described_class).to receive(:postgresql?).and_return(false)
- expect(MigrationTest.new.true_value).to eq 1
+ expect(described_class.true_value).to eq 1
end
end
@@ -164,13 +164,13 @@ describe Gitlab::Database, lib: true do
it 'returns correct value for PostgreSQL' do
expect(described_class).to receive(:postgresql?).and_return(true)
- expect(MigrationTest.new.false_value).to eq "'f'"
+ expect(described_class.false_value).to eq "'f'"
end
it 'returns correct value for MySQL' do
expect(described_class).to receive(:postgresql?).and_return(false)
- expect(MigrationTest.new.false_value).to eq 0
+ expect(described_class.false_value).to eq 0
end
end
end
diff --git a/spec/lib/gitlab/etag_caching/router_spec.rb b/spec/lib/gitlab/etag_caching/router_spec.rb
new file mode 100644
index 00000000000..f3dacb4ef04
--- /dev/null
+++ b/spec/lib/gitlab/etag_caching/router_spec.rb
@@ -0,0 +1,83 @@
+require 'spec_helper'
+
+describe Gitlab::EtagCaching::Router do
+ it 'matches issue notes endpoint' do
+ env = build_env(
+ '/my-group/and-subgroup/here-comes-the-project/noteable/issue/1/notes'
+ )
+
+ result = described_class.match(env)
+
+ expect(result).to be_present
+ expect(result.name).to eq 'issue_notes'
+ end
+
+ it 'matches issue title endpoint' do
+ env = build_env(
+ '/my-group/my-project/issues/123/rendered_title'
+ )
+
+ result = described_class.match(env)
+
+ expect(result).to be_present
+ expect(result.name).to eq 'issue_title'
+ end
+
+ it 'matches project pipelines endpoint' do
+ env = build_env(
+ '/my-group/my-project/pipelines.json'
+ )
+
+ result = described_class.match(env)
+
+ expect(result).to be_present
+ expect(result.name).to eq 'project_pipelines'
+ end
+
+ it 'matches commit pipelines endpoint' do
+ env = build_env(
+ '/my-group/my-project/commit/aa8260d253a53f73f6c26c734c72fdd600f6e6d4/pipelines.json'
+ )
+
+ result = described_class.match(env)
+
+ expect(result).to be_present
+ expect(result.name).to eq 'commit_pipelines'
+ end
+
+ it 'matches new merge request pipelines endpoint' do
+ env = build_env(
+ '/my-group/my-project/merge_requests/new.json'
+ )
+
+ result = described_class.match(env)
+
+ expect(result).to be_present
+ expect(result.name).to eq 'new_merge_request_pipelines'
+ end
+
+ it 'matches merge request pipelines endpoint' do
+ env = build_env(
+ '/my-group/my-project/merge_requests/234/pipelines.json'
+ )
+
+ result = described_class.match(env)
+
+ expect(result).to be_present
+ expect(result.name).to eq 'merge_request_pipelines'
+ end
+
+ it 'does not match blob with confusing name' do
+ env = build_env(
+ '/my-group/my-project/blob/master/pipelines.json'
+ )
+
+ result = described_class.match(env)
+
+ expect(result).to be_blank
+ end
+
+ def build_env(path)
+ { 'PATH_INFO' => path }
+ end
+end
diff --git a/spec/lib/gitlab/git/env_spec.rb b/spec/lib/gitlab/git/env_spec.rb
new file mode 100644
index 00000000000..d9df99bfe05
--- /dev/null
+++ b/spec/lib/gitlab/git/env_spec.rb
@@ -0,0 +1,102 @@
+require 'spec_helper'
+
+describe Gitlab::Git::Env do
+ describe "#set" do
+ context 'with RequestStore.store disabled' do
+ before do
+ allow(RequestStore).to receive(:active?).and_return(false)
+ end
+
+ it 'does not store anything' do
+ described_class.set(GIT_OBJECT_DIRECTORY: 'foo')
+
+ expect(described_class.all).to be_empty
+ end
+ end
+
+ context 'with RequestStore.store enabled' do
+ before do
+ allow(RequestStore).to receive(:active?).and_return(true)
+ end
+
+ it 'whitelist some `GIT_*` variables and stores them using RequestStore' do
+ described_class.set(
+ GIT_OBJECT_DIRECTORY: 'foo',
+ GIT_ALTERNATE_OBJECT_DIRECTORIES: 'bar',
+ GIT_EXEC_PATH: 'baz',
+ PATH: '~/.bin:/bin')
+
+ expect(described_class[:GIT_OBJECT_DIRECTORY]).to eq('foo')
+ expect(described_class[:GIT_ALTERNATE_OBJECT_DIRECTORIES]).to eq('bar')
+ expect(described_class[:GIT_EXEC_PATH]).to be_nil
+ expect(described_class[:bar]).to be_nil
+ end
+ end
+ end
+
+ describe "#all" do
+ context 'with RequestStore.store enabled' do
+ before do
+ allow(RequestStore).to receive(:active?).and_return(true)
+ described_class.set(
+ GIT_OBJECT_DIRECTORY: 'foo',
+ GIT_ALTERNATE_OBJECT_DIRECTORIES: 'bar')
+ end
+
+ it 'returns an env hash' do
+ expect(described_class.all).to eq({
+ 'GIT_OBJECT_DIRECTORY' => 'foo',
+ 'GIT_ALTERNATE_OBJECT_DIRECTORIES' => 'bar'
+ })
+ end
+ end
+ end
+
+ describe "#[]" do
+ context 'with RequestStore.store enabled' do
+ before do
+ allow(RequestStore).to receive(:active?).and_return(true)
+ end
+
+ before do
+ described_class.set(
+ GIT_OBJECT_DIRECTORY: 'foo',
+ GIT_ALTERNATE_OBJECT_DIRECTORIES: 'bar')
+ end
+
+ it 'returns a stored value for an existing key' do
+ expect(described_class[:GIT_OBJECT_DIRECTORY]).to eq('foo')
+ end
+
+ it 'returns nil for an non-existing key' do
+ expect(described_class[:foo]).to be_nil
+ end
+ end
+ end
+
+ describe 'thread-safety' do
+ context 'with RequestStore.store enabled' do
+ before do
+ allow(RequestStore).to receive(:active?).and_return(true)
+ described_class.set(GIT_OBJECT_DIRECTORY: 'foo')
+ end
+
+ it 'is thread-safe' do
+ another_thread = Thread.new do
+ described_class.set(GIT_OBJECT_DIRECTORY: 'bar')
+
+ Thread.stop
+ described_class[:GIT_OBJECT_DIRECTORY]
+ end
+
+ # Ensure another_thread runs first
+ sleep 0.1 until another_thread.stop?
+
+ expect(described_class[:GIT_OBJECT_DIRECTORY]).to eq('foo')
+
+ another_thread.run
+ expect(another_thread.value).to eq('bar')
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index 7e8bb796e03..3d6d7292b42 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -24,18 +24,49 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
- context 'with gitaly enabled' do
- before { stub_gitaly }
+ # TODO: Uncomment when feature is reenabled
+ # context 'with gitaly enabled' do
+ # before { stub_gitaly }
+ #
+ # it 'gets the branch name from GitalyClient' do
+ # expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:default_branch_name)
+ # repository.root_ref
+ # end
+ #
+ # it 'wraps GRPC exceptions' do
+ # expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:default_branch_name).
+ # and_raise(GRPC::Unknown)
+ # expect { repository.root_ref }.to raise_error(Gitlab::Git::CommandError)
+ # end
+ # end
+ end
- it 'gets the branch name from GitalyClient' do
- expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:default_branch_name)
- repository.root_ref
+ describe "#rugged" do
+ context 'with no Git env stored' do
+ before do
+ expect(Gitlab::Git::Env).to receive(:all).and_return({})
+ end
+
+ it "whitelist some variables and pass them via the alternates keyword argument" do
+ expect(Rugged::Repository).to receive(:new).with(repository.path, alternates: [])
+
+ repository.rugged
end
+ end
- it 'wraps GRPC exceptions' do
- expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:default_branch_name).
- and_raise(GRPC::Unknown)
- expect { repository.root_ref }.to raise_error(Gitlab::Git::CommandError)
+ context 'with some Git env stored' do
+ before do
+ expect(Gitlab::Git::Env).to receive(:all).and_return({
+ 'GIT_OBJECT_DIRECTORY' => 'foo',
+ 'GIT_ALTERNATE_OBJECT_DIRECTORIES' => 'bar',
+ 'GIT_OTHER' => 'another_env'
+ })
+ end
+
+ it "whitelist some variables and pass them via the alternates keyword argument" do
+ expect(Rugged::Repository).to receive(:new).with(repository.path, alternates: %w[foo bar])
+
+ repository.rugged
end
end
end
@@ -82,20 +113,21 @@ describe Gitlab::Git::Repository, seed_helper: true do
it { is_expected.to include("master") }
it { is_expected.not_to include("branch-from-space") }
- context 'with gitaly enabled' do
- before { stub_gitaly }
-
- it 'gets the branch names from GitalyClient' do
- expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:branch_names)
- subject
- end
-
- it 'wraps GRPC exceptions' do
- expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:branch_names).
- and_raise(GRPC::Unknown)
- expect { subject }.to raise_error(Gitlab::Git::CommandError)
- end
- end
+ # TODO: Uncomment when feature is reenabled
+ # context 'with gitaly enabled' do
+ # before { stub_gitaly }
+ #
+ # it 'gets the branch names from GitalyClient' do
+ # expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:branch_names)
+ # subject
+ # end
+ #
+ # it 'wraps GRPC exceptions' do
+ # expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:branch_names).
+ # and_raise(GRPC::Unknown)
+ # expect { subject }.to raise_error(Gitlab::Git::CommandError)
+ # end
+ # end
end
describe '#tag_names' do
@@ -113,20 +145,21 @@ describe Gitlab::Git::Repository, seed_helper: true do
it { is_expected.to include("v1.0.0") }
it { is_expected.not_to include("v5.0.0") }
- context 'with gitaly enabled' do
- before { stub_gitaly }
-
- it 'gets the tag names from GitalyClient' do
- expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:tag_names)
- subject
- end
-
- it 'wraps GRPC exceptions' do
- expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:tag_names).
- and_raise(GRPC::Unknown)
- expect { subject }.to raise_error(Gitlab::Git::CommandError)
- end
- end
+ # TODO: Uncomment when feature is reenabled
+ # context 'with gitaly enabled' do
+ # before { stub_gitaly }
+ #
+ # it 'gets the tag names from GitalyClient' do
+ # expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:tag_names)
+ # subject
+ # end
+ #
+ # it 'wraps GRPC exceptions' do
+ # expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:tag_names).
+ # and_raise(GRPC::Unknown)
+ # expect { subject }.to raise_error(Gitlab::Git::CommandError)
+ # end
+ # end
end
shared_examples 'archive check' do |extenstion|
diff --git a/spec/lib/gitlab/git/rev_list_spec.rb b/spec/lib/gitlab/git/rev_list_spec.rb
index d48629a296d..78894ba9409 100644
--- a/spec/lib/gitlab/git/rev_list_spec.rb
+++ b/spec/lib/gitlab/git/rev_list_spec.rb
@@ -3,58 +3,54 @@ require 'spec_helper'
describe Gitlab::Git::RevList, lib: true do
let(:project) { create(:project, :repository) }
- context "validations" do
- described_class::ALLOWED_VARIABLES.each do |var|
- context var do
- it "accepts values starting with the project repo path" do
- env = { var => "#{project.repository.path_to_repo}/objects" }
- rev_list = described_class.new('oldrev', 'newrev', project: project, env: env)
-
- expect(rev_list).to be_valid
- end
-
- it "rejects values starting not with the project repo path" do
- env = { var => "/some/other/path" }
- rev_list = described_class.new('oldrev', 'newrev', project: project, env: env)
-
- expect(rev_list).not_to be_valid
- end
-
- it "rejects values containing the project repo path but not starting with it" do
- env = { var => "/some/other/path/#{project.repository.path_to_repo}" }
- rev_list = described_class.new('oldrev', 'newrev', project: project, env: env)
-
- expect(rev_list).not_to be_valid
- end
-
- it "ignores nil values" do
- env = { var => nil }
- rev_list = described_class.new('oldrev', 'newrev', project: project, env: env)
-
- expect(rev_list).to be_valid
- end
- end
- end
+ before do
+ expect(Gitlab::Git::Env).to receive(:all).and_return({
+ GIT_OBJECT_DIRECTORY: 'foo',
+ GIT_ALTERNATE_OBJECT_DIRECTORIES: 'bar'
+ })
end
- context "#execute" do
- let(:env) { { "GIT_OBJECT_DIRECTORY" => project.repository.path_to_repo } }
- let(:rev_list) { Gitlab::Git::RevList.new('oldrev', 'newrev', project: project, env: env) }
-
- it "calls out to `popen` without environment variables if the record is invalid" do
- allow(rev_list).to receive(:valid?).and_return(false)
-
- expect(Open3).to receive(:popen3).with(hash_excluding(env), any_args)
-
- rev_list.execute
+ context "#new_refs" do
+ let(:rev_list) { Gitlab::Git::RevList.new(newrev: 'newrev', path_to_repo: project.repository.path_to_repo) }
+
+ it 'calls out to `popen`' do
+ expect(Gitlab::Popen).to receive(:popen).with([
+ Gitlab.config.git.bin_path,
+ "--git-dir=#{project.repository.path_to_repo}",
+ 'rev-list',
+ 'newrev',
+ '--not',
+ '--all'
+ ],
+ nil,
+ {
+ 'GIT_OBJECT_DIRECTORY' => 'foo',
+ 'GIT_ALTERNATE_OBJECT_DIRECTORIES' => 'bar'
+ }).and_return(["sha1\nsha2", 0])
+
+ expect(rev_list.new_refs).to eq(%w[sha1 sha2])
end
+ end
- it "calls out to `popen` with environment variables if the record is valid" do
- allow(rev_list).to receive(:valid?).and_return(true)
-
- expect(Open3).to receive(:popen3).with(hash_including(env), any_args)
-
- rev_list.execute
+ context "#missed_ref" do
+ let(:rev_list) { Gitlab::Git::RevList.new(oldrev: 'oldrev', newrev: 'newrev', path_to_repo: project.repository.path_to_repo) }
+
+ it 'calls out to `popen`' do
+ expect(Gitlab::Popen).to receive(:popen).with([
+ Gitlab.config.git.bin_path,
+ "--git-dir=#{project.repository.path_to_repo}",
+ 'rev-list',
+ '--max-count=1',
+ 'oldrev',
+ '^newrev'
+ ],
+ nil,
+ {
+ 'GIT_OBJECT_DIRECTORY' => 'foo',
+ 'GIT_ALTERNATE_OBJECT_DIRECTORIES' => 'bar'
+ }).and_return(["sha1\nsha2", 0])
+
+ expect(rev_list.missed_ref).to eq(%w[sha1 sha2])
end
end
end
diff --git a/spec/migrations/migrate_user_project_view_spec.rb b/spec/migrations/migrate_user_project_view_spec.rb
new file mode 100644
index 00000000000..dacaa834aa9
--- /dev/null
+++ b/spec/migrations/migrate_user_project_view_spec.rb
@@ -0,0 +1,17 @@
+# encoding: utf-8
+
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20170406142253_migrate_user_project_view.rb')
+
+describe MigrateUserProjectView do
+ let(:migration) { described_class.new }
+ let!(:user) { create(:user, project_view: 'readme') }
+
+ describe '#up' do
+ it 'updates project view setting with new value' do
+ migration.up
+
+ expect(user.reload.project_view).to eq('files')
+ end
+ end
+end
diff --git a/spec/migrations/schema_spec.rb b/spec/migrations/schema_spec.rb
new file mode 100644
index 00000000000..e132529d8d8
--- /dev/null
+++ b/spec/migrations/schema_spec.rb
@@ -0,0 +1,23 @@
+require 'spec_helper'
+
+# Check consistency of db/schema.rb version, migrations' timestamps, and the latest migration timestamp
+# stored in the database's schema_migrations table.
+
+describe ActiveRecord::Schema do
+ let(:latest_migration_timestamp) do
+ migrations = Dir[Rails.root.join('db', 'migrate', '*'), Rails.root.join('db', 'post_migrate', '*')]
+ migrations.map { |migration| File.basename(migration).split('_').first.to_i }.max
+ end
+
+ it '> schema version equals last migration timestamp' do
+ defined_schema_version = File.open(Rails.root.join('db', 'schema.rb')) do |file|
+ file.find { |line| line =~ /ActiveRecord::Schema.define/ }
+ end.match(/(\d+)/)[0].to_i
+
+ expect(defined_schema_version).to eq(latest_migration_timestamp)
+ end
+
+ it '> schema version should equal the latest migration timestamp stored in schema_migrations table' do
+ expect(latest_migration_timestamp).to eq(ActiveRecord::Migrator.current_version.to_i)
+ end
+end
diff --git a/spec/models/blob_spec.rb b/spec/models/blob_spec.rb
index 0f29766db41..e5dd57fc4bb 100644
--- a/spec/models/blob_spec.rb
+++ b/spec/models/blob_spec.rb
@@ -55,13 +55,13 @@ describe Blob do
describe '#pdf?' do
it 'is falsey when file extension is not .pdf' do
- git_blob = double(name: 'git_blob.txt')
+ git_blob = Gitlab::Git::Blob.new(name: 'git_blob.txt')
expect(described_class.decorate(git_blob)).not_to be_pdf
end
it 'is truthy when file extension is .pdf' do
- git_blob = double(name: 'git_blob.pdf')
+ git_blob = Gitlab::Git::Blob.new(name: 'git_blob.pdf')
expect(described_class.decorate(git_blob)).to be_pdf
end
@@ -140,7 +140,7 @@ describe Blob do
stl?: false
)
- described_class.decorate(double).tap do |blob|
+ described_class.decorate(Gitlab::Git::Blob.new({})).tap do |blob|
allow(blob).to receive_messages(overrides)
end
end
@@ -158,7 +158,7 @@ describe Blob do
it 'handles SVGs' do
blob = stubbed_blob(text?: true, svg?: true)
- expect(blob.to_partial_path(project)).to eq 'image'
+ expect(blob.to_partial_path(project)).to eq 'svg'
end
it 'handles images' do
@@ -167,7 +167,7 @@ describe Blob do
end
it 'handles text' do
- blob = stubbed_blob(text?: true)
+ blob = stubbed_blob(text?: true, name: 'test.txt')
expect(blob.to_partial_path(project)).to eq 'text'
end
diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb
index 980a1b70ef5..ce31c8ed94c 100644
--- a/spec/models/commit_spec.rb
+++ b/spec/models/commit_spec.rb
@@ -389,31 +389,32 @@ eos
end
end
- describe '#raw_diffs' do
- context 'Gitaly commit_raw_diffs feature enabled' do
- before do
- allow(Gitlab::GitalyClient).to receive(:feature_enabled?).with(:commit_raw_diffs).and_return(true)
- end
-
- context 'when a truthy deltas_only is not passed to args' do
- it 'fetches diffs from Gitaly server' do
- expect(Gitlab::GitalyClient::Commit).to receive(:diff_from_parent).
- with(commit)
-
- commit.raw_diffs
- end
- end
-
- context 'when a truthy deltas_only is passed to args' do
- it 'fetches diffs using Rugged' do
- opts = { deltas_only: true }
-
- expect(Gitlab::GitalyClient::Commit).not_to receive(:diff_from_parent)
- expect(commit.raw).to receive(:diffs).with(opts)
-
- commit.raw_diffs(opts)
- end
- end
- end
- end
+ # describe '#raw_diffs' do
+ # TODO: Uncomment when feature is reenabled
+ # context 'Gitaly commit_raw_diffs feature enabled' do
+ # before do
+ # allow(Gitlab::GitalyClient).to receive(:feature_enabled?).with(:commit_raw_diffs).and_return(true)
+ # end
+ #
+ # context 'when a truthy deltas_only is not passed to args' do
+ # it 'fetches diffs from Gitaly server' do
+ # expect(Gitlab::GitalyClient::Commit).to receive(:diff_from_parent).
+ # with(commit)
+ #
+ # commit.raw_diffs
+ # end
+ # end
+ #
+ # context 'when a truthy deltas_only is passed to args' do
+ # it 'fetches diffs using Rugged' do
+ # opts = { deltas_only: true }
+ #
+ # expect(Gitlab::GitalyClient::Commit).not_to receive(:diff_from_parent)
+ # expect(commit.raw).to receive(:diffs).with(opts)
+ #
+ # commit.raw_diffs(opts)
+ # end
+ # end
+ # end
+ # end
end
diff --git a/spec/models/concerns/ignorable_column_spec.rb b/spec/models/concerns/ignorable_column_spec.rb
new file mode 100644
index 00000000000..dba9fe43327
--- /dev/null
+++ b/spec/models/concerns/ignorable_column_spec.rb
@@ -0,0 +1,38 @@
+require 'spec_helper'
+
+describe IgnorableColumn do
+ let :base_class do
+ Class.new do
+ def self.columns
+ # This method does not have access to "double"
+ [Struct.new(:name).new('id'), Struct.new(:name).new('title')]
+ end
+ end
+ end
+
+ let :model do
+ Class.new(base_class) do
+ include IgnorableColumn
+ end
+ end
+
+ describe '.columns' do
+ it 'returns the columns, excluding the ignored ones' do
+ model.ignore_column(:title)
+
+ expect(model.columns.map(&:name)).to eq(%w(id))
+ end
+ end
+
+ describe '.ignored_columns' do
+ it 'returns a Set' do
+ expect(model.ignored_columns).to be_an_instance_of(Set)
+ end
+
+ it 'returns the names of the ignored columns' do
+ model.ignore_column(:title)
+
+ expect(model.ignored_columns).to eq(Set.new(%w(title)))
+ end
+ end
+end
diff --git a/spec/models/container_repository_spec.rb b/spec/models/container_repository_spec.rb
index f7ee0b57072..6d6c9f2adfc 100644
--- a/spec/models/container_repository_spec.rb
+++ b/spec/models/container_repository_spec.rb
@@ -4,7 +4,7 @@ describe ContainerRepository do
let(:group) { create(:group, name: 'group') }
let(:project) { create(:project, path: 'test', group: group) }
- let(:container_repository) do
+ let(:repository) do
create(:container_repository, name: 'my_image', project: project)
end
@@ -23,48 +23,48 @@ describe ContainerRepository do
describe 'associations' do
it 'belongs to the project' do
- expect(container_repository).to belong_to(:project)
+ expect(repository).to belong_to(:project)
end
end
describe '#tag' do
it 'has a test tag' do
- expect(container_repository.tag('test')).not_to be_nil
+ expect(repository.tag('test')).not_to be_nil
end
end
describe '#path' do
it 'returns a full path to the repository' do
- expect(container_repository.path).to eq('group/test/my_image')
+ expect(repository.path).to eq('group/test/my_image')
end
end
describe '#manifest' do
- subject { container_repository.manifest }
-
- it { is_expected.not_to be_nil }
+ it 'returns non-empty manifest' do
+ expect(repository.manifest).not_to be_nil
+ end
end
describe '#valid?' do
- subject { container_repository.valid? }
-
- it { is_expected.to be_truthy }
+ it 'is a valid repository' do
+ expect(repository).to be_valid
+ end
end
describe '#tags' do
- subject { container_repository.tags }
-
- it { is_expected.not_to be_empty }
+ it 'returns non-empty tags list' do
+ expect(repository.tags).not_to be_empty
+ end
end
describe '#has_tags?' do
it 'has tags' do
- expect(container_repository).to have_tags
+ expect(repository).to have_tags
end
end
describe '#delete_tags!' do
- let(:container_repository) do
+ let(:repository) do
create(:container_repository, name: 'my_image',
tags: %w[latest rc1],
project: project)
@@ -72,21 +72,36 @@ describe ContainerRepository do
context 'when action succeeds' do
it 'returns status that indicates success' do
- expect(container_repository.client)
+ expect(repository.client)
.to receive(:delete_repository_tag)
.and_return(true)
- expect(container_repository.delete_tags!).to be_truthy
+ expect(repository.delete_tags!).to be_truthy
end
end
context 'when action fails' do
it 'returns status that indicates failure' do
- expect(container_repository.client)
+ expect(repository.client)
.to receive(:delete_repository_tag)
.and_return(false)
- expect(container_repository.delete_tags!).to be_falsey
+ expect(repository.delete_tags!).to be_falsey
+ end
+ end
+ end
+
+ describe '#location' do
+ context 'when registry is running on a custom port' do
+ before do
+ stub_container_registry_config(enabled: true,
+ api_url: 'http://registry.gitlab:5000',
+ host_port: 'registry.gitlab:5000')
+ end
+
+ it 'returns a full location of the repository' do
+ expect(repository.location)
+ .to eq 'registry.gitlab:5000/group/test/my_image'
end
end
end
@@ -102,7 +117,7 @@ describe ContainerRepository do
context 'when repository is not a root repository' do
it 'returns false' do
- expect(container_repository).not_to be_root_repository
+ expect(repository).not_to be_root_repository
end
end
end
diff --git a/spec/models/diff_note_spec.rb b/spec/models/diff_note_spec.rb
index fb80b74b226..f32b6b99b3d 100644
--- a/spec/models/diff_note_spec.rb
+++ b/spec/models/diff_note_spec.rb
@@ -155,6 +155,23 @@ describe DiffNote, models: true do
end
end
+ describe '#latest_merge_request_diff' do
+ context 'when active' do
+ it 'returns the current merge request diff' do
+ expect(subject.latest_merge_request_diff).to eq(merge_request.merge_request_diff)
+ end
+ end
+
+ context 'when outdated' do
+ let!(:old_merge_request_diff) { merge_request.merge_request_diff }
+ let!(:new_merge_request_diff) { merge_request.merge_request_diffs.create(diff_refs: commit.diff_refs) }
+
+ it 'returns the latest merge request diff that this diff note applied to' do
+ expect(subject.latest_merge_request_diff).to eq(old_merge_request_diff)
+ end
+ end
+ end
+
describe "creation" do
describe "updating of position" do
context "when noteable is a commit" do
diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb
index af7753caba6..070716e859a 100644
--- a/spec/models/environment_spec.rb
+++ b/spec/models/environment_spec.rb
@@ -110,17 +110,18 @@ describe Environment, models: true do
end
end
- context 'Gitaly find_ref_name feature enabled' do
- before do
- allow(Gitlab::GitalyClient).to receive(:feature_enabled?).with(:find_ref_name).and_return(true)
- end
-
- it 'calls GitalyClient' do
- expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:find_ref_name)
-
- environment.first_deployment_for(commit)
- end
- end
+ # TODO: Uncomment when feature is reenabled
+ # context 'Gitaly find_ref_name feature enabled' do
+ # before do
+ # allow(Gitlab::GitalyClient).to receive(:feature_enabled?).with(:find_ref_name).and_return(true)
+ # end
+ #
+ # it 'calls GitalyClient' do
+ # expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:find_ref_name)
+ #
+ # environment.first_deployment_for(commit)
+ # end
+ # end
end
describe '#environment_type' do
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index 4bdd46a581d..d057c9cf6e9 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -134,15 +134,6 @@ describe Issue, models: true do
end
end
- describe '#is_being_reassigned?' do
- it 'returns issues assigned to user' do
- user = create(:user)
- create_list(:issue, 2, assignee: user)
-
- expect(Issue.open_for(user).count).to eq 2
- end
- end
-
describe '#closed_by_merge_requests' do
let(:project) { create(:project, :repository) }
let(:issue) { create(:issue, project: project)}
diff --git a/spec/models/label_spec.rb b/spec/models/label_spec.rb
index a9139f7d4ab..80ca19acdda 100644
--- a/spec/models/label_spec.rb
+++ b/spec/models/label_spec.rb
@@ -42,11 +42,27 @@ describe Label, models: true do
end
end
+ describe '#color' do
+ it 'strips color' do
+ label = described_class.new(color: ' #abcdef ')
+ label.valid?
+
+ expect(label.color).to eq('#abcdef')
+ end
+ end
+
describe '#title' do
it 'sanitizes title' do
label = described_class.new(title: '<b>foo & bar?</b>')
expect(label.title).to eq('foo & bar?')
end
+
+ it 'strips title' do
+ label = described_class.new(title: ' label ')
+ label.valid?
+
+ expect(label.title).to eq('label')
+ end
end
describe 'priorization' do
diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb
index 3c4bf3f4ddb..557ea97b008 100644
--- a/spec/models/note_spec.rb
+++ b/spec/models/note_spec.rb
@@ -622,12 +622,22 @@ describe Note, models: true do
describe 'expiring ETag cache' do
let(:note) { build(:note_on_issue) }
- it "expires cache for note's issue when note is saved" do
+ def expect_expiration(note)
expect_any_instance_of(Gitlab::EtagCaching::Store)
.to receive(:touch)
.with("/#{note.project.namespace.to_param}/#{note.project.to_param}/noteable/issue/#{note.noteable.id}/notes")
+ end
+
+ it "expires cache for note's issue when note is saved" do
+ expect_expiration(note)
note.save!
end
+
+ it "expires cache for note's issue when note is destroyed" do
+ expect_expiration(note)
+
+ note.destroy!
+ end
end
end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index d805e65b3c6..5e5c2b016b6 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -1283,8 +1283,6 @@ describe Repository, models: true do
describe '#after_import' do
it 'flushes and builds the cache' do
expect(repository).to receive(:expire_content_cache)
- expect(repository).to receive(:expire_tags_cache)
- expect(repository).to receive(:expire_branches_cache)
repository.after_import
end
@@ -1831,16 +1829,17 @@ describe Repository, models: true do
end
end
- describe '#is_ancestor?' do
- context 'Gitaly is_ancestor feature enabled' do
- it 'asks Gitaly server if it\'s an ancestor' do
- commit = repository.commit
- allow(Gitlab::GitalyClient).to receive(:feature_enabled?).with(:is_ancestor).and_return(true)
- expect(Gitlab::GitalyClient::Commit).to receive(:is_ancestor).
- with(repository.raw_repository, commit.id, commit.id).and_return(true)
-
- expect(repository.is_ancestor?(commit.id, commit.id)).to be true
- end
- end
- end
+ # TODO: Uncomment when feature is reenabled
+ # describe '#is_ancestor?' do
+ # context 'Gitaly is_ancestor feature enabled' do
+ # it 'asks Gitaly server if it\'s an ancestor' do
+ # commit = repository.commit
+ # allow(Gitlab::GitalyClient).to receive(:feature_enabled?).with(:is_ancestor).and_return(true)
+ # expect(Gitlab::GitalyClient::Commit).to receive(:is_ancestor).
+ # with(repository.raw_repository, commit.id, commit.id).and_return(true)
+ #
+ # expect(repository.is_ancestor?(commit.id, commit.id)).to be true
+ # end
+ # end
+ # end
end
diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb
index 5c34ff04152..2077c14ff7a 100644
--- a/spec/policies/group_policy_spec.rb
+++ b/spec/policies/group_policy_spec.rb
@@ -22,7 +22,8 @@ describe GroupPolicy, models: true do
:admin_group,
:admin_namespace,
:admin_group_member,
- :change_visibility_level
+ :change_visibility_level,
+ :create_subgroup
]
end
diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb
index eed45d37444..4be67df5a00 100644
--- a/spec/requests/api/internal_spec.rb
+++ b/spec/requests/api/internal_spec.rb
@@ -153,6 +153,22 @@ describe API::Internal, api: true do
project.team << [user, :developer]
end
+ context 'with env passed as a JSON' do
+ it 'sets env in RequestStore' do
+ expect(Gitlab::Git::Env).to receive(:set).with({
+ 'GIT_OBJECT_DIRECTORY' => 'foo',
+ 'GIT_ALTERNATE_OBJECT_DIRECTORIES' => 'bar'
+ })
+
+ push(key, project.wiki, env: {
+ GIT_OBJECT_DIRECTORY: 'foo',
+ GIT_ALTERNATE_OBJECT_DIRECTORIES: 'bar'
+ }.to_json)
+
+ expect(response).to have_http_status(200)
+ end
+ end
+
context "git push with project.wiki" do
it 'responds with success' do
push(key, project.wiki)
@@ -463,7 +479,7 @@ describe API::Internal, api: true do
)
end
- def push(key, project, protocol = 'ssh')
+ def push(key, project, protocol = 'ssh', env: nil)
post(
api("/internal/allowed"),
changes: 'd14d6c0abdd253381df51a723d58691b2ee1ab08 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/master',
@@ -471,7 +487,8 @@ describe API::Internal, api: true do
project: project.repository.path_to_repo,
action: 'git-receive-pack',
secret_token: secret_token,
- protocol: protocol
+ protocol: protocol,
+ env: env
)
end
diff --git a/spec/requests/api/project_hooks_spec.rb b/spec/requests/api/project_hooks_spec.rb
index b1f8c249092..b1603233f9e 100644
--- a/spec/requests/api/project_hooks_spec.rb
+++ b/spec/requests/api/project_hooks_spec.rb
@@ -22,8 +22,8 @@ describe API::ProjectHooks, 'ProjectHooks', api: true do
context "authorized user" do
it "returns project hooks" do
get api("/projects/#{project.id}/hooks", user)
- expect(response).to have_http_status(200)
+ expect(response).to have_http_status(200)
expect(json_response).to be_an Array
expect(response).to include_pagination_headers
expect(json_response.count).to eq(1)
@@ -43,6 +43,7 @@ describe API::ProjectHooks, 'ProjectHooks', api: true do
context "unauthorized user" do
it "does not access project hooks" do
get api("/projects/#{project.id}/hooks", user3)
+
expect(response).to have_http_status(403)
end
end
@@ -52,6 +53,7 @@ describe API::ProjectHooks, 'ProjectHooks', api: true do
context "authorized user" do
it "returns a project hook" do
get api("/projects/#{project.id}/hooks/#{hook.id}", user)
+
expect(response).to have_http_status(200)
expect(json_response['url']).to eq(hook.url)
expect(json_response['issues_events']).to eq(hook.issues_events)
@@ -67,6 +69,7 @@ describe API::ProjectHooks, 'ProjectHooks', api: true do
it "returns a 404 error if hook id is not available" do
get api("/projects/#{project.id}/hooks/1234", user)
+
expect(response).to have_http_status(404)
end
end
@@ -88,7 +91,8 @@ describe API::ProjectHooks, 'ProjectHooks', api: true do
it "adds hook to project" do
expect do
post api("/projects/#{project.id}/hooks", user),
- url: "http://example.com", issues_events: true, wiki_page_events: true
+ url: "http://example.com", issues_events: true, wiki_page_events: true,
+ job_events: true
end.to change {project.hooks.count}.by(1)
expect(response).to have_http_status(201)
@@ -98,7 +102,7 @@ describe API::ProjectHooks, 'ProjectHooks', api: true do
expect(json_response['merge_requests_events']).to eq(false)
expect(json_response['tag_push_events']).to eq(false)
expect(json_response['note_events']).to eq(false)
- expect(json_response['job_events']).to eq(false)
+ expect(json_response['job_events']).to eq(true)
expect(json_response['pipeline_events']).to eq(false)
expect(json_response['wiki_page_events']).to eq(true)
expect(json_response['enable_ssl_verification']).to eq(true)
@@ -136,7 +140,8 @@ describe API::ProjectHooks, 'ProjectHooks', api: true do
describe "PUT /projects/:id/hooks/:hook_id" do
it "updates an existing project hook" do
put api("/projects/#{project.id}/hooks/#{hook.id}", user),
- url: 'http://example.org', push_events: false
+ url: 'http://example.org', push_events: false, job_events: true
+
expect(response).to have_http_status(200)
expect(json_response['url']).to eq('http://example.org')
expect(json_response['issues_events']).to eq(hook.issues_events)
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 2e291eb3cea..74bc4847247 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -1076,6 +1076,13 @@ describe API::Projects, :api do
before { project_member3 }
before { project_member2 }
+ it 'returns 400 when nothing sent' do
+ project_param = {}
+ put api("/projects/#{project.id}", user), project_param
+ expect(response).to have_http_status(400)
+ expect(json_response['error']).to match('at least one parameter must be provided')
+ end
+
context 'when unauthenticated' do
it 'returns authentication error' do
project_param = { name: 'bar' }
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index 617c8eaf3d5..989fd90cda9 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -1181,6 +1181,22 @@ describe NotificationService, services: true do
should_not_email(@u_disabled)
end
end
+
+ describe '#project_exported' do
+ it do
+ notification.project_exported(project, @u_disabled)
+
+ should_only_email(@u_disabled)
+ end
+ end
+
+ describe '#project_not_exported' do
+ it do
+ notification.project_not_exported(project, @u_disabled, ['error'])
+
+ should_only_email(@u_disabled)
+ end
+ end
end
describe 'GroupMember' do
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 4eb5b150af5..a3665795452 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -59,6 +59,10 @@ RSpec.configure do |config|
TestEnv.init
end
+ config.after(:suite) do
+ TestEnv.cleanup
+ end
+
if ENV['CI']
# Retry only on feature specs that use JS
config.around :each, :js do |ex|
diff --git a/spec/features/discussion_comments_spec.rb b/spec/support/features/discussion_comments_shared_example.rb
index ae778118c5c..1a061ef069e 100644
--- a/spec/features/discussion_comments_spec.rb
+++ b/spec/support/features/discussion_comments_shared_example.rb
@@ -1,5 +1,3 @@
-require 'spec_helper'
-
shared_examples 'discussion comments' do |resource_name|
let(:form_selector) { '.js-main-target-form' }
let(:dropdown_selector) { "#{form_selector} .comment-type-dropdown" }
@@ -9,11 +7,9 @@ shared_examples 'discussion comments' do |resource_name|
let(:close_selector) { "#{form_selector} .btn-comment-and-close" }
let(:comments_selector) { '.timeline > .note.timeline-entry' }
- it 'should show a comment type toggle' do
+ it 'clicking "Comment" will post a comment' do
expect(page).to have_selector toggle_selector
- end
- it 'clicking "Comment" will post a comment' do
find("#{form_selector} .note-textarea").send_keys('a')
find(submit_selector).click
@@ -49,44 +45,29 @@ shared_examples 'discussion comments' do |resource_name|
find(toggle_selector).click
end
- it 'opens a comment type dropdown with "Comment" and "Start discussion"' do
+ it 'has a "Comment" item (selected by default) and "Start discussion" item' do
expect(page).to have_selector menu_selector
- end
-
- it 'has a "Comment" item' do
- menu = find(menu_selector)
-
- expect(menu).to have_content 'Comment'
- expect(menu).to have_content "Add a general comment to this #{resource_name}."
- end
- it 'has a "Start discussion" item' do
- menu = find(menu_selector)
-
- expect(menu).to have_content 'Start discussion'
- expect(menu).to have_content "Discuss a specific suggestion or question#{' that needs to be resolved' if resource_name == 'merge request'}."
- end
-
- it 'has the "Comment" item selected by default' do
find("#{menu_selector} li", match: :first)
items = all("#{menu_selector} li")
expect(items.first).to have_content 'Comment'
+ expect(items.first).to have_content "Add a general comment to this #{resource_name}."
expect(items.first).to have_selector '.fa-check'
expect(items.first['class']).to match 'droplab-item-selected'
expect(items.last).to have_content 'Start discussion'
+ expect(items.last).to have_content "Discuss a specific suggestion or question#{' that needs to be resolved' if resource_name == 'merge request'}."
expect(items.last).not_to have_selector '.fa-check'
expect(items.last['class']).not_to match 'droplab-item-selected'
end
- it 'closes the menu when clicking the toggle' do
+ it 'closes the menu when clicking the toggle or body' do
find(toggle_selector).click
expect(page).not_to have_selector menu_selector
- end
- it 'closes the menu when clicking the body' do
+ find(toggle_selector).click
find('body').click
expect(page).not_to have_selector menu_selector
@@ -104,12 +85,10 @@ shared_examples 'discussion comments' do |resource_name|
all("#{menu_selector} li").last.click
end
- it 'updates the note_type input to "DiscussionNote"' do
- expect(find("#{form_selector} #note_type", visible: false).value).to eq('DiscussionNote')
- end
-
- it 'updates the submit button text' do
+ it 'updates the submit button text, note_type input and closes the dropdown' do
expect(find(dropdown_selector)).to have_content 'Start discussion'
+ expect(find("#{form_selector} #note_type", visible: false).value).to eq('DiscussionNote')
+ expect(page).not_to have_selector menu_selector
end
if resource_name =~ /(issue|merge request)/
@@ -124,10 +103,6 @@ shared_examples 'discussion comments' do |resource_name|
end
end
- it 'closes the dropdown' do
- expect(page).not_to have_selector menu_selector
- end
-
it 'clicking "Start discussion" will post a discussion' do
find(submit_selector).click
@@ -176,12 +151,10 @@ shared_examples 'discussion comments' do |resource_name|
find("#{menu_selector} li", match: :first).click
end
- it 'clears the note_type input"' do
- expect(find("#{form_selector} #note_type", visible: false).value).to eq('')
- end
-
- it 'updates the submit button text' do
+ it 'updates the submit button text, clears the note_type input and closes the dropdown' do
expect(find(dropdown_selector)).to have_content 'Comment'
+ expect(find("#{form_selector} #note_type", visible: false).value).to eq('')
+ expect(page).not_to have_selector menu_selector
end
if resource_name =~ /(issue|merge request)/
@@ -196,10 +169,6 @@ shared_examples 'discussion comments' do |resource_name|
end
end
- it 'closes the dropdown' do
- expect(page).not_to have_selector menu_selector
- end
-
it 'should have "Comment" selected when opening the menu' do
find(toggle_selector).click
@@ -242,54 +211,3 @@ shared_examples 'discussion comments' do |resource_name|
end
end
end
-
-describe 'Discussion Comments', :feature, :js do
- include RepoHelpers
-
- let(:user) { create(:user) }
- let(:project) { create(:project) }
-
- before do
- project.team << [user, :developer]
-
- login_as(user)
- end
-
- describe 'on a merge request' do
- let(:merge_request) { create(:merge_request, source_project: project) }
-
- before do
- visit namespace_project_merge_request_path(project.namespace, project, merge_request)
- end
-
- it_behaves_like 'discussion comments', 'merge request'
- end
-
- describe 'on an issue' do
- let(:issue) { create(:issue, project: project) }
-
- before do
- visit namespace_project_issue_path(project.namespace, project, issue)
- end
-
- it_behaves_like 'discussion comments', 'issue'
- end
-
- describe 'on an snippet' do
- let(:snippet) { create(:project_snippet, :private, project: project, author: user) }
-
- before do
- visit namespace_project_snippet_path(project.namespace, project, snippet)
- end
-
- it_behaves_like 'discussion comments', 'snippet'
- end
-
- describe 'on a commit' do
- before do
- visit namespace_project_commit_path(project.namespace, project, sample_commit.id)
- end
-
- it_behaves_like 'discussion comments', 'commit'
- end
-end
diff --git a/spec/support/gitaly.rb b/spec/support/gitaly.rb
new file mode 100644
index 00000000000..7aca902fc61
--- /dev/null
+++ b/spec/support/gitaly.rb
@@ -0,0 +1,7 @@
+if Gitlab::GitalyClient.enabled?
+ RSpec.configure do |config|
+ config.before(:each) do
+ allow(Gitlab::GitalyClient).to receive(:feature_enabled?).and_return(true)
+ end
+ end
+end
diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb
index 1b5cb71a6b0..60c2096a126 100644
--- a/spec/support/test_env.rb
+++ b/spec/support/test_env.rb
@@ -64,6 +64,8 @@ module TestEnv
# Setup GitLab shell for test instance
setup_gitlab_shell
+ setup_gitaly if Gitlab::GitalyClient.enabled?
+
# Create repository for FactoryGirl.create(:project)
setup_factory_repo
@@ -71,6 +73,10 @@ module TestEnv
setup_forked_repo
end
+ def cleanup
+ stop_gitaly
+ end
+
def disable_mailer
allow_any_instance_of(NotificationService).to receive(:mailer).
and_return(double.as_null_object)
@@ -92,7 +98,7 @@ module TestEnv
tmp_test_path = Rails.root.join('tmp', 'tests', '**')
Dir[tmp_test_path].each do |entry|
- unless File.basename(entry) =~ /\Agitlab-(shell|test|test_bare|test-fork|test-fork_bare)\z/
+ unless File.basename(entry) =~ /\A(gitaly|gitlab-(shell|test|test_bare|test-fork|test-fork_bare))\z/
FileUtils.rm_rf(entry)
end
end
@@ -110,6 +116,28 @@ module TestEnv
end
end
+ def setup_gitaly
+ socket_path = Gitlab::GitalyClient.get_address('default').sub(/\Aunix:/, '')
+ gitaly_dir = File.dirname(socket_path)
+
+ unless File.directory?(gitaly_dir) || system('rake', "gitlab:gitaly:install[#{gitaly_dir}]")
+ raise "Can't clone gitaly"
+ end
+
+ start_gitaly(gitaly_dir, socket_path)
+ end
+
+ def start_gitaly(gitaly_dir, socket_path)
+ gitaly_exec = File.join(gitaly_dir, 'gitaly')
+ @gitaly_pid = spawn({ "GITALY_SOCKET_PATH" => socket_path }, gitaly_exec, [:out, :err] => '/dev/null')
+ end
+
+ def stop_gitaly
+ return unless @gitaly_pid
+
+ Process.kill('KILL', @gitaly_pid)
+ end
+
def setup_factory_repo
setup_repo(factory_repo_path, factory_repo_path_bare, factory_repo_name,
BRANCH_SHA)
diff --git a/spec/views/projects/registry/repositories/index.html.haml_spec.rb b/spec/views/projects/registry/repositories/index.html.haml_spec.rb
new file mode 100644
index 00000000000..ceeace3dc8d
--- /dev/null
+++ b/spec/views/projects/registry/repositories/index.html.haml_spec.rb
@@ -0,0 +1,36 @@
+require 'spec_helper'
+
+describe 'projects/registry/repositories/index', :view do
+ let(:group) { create(:group, path: 'group') }
+ let(:project) { create(:empty_project, group: group, path: 'test') }
+
+ let(:repository) do
+ create(:container_repository, project: project, name: 'image')
+ end
+
+ before do
+ stub_container_registry_config(enabled: true,
+ host_port: 'registry.gitlab',
+ api_url: 'http://registry.gitlab')
+
+ stub_container_registry_tags(repository: :any, tags: [:latest])
+
+ assign(:project, project)
+ assign(:images, [repository])
+
+ allow(view).to receive(:can?).and_return(true)
+ end
+
+ it 'contains container repository path' do
+ render
+
+ expect(rendered).to have_content 'group/test/image'
+ end
+
+ it 'contains attribute for copying tag location into clipboard' do
+ render
+
+ expect(rendered).to have_css 'button[data-clipboard-text="docker pull ' \
+ 'registry.gitlab/group/test/image:latest"]'
+ end
+end
diff --git a/vendor/assets/javascripts/notebooklab.js b/vendor/assets/javascripts/notebooklab.js
index 296271205d1..601a645b655 100644
--- a/vendor/assets/javascripts/notebooklab.js
+++ b/vendor/assets/javascripts/notebooklab.js
@@ -233,22 +233,6 @@ var Component = __webpack_require__(0)(
/* cssModules */
null
)
-Component.options.__file = "/Users/phil/Projects/notebooklab/src/cells/prompt.vue"
-if (Component.esModule && Object.keys(Component.esModule).some(function (key) {return key !== "default" && key !== "__esModule"})) {console.error("named exports are not supported in *.vue files.")}
-if (Component.options.functional) {console.error("[vue-loader] prompt.vue: functional components are not supported with templates, they should use render functions.")}
-
-/* hot reload */
-if (false) {(function () {
- var hotAPI = require("vue-hot-reload-api")
- hotAPI.install(require("vue"), false)
- if (!hotAPI.compatible) return
- module.hot.accept()
- if (!module.hot.data) {
- hotAPI.createRecord("data-v-4f6bf458", Component.options)
- } else {
- hotAPI.reload("data-v-4f6bf458", Component.options)
- }
-})()}
module.exports = Component.exports
@@ -515,22 +499,6 @@ var Component = __webpack_require__(0)(
/* cssModules */
null
)
-Component.options.__file = "/Users/phil/Projects/notebooklab/src/cells/code/index.vue"
-if (Component.esModule && Object.keys(Component.esModule).some(function (key) {return key !== "default" && key !== "__esModule"})) {console.error("named exports are not supported in *.vue files.")}
-if (Component.options.functional) {console.error("[vue-loader] index.vue: functional components are not supported with templates, they should use render functions.")}
-
-/* hot reload */
-if (false) {(function () {
- var hotAPI = require("vue-hot-reload-api")
- hotAPI.install(require("vue"), false)
- if (!hotAPI.compatible) return
- module.hot.accept()
- if (!module.hot.data) {
- hotAPI.createRecord("data-v-d42105b8", Component.options)
- } else {
- hotAPI.reload("data-v-d42105b8", Component.options)
- }
-})()}
module.exports = Component.exports
@@ -553,22 +521,6 @@ var Component = __webpack_require__(0)(
/* cssModules */
null
)
-Component.options.__file = "/Users/phil/Projects/notebooklab/src/index.vue"
-if (Component.esModule && Object.keys(Component.esModule).some(function (key) {return key !== "default" && key !== "__esModule"})) {console.error("named exports are not supported in *.vue files.")}
-if (Component.options.functional) {console.error("[vue-loader] index.vue: functional components are not supported with templates, they should use render functions.")}
-
-/* hot reload */
-if (false) {(function () {
- var hotAPI = require("vue-hot-reload-api")
- hotAPI.install(require("vue"), false)
- if (!hotAPI.compatible) return
- module.hot.accept()
- if (!module.hot.data) {
- hotAPI.createRecord("data-v-4cb2b168", Component.options)
- } else {
- hotAPI.reload("data-v-4cb2b168", Component.options)
- }
-})()}
module.exports = Component.exports
@@ -630,9 +582,9 @@ exports.default = {
rawInputCode: function rawInputCode() {
if (this.cell.source) {
return this.cell.source.join('');
- } else {
- return '';
}
+
+ return '';
},
hasOutput: function hasOutput() {
return this.cell.outputs.length;
@@ -1030,13 +982,14 @@ exports.default = {
cells: []
};
- return this.notebook.worksheets.reduce(function (data, sheet) {
- data.cells = data.cells.concat(sheet.cells);
- return data;
+ return this.notebook.worksheets.reduce(function (cellData, sheet) {
+ var cellDataCopy = cellData;
+ cellDataCopy.cells = cellDataCopy.cells.concat(sheet.cells);
+ return cellDataCopy;
}, data).cells;
- } else {
- return this.notebook.cells;
}
+
+ return this.notebook.cells;
},
hasNotebook: function hasNotebook() {
return Object.keys(this.notebook).length;
@@ -3052,7 +3005,7 @@ exports = module.exports = __webpack_require__(1)(undefined);
// module
-exports.push([module.i, "\n.cell[data-v-3ac4c361] {\n flex-direction: column;\n}\n", ""]);
+exports.push([module.i, ".cell[data-v-3ac4c361]{flex-direction:column}", ""]);
// exports
@@ -3066,7 +3019,7 @@ exports = module.exports = __webpack_require__(1)(undefined);
// module
-exports.push([module.i, "\n.cell,\n.input,\n.output {\n display: flex;\n width: 100%;\n margin-bottom: 10px;\n}\n.cell pre {\n margin: 0;\n width: 100%;\n}\n", ""]);
+exports.push([module.i, ".cell,.input,.output{display:flex;width:100%;margin-bottom:10px}.cell pre{margin:0;width:100%}", ""]);
// exports
@@ -3080,7 +3033,7 @@ exports = module.exports = __webpack_require__(1)(undefined);
// module
-exports.push([module.i, "\n.prompt[data-v-4f6bf458] {\n padding: 0 10px;\n min-width: 7em;\n font-family: monospace;\n}\n", ""]);
+exports.push([module.i, ".prompt[data-v-4f6bf458]{padding:0 10px;min-width:7em;font-family:monospace}", ""]);
// exports
@@ -3094,7 +3047,7 @@ exports = module.exports = __webpack_require__(1)(undefined);
// module
-exports.push([module.i, "\n.markdown .katex {\n display: block;\n text-align: center;\n}\n", ""]);
+exports.push([module.i, ".markdown .katex{display:block;text-align:center}", ""]);
// exports
@@ -5382,22 +5335,6 @@ var Component = __webpack_require__(0)(
/* cssModules */
null
)
-Component.options.__file = "/Users/phil/Projects/notebooklab/src/cells/code.vue"
-if (Component.esModule && Object.keys(Component.esModule).some(function (key) {return key !== "default" && key !== "__esModule"})) {console.error("named exports are not supported in *.vue files.")}
-if (Component.options.functional) {console.error("[vue-loader] code.vue: functional components are not supported with templates, they should use render functions.")}
-
-/* hot reload */
-if (false) {(function () {
- var hotAPI = require("vue-hot-reload-api")
- hotAPI.install(require("vue"), false)
- if (!hotAPI.compatible) return
- module.hot.accept()
- if (!module.hot.data) {
- hotAPI.createRecord("data-v-3ac4c361", Component.options)
- } else {
- hotAPI.reload("data-v-3ac4c361", Component.options)
- }
-})()}
module.exports = Component.exports
@@ -5420,22 +5357,6 @@ var Component = __webpack_require__(0)(
/* cssModules */
null
)
-Component.options.__file = "/Users/phil/Projects/notebooklab/src/cells/markdown.vue"
-if (Component.esModule && Object.keys(Component.esModule).some(function (key) {return key !== "default" && key !== "__esModule"})) {console.error("named exports are not supported in *.vue files.")}
-if (Component.options.functional) {console.error("[vue-loader] markdown.vue: functional components are not supported with templates, they should use render functions.")}
-
-/* hot reload */
-if (false) {(function () {
- var hotAPI = require("vue-hot-reload-api")
- hotAPI.install(require("vue"), false)
- if (!hotAPI.compatible) return
- module.hot.accept()
- if (!module.hot.data) {
- hotAPI.createRecord("data-v-7342b363", Component.options)
- } else {
- hotAPI.reload("data-v-7342b363", Component.options)
- }
-})()}
module.exports = Component.exports
@@ -5454,22 +5375,6 @@ var Component = __webpack_require__(0)(
/* cssModules */
null
)
-Component.options.__file = "/Users/phil/Projects/notebooklab/src/cells/output/html.vue"
-if (Component.esModule && Object.keys(Component.esModule).some(function (key) {return key !== "default" && key !== "__esModule"})) {console.error("named exports are not supported in *.vue files.")}
-if (Component.options.functional) {console.error("[vue-loader] html.vue: functional components are not supported with templates, they should use render functions.")}
-
-/* hot reload */
-if (false) {(function () {
- var hotAPI = require("vue-hot-reload-api")
- hotAPI.install(require("vue"), false)
- if (!hotAPI.compatible) return
- module.hot.accept()
- if (!module.hot.data) {
- hotAPI.createRecord("data-v-48ada535", Component.options)
- } else {
- hotAPI.reload("data-v-48ada535", Component.options)
- }
-})()}
module.exports = Component.exports
@@ -5488,22 +5393,6 @@ var Component = __webpack_require__(0)(
/* cssModules */
null
)
-Component.options.__file = "/Users/phil/Projects/notebooklab/src/cells/output/image.vue"
-if (Component.esModule && Object.keys(Component.esModule).some(function (key) {return key !== "default" && key !== "__esModule"})) {console.error("named exports are not supported in *.vue files.")}
-if (Component.options.functional) {console.error("[vue-loader] image.vue: functional components are not supported with templates, they should use render functions.")}
-
-/* hot reload */
-if (false) {(function () {
- var hotAPI = require("vue-hot-reload-api")
- hotAPI.install(require("vue"), false)
- if (!hotAPI.compatible) return
- module.hot.accept()
- if (!module.hot.data) {
- hotAPI.createRecord("data-v-09b68c41", Component.options)
- } else {
- hotAPI.reload("data-v-09b68c41", Component.options)
- }
-})()}
module.exports = Component.exports
@@ -5522,29 +5411,13 @@ var Component = __webpack_require__(0)(
/* cssModules */
null
)
-Component.options.__file = "/Users/phil/Projects/notebooklab/src/cells/output/index.vue"
-if (Component.esModule && Object.keys(Component.esModule).some(function (key) {return key !== "default" && key !== "__esModule"})) {console.error("named exports are not supported in *.vue files.")}
-if (Component.options.functional) {console.error("[vue-loader] index.vue: functional components are not supported with templates, they should use render functions.")}
-
-/* hot reload */
-if (false) {(function () {
- var hotAPI = require("vue-hot-reload-api")
- hotAPI.install(require("vue"), false)
- if (!hotAPI.compatible) return
- module.hot.accept()
- if (!module.hot.data) {
- hotAPI.createRecord("data-v-0dec7838", Component.options)
- } else {
- hotAPI.reload("data-v-0dec7838", Component.options)
- }
-})()}
module.exports = Component.exports
/***/ }),
/* 34 */
-/***/ (function(module, exports, __webpack_require__) {
+/***/ (function(module, exports) {
module.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;
return _c('div', {
@@ -5555,17 +5428,10 @@ module.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c
}
})], 1)
},staticRenderFns: []}
-module.exports.render._withStripped = true
-if (false) {
- module.hot.accept()
- if (module.hot.data) {
- require("vue-hot-reload-api").rerender("data-v-09b68c41", module.exports)
- }
-}
/***/ }),
/* 35 */
-/***/ (function(module, exports, __webpack_require__) {
+/***/ (function(module, exports) {
module.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;
return _c(_vm.componentName, {
@@ -5579,17 +5445,10 @@ module.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c
}
})
},staticRenderFns: []}
-module.exports.render._withStripped = true
-if (false) {
- module.hot.accept()
- if (module.hot.data) {
- require("vue-hot-reload-api").rerender("data-v-0dec7838", module.exports)
- }
-}
/***/ }),
/* 36 */
-/***/ (function(module, exports, __webpack_require__) {
+/***/ (function(module, exports) {
module.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;
return _c('div', {
@@ -5609,17 +5468,10 @@ module.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c
}
}) : _vm._e()], 1)
},staticRenderFns: []}
-module.exports.render._withStripped = true
-if (false) {
- module.hot.accept()
- if (module.hot.data) {
- require("vue-hot-reload-api").rerender("data-v-3ac4c361", module.exports)
- }
-}
/***/ }),
/* 37 */
-/***/ (function(module, exports, __webpack_require__) {
+/***/ (function(module, exports) {
module.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;
return _c('div', {
@@ -5630,17 +5482,10 @@ module.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c
}
})], 1)
},staticRenderFns: []}
-module.exports.render._withStripped = true
-if (false) {
- module.hot.accept()
- if (module.hot.data) {
- require("vue-hot-reload-api").rerender("data-v-48ada535", module.exports)
- }
-}
/***/ }),
/* 38 */
-/***/ (function(module, exports, __webpack_require__) {
+/***/ (function(module, exports) {
module.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;
return (_vm.hasNotebook) ? _c('div', _vm._l((_vm.cells), function(cell, index) {
@@ -5654,34 +5499,20 @@ module.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c
})
})) : _vm._e()
},staticRenderFns: []}
-module.exports.render._withStripped = true
-if (false) {
- module.hot.accept()
- if (module.hot.data) {
- require("vue-hot-reload-api").rerender("data-v-4cb2b168", module.exports)
- }
-}
/***/ }),
/* 39 */
-/***/ (function(module, exports, __webpack_require__) {
+/***/ (function(module, exports) {
module.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;
return _c('div', {
staticClass: "prompt"
}, [(_vm.type && _vm.count) ? _c('span', [_vm._v("\n " + _vm._s(_vm.type) + " [" + _vm._s(_vm.count) + "]:\n ")]) : _vm._e()])
},staticRenderFns: []}
-module.exports.render._withStripped = true
-if (false) {
- module.hot.accept()
- if (module.hot.data) {
- require("vue-hot-reload-api").rerender("data-v-4f6bf458", module.exports)
- }
-}
/***/ }),
/* 40 */
-/***/ (function(module, exports, __webpack_require__) {
+/***/ (function(module, exports) {
module.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;
return _c('div', {
@@ -5693,17 +5524,10 @@ module.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c
}
})], 1)
},staticRenderFns: []}
-module.exports.render._withStripped = true
-if (false) {
- module.hot.accept()
- if (module.hot.data) {
- require("vue-hot-reload-api").rerender("data-v-7342b363", module.exports)
- }
-}
/***/ }),
/* 41 */
-/***/ (function(module, exports, __webpack_require__) {
+/***/ (function(module, exports) {
module.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;
return _c('div', {
@@ -5722,13 +5546,6 @@ module.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c
}
}, [_vm._v("\n ")])], 1)
},staticRenderFns: []}
-module.exports.render._withStripped = true
-if (false) {
- module.hot.accept()
- if (module.hot.data) {
- require("vue-hot-reload-api").rerender("data-v-d42105b8", module.exports)
- }
-}
/***/ }),
/* 42 */
@@ -5741,13 +5558,13 @@ var content = __webpack_require__(19);
if(typeof content === 'string') content = [[module.i, content, '']];
if(content.locals) module.exports = content.locals;
// add the styles to the DOM
-var update = __webpack_require__(3)("06fc6a9f", content, false);
+var update = __webpack_require__(3)("74a276de", content, true);
// Hot Module Replacement
if(false) {
// When the styles change, update the <style> tags
if(!content.locals) {
- module.hot.accept("!!../../node_modules/css-loader/index.js!../../node_modules/vue-loader/lib/style-compiler/index.js?{\"id\":\"data-v-3ac4c361\",\"scoped\":true,\"hasInlineConfig\":false}!../../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./code.vue", function() {
- var newContent = require("!!../../node_modules/css-loader/index.js!../../node_modules/vue-loader/lib/style-compiler/index.js?{\"id\":\"data-v-3ac4c361\",\"scoped\":true,\"hasInlineConfig\":false}!../../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./code.vue");
+ module.hot.accept("!!../../node_modules/css-loader/index.js?minimize!../../node_modules/vue-loader/lib/style-compiler/index.js?{\"id\":\"data-v-3ac4c361\",\"scoped\":true,\"hasInlineConfig\":false}!../../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./code.vue", function() {
+ var newContent = require("!!../../node_modules/css-loader/index.js?minimize!../../node_modules/vue-loader/lib/style-compiler/index.js?{\"id\":\"data-v-3ac4c361\",\"scoped\":true,\"hasInlineConfig\":false}!../../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./code.vue");
if(typeof newContent === 'string') newContent = [[module.id, newContent, '']];
update(newContent);
});
@@ -5767,13 +5584,13 @@ var content = __webpack_require__(20);
if(typeof content === 'string') content = [[module.i, content, '']];
if(content.locals) module.exports = content.locals;
// add the styles to the DOM
-var update = __webpack_require__(3)("87c28124", content, false);
+var update = __webpack_require__(3)("55f9d67b", content, true);
// Hot Module Replacement
if(false) {
// When the styles change, update the <style> tags
if(!content.locals) {
- module.hot.accept("!!../node_modules/css-loader/index.js!../node_modules/vue-loader/lib/style-compiler/index.js?{\"id\":\"data-v-4cb2b168\",\"scoped\":false,\"hasInlineConfig\":false}!../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./index.vue", function() {
- var newContent = require("!!../node_modules/css-loader/index.js!../node_modules/vue-loader/lib/style-compiler/index.js?{\"id\":\"data-v-4cb2b168\",\"scoped\":false,\"hasInlineConfig\":false}!../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./index.vue");
+ module.hot.accept("!!../node_modules/css-loader/index.js?minimize!../node_modules/vue-loader/lib/style-compiler/index.js?{\"id\":\"data-v-4cb2b168\",\"scoped\":false,\"hasInlineConfig\":false}!../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./index.vue", function() {
+ var newContent = require("!!../node_modules/css-loader/index.js?minimize!../node_modules/vue-loader/lib/style-compiler/index.js?{\"id\":\"data-v-4cb2b168\",\"scoped\":false,\"hasInlineConfig\":false}!../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./index.vue");
if(typeof newContent === 'string') newContent = [[module.id, newContent, '']];
update(newContent);
});
@@ -5793,13 +5610,13 @@ var content = __webpack_require__(21);
if(typeof content === 'string') content = [[module.i, content, '']];
if(content.locals) module.exports = content.locals;
// add the styles to the DOM
-var update = __webpack_require__(3)("5b60b003", content, false);
+var update = __webpack_require__(3)("1096aefc", content, true);
// Hot Module Replacement
if(false) {
// When the styles change, update the <style> tags
if(!content.locals) {
- module.hot.accept("!!../../node_modules/css-loader/index.js!../../node_modules/vue-loader/lib/style-compiler/index.js?{\"id\":\"data-v-4f6bf458\",\"scoped\":true,\"hasInlineConfig\":false}!../../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./prompt.vue", function() {
- var newContent = require("!!../../node_modules/css-loader/index.js!../../node_modules/vue-loader/lib/style-compiler/index.js?{\"id\":\"data-v-4f6bf458\",\"scoped\":true,\"hasInlineConfig\":false}!../../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./prompt.vue");
+ module.hot.accept("!!../../node_modules/css-loader/index.js?minimize!../../node_modules/vue-loader/lib/style-compiler/index.js?{\"id\":\"data-v-4f6bf458\",\"scoped\":true,\"hasInlineConfig\":false}!../../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./prompt.vue", function() {
+ var newContent = require("!!../../node_modules/css-loader/index.js?minimize!../../node_modules/vue-loader/lib/style-compiler/index.js?{\"id\":\"data-v-4f6bf458\",\"scoped\":true,\"hasInlineConfig\":false}!../../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./prompt.vue");
if(typeof newContent === 'string') newContent = [[module.id, newContent, '']];
update(newContent);
});
@@ -5819,13 +5636,13 @@ var content = __webpack_require__(22);
if(typeof content === 'string') content = [[module.i, content, '']];
if(content.locals) module.exports = content.locals;
// add the styles to the DOM
-var update = __webpack_require__(3)("48dda57c", content, false);
+var update = __webpack_require__(3)("58a0689d", content, true);
// Hot Module Replacement
if(false) {
// When the styles change, update the <style> tags
if(!content.locals) {
- module.hot.accept("!!../../node_modules/css-loader/index.js!../../node_modules/vue-loader/lib/style-compiler/index.js?{\"id\":\"data-v-7342b363\",\"scoped\":false,\"hasInlineConfig\":false}!../../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./markdown.vue", function() {
- var newContent = require("!!../../node_modules/css-loader/index.js!../../node_modules/vue-loader/lib/style-compiler/index.js?{\"id\":\"data-v-7342b363\",\"scoped\":false,\"hasInlineConfig\":false}!../../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./markdown.vue");
+ module.hot.accept("!!../../node_modules/css-loader/index.js?minimize!../../node_modules/vue-loader/lib/style-compiler/index.js?{\"id\":\"data-v-7342b363\",\"scoped\":false,\"hasInlineConfig\":false}!../../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./markdown.vue", function() {
+ var newContent = require("!!../../node_modules/css-loader/index.js?minimize!../../node_modules/vue-loader/lib/style-compiler/index.js?{\"id\":\"data-v-7342b363\",\"scoped\":false,\"hasInlineConfig\":false}!../../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./markdown.vue");
if(typeof newContent === 'string') newContent = [[module.id, newContent, '']];
update(newContent);
});
diff --git a/vendor/assets/javascripts/pdf.worker.js b/vendor/assets/javascripts/pdf.worker.js
index f8a94e207f8..970caaaba86 100644
--- a/vendor/assets/javascripts/pdf.worker.js
+++ b/vendor/assets/javascripts/pdf.worker.js
@@ -73,7 +73,7 @@ return /******/ (function(modules) { // webpackBootstrap
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
-/******/ return __webpack_require__(__webpack_require__.s = 16);
+/******/ return __webpack_require__(__webpack_require__.s = 24);
/******/ })
/************************************************************************/
/******/ ({
@@ -20214,6 +20214,7 @@ var stringToUTF8String = sharedUtil.stringToUTF8String;
var warn = sharedUtil.warn;
var createValidAbsoluteUrl = sharedUtil.createValidAbsoluteUrl;
var Util = sharedUtil.Util;
+var Dict = corePrimitives.Dict;
var Ref = corePrimitives.Ref;
var RefSet = corePrimitives.RefSet;
var RefSetCache = corePrimitives.RefSetCache;
@@ -20233,9 +20234,10 @@ var Catalog = function CatalogClosure() {
this.pdfManager = pdfManager;
this.xref = xref;
this.catDict = xref.getCatalogObj();
+ assert(isDict(this.catDict), 'catalog object is not a dictionary');
this.fontCache = new RefSetCache();
this.builtInCMapCache = Object.create(null);
- assert(isDict(this.catDict), 'catalog object is not a dictionary');
+ this.pageKidsCountCache = new RefSetCache();
this.pageFactory = pageFactory;
this.pagePromises = [];
}
@@ -20551,6 +20553,7 @@ var Catalog = function CatalogClosure() {
return shadow(this, 'javaScript', javaScript);
},
cleanup: function Catalog_cleanup() {
+ this.pageKidsCountCache.clear();
var promises = [];
this.fontCache.forEach(function (promise) {
promises.push(promise);
@@ -20577,15 +20580,25 @@ var Catalog = function CatalogClosure() {
getPageDict: function Catalog_getPageDict(pageIndex) {
var capability = createPromiseCapability();
var nodesToVisit = [this.catDict.getRaw('Pages')];
- var currentPageIndex = 0;
- var xref = this.xref;
+ var count,
+ currentPageIndex = 0;
+ var xref = this.xref,
+ pageKidsCountCache = this.pageKidsCountCache;
function next() {
while (nodesToVisit.length) {
var currentNode = nodesToVisit.pop();
if (isRef(currentNode)) {
+ count = pageKidsCountCache.get(currentNode);
+ if (count > 0 && currentPageIndex + count < pageIndex) {
+ currentPageIndex += count;
+ continue;
+ }
xref.fetchAsync(currentNode).then(function (obj) {
if (isDict(obj, 'Page') || isDict(obj) && !obj.has('Kids')) {
if (pageIndex === currentPageIndex) {
+ if (currentNode && !pageKidsCountCache.has(currentNode)) {
+ pageKidsCountCache.put(currentNode, 1);
+ }
capability.resolve([obj, currentNode]);
} else {
currentPageIndex++;
@@ -20599,7 +20612,11 @@ var Catalog = function CatalogClosure() {
return;
}
assert(isDict(currentNode), 'page dictionary kid reference points to wrong type of object');
- var count = currentNode.get('Count');
+ count = currentNode.get('Count');
+ var objId = currentNode.objId;
+ if (objId && !pageKidsCountCache.has(objId)) {
+ pageKidsCountCache.put(objId, count);
+ }
if (currentPageIndex + count <= pageIndex) {
currentPageIndex += count;
continue;
@@ -21191,7 +21208,7 @@ var XRef = function XRefClosure() {
var num = ref.num;
if (num in this.cache) {
var cacheEntry = this.cache[num];
- if (isDict(cacheEntry) && !cacheEntry.objId) {
+ if (cacheEntry instanceof Dict && !cacheEntry.objId) {
cacheEntry.objId = ref.toString();
}
return cacheEntry;
@@ -26178,7 +26195,7 @@ var CMapFactory = function CMapFactoryClosure() {
return Promise.resolve(new IdentityCMap(true, 2));
}
if (BUILT_IN_CMAPS.indexOf(name) === -1) {
- return Promise.reject(new Error('Unknown cMap name: ' + name));
+ return Promise.reject(new Error('Unknown CMap name: ' + name));
}
assert(fetchBuiltInCMap, 'Built-in CMap parameters are not provided.');
return fetchBuiltInCMap(name).then(function (data) {
@@ -28458,9 +28475,6 @@ var Font = function FontClosure() {
}
glyphId = offsetIndex < 0 ? j : offsets[offsetIndex + j - start];
glyphId = glyphId + delta & 0xFFFF;
- if (glyphId === 0) {
- continue;
- }
mappings.push({
charCode: j,
glyphId: glyphId
@@ -37160,8 +37174,8 @@ exports.Type1Parser = Type1Parser;
"use strict";
-var pdfjsVersion = '1.7.395';
-var pdfjsBuild = '07f7c97b';
+var pdfjsVersion = '1.8.172';
+var pdfjsBuild = '8ff1fbe7';
var pdfjsCoreWorker = __w_pdfjs_require__(8);
{
__w_pdfjs_require__(19);
@@ -37646,20 +37660,28 @@ if (typeof PDFJS === 'undefined' || !PDFJS.compatibilityChecked) {
}
})();
(function checkRequestAnimationFrame() {
- function fakeRequestAnimationFrame(callback) {
- window.setTimeout(callback, 20);
+ function installFakeAnimationFrameFunctions() {
+ window.requestAnimationFrame = function (callback) {
+ return window.setTimeout(callback, 20);
+ };
+ window.cancelAnimationFrame = function (timeoutID) {
+ window.clearTimeout(timeoutID);
+ };
}
if (!hasDOM) {
return;
}
if (isIOS) {
- window.requestAnimationFrame = fakeRequestAnimationFrame;
+ installFakeAnimationFrameFunctions();
return;
}
if ('requestAnimationFrame' in window) {
return;
}
- window.requestAnimationFrame = window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || fakeRequestAnimationFrame;
+ window.requestAnimationFrame = window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame;
+ if (!('requestAnimationFrame' in window)) {
+ installFakeAnimationFrameFunctions();
+ }
})();
(function checkCanvasSizeLimitation() {
if (isIOS || isAndroid) {
@@ -38588,7 +38610,7 @@ if (typeof PDFJS === 'undefined' || !PDFJS.compatibilityChecked) {
/***/ }),
-/***/ 16:
+/***/ 24:
/***/ (function(module, exports, __webpack_require__) {
/* Copyright 2016 Mozilla Foundation
diff --git a/vendor/assets/javascripts/pdflab.js b/vendor/assets/javascripts/pdflab.js
index 94e7c40e75e..5d9c348ce35 100644
--- a/vendor/assets/javascripts/pdflab.js
+++ b/vendor/assets/javascripts/pdflab.js
@@ -71,17 +71,10 @@ return /******/ (function(modules) { // webpackBootstrap
/******/ if(installedChunks[chunkId] === 0)
/******/ return Promise.resolve();
/******/
-/******/ // a Promise means "currently loading".
+/******/ // an Promise means "currently loading".
/******/ if(installedChunks[chunkId]) {
/******/ return installedChunks[chunkId][2];
/******/ }
-/******/
-/******/ // setup Promise in chunk cache
-/******/ var promise = new Promise(function(resolve, reject) {
-/******/ installedChunks[chunkId] = [resolve, reject];
-/******/ });
-/******/ installedChunks[chunkId][2] = promise;
-/******/
/******/ // start chunk loading
/******/ var head = document.getElementsByTagName('head')[0];
/******/ var script = document.createElement('script');
@@ -106,8 +99,13 @@ return /******/ (function(modules) { // webpackBootstrap
/******/ installedChunks[chunkId] = undefined;
/******/ }
/******/ };
-/******/ head.appendChild(script);
/******/
+/******/ var promise = new Promise(function(resolve, reject) {
+/******/ installedChunks[chunkId] = [resolve, reject];
+/******/ });
+/******/ installedChunks[chunkId][2] = promise;
+/******/
+/******/ head.appendChild(script);
/******/ return promise;
/******/ };
/******/
@@ -150,7 +148,7 @@ return /******/ (function(modules) { // webpackBootstrap
/******/ __webpack_require__.oe = function(err) { console.error(err); throw err; };
/******/
/******/ // Load entry module and return exports
-/******/ return __webpack_require__(__webpack_require__.s = 7);
+/******/ return __webpack_require__(__webpack_require__.s = 23);
/******/ })
/************************************************************************/
/******/ ([
@@ -1615,7 +1613,10 @@ var DOMCMapReaderFactory = function DOMCMapReaderFactoryClosure() {
request.responseType = 'arraybuffer';
}
request.onreadystatechange = function () {
- if (request.readyState === XMLHttpRequest.DONE && (request.status === 200 || request.status === 0)) {
+ if (request.readyState !== XMLHttpRequest.DONE) {
+ return;
+ }
+ if (request.status === 200 || request.status === 0) {
var data;
if (this.isCompressed && request.response) {
data = new Uint8Array(request.response);
@@ -1629,8 +1630,8 @@ var DOMCMapReaderFactory = function DOMCMapReaderFactoryClosure() {
});
return;
}
- reject(new Error('Unable to load ' + (this.isCompressed ? 'binary ' : '') + 'CMap at: ' + url));
}
+ reject(new Error('Unable to load ' + (this.isCompressed ? 'binary ' : '') + 'CMap at: ' + url));
}.bind(this);
request.send(null);
}.bind(this));
@@ -1670,6 +1671,16 @@ var CustomStyle = function CustomStyleClosure() {
};
return CustomStyle;
}();
+var RenderingCancelledException = function RenderingCancelledException() {
+ function RenderingCancelledException(msg, type) {
+ this.message = msg;
+ this.type = type;
+ }
+ RenderingCancelledException.prototype = new Error();
+ RenderingCancelledException.prototype.name = 'RenderingCancelledException';
+ RenderingCancelledException.constructor = RenderingCancelledException;
+ return RenderingCancelledException;
+}();
var hasCanvasTypedArrays;
hasCanvasTypedArrays = function hasCanvasTypedArrays() {
var canvas = document.createElement('canvas');
@@ -1762,6 +1773,8 @@ function getDefaultSetting(id) {
return globalSettings ? globalSettings.externalLinkRel : DEFAULT_LINK_REL;
case 'enableStats':
return !!(globalSettings && globalSettings.enableStats);
+ case 'pdfjsNext':
+ return !!(globalSettings && globalSettings.pdfjsNext);
default:
throw new Error('Unknown default setting: ' + id);
}
@@ -1789,6 +1802,7 @@ exports.isExternalLinkTargetSet = isExternalLinkTargetSet;
exports.isValidUrl = isValidUrl;
exports.getFilenameFromUrl = getFilenameFromUrl;
exports.LinkTarget = LinkTarget;
+exports.RenderingCancelledException = RenderingCancelledException;
exports.hasCanvasTypedArrays = hasCanvasTypedArrays;
exports.getDefaultSetting = getDefaultSetting;
exports.DEFAULT_LINK_REL = DEFAULT_LINK_REL;
@@ -2450,6 +2464,7 @@ var FontFaceObject = displayFontLoader.FontFaceObject;
var FontLoader = displayFontLoader.FontLoader;
var CanvasGraphics = displayCanvas.CanvasGraphics;
var Metadata = displayMetadata.Metadata;
+var RenderingCancelledException = displayDOMUtils.RenderingCancelledException;
var getDefaultSetting = displayDOMUtils.getDefaultSetting;
var DOMCanvasFactory = displayDOMUtils.DOMCanvasFactory;
var DOMCMapReaderFactory = displayDOMUtils.DOMCMapReaderFactory;
@@ -3711,7 +3726,11 @@ var InternalRenderTask = function InternalRenderTaskClosure() {
cancel: function InternalRenderTask_cancel() {
this.running = false;
this.cancelled = true;
- this.callback('cancelled');
+ if (getDefaultSetting('pdfjsNext')) {
+ this.callback(new RenderingCancelledException('Rendering cancelled, page ' + this.pageNumber, 'canvas'));
+ } else {
+ this.callback('cancelled');
+ }
},
operatorListChanged: function InternalRenderTask_operatorListChanged() {
if (!this.graphicsReady) {
@@ -3776,8 +3795,8 @@ var _UnsupportedManager = function UnsupportedManagerClosure() {
}
};
}();
-exports.version = '1.7.395';
-exports.build = '07f7c97b';
+exports.version = '1.8.172';
+exports.build = '8ff1fbe7';
exports.getDocument = getDocument;
exports.PDFDataRangeTransport = PDFDataRangeTransport;
exports.PDFWorker = PDFWorker;
@@ -5716,8 +5735,8 @@ if (!globalScope.PDFJS) {
globalScope.PDFJS = {};
}
var PDFJS = globalScope.PDFJS;
-PDFJS.version = '1.7.395';
-PDFJS.build = '07f7c97b';
+PDFJS.version = '1.8.172';
+PDFJS.build = '8ff1fbe7';
PDFJS.pdfBug = false;
if (PDFJS.verbosity !== undefined) {
sharedUtil.setVerbosityLevel(PDFJS.verbosity);
@@ -5777,6 +5796,7 @@ PDFJS.disableWebGL = PDFJS.disableWebGL === undefined ? true : PDFJS.disableWebG
PDFJS.externalLinkTarget = PDFJS.externalLinkTarget === undefined ? LinkTarget.NONE : PDFJS.externalLinkTarget;
PDFJS.externalLinkRel = PDFJS.externalLinkRel === undefined ? DEFAULT_LINK_REL : PDFJS.externalLinkRel;
PDFJS.isEvalSupported = PDFJS.isEvalSupported === undefined ? true : PDFJS.isEvalSupported;
+PDFJS.pdfjsNext = PDFJS.pdfjsNext === undefined ? false : PDFJS.pdfjsNext;
var savedOpenExternalLinksInNewWindow = PDFJS.openExternalLinksInNewWindow;
delete PDFJS.openExternalLinksInNewWindow;
Object.defineProperty(PDFJS, 'openExternalLinksInNewWindow', {
@@ -8227,8 +8247,8 @@ exports.TilingPattern = TilingPattern;
"use strict";
-var pdfjsVersion = '1.7.395';
-var pdfjsBuild = '07f7c97b';
+var pdfjsVersion = '1.8.172';
+var pdfjsBuild = '8ff1fbe7';
var pdfjsSharedUtil = __w_pdfjs_require__(0);
var pdfjsDisplayGlobal = __w_pdfjs_require__(9);
var pdfjsDisplayAPI = __w_pdfjs_require__(3);
@@ -8259,6 +8279,7 @@ exports.createObjectURL = pdfjsSharedUtil.createObjectURL;
exports.removeNullCharacters = pdfjsSharedUtil.removeNullCharacters;
exports.shadow = pdfjsSharedUtil.shadow;
exports.createBlob = pdfjsSharedUtil.createBlob;
+exports.RenderingCancelledException = pdfjsDisplayDOMUtils.RenderingCancelledException;
exports.getFilenameFromUrl = pdfjsDisplayDOMUtils.getFilenameFromUrl;
exports.addLinkAttributes = pdfjsDisplayDOMUtils.addLinkAttributes;
@@ -8740,20 +8761,28 @@ if (typeof PDFJS === 'undefined' || !PDFJS.compatibilityChecked) {
}
})();
(function checkRequestAnimationFrame() {
- function fakeRequestAnimationFrame(callback) {
- window.setTimeout(callback, 20);
+ function installFakeAnimationFrameFunctions() {
+ window.requestAnimationFrame = function (callback) {
+ return window.setTimeout(callback, 20);
+ };
+ window.cancelAnimationFrame = function (timeoutID) {
+ window.clearTimeout(timeoutID);
+ };
}
if (!hasDOM) {
return;
}
if (isIOS) {
- window.requestAnimationFrame = fakeRequestAnimationFrame;
+ installFakeAnimationFrameFunctions();
return;
}
if ('requestAnimationFrame' in window) {
return;
}
- window.requestAnimationFrame = window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || fakeRequestAnimationFrame;
+ window.requestAnimationFrame = window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame;
+ if (!('requestAnimationFrame' in window)) {
+ installFakeAnimationFrameFunctions();
+ }
})();
(function checkCanvasSizeLimitation() {
if (isIOS || isAndroid) {
@@ -9760,7 +9789,7 @@ function toComment(sourceMap) {
return '/*# ' + data + ' */';
}
-/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(11).Buffer))
+/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(10).Buffer))
/***/ }),
/* 4 */
@@ -9839,7 +9868,7 @@ if (typeof DEBUG !== 'undefined' && DEBUG) {
) }
}
-var listToStyles = __webpack_require__(23)
+var listToStyles = __webpack_require__(21)
/*
type StyleObject = {
@@ -10046,34 +10075,18 @@ function applyToTag (styleElement, obj) {
/* styles */
-__webpack_require__(21)
+__webpack_require__(19)
var Component = __webpack_require__(4)(
/* script */
- __webpack_require__(8),
+ __webpack_require__(7),
/* template */
- __webpack_require__(19),
+ __webpack_require__(17),
/* scopeId */
null,
/* cssModules */
null
)
-Component.options.__file = "/Users/samrose/Projects/pdflab/src/index.vue"
-if (Component.esModule && Object.keys(Component.esModule).some(function (key) {return key !== "default" && key !== "__esModule"})) {console.error("named exports are not supported in *.vue files.")}
-if (Component.options.functional) {console.error("[vue-loader] index.vue: functional components are not supported with templates, they should use render functions.")}
-
-/* hot reload */
-if (false) {(function () {
- var hotAPI = require("vue-hot-reload-api")
- hotAPI.install(require("vue"), false)
- if (!hotAPI.compatible) return
- module.hot.accept()
- if (!module.hot.data) {
- hotAPI.createRecord("data-v-7c7bed7e", Component.options)
- } else {
- hotAPI.reload("data-v-7c7bed7e", Component.options)
- }
-})()}
module.exports = Component.exports
@@ -10085,25 +10098,6 @@ module.exports = Component.exports
"use strict";
-var PDF = __webpack_require__(6);
-var pdfjsLib = __webpack_require__(2);
-
-module.exports = {
- install: function install(_vue, _ref) {
- var workerSrc = _ref.workerSrc;
-
- pdfjsLib.PDFJS.workerSrc = workerSrc;
- _vue.component('pdf-lab', PDF);
- }
-};
-
-/***/ }),
-/* 8 */
-/***/ (function(module, exports, __webpack_require__) {
-
-"use strict";
-
-
Object.defineProperty(exports, "__esModule", {
value: true
});
@@ -10112,7 +10106,7 @@ var _pdfjsDist = __webpack_require__(2);
var _pdfjsDist2 = _interopRequireDefault(_pdfjsDist);
-var _index = __webpack_require__(18);
+var _index = __webpack_require__(16);
var _index2 = _interopRequireDefault(_index);
@@ -10138,7 +10132,7 @@ exports.default = {
},
data: function data() {
return {
- isLoading: false,
+ loading: false,
pages: []
};
},
@@ -10163,17 +10157,17 @@ exports.default = {
}).catch(function (error) {
return _this.$emit('pdflaberror', error);
}).then(function () {
- return _this.isLoading = false;
+ _this.loading = false;
});
},
renderPages: function renderPages(pdf) {
var _this2 = this;
var pagePromises = [];
- this.isLoading = true;
- for (var num = 1; num <= pdf.numPages; num++) {
- pagePromises.push(pdf.getPage(num).then(function (page) {
- return _this2.pages.push(page);
+ this.loading = true;
+ for (var num = 1; num <= pdf.numPages; num += 1) {
+ pagePromises.push(pdf.getPage(num).then(function (p) {
+ return _this2.pages.push(p);
}));
}
return Promise.all(pagePromises);
@@ -10185,7 +10179,7 @@ exports.default = {
};
/***/ }),
-/* 9 */
+/* 8 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
@@ -10213,10 +10207,16 @@ exports.default = {
required: true
}
},
+ data: function data() {
+ return {
+ scale: 4,
+ rendering: false
+ };
+ },
+
computed: {
viewport: function viewport() {
- var scale = 4;
- return this.page.getViewport(scale);
+ return this.page.getViewport(this.scale);
},
context: function context() {
return this.$refs.canvas.getContext('2d');
@@ -10229,14 +10229,19 @@ exports.default = {
}
},
mounted: function mounted() {
+ var _this = this;
+
this.$refs.canvas.height = this.viewport.height;
this.$refs.canvas.width = this.viewport.width;
- this.page.render(this.renderContext);
+ this.rendering = true;
+ this.page.render(this.renderContext).then(function () {
+ _this.rendering = false;
+ });
}
};
/***/ }),
-/* 10 */
+/* 9 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
@@ -10357,7 +10362,7 @@ function fromByteArray (uint8) {
/***/ }),
-/* 11 */
+/* 10 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
@@ -10371,9 +10376,9 @@ function fromByteArray (uint8) {
-var base64 = __webpack_require__(10)
-var ieee754 = __webpack_require__(14)
-var isArray = __webpack_require__(15)
+var base64 = __webpack_require__(9)
+var ieee754 = __webpack_require__(13)
+var isArray = __webpack_require__(14)
exports.Buffer = Buffer
exports.SlowBuffer = SlowBuffer
@@ -12151,10 +12156,10 @@ function isnan (val) {
return val !== val // eslint-disable-line no-self-compare
}
-/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(24)))
+/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(22)))
/***/ }),
-/* 12 */
+/* 11 */
/***/ (function(module, exports, __webpack_require__) {
exports = module.exports = __webpack_require__(3)(undefined);
@@ -12162,13 +12167,13 @@ exports = module.exports = __webpack_require__(3)(undefined);
// module
-exports.push([module.i, "\n.pdf-viewer {\n background: url(" + __webpack_require__(17) + ");\n display: flex;\n flex-flow: column nowrap;\n}\n", ""]);
+exports.push([module.i, ".pdf-viewer{background:url(" + __webpack_require__(15) + ");display:flex;flex-flow:column nowrap}", ""]);
// exports
/***/ }),
-/* 13 */
+/* 12 */
/***/ (function(module, exports, __webpack_require__) {
exports = module.exports = __webpack_require__(3)(undefined);
@@ -12176,13 +12181,13 @@ exports = module.exports = __webpack_require__(3)(undefined);
// module
-exports.push([module.i, "\n.pdf-page {\n margin: 8px auto 0 auto;\n border-top: 1px #ddd solid;\n border-bottom: 1px #ddd solid;\n width: 100%;\n}\n.pdf-page:first-child {\n margin-top: 0px;\n border-top: 0px;\n}\n.pdf-page:last-child {\n margin-bottom: 0px;\n border-bottom: 0px;\n}\n", ""]);
+exports.push([module.i, ".pdf-page{margin:8px auto 0;border-top:1px solid #ddd;border-bottom:1px solid #ddd;width:100%}.pdf-page:first-child{margin-top:0;border-top:0}.pdf-page:last-child{margin-bottom:0;border-bottom:0}", ""]);
// exports
/***/ }),
-/* 14 */
+/* 13 */
/***/ (function(module, exports) {
exports.read = function (buffer, offset, isLE, mLen, nBytes) {
@@ -12272,7 +12277,7 @@ exports.write = function (buffer, value, offset, isLE, mLen, nBytes) {
/***/ }),
-/* 15 */
+/* 14 */
/***/ (function(module, exports) {
var toString = {}.toString;
@@ -12283,53 +12288,36 @@ module.exports = Array.isArray || function (arr) {
/***/ }),
-/* 16 */,
-/* 17 */
+/* 15 */
/***/ (function(module, exports) {
module.exports = "data:image/gif;base64,R0lGODlhCgAKAIAAAOXl5f///yH5BAAAAAAALAAAAAAKAAoAAAIRhB2ZhxoM3GMSykqd1VltzxQAOw=="
/***/ }),
-/* 18 */
+/* 16 */
/***/ (function(module, exports, __webpack_require__) {
/* styles */
-__webpack_require__(22)
+__webpack_require__(20)
var Component = __webpack_require__(4)(
/* script */
- __webpack_require__(9),
+ __webpack_require__(8),
/* template */
- __webpack_require__(20),
+ __webpack_require__(18),
/* scopeId */
null,
/* cssModules */
null
)
-Component.options.__file = "/Users/samrose/Projects/pdflab/src/page/index.vue"
-if (Component.esModule && Object.keys(Component.esModule).some(function (key) {return key !== "default" && key !== "__esModule"})) {console.error("named exports are not supported in *.vue files.")}
-if (Component.options.functional) {console.error("[vue-loader] index.vue: functional components are not supported with templates, they should use render functions.")}
-
-/* hot reload */
-if (false) {(function () {
- var hotAPI = require("vue-hot-reload-api")
- hotAPI.install(require("vue"), false)
- if (!hotAPI.compatible) return
- module.hot.accept()
- if (!module.hot.data) {
- hotAPI.createRecord("data-v-7e912b1a", Component.options)
- } else {
- hotAPI.reload("data-v-7e912b1a", Component.options)
- }
-})()}
module.exports = Component.exports
/***/ }),
-/* 19 */
-/***/ (function(module, exports, __webpack_require__) {
+/* 17 */
+/***/ (function(module, exports) {
module.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;
return (_vm.hasPDF) ? _c('div', {
@@ -12338,24 +12326,17 @@ module.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c
return _c('page', {
key: index,
attrs: {
- "v-if": !_vm.isLoading,
+ "v-if": !_vm.loading,
"page": page,
"number": index + 1
}
})
})) : _vm._e()
},staticRenderFns: []}
-module.exports.render._withStripped = true
-if (false) {
- module.hot.accept()
- if (module.hot.data) {
- require("vue-hot-reload-api").rerender("data-v-7c7bed7e", module.exports)
- }
-}
/***/ }),
-/* 20 */
-/***/ (function(module, exports, __webpack_require__) {
+/* 18 */
+/***/ (function(module, exports) {
module.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;
return _c('canvas', {
@@ -12366,32 +12347,25 @@ module.exports={render:function (){var _vm=this;var _h=_vm.$createElement;var _c
}
})
},staticRenderFns: []}
-module.exports.render._withStripped = true
-if (false) {
- module.hot.accept()
- if (module.hot.data) {
- require("vue-hot-reload-api").rerender("data-v-7e912b1a", module.exports)
- }
-}
/***/ }),
-/* 21 */
+/* 19 */
/***/ (function(module, exports, __webpack_require__) {
// style-loader: Adds some css to the DOM by adding a <style> tag
// load the styles
-var content = __webpack_require__(12);
+var content = __webpack_require__(11);
if(typeof content === 'string') content = [[module.i, content, '']];
if(content.locals) module.exports = content.locals;
// add the styles to the DOM
-var update = __webpack_require__(5)("8018213c", content, false);
+var update = __webpack_require__(5)("59cf066f", content, true);
// Hot Module Replacement
if(false) {
// When the styles change, update the <style> tags
if(!content.locals) {
- module.hot.accept("!!../node_modules/css-loader/index.js!../node_modules/vue-loader/lib/style-compiler/index.js?{\"id\":\"data-v-7c7bed7e\",\"scoped\":false,\"hasInlineConfig\":false}!../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./index.vue", function() {
- var newContent = require("!!../node_modules/css-loader/index.js!../node_modules/vue-loader/lib/style-compiler/index.js?{\"id\":\"data-v-7c7bed7e\",\"scoped\":false,\"hasInlineConfig\":false}!../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./index.vue");
+ module.hot.accept("!!../node_modules/css-loader/index.js?minimize!../node_modules/vue-loader/lib/style-compiler/index.js?{\"id\":\"data-v-7c7bed7e\",\"scoped\":false,\"hasInlineConfig\":false}!../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./index.vue", function() {
+ var newContent = require("!!../node_modules/css-loader/index.js?minimize!../node_modules/vue-loader/lib/style-compiler/index.js?{\"id\":\"data-v-7c7bed7e\",\"scoped\":false,\"hasInlineConfig\":false}!../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./index.vue");
if(typeof newContent === 'string') newContent = [[module.id, newContent, '']];
update(newContent);
});
@@ -12401,23 +12375,23 @@ if(false) {
}
/***/ }),
-/* 22 */
+/* 20 */
/***/ (function(module, exports, __webpack_require__) {
// style-loader: Adds some css to the DOM by adding a <style> tag
// load the styles
-var content = __webpack_require__(13);
+var content = __webpack_require__(12);
if(typeof content === 'string') content = [[module.i, content, '']];
if(content.locals) module.exports = content.locals;
// add the styles to the DOM
-var update = __webpack_require__(5)("6d9dea59", content, false);
+var update = __webpack_require__(5)("09f1e2d8", content, true);
// Hot Module Replacement
if(false) {
// When the styles change, update the <style> tags
if(!content.locals) {
- module.hot.accept("!!../../node_modules/css-loader/index.js!../../node_modules/vue-loader/lib/style-compiler/index.js?{\"id\":\"data-v-7e912b1a\",\"scoped\":false,\"hasInlineConfig\":false}!../../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./index.vue", function() {
- var newContent = require("!!../../node_modules/css-loader/index.js!../../node_modules/vue-loader/lib/style-compiler/index.js?{\"id\":\"data-v-7e912b1a\",\"scoped\":false,\"hasInlineConfig\":false}!../../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./index.vue");
+ module.hot.accept("!!../../node_modules/css-loader/index.js?minimize!../../node_modules/vue-loader/lib/style-compiler/index.js?{\"id\":\"data-v-7e912b1a\",\"scoped\":false,\"hasInlineConfig\":false}!../../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./index.vue", function() {
+ var newContent = require("!!../../node_modules/css-loader/index.js?minimize!../../node_modules/vue-loader/lib/style-compiler/index.js?{\"id\":\"data-v-7e912b1a\",\"scoped\":false,\"hasInlineConfig\":false}!../../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./index.vue");
if(typeof newContent === 'string') newContent = [[module.id, newContent, '']];
update(newContent);
});
@@ -12427,7 +12401,7 @@ if(false) {
}
/***/ }),
-/* 23 */
+/* 21 */
/***/ (function(module, exports) {
/**
@@ -12460,7 +12434,7 @@ module.exports = function listToStyles (parentId, list) {
/***/ }),
-/* 24 */
+/* 22 */
/***/ (function(module, exports) {
var g;
@@ -12486,6 +12460,25 @@ try {
module.exports = g;
+/***/ }),
+/* 23 */
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+var PDF = __webpack_require__(6);
+var pdfjsLib = __webpack_require__(2);
+
+module.exports = {
+ install: function install(_vue) {
+ var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+
+ pdfjsLib.PDFJS.workerSrc = options.workerSrc || '';
+ _vue.component('pdf-lab', PDF);
+ }
+};
+
/***/ })
/******/ ]);
}); \ No newline at end of file
diff --git a/vendor/gitignore/C.gitignore b/vendor/gitignore/C.gitignore
index 8a365b3d829..c6127b38c1a 100644
--- a/vendor/gitignore/C.gitignore
+++ b/vendor/gitignore/C.gitignore
@@ -45,6 +45,7 @@
# Kernel Module Compile Results
*.mod*
*.cmd
+.tmp_versions/
modules.order
Module.symvers
Mkfile.old
diff --git a/vendor/gitignore/Dart.gitignore b/vendor/gitignore/Dart.gitignore
index 4b366585ddc..4d2a4d6db7c 100644
--- a/vendor/gitignore/Dart.gitignore
+++ b/vendor/gitignore/Dart.gitignore
@@ -1,33 +1,12 @@
# See https://www.dartlang.org/tools/private-files.html
# Files and directories created by pub
-
-# SDK 1.20 and later (no longer creates packages directories)
.packages
.pub/
build/
-
-# Older SDK versions
-# (Include if the minimum SDK version specified in pubsepc.yaml is earlier than 1.20)
-.project
-.buildlog
-**/packages/
-
-
-# Files created by dart2js
-# (Most Dart developers will use pub build to compile Dart, use/modify these
-# rules if you intend to use dart2js directly
-# Convention is to use extension '.dart.js' for Dart compiled to Javascript to
-# differentiate from explicit Javascript files)
-*.dart.js
-*.part.js
-*.js.deps
-*.js.map
-*.info.json
+# If you're building an application, you may want to check-in your pubspec.lock
+pubspec.lock
# Directory created by dartdoc
+# If you don't generate documentation locally you can remove this line.
doc/api/
-
-# Don't commit pubspec lock file
-# (Library packages only! Remove pattern if developing an application package)
-pubspec.lock
diff --git a/vendor/gitignore/Global/Eclipse.gitignore b/vendor/gitignore/Global/Eclipse.gitignore
index 4f88399d2d8..ce1c12cdb7a 100644
--- a/vendor/gitignore/Global/Eclipse.gitignore
+++ b/vendor/gitignore/Global/Eclipse.gitignore
@@ -11,9 +11,6 @@ local.properties
.loadpath
.recommenders
-# Eclipse Core
-.project
-
# External tool builders
.externalToolBuilders/
@@ -26,9 +23,6 @@ local.properties
# CDT-specific (C/C++ Development Tooling)
.cproject
-# JDT-specific (Eclipse Java Development Tools)
-.classpath
-
# Java annotation processor (APT)
.factorypath
diff --git a/vendor/gitignore/Global/JetBrains.gitignore b/vendor/gitignore/Global/JetBrains.gitignore
index ec7e95c6ab5..a5d4cc86d33 100644
--- a/vendor/gitignore/Global/JetBrains.gitignore
+++ b/vendor/gitignore/Global/JetBrains.gitignore
@@ -36,6 +36,9 @@
# JIRA plugin
atlassian-ide-plugin.xml
+# Cursive Clojure plugin
+.idea/replstate.xml
+
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
diff --git a/vendor/gitignore/Global/macOS.gitignore b/vendor/gitignore/Global/macOS.gitignore
index f0f3fbc06c8..5972fe50f66 100644
--- a/vendor/gitignore/Global/macOS.gitignore
+++ b/vendor/gitignore/Global/macOS.gitignore
@@ -1,26 +1,25 @@
-*.DS_Store
-.AppleDouble
-.LSOverride
-
-# Icon must end with two \r
-Icon
-
-
-# Thumbnails
-._*
-
-# Files that might appear in the root of a volume
-.DocumentRevisions-V100
-.fseventsd
-.Spotlight-V100
-.TemporaryItems
-.Trashes
-.VolumeIcon.icns
-.com.apple.timemachine.donotpresent
-
-# Directories potentially created on remote AFP share
-.AppleDB
-.AppleDesktop
-Network Trash Folder
-Temporary Items
-.apdisk
+*.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
diff --git a/vendor/gitignore/Python.gitignore b/vendor/gitignore/Python.gitignore
index 62c1e736924..ff65a437185 100644
--- a/vendor/gitignore/Python.gitignore
+++ b/vendor/gitignore/Python.gitignore
@@ -92,3 +92,6 @@ ENV/
# Rope project settings
.ropeproject
+
+# mkdocs documentation
+/site
diff --git a/vendor/gitignore/Rails.gitignore b/vendor/gitignore/Rails.gitignore
index e97427608c1..42aeb55000a 100644
--- a/vendor/gitignore/Rails.gitignore
+++ b/vendor/gitignore/Rails.gitignore
@@ -8,7 +8,7 @@ capybara-*.html
/public/system
/coverage/
/spec/tmp
-**.orig
+*.orig
rerun.txt
pickle-email-*.html
diff --git a/vendor/gitignore/TeX.gitignore b/vendor/gitignore/TeX.gitignore
index 57ed9f5d972..a0322dbd35a 100644
--- a/vendor/gitignore/TeX.gitignore
+++ b/vendor/gitignore/TeX.gitignore
@@ -148,6 +148,9 @@ _minted*
# pax
*.pax
+# pdfpcnotes
+*.pdfpc
+
# sagetex
*.sagetex.sage
*.sagetex.py
diff --git a/vendor/gitignore/Unity.gitignore b/vendor/gitignore/Unity.gitignore
index b829399ae85..eb83a8f122d 100644
--- a/vendor/gitignore/Unity.gitignore
+++ b/vendor/gitignore/Unity.gitignore
@@ -23,7 +23,6 @@ ExportedObj/
*.svd
*.pdb
-
# Unity3D generated meta files
*.pidb.meta
diff --git a/vendor/gitignore/VisualStudio.gitignore b/vendor/gitignore/VisualStudio.gitignore
index a752eacca7d..940794e60f2 100644
--- a/vendor/gitignore/VisualStudio.gitignore
+++ b/vendor/gitignore/VisualStudio.gitignore
@@ -219,6 +219,7 @@ UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
+*.ndf
# Business Intelligence projects
*.rdl.data
@@ -284,4 +285,4 @@ __pycache__/
*.btp.cs
*.btm.cs
*.odx.cs
-*.xsd.cs \ No newline at end of file
+*.xsd.cs
diff --git a/vendor/gitlab-ci-yml/CONTRIBUTING.md b/vendor/gitlab-ci-yml/CONTRIBUTING.md
new file mode 100644
index 00000000000..6e5160a2487
--- /dev/null
+++ b/vendor/gitlab-ci-yml/CONTRIBUTING.md
@@ -0,0 +1,5 @@
+The canonical repository for `.gitlab-ci.yml` templates is
+https://gitlab.com/gitlab-org/gitlab-ci-yml.
+
+GitLab only mirrors the templates. Please submit your merge requests to
+https://gitlab.com/gitlab-org/gitlab-ci-yml.
diff --git a/vendor/gitlab-ci-yml/Django.gitlab-ci.yml b/vendor/gitlab-ci-yml/Django.gitlab-ci.yml
index b3106863cca..5ded2f5ce76 100644
--- a/vendor/gitlab-ci-yml/Django.gitlab-ci.yml
+++ b/vendor/gitlab-ci-yml/Django.gitlab-ci.yml
@@ -26,9 +26,24 @@ before_script:
# - apt-get update -q && apt-get install nodejs -yqq
- pip install -r requirements.txt
+# To get Django tests to work you may need to create a settings file using
+# the following DATABASES:
+#
+# DATABASES = {
+# 'default': {
+# 'ENGINE': 'django.db.backends.postgresql_psycopg2',
+# 'NAME': 'ci',
+# 'USER': 'postgres',
+# 'PASSWORD': 'postgres',
+# 'HOST': 'postgres',
+# 'PORT': '5432',
+# },
+# }
+#
+# and then adding `--settings app.settings.ci` (or similar) to the test command
+
test:
variables:
DATABASE_URL: "postgresql://postgres:postgres@postgres:5432/$POSTGRES_DB"
script:
- - python manage.py migrate
- python manage.py test
diff --git a/vendor/gitlab-ci-yml/Pages/Hexo.gitlab-ci.yml b/vendor/gitlab-ci-yml/Pages/Hexo.gitlab-ci.yml
index 908463c9d12..02d02250bbf 100644
--- a/vendor/gitlab-ci-yml/Pages/Hexo.gitlab-ci.yml
+++ b/vendor/gitlab-ci-yml/Pages/Hexo.gitlab-ci.yml
@@ -1,17 +1,16 @@
# Full project: https://gitlab.com/pages/hexo
-image: node:4.2.2
+image: node:6.10.0
pages:
- cache:
- paths:
- - node_modules/
-
script:
- - npm install hexo-cli -g
- npm install
- - hexo deploy
+ - ./node_modules/hexo/bin/hexo generate
artifacts:
paths:
- public
+ cache:
+ paths:
+ - node_modules
+ key: project
only:
- master
diff --git a/vendor/gitlab-ci-yml/Pages/Jekyll.gitlab-ci.yml b/vendor/gitlab-ci-yml/Pages/Jekyll.gitlab-ci.yml
index d98cf94d635..37f50554036 100644
--- a/vendor/gitlab-ci-yml/Pages/Jekyll.gitlab-ci.yml
+++ b/vendor/gitlab-ci-yml/Pages/Jekyll.gitlab-ci.yml
@@ -1,8 +1,10 @@
# Template project: https://gitlab.com/pages/jekyll
# Docs: https://docs.gitlab.com/ce/pages/
-# Jekyll version: 3.4.0
image: ruby:2.3
+variables:
+ JEKYLL_ENV: production
+
before_script:
- bundle install
@@ -25,4 +27,4 @@ pages:
- public
only:
- master
- \ No newline at end of file
+
diff --git a/vendor/gitlab-ci-yml/Scala.gitlab-ci.yml b/vendor/gitlab-ci-yml/Scala.gitlab-ci.yml
index 443ba42e38c..b4208ed9d7d 100644
--- a/vendor/gitlab-ci-yml/Scala.gitlab-ci.yml
+++ b/vendor/gitlab-ci-yml/Scala.gitlab-ci.yml
@@ -9,7 +9,7 @@ before_script:
- apt-get install apt-transport-https -yqq
# Add keyserver for SBT
- echo "deb http://dl.bintray.com/sbt/debian /" | tee -a /etc/apt/sources.list.d/sbt.list
- - apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 642AC823
+ - apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 2EE0EA64E40A89B84B2DF73499E82A75642AC823
# Install SBT
- apt-get update -yqq
- apt-get install sbt -yqq
diff --git a/vendor/licenses.csv b/vendor/licenses.csv
index a2cbef126ad..6441df25fe1 100644
--- a/vendor/licenses.csv
+++ b/vendor/licenses.csv
@@ -1,9 +1,9 @@
RedCloth,4.3.2,MIT
abbrev,1.0.9,ISC
accepts,1.3.3,MIT
-ace-rails-ap,4.1.0,MIT
-acorn,4.0.4,MIT
-acorn-dynamic-import,2.0.1,MIT
+ace-rails-ap,4.1.2,MIT
+acorn,4.0.11,MIT
+acorn-dynamic-import,2.0.2,MIT
acorn-jsx,3.0.1,MIT
actionmailer,4.2.8,MIT
actionpack,4.2.8,MIT
@@ -16,19 +16,20 @@ acts-as-taggable-on,4.0.0,MIT
addressable,2.3.8,Apache 2.0
after,0.8.2,MIT
after_commit_queue,1.3.0,MIT
-ajv,4.11.2,MIT
+ajv,4.11.5,MIT
ajv-keywords,1.5.1,MIT
akismet,2.0.0,MIT
align-text,0.1.4,MIT
allocations,1.0.5,MIT
+alphanum-sort,1.0.2,MIT
amdefine,1.0.1,BSD-3-Clause OR MIT
ansi-escapes,1.4.0,MIT
-ansi-html,0.0.7,Apache 2.0
+ansi-html,0.0.5,"Apache, Version 2.0"
ansi-regex,2.1.1,MIT
ansi-styles,2.2.1,MIT
anymatch,1.3.0,ISC
append-transform,0.4.0,MIT
-aproba,1.1.0,ISC
+aproba,1.1.1,ISC
are-we-there-yet,1.1.2,ISC
arel,6.0.4,MIT
argparse,1.0.9,MIT
@@ -55,13 +56,14 @@ asynckit,0.4.0,MIT
attr_encrypted,3.0.3,MIT
attr_required,1.0.0,MIT
autoparse,0.3.3,Apache 2.0
+autoprefixer,6.7.7,MIT
autoprefixer-rails,6.2.3,MIT
aws-sign2,0.6.0,Apache 2.0
aws4,1.6.0,MIT
axiom-types,0.1.1,MIT
babel-code-frame,6.22.0,MIT
-babel-core,6.23.1,MIT
-babel-generator,6.23.0,MIT
+babel-core,6.24.0,MIT
+babel-generator,6.24.0,MIT
babel-helper-bindify-decorators,6.22.0,MIT
babel-helper-builder-binary-assignment-operator-visitor,6.22.0,MIT
babel-helper-call-delegate,6.22.0,MIT
@@ -76,10 +78,10 @@ babel-helper-regex,6.22.0,MIT
babel-helper-remap-async-to-generator,6.22.0,MIT
babel-helper-replace-supers,6.23.0,MIT
babel-helpers,6.23.0,MIT
-babel-loader,6.2.10,MIT
+babel-loader,6.4.1,MIT
babel-messages,6.23.0,MIT
babel-plugin-check-es2015-constants,6.22.0,MIT
-babel-plugin-istanbul,4.0.0,New BSD
+babel-plugin-istanbul,4.1.1,New BSD
babel-plugin-syntax-async-functions,6.13.0,MIT
babel-plugin-syntax-async-generators,6.13.0,MIT
babel-plugin-syntax-class-properties,6.13.0,MIT
@@ -92,6 +94,7 @@ babel-plugin-transform-async-generator-functions,6.22.0,MIT
babel-plugin-transform-async-to-generator,6.22.0,MIT
babel-plugin-transform-class-properties,6.23.0,MIT
babel-plugin-transform-decorators,6.22.0,MIT
+babel-plugin-transform-define,1.2.0,MIT
babel-plugin-transform-es2015-arrow-functions,6.22.0,MIT
babel-plugin-transform-es2015-block-scoped-functions,6.22.0,MIT
babel-plugin-transform-es2015-block-scoping,6.23.0,MIT
@@ -102,10 +105,10 @@ babel-plugin-transform-es2015-duplicate-keys,6.22.0,MIT
babel-plugin-transform-es2015-for-of,6.23.0,MIT
babel-plugin-transform-es2015-function-name,6.22.0,MIT
babel-plugin-transform-es2015-literals,6.22.0,MIT
-babel-plugin-transform-es2015-modules-amd,6.22.0,MIT
-babel-plugin-transform-es2015-modules-commonjs,6.23.0,MIT
+babel-plugin-transform-es2015-modules-amd,6.24.0,MIT
+babel-plugin-transform-es2015-modules-commonjs,6.24.0,MIT
babel-plugin-transform-es2015-modules-systemjs,6.23.0,MIT
-babel-plugin-transform-es2015-modules-umd,6.23.0,MIT
+babel-plugin-transform-es2015-modules-umd,6.24.0,MIT
babel-plugin-transform-es2015-object-super,6.22.0,MIT
babel-plugin-transform-es2015-parameters,6.23.0,MIT
babel-plugin-transform-es2015-shorthand-properties,6.22.0,MIT
@@ -118,16 +121,19 @@ babel-plugin-transform-exponentiation-operator,6.22.0,MIT
babel-plugin-transform-object-rest-spread,6.23.0,MIT
babel-plugin-transform-regenerator,6.22.0,MIT
babel-plugin-transform-strict-mode,6.22.0,MIT
-babel-preset-es2015,6.22.0,MIT
+babel-preset-es2015,6.24.0,MIT
+babel-preset-es2016,6.22.0,MIT
+babel-preset-es2017,6.22.0,MIT
+babel-preset-latest,6.24.0,MIT
babel-preset-stage-2,6.22.0,MIT
babel-preset-stage-3,6.22.0,MIT
-babel-register,6.23.0,MIT
-babel-runtime,6.22.0,MIT
+babel-register,6.24.0,MIT
+babel-runtime,6.23.0,MIT
babel-template,6.23.0,MIT
babel-traverse,6.23.1,MIT
babel-types,6.23.0,MIT
babosa,1.0.2,MIT
-babylon,6.15.0,MIT
+babylon,6.16.1,MIT
backo2,1.0.2,MIT
balanced-match,0.4.2,MIT
base32,0.3.2,MIT
@@ -143,21 +149,22 @@ binary-extensions,1.8.0,MIT
bindata,2.3.5,ruby
blob,0.0.4,unknown
block-stream,0.0.9,ISC
-bluebird,3.4.7,MIT
+bluebird,3.5.0,MIT
bn.js,4.11.6,MIT
-body-parser,1.16.0,MIT
+body-parser,1.17.1,MIT
boom,2.10.1,New BSD
bootstrap-sass,3.3.6,MIT
brace-expansion,1.1.6,MIT
braces,1.8.5,MIT
-brorand,1.0.7,MIT
+brorand,1.1.0,MIT
browser,2.2.0,MIT
browserify-aes,1.0.6,MIT
browserify-cipher,1.0.0,MIT
browserify-des,1.0.0,MIT
browserify-rsa,4.0.1,MIT
-browserify-sign,4.0.0,ISC
+browserify-sign,4.0.4,ISC
browserify-zlib,0.1.4,MIT
+browserslist,1.7.7,MIT
buffer,4.9.1,MIT
buffer-shims,1.0.0,MIT
buffer-xor,1.0.3,MIT
@@ -169,8 +176,10 @@ caller-path,0.1.0,MIT
callsite,1.0.0,unknown
callsites,0.2.0,MIT
camelcase,1.2.1,MIT
+caniuse-api,1.6.1,MIT
+caniuse-db,1.0.30000649,CC-BY-4.0
carrierwave,0.11.2,MIT
-caseless,0.11.0,Apache 2.0
+caseless,0.12.0,Apache 2.0
cause,0.1,MIT
center-align,0.1.3,MIT
chalk,1.1.3,MIT
@@ -181,16 +190,24 @@ chronic_duration,0.10.6,MIT
chunky_png,1.3.5,MIT
cipher-base,1.0.3,MIT
circular-json,0.3.1,MIT
+citrus,3.0.2,MIT
+clap,1.1.3,MIT
cli-cursor,1.0.2,MIT
cli-width,2.1.0,ISC
cliui,2.1.0,ISC
clone,1.0.2,MIT
co,4.6.0,MIT
+coa,1.0.1,MIT
code-point-at,1.1.0,MIT
coercible,1.0.0,MIT
coffee-rails,4.1.1,MIT
coffee-script,2.4.1,MIT
coffee-script-source,1.10.0,MIT
+color,0.11.4,MIT
+color-convert,1.9.0,MIT
+color-name,1.1.2,MIT
+color-string,0.3.0,MIT
+colormin,1.1.2,MIT
colors,1.1.2,MIT
combine-lists,1.0.1,MIT
combined-stream,1.0.5,MIT
@@ -199,26 +216,29 @@ commondir,1.0.1,MIT
component-bind,1.0.0,unknown
component-emitter,1.2.1,MIT
component-inherit,0.0.3,unknown
-compressible,2.0.9,MIT
+compressible,2.0.10,MIT
compression,1.6.2,MIT
compression-webpack-plugin,0.3.2,MIT
concat-map,0.0.1,MIT
concat-stream,1.6.0,MIT
-concurrent-ruby,1.0.4,MIT
-connect,3.5.0,MIT
+config-chain,1.1.11,MIT
+configstore,1.4.0,Simplified BSD
+connect,3.6.0,MIT
connect-history-api-fallback,1.3.0,MIT
connection_pool,2.2.1,MIT
console-browserify,1.1.0,MIT
console-control-strings,1.1.0,ISC
+consolidate,0.14.5,MIT
constants-browserify,1.0.0,MIT
contains-path,0.1.0,MIT
content-disposition,0.5.2,MIT
content-type,1.0.2,MIT
-convert-source-map,1.3.0,MIT
+convert-source-map,1.5.0,MIT
cookie,0.3.1,MIT
cookie-signature,1.0.6,MIT
core-js,2.4.1,MIT
core-util-is,1.0.2,MIT
+cosmiconfig,2.1.1,MIT
crack,0.4.3,MIT
create-ecdh,4.0.0,MIT
create-hash,1.1.2,MIT
@@ -226,14 +246,21 @@ create-hmac,1.1.4,MIT
creole,0.5.0,ruby
cryptiles,2.0.5,New BSD
crypto-browserify,3.11.0,MIT
+css-color-names,0.0.4,MIT
+css-loader,0.28.0,MIT
+css-selector-tokenizer,0.7.0,MIT
css_parser,1.4.1,MIT
+cssesc,0.1.0,MIT
+cssnano,3.10.0,MIT
+csso,2.3.2,MIT
custom-event,1.0.1,MIT
-d,0.1.1,MIT
-d3,3.5.11,New BSD
+d,1.0.0,MIT
+d3,3.5.17,New BSD
d3_rails,3.5.11,MIT
dashdash,1.14.1,MIT
date-now,0.1.4,MIT
-debug,2.6.0,MIT
+de-indent,1.0.2,MIT
+debug,2.6.3,MIT
decamelize,1.2.0,MIT
deckar01-task_list,1.0.6,MIT
deep-extend,0.4.1,MIT
@@ -241,6 +268,7 @@ deep-is,0.1.3,MIT
default-require-extensions,1.0.0,MIT
default_value_for,3.0.2,MIT
defaults,1.0.3,MIT
+defined,1.0.0,MIT
del,2.2.2,MIT
delayed-stream,1.0.0,MIT
delegates,1.0.0,MIT
@@ -255,62 +283,74 @@ di,0.0.1,MIT
diff-lcs,1.2.5,"MIT,Perl Artistic v2,GNU GPL v2"
diffie-hellman,5.0.2,MIT
diffy,3.1.0,MIT
-doctrine,1.5.0,BSD
-document-register-element,1.3.0,MIT
+doctrine,2.0.0,Apache 2.0
+document-register-element,1.4.1,MIT
dom-serialize,2.2.1,MIT
+dom-serializer,0.1.0,MIT
domain-browser,1.1.7,MIT
domain_name,0.5.20161021,"Simplified BSD,New BSD,Mozilla Public License 2.0"
+domelementtype,1.3.0,unknown
+domhandler,2.3.0,unknown
+domutils,1.5.1,unknown
doorkeeper,4.2.0,MIT
doorkeeper-openid_connect,1.1.2,MIT
-dropzone,4.2.0,MIT
+dropzone,4.3.0,MIT
dropzonejs-rails,0.7.2,MIT
duplexer,0.1.1,MIT
+duplexify,3.5.0,MIT
ecc-jsbn,0.1.1,MIT
+editorconfig,0.13.2,MIT
ee-first,1.1.1,MIT
ejs,2.5.6,Apache 2.0
-elliptic,6.3.3,MIT
+electron-to-chromium,1.3.3,ISC
+elliptic,6.4.0,MIT
email_reply_trimmer,0.1.6,MIT
emoji-unicode-version,0.2.1,MIT
emojis-list,2.1.0,MIT
encodeurl,1.0.1,MIT
encryptor,3.0.0,MIT
-engine.io,1.8.2,MIT
-engine.io-client,1.8.2,MIT
+end-of-stream,1.0.0,MIT
+engine.io,1.8.3,MIT
+engine.io-client,1.8.3,MIT
engine.io-parser,1.3.2,MIT
enhanced-resolve,3.1.0,MIT
ent,2.2.0,MIT
+entities,1.1.1,BSD-like
equalizer,0.0.11,MIT
errno,0.1.4,MIT
-error-ex,1.3.0,MIT
+error-ex,1.3.1,MIT
erubis,2.7.0,MIT
-es5-ext,0.10.12,MIT
-es6-iterator,2.0.0,MIT
-es6-map,0.1.4,MIT
-es6-promise,4.0.5,MIT
-es6-set,0.1.4,MIT
-es6-symbol,3.1.0,MIT
-es6-weak-map,2.0.1,MIT
+es5-ext,0.10.15,MIT
+es6-iterator,2.0.1,MIT
+es6-map,0.1.5,MIT
+es6-promise,3.0.2,MIT
+es6-set,0.1.5,MIT
+es6-symbol,3.1.1,MIT
+es6-weak-map,2.0.2,MIT
escape-html,1.0.3,MIT
escape-string-regexp,1.0.5,MIT
escape_utils,1.1.1,MIT
escodegen,1.8.1,Simplified BSD
escope,3.6.0,Simplified BSD
-eslint,3.15.0,MIT
+eslint,3.19.0,MIT
eslint-config-airbnb-base,10.0.1,MIT
eslint-import-resolver-node,0.2.3,MIT
eslint-import-resolver-webpack,0.8.1,MIT
eslint-module-utils,2.0.0,MIT
eslint-plugin-filenames,1.1.0,MIT
+eslint-plugin-html,2.0.1,ISC
eslint-plugin-import,2.2.0,MIT
eslint-plugin-jasmine,2.2.0,MIT
-espree,3.4.0,Simplified BSD
-esprima,3.1.3,Simplified BSD
+espree,3.4.1,Simplified BSD
+esprima,2.7.3,Simplified BSD
+esquery,1.0.0,BSD
esrecurse,4.1.0,Simplified BSD
estraverse,4.1.1,Simplified BSD
esutils,2.0.2,BSD
-etag,1.7.0,MIT
+etag,1.8.0,MIT
eve-raphael,0.5.0,Apache 2.0
-event-emitter,0.3.4,MIT
+event-emitter,0.3.5,MIT
+event-stream,3.3.4,MIT
eventemitter3,1.2.0,MIT
events,1.1.1,MIT
eventsource,0.1.6,MIT
@@ -321,7 +361,7 @@ exit-hook,1.1.1,MIT
expand-braces,0.1.2,MIT
expand-brackets,0.1.5,MIT
expand-range,1.8.2,MIT
-express,4.14.1,MIT
+express,4.15.2,MIT
expression_parser,0.9.0,MIT
extend,3.0.0,MIT
extglob,0.3.2,MIT
@@ -332,20 +372,23 @@ faraday,0.9.2,MIT
faraday_middleware,0.10.0,MIT
faraday_middleware-multi_json,0.0.6,MIT
fast-levenshtein,2.0.6,MIT
-faye-websocket,0.10.0,MIT
+fastparse,1.1.1,MIT
+faye-websocket,0.7.3,MIT
fd-slicer,1.0.1,MIT
ffi,1.9.10,BSD
figures,1.7.0,MIT
file-entry-cache,2.0.0,MIT
+file-loader,0.11.1,MIT
filename-regex,2.0.0,MIT
fileset,2.0.3,MIT
-filesize,3.5.4,New BSD
+filesize,3.3.0,New BSD
fill-range,2.2.3,MIT
-finalhandler,0.5.1,MIT
+finalhandler,1.0.1,MIT
find-cache-dir,0.1.1,MIT
find-root,0.1.2,MIT
find-up,2.1.0,MIT
flat-cache,1.2.2,MIT
+flatten,1.0.2,MIT
flowdock,0.7.1,MIT
fog-aws,0.11.0,MIT
fog-core,1.42.0,MIT
@@ -356,20 +399,21 @@ fog-openstack,0.1.6,MIT
fog-rackspace,0.1.1,MIT
fog-xml,0.1.2,MIT
font-awesome-rails,4.7.0.1,"MIT,SIL Open Font License"
-for-in,0.1.6,MIT
-for-own,0.1.4,MIT
+for-in,1.0.2,MIT
+for-own,0.1.5,MIT
forever-agent,0.6.1,Apache 2.0
form-data,2.1.2,MIT
formatador,0.2.5,MIT
forwarded,0.1.0,MIT
-fresh,0.3.0,MIT
+fresh,0.5.0,MIT
+from,0.1.7,MIT
fs-extra,1.0.0,MIT
fs.realpath,1.0.0,ISC
fsevents,,unknown
-fstream,1.0.10,ISC
+fstream,1.0.11,ISC
fstream-ignore,1.0.5,ISC
function-bind,1.1.0,MIT
-gauge,2.7.2,ISC
+gauge,2.7.3,ISC
gemnasium-gitlab-service,0.2.6,MIT
gemojione,3.0.1,MIT
generate-function,2.0.0,MIT
@@ -377,7 +421,7 @@ generate-object-property,1.2.0,MIT
get-caller-file,1.0.2,ISC
get_process_mem,0.2.0,MIT
getpass,0.1.6,MIT
-gitaly,0.2.1,MIT
+gitaly,0.5.0,MIT
github-linguist,4.7.6,MIT
github-markup,1.4.0,MIT
gitlab-flowdock-git-hook,1.0.1,MIT
@@ -388,15 +432,16 @@ glob,7.1.1,ISC
glob-base,0.3.0,MIT
glob-parent,2.0.0,ISC
globalid,0.3.7,MIT
-globals,9.14.0,MIT
+globals,9.17.0,MIT
globby,5.0.0,MIT
gollum-grit_adapter,1.0.1,MIT
gollum-lib,4.2.1,MIT
-gollum-rugged_adapter,0.4.2,MIT
+gollum-rugged_adapter,0.4.4,MIT
gon,6.1.0,MIT
google-api-client,0.8.7,Apache 2.0
-google-protobuf,3.2.0,New BSD
+google-protobuf,3.2.0.2,New BSD
googleauth,0.5.1,Apache 2.0
+got,3.3.1,MIT
graceful-fs,4.1.11,ISC
graceful-readlink,1.0.1,MIT
grape,0.19.1,MIT
@@ -406,34 +451,40 @@ gzip-size,3.0.0,MIT
hamlit,2.6.1,MIT
handle-thing,1.2.5,MIT
handlebars,4.0.6,MIT
-har-validator,2.0.6,ISC
+har-schema,1.0.5,ISC
+har-validator,4.2.1,ISC
has,1.0.1,MIT
has-ansi,2.0.0,MIT
has-binary,0.1.7,MIT
has-cors,1.1.0,MIT
has-flag,1.0.0,MIT
has-unicode,2.0.1,ISC
+hash-sum,1.0.2,MIT
hash.js,1.0.3,MIT
hasha,2.2.0,MIT
hashie,3.5.5,MIT
hawk,3.1.3,New BSD
+he,1.1.1,MIT
health_check,2.6.0,MIT
hipchat,1.5.2,MIT
+hmac-drbg,1.0.0,MIT
hoek,2.16.3,New BSD
home-or-tmp,2.0.0,MIT
-hosted-git-info,2.2.0,ISC
+hosted-git-info,2.4.1,ISC
hpack.js,2.1.6,MIT
+html-comment-regex,1.1.1,MIT
html-entities,1.2.0,MIT
html-pipeline,1.11.0,MIT
html2text,0.2.0,MIT
htmlentities,4.3.4,MIT
+htmlparser2,3.9.2,MIT
http,0.9.8,MIT
http-cookie,1.0.3,MIT
http-deceiver,1.2.7,MIT
-http-errors,1.5.1,MIT
+http-errors,1.6.1,MIT
http-form_data,1.0.1,MIT
http-proxy,1.16.2,MIT
-http-proxy-middleware,0.17.3,MIT
+http-proxy-middleware,0.17.4,MIT
http-signature,1.1.1,MIT
http_parser.rb,0.6.0,MIT
httparty,0.13.7,MIT
@@ -442,24 +493,30 @@ https-browserify,0.0.1,MIT
i18n,0.8.1,MIT
ice_nine,0.11.2,MIT
iconv-lite,0.4.15,MIT
+icss-replace-symbols,1.0.2,ISC
ieee754,1.1.8,New BSD
-ignore,3.2.2,MIT
+ignore,3.2.6,MIT
+ignore-by-default,1.0.1,ISC
+immediate,3.0.6,MIT
imurmurhash,0.1.4,MIT
+indexes-of,1.0.1,MIT
indexof,0.0.1,unknown
+infinity-agent,2.0.3,MIT
inflight,1.0.6,ISC
influxdb,0.2.3,MIT
inherits,2.0.3,ISC
ini,1.3.4,ISC
inquirer,0.12.0,MIT
-interpret,1.0.1,MIT
+interpret,1.0.2,MIT
invariant,2.2.2,New BSD
invert-kv,1.0.0,MIT
-ipaddr.js,1.2.0,MIT
+ipaddr.js,1.3.0,MIT
ipaddress,0.8.3,MIT
is-absolute,0.2.6,MIT
+is-absolute-url,2.1.0,MIT
is-arrayish,0.2.1,MIT
is-binary-path,1.0.1,MIT
-is-buffer,1.1.4,MIT
+is-buffer,1.1.5,MIT
is-builtin-module,1.0.0,MIT
is-dotfile,1.0.2,MIT
is-equal-shallow,0.1.3,MIT
@@ -468,46 +525,52 @@ is-extglob,1.0.0,MIT
is-finite,1.0.2,MIT
is-fullwidth-code-point,1.0.0,MIT
is-glob,2.0.1,MIT
-is-my-json-valid,2.15.0,MIT
+is-my-json-valid,2.16.0,MIT
+is-npm,1.0.0,MIT
is-number,2.1.0,MIT
is-path-cwd,1.0.0,MIT
is-path-in-cwd,1.0.0,MIT
is-path-inside,1.0.0,MIT
+is-plain-obj,1.1.0,MIT
is-posix-bracket,0.1.1,MIT
is-primitive,2.0.0,MIT
is-property,1.0.2,MIT
+is-redirect,1.0.0,MIT
is-relative,0.2.1,MIT
is-resolvable,1.0.0,MIT
is-stream,1.1.0,MIT
+is-svg,2.1.0,MIT
is-typedarray,1.0.0,MIT
is-unc-path,0.1.2,MIT
is-utf8,0.2.1,MIT
is-windows,0.2.0,MIT
isarray,1.0.0,MIT
isbinaryfile,3.0.2,MIT
-isexe,1.1.2,ISC
+isexe,2.0.0,ISC
isobject,2.1.0,MIT
isstream,0.1.2,MIT
istanbul,0.4.5,New BSD
-istanbul-api,1.1.1,New BSD
-istanbul-lib-coverage,1.0.1,New BSD
-istanbul-lib-hook,1.0.0,New BSD
-istanbul-lib-instrument,1.4.2,New BSD
-istanbul-lib-report,1.0.0-alpha.3,New BSD
-istanbul-lib-source-maps,1.1.0,New BSD
-istanbul-reports,1.0.1,New BSD
+istanbul-api,1.1.7,New BSD
+istanbul-lib-coverage,1.0.2,New BSD
+istanbul-lib-hook,1.0.5,New BSD
+istanbul-lib-instrument,1.7.0,New BSD
+istanbul-lib-report,1.0.0,New BSD
+istanbul-lib-source-maps,1.1.1,New BSD
+istanbul-reports,1.0.2,New BSD
jasmine-core,2.5.2,MIT
jasmine-jquery,2.1.1,MIT
jira-ruby,1.1.2,MIT
jodid25519,1.0.2,MIT
-jquery,2.2.1,MIT
+jquery,2.2.4,MIT
jquery-atwho-rails,1.3.2,MIT
jquery-rails,4.1.1,MIT
-jquery-ujs,1.2.1,MIT
-js-cookie,2.1.3,MIT
+jquery-ujs,1.2.2,MIT
+js-base64,2.1.9,BSD
+js-beautify,1.6.12,MIT
+js-cookie,2.1.4,MIT
js-tokens,3.0.1,MIT
-js-yaml,3.8.1,MIT
-jsbn,0.1.0,BSD
+js-yaml,3.7.0,MIT
+jsbn,0.1.1,MIT
jsesc,1.3.0,MIT
json,1.8.6,ruby
json-jwt,1.7.1,MIT
@@ -520,51 +583,72 @@ json5,0.5.1,MIT
jsonfile,2.4.0,MIT
jsonify,0.0.0,Public Domain
jsonpointer,4.0.1,MIT
-jsprim,1.3.1,MIT
+jsprim,1.4.0,MIT
+jszip,3.1.3,(MIT OR GPL-3.0)
+jszip-utils,0.0.2,MIT or GPLv3
jwt,1.5.6,MIT
kaminari,0.17.0,MIT
-karma,1.4.1,MIT
-karma-coverage-istanbul-reporter,0.2.0,MIT
+karma,1.6.0,MIT
+karma-coverage-istanbul-reporter,0.2.3,MIT
karma-jasmine,1.1.0,MIT
-karma-mocha-reporter,2.2.2,MIT
-karma-phantomjs-launcher,1.0.2,MIT
+karma-mocha-reporter,2.2.3,MIT
+karma-phantomjs-launcher,1.0.4,MIT
karma-sourcemap-loader,0.3.7,MIT
-karma-webpack,2.0.2,MIT
+karma-webpack,2.0.3,MIT
kew,0.7.0,Apache 2.0
kgio,2.10.0,LGPL-2.1+
kind-of,3.1.0,MIT
klaw,1.3.1,MIT
kubeclient,2.2.0,MIT
+latest-version,1.0.1,MIT
launchy,2.4.3,ISC
lazy-cache,1.0.4,MIT
lcid,1.0.0,MIT
levn,0.3.0,MIT
licensee,8.7.0,MIT
+lie,3.1.1,MIT
little-plugger,1.1.4,MIT
load-json-file,1.1.0,MIT
loader-runner,2.3.0,MIT
-loader-utils,0.2.16,MIT
+loader-utils,0.2.17,MIT
locate-path,2.0.0,MIT
lodash,4.17.4,MIT
+lodash._baseassign,3.2.0,MIT
+lodash._basecopy,3.0.1,MIT
lodash._baseget,3.7.2,MIT
+lodash._bindcallback,3.0.1,MIT
+lodash._createassigner,3.1.1,MIT
+lodash._getnative,3.9.1,MIT
+lodash._isiterateecall,3.0.9,MIT
lodash._topath,3.8.1,MIT
-lodash.camelcase,4.1.1,MIT
+lodash.assign,3.2.0,MIT
+lodash.camelcase,4.3.0,MIT
lodash.capitalize,4.2.1,MIT
lodash.cond,4.5.2,MIT
lodash.deburr,4.1.0,MIT
-lodash.get,3.7.0,MIT
+lodash.defaults,3.1.2,MIT
+lodash.get,4.4.2,MIT
+lodash.isarguments,3.1.0,MIT
lodash.isarray,3.0.4,MIT
lodash.kebabcase,4.0.1,MIT
+lodash.keys,3.1.2,MIT
+lodash.memoize,4.1.2,MIT
+lodash.restparam,3.6.1,MIT
lodash.snakecase,4.0.1,MIT
+lodash.uniq,4.5.0,MIT
lodash.words,4.2.0,MIT
log4js,0.6.38,Apache 2.0
logging,2.1.0,MIT
longest,1.0.1,MIT
loofah,2.0.3,MIT
loose-envify,1.3.1,MIT
-lru-cache,2.2.4,MIT
+lowercase-keys,1.0.0,MIT
+lru-cache,3.2.0,ISC
+macaddress,0.2.8,MIT
mail,2.6.4,MIT
mail_room,0.9.1,MIT
+map-stream,0.1.0,unknown
+math-expression-evaluator,1.2.16,MIT
media-typer,0.3.0,MIT
memoist,0.15.0,MIT
memory-fs,0.4.1,MIT
@@ -574,16 +658,17 @@ methods,1.1.2,MIT
micromatch,2.3.11,MIT
miller-rabin,4.0.0,MIT
mime,1.3.4,MIT
-mime-db,1.26.0,MIT
+mime-db,1.27.0,MIT
mime-types,2.99.3,"MIT,Artistic-2.0,GPL-2.0"
mimemagic,0.3.0,MIT
mini_portile2,2.1.0,MIT
minimalistic-assert,1.0.0,ISC
+minimalistic-crypto-utils,1.0.1,MIT
minimatch,3.0.3,ISC
minimist,0.0.8,MIT
mkdirp,0.5.1,MIT
-moment,2.17.1,MIT
-mousetrap,1.4.6,Apache 2.0
+moment,2.18.1,MIT
+mousetrap,1.6.1,Apache 2.0
mousetrap-rails,1.4.6,"MIT,Apache"
ms,0.7.2,MIT
multi_json,1.12.1,MIT
@@ -595,17 +680,22 @@ mute-stream,0.0.5,ISC
nan,2.5.1,MIT
natural-compare,1.4.0,MIT
negotiator,0.6.1,MIT
+nested-error-stacks,1.0.2,MIT
net-ldap,0.12.1,MIT
net-ssh,3.0.1,MIT
netrc,0.11.0,MIT
node-libs-browser,2.0.0,MIT
-node-pre-gyp,0.6.33,New BSD
+node-pre-gyp,0.6.34,New BSD
node-zopfli,2.0.2,MIT
+nodemon,1.11.0,MIT
nokogiri,1.6.8.1,MIT
-nopt,3.0.6,ISC
-normalize-package-data,2.3.5,Simplified BSD
-normalize-path,2.0.1,MIT
+nopt,4.0.1,ISC
+normalize-package-data,2.3.6,Simplified BSD
+normalize-path,2.1.1,MIT
+normalize-range,0.1.2,MIT
+normalize-url,1.9.1,MIT
npmlog,4.0.2,ISC
+num2fraction,1.2.2,MIT
number-is-nan,1.0.1,MIT
numerizer,0.1.1,MIT
oauth,0.5.1,MIT
@@ -637,7 +727,7 @@ omniauth-twitter,1.2.1,MIT
omniauth_crowd,2.2.3,MIT
on-finished,2.3.0,MIT
on-headers,1.0.1,MIT
-once,1.3.3,ISC
+once,1.4.0,ISC
onetime,1.1.0,MIT
opener,1.4.3,(WTFPL OR MIT)
opn,4.0.2,MIT
@@ -652,11 +742,13 @@ os-browserify,0.2.1,MIT
os-homedir,1.0.2,MIT
os-locale,1.4.0,MIT
os-tmpdir,1.0.2,MIT
+osenv,0.1.4,ISC
p-limit,1.1.0,MIT
p-locate,2.0.0,MIT
-pako,0.2.9,MIT
+package-json,1.2.0,MIT
+pako,1.0.5,(MIT AND Zlib)
paranoia,2.2.0,MIT
-parse-asn1,5.0.0,ISC
+parse-asn1,5.1.0,ISC
parse-glob,3.0.4,MIT
parse-json,2.2.0,MIT
parsejson,0.0.3,MIT
@@ -670,8 +762,10 @@ path-is-inside,1.0.2,(WTFPL OR MIT)
path-parse,1.0.5,MIT
path-to-regexp,0.1.7,MIT
path-type,1.1.0,MIT
+pause-stream,0.0.11,"Apache2,MIT"
pbkdf2,3.0.9,MIT
pend,1.2.0,MIT
+performance-now,0.2.0,MIT
pg,0.18.4,"BSD,ruby,GPL"
phantomjs-prebuilt,2.1.14,Apache 2.0
pify,2.3.0,MIT
@@ -683,21 +777,63 @@ pkg-up,1.0.0,MIT
pluralize,1.2.1,MIT
portfinder,1.0.13,MIT
posix-spawn,0.3.11,"MIT,LGPL"
+postcss,5.2.16,MIT
+postcss-calc,5.3.1,MIT
+postcss-colormin,2.2.2,MIT
+postcss-convert-values,2.6.1,MIT
+postcss-discard-comments,2.0.4,MIT
+postcss-discard-duplicates,2.1.0,MIT
+postcss-discard-empty,2.1.0,MIT
+postcss-discard-overridden,0.1.1,MIT
+postcss-discard-unused,2.2.3,MIT
+postcss-filter-plugins,2.0.2,MIT
+postcss-load-config,1.2.0,MIT
+postcss-load-options,1.2.0,MIT
+postcss-load-plugins,2.3.0,MIT
+postcss-merge-idents,2.1.7,MIT
+postcss-merge-longhand,2.0.2,MIT
+postcss-merge-rules,2.1.2,MIT
+postcss-message-helpers,2.0.0,MIT
+postcss-minify-font-values,1.0.5,MIT
+postcss-minify-gradients,1.0.5,MIT
+postcss-minify-params,1.2.2,MIT
+postcss-minify-selectors,2.1.1,MIT
+postcss-modules-extract-imports,1.0.1,ISC
+postcss-modules-local-by-default,1.1.1,MIT
+postcss-modules-scope,1.0.2,ISC
+postcss-modules-values,1.2.2,ISC
+postcss-normalize-charset,1.1.1,MIT
+postcss-normalize-url,3.0.8,MIT
+postcss-ordered-values,2.2.3,MIT
+postcss-reduce-idents,2.4.0,MIT
+postcss-reduce-initial,1.0.1,MIT
+postcss-reduce-transforms,1.0.4,MIT
+postcss-selector-parser,2.2.3,MIT
+postcss-svgo,2.1.6,MIT
+postcss-unique-selectors,2.0.2,MIT
+postcss-value-parser,3.3.0,MIT
+postcss-zindex,2.2.0,MIT
prelude-ls,1.1.2,MIT
premailer,1.8.6,New BSD
premailer-rails,1.9.2,MIT
+prepend-http,1.0.4,MIT
preserve,0.2.0,MIT
private,0.1.7,MIT
process,0.11.9,MIT
process-nextick-args,1.0.7,MIT
progress,1.1.8,MIT
-proxy-addr,1.1.3,MIT
+proto-list,1.2.4,ISC
+proxy-addr,1.1.4,MIT
prr,0.0.0,MIT
+ps-tree,1.1.0,MIT
+pseudomap,1.0.2,ISC
public-encrypt,4.0.0,MIT
punycode,1.4.1,MIT
pyu-ruby-sasl,0.0.3.3,MIT
+q,1.5.0,MIT
qjobs,1.1.5,MIT
-qs,6.2.0,New BSD
+qs,6.4.0,New BSD
+query-string,4.3.2,MIT
querystring,0.2.0,MIT
querystring-es3,0.2.1,MIT
querystringify,0.0.4,MIT
@@ -723,16 +859,19 @@ range-parser,1.2.0,MIT
raphael,2.2.7,MIT
raw-body,2.2.0,MIT
raw-loader,0.5.1,MIT
-rc,1.1.6,(BSD-2-Clause OR MIT OR Apache-2.0)
+rc,1.2.1,(BSD-2-Clause OR MIT OR Apache-2.0)
rdoc,4.2.2,ruby
+react-dev-utils,0.5.2,New BSD
+read-all-stream,3.1.0,MIT
read-pkg,1.1.0,MIT
read-pkg-up,1.0.1,MIT
-readable-stream,2.1.5,MIT
+readable-stream,2.0.6,MIT
readdirp,2.1.0,MIT
readline2,1.0.1,MIT
recaptcha,3.0.0,MIT
rechoir,0.6.2,MIT
recursive-open-struct,1.0.0,MIT
+recursive-readdir,2.1.1,MIT
redcarpet,3.4.0,MIT
redis,3.2.2,MIT
redis-actionpack,5.0.1,MIT
@@ -741,31 +880,36 @@ redis-namespace,1.5.2,MIT
redis-rack,1.6.0,MIT
redis-rails,5.0.1,MIT
redis-store,1.2.0,MIT
+reduce-css-calc,1.3.0,MIT
+reduce-function-call,1.0.2,MIT
regenerate,1.3.2,MIT
-regenerator-runtime,0.10.1,MIT
+regenerator-runtime,0.10.3,MIT
regenerator-transform,0.9.8,BSD
regex-cache,0.4.3,MIT
regexpu-core,2.0.0,MIT
+registry-url,3.1.0,MIT
regjsgen,0.2.0,MIT
regjsparser,0.1.5,BSD
+remove-trailing-separator,1.0.1,ISC
repeat-element,1.1.2,MIT
repeat-string,1.6.1,MIT
repeating,2.0.1,MIT
-request,2.79.0,Apache 2.0
+request,2.81.0,Apache 2.0
request-progress,2.0.1,MIT
request_store,1.3.1,MIT
require-directory,2.1.1,MIT
+require-from-string,1.2.1,MIT
require-main-filename,1.0.1,ISC
require-uncached,1.0.3,MIT
requires-port,1.0.0,MIT
-resolve,1.2.0,MIT
+resolve,1.3.2,MIT
resolve-from,1.0.1,MIT
responders,2.3.0,MIT
rest-client,2.0.0,MIT
restore-cursor,1.0.1,MIT
retriable,1.4.1,MIT
right-align,0.1.3,MIT
-rimraf,2.5.4,ISC
+rimraf,2.6.1,ISC
rinku,2.0.0,ISC
ripemd160,1.0.1,New BSD
rotp,2.1.2,MIT
@@ -778,7 +922,7 @@ ruby-saml,1.4.1,MIT
rubyntlm,0.5.2,MIT
rubypants,0.2.0,BSD
rufus-scheduler,3.1.10,MIT
-rugged,0.24.0,MIT
+rugged,0.25.1.1,MIT
run-async,0.1.0,MIT
rx-lite,3.1.2,Apache 2.0
safe-buffer,5.0.1,MIT
@@ -787,158 +931,190 @@ sanitize,2.1.0,MIT
sass,3.4.22,MIT
sass-rails,5.0.6,MIT
sawyer,0.8.1,MIT
+sax,1.2.2,ISC
securecompare,1.0.0,MIT
seed-fu,2.3.6,MIT
select-hose,2.0.0,MIT
select2,3.5.2-browserify,unknown
select2-rails,3.5.9.3,MIT
semver,5.3.0,ISC
-send,0.14.2,MIT
-sentry-raven,2.0.2,Apache 2.0
+semver-diff,2.1.0,MIT
+send,0.15.1,MIT
+sentry-raven,2.4.0,Apache 2.0
serve-index,1.8.0,MIT
-serve-static,1.11.2,MIT
+serve-static,1.12.1,MIT
set-blocking,2.0.0,ISC
set-immediate-shim,1.0.1,MIT
setimmediate,1.0.5,MIT
-setprototypeof,1.0.2,ISC
+setprototypeof,1.0.3,ISC
settingslogic,2.0.9,MIT
sha.js,2.4.8,MIT
-shelljs,0.7.6,New BSD
+shelljs,0.7.7,New BSD
sidekiq,4.2.7,LGPL
sidekiq-cron,0.4.4,MIT
sidekiq-limit_fetch,3.4.0,MIT
+sigmund,1.0.1,ISC
signal-exit,3.0.2,ISC
signet,0.7.3,Apache 2.0
slack-notifier,1.5.1,MIT
slash,1.0.0,MIT
slice-ansi,0.0.4,MIT
+slide,1.1.6,ISC
sntp,1.0.9,BSD
-socket.io,1.7.2,MIT
+socket.io,1.7.3,MIT
socket.io-adapter,0.5.0,MIT
-socket.io-client,1.7.2,MIT
+socket.io-client,1.7.3,MIT
socket.io-parser,2.3.1,MIT
sockjs,0.3.18,MIT
-sockjs-client,1.1.1,MIT
+sockjs-client,1.0.1,MIT
+sort-keys,1.1.2,MIT
source-list-map,0.1.8,MIT
source-map,0.5.6,New BSD
-source-map-support,0.4.11,MIT
+source-map-support,0.4.14,MIT
spdx-correct,1.0.2,Apache 2.0
spdx-expression-parse,1.0.4,(MIT AND CC-BY-3.0)
spdx-license-ids,1.2.2,Unlicense
spdy,3.4.4,MIT
spdy-transport,2.0.18,MIT
+split,0.3.3,MIT
sprintf-js,1.0.3,New BSD
sprockets,3.7.1,MIT
sprockets-rails,3.2.0,MIT
-sshpk,1.10.2,MIT
+sshpk,1.11.0,MIT
state_machines,0.4.0,MIT
state_machines-activemodel,0.4.0,MIT
state_machines-activerecord,0.4.0,MIT
stats-webpack-plugin,0.4.3,MIT
statuses,1.3.1,MIT
stream-browserify,2.0.1,MIT
-stream-http,2.6.3,MIT
+stream-combiner,0.0.4,MIT
+stream-http,2.7.0,MIT
+stream-shift,1.0.0,MIT
+strict-uri-encode,1.1.0,MIT
+string-length,1.0.1,MIT
string-width,1.0.2,MIT
-string.fromcodepoint,0.2.1,MIT
-string.prototype.codepointat,0.2.0,MIT
string_decoder,0.10.31,MIT
stringex,2.5.2,MIT
stringstream,0.0.5,MIT
strip-ansi,3.0.1,MIT
strip-bom,2.0.0,MIT
-strip-json-comments,1.0.4,MIT
-supports-color,0.2.0,MIT
+strip-json-comments,2.0.1,MIT
+supports-color,3.2.3,MIT
+svgo,0.7.2,MIT
sys-filesystem,1.1.6,Artistic 2.0
table,3.8.3,New BSD
tapable,0.2.6,MIT
tar,2.2.1,ISC
-tar-pack,3.3.0,Simplified BSD
+tar-pack,3.4.0,Simplified BSD
temple,0.7.7,MIT
-test-exclude,4.0.0,ISC
+test-exclude,4.0.3,ISC
text-table,0.2.0,MIT
thor,0.19.4,MIT
thread_safe,0.3.6,Apache 2.0
+three,0.84.0,MIT
+three-orbit-controls,82.1.0,MIT
+three-stl-loader,1.0.4,MIT
throttleit,1.0.0,MIT
through,2.3.8,MIT
tilt,2.0.6,MIT
timeago.js,2.0.5,MIT
+timed-out,2.0.0,MIT
timers-browserify,2.0.2,MIT
timfel-krb5-auth,0.8.3,LGPL
-tmp,0.0.28,MIT
+tmp,0.0.31,MIT
to-array,0.1.4,MIT
to-arraybuffer,1.0.1,MIT
to-fast-properties,1.0.2,MIT
+toml-rb,0.3.15,MIT
tool,0.2.3,MIT
+touch,1.0.0,ISC
tough-cookie,2.3.2,New BSD
+traverse,0.6.6,MIT
trim-right,1.0.1,MIT
truncato,0.7.8,MIT
tryit,1.0.3,MIT
tty-browserify,0.0.0,MIT
-tunnel-agent,0.4.3,Apache 2.0
+tunnel-agent,0.6.0,Apache 2.0
tweetnacl,0.14.5,Unlicense
type-check,0.3.2,MIT
-type-is,1.6.14,MIT
+type-is,1.6.15,MIT
typedarray,0.0.6,MIT
tzinfo,1.2.2,MIT
u2f,0.2.1,MIT
uglifier,2.7.2,MIT
-uglify-js,2.7.5,Simplified BSD
+uglify-js,2.8.21,Simplified BSD
uglify-to-browserify,1.0.2,MIT
uid-number,0.0.6,ISC
ultron,1.0.2,MIT
unc-path-regex,0.1.2,MIT
+undefsafe,0.0.3,MIT / http://rem.mit-license.org
underscore,1.8.3,MIT
underscore-rails,1.8.3,MIT
unf,0.1.4,BSD
unf_ext,0.0.7.2,MIT
unicorn,5.1.0,ruby
unicorn-worker-killer,0.4.4,ruby
+uniq,1.0.1,MIT
+uniqid,4.1.1,MIT
+uniqs,2.0.0,MIT
unpipe,1.0.0,MIT
+update-notifier,0.5.0,Simplified BSD
url,0.11.0,MIT
url-parse,1.0.5,MIT
url_safe_base64,0.2.2,MIT
user-home,2.0.0,MIT
-useragent,2.1.12,MIT
+useragent,2.1.13,MIT
util,0.10.3,MIT
util-deprecate,1.0.2,MIT
utils-merge,1.0.0,MIT
uuid,3.0.1,MIT
validate-npm-package-license,3.0.1,Apache 2.0
validates_hostname,1.0.6,MIT
-vary,1.1.0,MIT
+vary,1.1.1,MIT
+vendors,1.0.1,MIT
verror,1.3.6,MIT
version_sorter,2.1.0,MIT
virtus,1.0.5,MIT
+visibilityjs,1.2.4,MIT
vm-browserify,0.0.4,MIT
vmstat,2.3.0,MIT
void-elements,2.0.1,MIT
-vue,2.1.10,MIT
+vue,2.2.6,MIT
+vue-hot-reload-api,2.0.11,MIT
+vue-loader,11.3.4,MIT
vue-resource,0.9.3,MIT
+vue-style-loader,2.0.5,MIT
+vue-template-compiler,2.2.6,MIT
+vue-template-es2015-compiler,1.5.2,MIT
warden,1.2.6,MIT
-watchpack,1.2.1,MIT
+watchpack,1.3.1,MIT
wbuf,1.7.2,MIT
-webpack,2.2.1,MIT
-webpack-bundle-analyzer,2.3.0,MIT
-webpack-dev-middleware,1.10.0,MIT
-webpack-dev-server,2.3.0,MIT
-webpack-rails,0.9.9,MIT
-webpack-sources,0.1.4,MIT
+webpack,2.3.3,MIT
+webpack-bundle-analyzer,2.3.1,MIT
+webpack-dev-middleware,1.10.1,MIT
+webpack-dev-server,2.4.2,MIT
+webpack-rails,0.9.10,MIT
+webpack-sources,0.1.5,MIT
websocket-driver,0.6.5,MIT
websocket-extensions,0.1.1,MIT
-which,1.2.12,ISC
+whet.extend,0.9.9,MIT
+which,1.2.14,ISC
which-module,1.0.0,ISC
wide-align,1.1.0,ISC
wikicloth,0.8.1,MIT
window-size,0.1.0,MIT
-wordwrap,0.0.2,MIT/X11
+wordwrap,1.0.0,MIT
wrap-ansi,2.1.0,MIT
wrappy,1.0.2,ISC
write,0.2.1,MIT
-ws,1.1.1,MIT
+write-file-atomic,1.3.1,ISC
+ws,1.1.2,MIT
wtf-8,1.0.0,MIT
+xdg-basedir,2.0.0,MIT
xmlhttprequest-ssl,1.5.3,MIT
xtend,4.0.1,MIT
y18n,3.2.1,ISC
+yallist,2.1.2,ISC
yargs,3.10.0,MIT
yargs-parser,4.2.1,ISC
yauzl,2.4.1,MIT
diff --git a/yarn.lock b/yarn.lock
index 2434b3a8a48..e16cd9c3673 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -60,6 +60,10 @@ align-text@^0.1.1, align-text@^0.1.3:
longest "^1.0.1"
repeat-string "^1.5.2"
+alphanum-sort@^1.0.1, alphanum-sort@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3"
+
amdefine@>=0.0.4:
version "1.0.1"
resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
@@ -68,6 +72,10 @@ ansi-escapes@^1.1.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e"
+ansi-html@0.0.5:
+ version "0.0.5"
+ resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.5.tgz#0dcaa5a081206866bc240a3b773a184ea3b88b64"
+
ansi-html@0.0.7:
version "0.0.7"
resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e"
@@ -206,6 +214,17 @@ asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
+autoprefixer@^6.3.1:
+ version "6.7.7"
+ resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.7.7.tgz#1dbd1c835658e35ce3f9984099db00585c782014"
+ dependencies:
+ browserslist "^1.7.6"
+ caniuse-db "^1.0.30000634"
+ normalize-range "^0.1.2"
+ num2fraction "^1.2.2"
+ postcss "^5.2.16"
+ postcss-value-parser "^3.2.3"
+
aws-sign2@~0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f"
@@ -214,7 +233,7 @@ aws4@^1.2.1:
version "1.6.0"
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e"
-babel-code-frame@^6.16.0, babel-code-frame@^6.22.0:
+babel-code-frame@^6.11.0, babel-code-frame@^6.16.0, babel-code-frame@^6.22.0:
version "6.22.0"
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.22.0.tgz#027620bee567a88c32561574e7fd0801d33118e4"
dependencies:
@@ -805,7 +824,7 @@ backo2@1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947"
-balanced-match@^0.4.1:
+balanced-match@^0.4.1, balanced-match@^0.4.2:
version "0.4.2"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838"
@@ -855,7 +874,7 @@ block-stream@*:
dependencies:
inherits "~2.0.0"
-bluebird@^3.3.0:
+bluebird@^3.0.5, bluebird@^3.1.1, bluebird@^3.3.0:
version "3.4.7"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.7.tgz#f72d760be09b7f76d08ed8fae98b289a8d05fab3"
@@ -964,6 +983,13 @@ browserify-zlib@^0.1.4:
dependencies:
pako "~0.2.0"
+browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6:
+ version "1.7.7"
+ resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.7.7.tgz#0bd76704258be829b2398bb50e4b62d1a166b0b9"
+ dependencies:
+ caniuse-db "^1.0.30000639"
+ electron-to-chromium "^1.2.7"
+
buffer-shims@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51"
@@ -1018,6 +1044,19 @@ camelcase@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a"
+caniuse-api@^1.5.2:
+ version "1.6.1"
+ resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-1.6.1.tgz#b534e7c734c4f81ec5fbe8aca2ad24354b962c6c"
+ dependencies:
+ browserslist "^1.3.6"
+ caniuse-db "^1.0.30000529"
+ lodash.memoize "^4.1.2"
+ lodash.uniq "^4.5.0"
+
+caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639:
+ version "1.0.30000649"
+ resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000649.tgz#1ee1754a6df235450c8b7cd15e0ebf507221a86a"
+
caseless@~0.11.0:
version "0.11.0"
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7"
@@ -1064,6 +1103,12 @@ circular-json@^0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.1.tgz#be8b36aefccde8b3ca7aa2d6afc07a37242c0d2d"
+clap@^1.0.9:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/clap/-/clap-1.1.3.tgz#b3bd36e93dd4cbfb395a3c26896352445265c05b"
+ dependencies:
+ chalk "^1.1.3"
+
cli-cursor@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987"
@@ -1098,11 +1143,49 @@ co@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
+coa@~1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/coa/-/coa-1.0.1.tgz#7f959346cfc8719e3f7233cd6852854a7c67d8a3"
+ dependencies:
+ q "^1.1.2"
+
code-point-at@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
-colors@^1.1.0:
+color-convert@^1.3.0:
+ version "1.9.0"
+ resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.0.tgz#1accf97dd739b983bf994d56fec8f95853641b7a"
+ dependencies:
+ color-name "^1.1.1"
+
+color-name@^1.0.0, color-name@^1.1.1:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.2.tgz#5c8ab72b64bd2215d617ae9559ebb148475cf98d"
+
+color-string@^0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/color-string/-/color-string-0.3.0.tgz#27d46fb67025c5c2fa25993bfbf579e47841b991"
+ dependencies:
+ color-name "^1.0.0"
+
+color@^0.11.0:
+ version "0.11.4"
+ resolved "https://registry.yarnpkg.com/color/-/color-0.11.4.tgz#6d7b5c74fb65e841cd48792ad1ed5e07b904d764"
+ dependencies:
+ clone "^1.0.2"
+ color-convert "^1.3.0"
+ color-string "^0.3.0"
+
+colormin@^1.0.5:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/colormin/-/colormin-1.1.2.tgz#ea2f7420a72b96881a38aae59ec124a6f7298133"
+ dependencies:
+ color "^0.11.0"
+ css-color-names "0.0.4"
+ has "^1.0.1"
+
+colors@^1.1.0, colors@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63"
@@ -1190,6 +1273,26 @@ concat-stream@^1.4.6:
readable-stream "^2.2.2"
typedarray "^0.0.6"
+config-chain@~1.1.5:
+ version "1.1.11"
+ resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.11.tgz#aba09747dfbe4c3e70e766a6e41586e1859fc6f2"
+ dependencies:
+ ini "^1.3.4"
+ proto-list "~1.2.1"
+
+configstore@^1.0.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/configstore/-/configstore-1.4.0.tgz#c35781d0501d268c25c54b8b17f6240e8a4fb021"
+ dependencies:
+ graceful-fs "^4.1.2"
+ mkdirp "^0.5.0"
+ object-assign "^4.0.1"
+ os-tmpdir "^1.0.0"
+ osenv "^0.1.0"
+ uuid "^2.0.1"
+ write-file-atomic "^1.1.2"
+ xdg-basedir "^2.0.0"
+
connect-history-api-fallback@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.3.0.tgz#e51d17f8f0ef0db90a64fdb47de3051556e9f169"
@@ -1213,6 +1316,12 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
+consolidate@^0.14.0:
+ version "0.14.5"
+ resolved "https://registry.yarnpkg.com/consolidate/-/consolidate-0.14.5.tgz#5a25047bc76f73072667c8cb52c989888f494c63"
+ dependencies:
+ bluebird "^3.1.1"
+
constants-browserify@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75"
@@ -1253,6 +1362,17 @@ core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
+cosmiconfig@^2.1.0, cosmiconfig@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-2.1.1.tgz#817f2c2039347a1e9bf7d090c0923e53f749ca82"
+ dependencies:
+ js-yaml "^3.4.3"
+ minimist "^1.2.0"
+ object-assign "^4.1.0"
+ os-homedir "^1.0.1"
+ parse-json "^2.2.0"
+ require-from-string "^1.1.0"
+
create-ecdh@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.0.tgz#888c723596cdf7612f6498233eebd7a35301737d"
@@ -1297,6 +1417,91 @@ crypto-browserify@^3.11.0:
public-encrypt "^4.0.0"
randombytes "^2.0.0"
+css-color-names@0.0.4:
+ version "0.0.4"
+ resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0"
+
+css-loader@^0.28.0:
+ version "0.28.0"
+ resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-0.28.0.tgz#417cfa9789f8cde59a30ccbf3e4da7a806889bad"
+ dependencies:
+ babel-code-frame "^6.11.0"
+ css-selector-tokenizer "^0.7.0"
+ cssnano ">=2.6.1 <4"
+ loader-utils "^1.0.2"
+ lodash.camelcase "^4.3.0"
+ object-assign "^4.0.1"
+ postcss "^5.0.6"
+ postcss-modules-extract-imports "^1.0.0"
+ postcss-modules-local-by-default "^1.0.1"
+ postcss-modules-scope "^1.0.0"
+ postcss-modules-values "^1.1.0"
+ source-list-map "^0.1.7"
+
+css-selector-tokenizer@^0.6.0:
+ version "0.6.0"
+ resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.6.0.tgz#6445f582c7930d241dcc5007a43d6fcb8f073152"
+ dependencies:
+ cssesc "^0.1.0"
+ fastparse "^1.1.1"
+ regexpu-core "^1.0.0"
+
+css-selector-tokenizer@^0.7.0:
+ version "0.7.0"
+ resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz#e6988474ae8c953477bf5e7efecfceccd9cf4c86"
+ dependencies:
+ cssesc "^0.1.0"
+ fastparse "^1.1.1"
+ regexpu-core "^1.0.0"
+
+cssesc@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4"
+
+"cssnano@>=2.6.1 <4":
+ version "3.10.0"
+ resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-3.10.0.tgz#4f38f6cea2b9b17fa01490f23f1dc68ea65c1c38"
+ dependencies:
+ autoprefixer "^6.3.1"
+ decamelize "^1.1.2"
+ defined "^1.0.0"
+ has "^1.0.1"
+ object-assign "^4.0.1"
+ postcss "^5.0.14"
+ postcss-calc "^5.2.0"
+ postcss-colormin "^2.1.8"
+ postcss-convert-values "^2.3.4"
+ postcss-discard-comments "^2.0.4"
+ postcss-discard-duplicates "^2.0.1"
+ postcss-discard-empty "^2.0.1"
+ postcss-discard-overridden "^0.1.1"
+ postcss-discard-unused "^2.2.1"
+ postcss-filter-plugins "^2.0.0"
+ postcss-merge-idents "^2.1.5"
+ postcss-merge-longhand "^2.0.1"
+ postcss-merge-rules "^2.0.3"
+ postcss-minify-font-values "^1.0.2"
+ postcss-minify-gradients "^1.0.1"
+ postcss-minify-params "^1.0.4"
+ postcss-minify-selectors "^2.0.4"
+ postcss-normalize-charset "^1.1.0"
+ postcss-normalize-url "^3.0.7"
+ postcss-ordered-values "^2.1.0"
+ postcss-reduce-idents "^2.2.2"
+ postcss-reduce-initial "^1.0.0"
+ postcss-reduce-transforms "^1.0.3"
+ postcss-svgo "^2.1.1"
+ postcss-unique-selectors "^2.0.2"
+ postcss-value-parser "^3.2.3"
+ postcss-zindex "^2.0.1"
+
+csso@~2.3.1:
+ version "2.3.2"
+ resolved "https://registry.yarnpkg.com/csso/-/csso-2.3.2.tgz#ddd52c587033f49e94b71fc55569f252e8ff5f85"
+ dependencies:
+ clap "^1.0.9"
+ source-map "^0.5.3"
+
custom-event@~1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz#5d02a46850adf1b4a317946a3928fccb5bfd0425"
@@ -1321,6 +1526,10 @@ date-now@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b"
+de-indent@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
+
debug@0.7.4:
version "0.7.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-0.7.4.tgz#06e1ea8082c2cb14e39806e22e2f6f757f92af39"
@@ -1337,13 +1546,13 @@ debug@2.3.3:
dependencies:
ms "0.7.2"
-debug@2.6.0, debug@^2.1.1, debug@^2.2.0:
+debug@2.6.0, debug@^2.1.0, debug@^2.1.1, debug@^2.2.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.0.tgz#bc596bcabe7617f11d9fa15361eded5608b8499b"
dependencies:
ms "0.7.2"
-decamelize@^1.0.0, decamelize@^1.1.1:
+decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2:
version "1.2.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
@@ -1367,6 +1576,10 @@ defaults@^1.0.2:
dependencies:
clone "^1.0.2"
+defined@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693"
+
del@^2.0.2:
version "2.2.2"
resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8"
@@ -1440,24 +1653,70 @@ dom-serialize@^2.2.0:
extend "^3.0.0"
void-elements "^2.0.0"
+dom-serializer@0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82"
+ dependencies:
+ domelementtype "~1.1.1"
+ entities "~1.1.1"
+
domain-browser@^1.1.1:
version "1.1.7"
resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc"
+domelementtype@1, domelementtype@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2"
+
+domelementtype@~1.1.1:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b"
+
+domhandler@^2.3.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.3.0.tgz#2de59a0822d5027fabff6f032c2b25a2a8abe738"
+ dependencies:
+ domelementtype "1"
+
+domutils@^1.5.1:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf"
+ dependencies:
+ dom-serializer "0"
+ domelementtype "1"
+
dropzone@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/dropzone/-/dropzone-4.2.0.tgz#fbe7acbb9918e0706489072ef663effeef8a79f3"
-duplexer@^0.1.1:
+duplexer@^0.1.1, duplexer@~0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1"
+duplexify@^3.2.0:
+ version "3.5.0"
+ resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.5.0.tgz#1aa773002e1578457e9d9d4a50b0ccaaebcbd604"
+ dependencies:
+ end-of-stream "1.0.0"
+ inherits "^2.0.1"
+ readable-stream "^2.0.0"
+ stream-shift "^1.0.0"
+
ecc-jsbn@~0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505"
dependencies:
jsbn "~0.1.0"
+editorconfig@^0.13.2:
+ version "0.13.2"
+ resolved "https://registry.yarnpkg.com/editorconfig/-/editorconfig-0.13.2.tgz#8e57926d9ee69ab6cb999f027c2171467acceb35"
+ dependencies:
+ bluebird "^3.0.5"
+ commander "^2.9.0"
+ lru-cache "^3.2.0"
+ sigmund "^1.0.1"
+
ee-first@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
@@ -1466,6 +1725,10 @@ ejs@^2.5.5:
version "2.5.6"
resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.6.tgz#479636bfa3fe3b1debd52087f0acb204b4f19c88"
+electron-to-chromium@^1.2.7:
+ version "1.3.3"
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.3.tgz#651eb63fe89f39db70ffc8dbd5d9b66958bc6a0e"
+
elliptic@^6.0.0:
version "6.3.3"
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.3.3.tgz#5482d9646d54bcb89fd7d994fc9e2e9568876e3f"
@@ -1487,6 +1750,12 @@ encodeurl@~1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.1.tgz#79e3d58655346909fe6f0f45a5de68103b294d20"
+end-of-stream@1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.0.0.tgz#d4596e702734a93e40e9af864319eabd99ff2f0e"
+ dependencies:
+ once "~1.3.0"
+
engine.io-client@1.8.2:
version "1.8.2"
resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-1.8.2.tgz#c38767547f2a7d184f5752f6f0ad501006703766"
@@ -1547,6 +1816,10 @@ ent@~2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d"
+entities@^1.1.1, entities@~1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0"
+
errno@^0.1.3:
version "0.1.4"
resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.4.tgz#b896e23a9e5e8ba33871fc996abd3635fc9a1c7d"
@@ -1585,7 +1858,7 @@ es6-map@^0.1.3:
es6-symbol "~3.1.0"
event-emitter "~0.3.4"
-es6-promise@~3.0.2:
+es6-promise@^3.0.2, es6-promise@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.0.2.tgz#010d5858423a5f118979665f46486a95c6ee2bb6"
@@ -1623,7 +1896,7 @@ escape-html@~1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
-escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
+escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
@@ -1690,6 +1963,12 @@ eslint-plugin-filenames@^1.1.0:
lodash.kebabcase "4.0.1"
lodash.snakecase "4.0.1"
+eslint-plugin-html@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-html/-/eslint-plugin-html-2.0.1.tgz#3a829510e82522f1e2e44d55d7661a176121fce1"
+ dependencies:
+ htmlparser2 "^3.8.2"
+
eslint-plugin-import@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.2.0.tgz#72ba306fad305d67c4816348a4699a4229ac8b4e"
@@ -1755,7 +2034,7 @@ espree@^3.4.0:
acorn "4.0.4"
acorn-jsx "^3.0.0"
-esprima@2.7.x, esprima@^2.7.1:
+esprima@2.7.x, esprima@^2.6.0, esprima@^2.7.1:
version "2.7.3"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581"
@@ -1801,6 +2080,18 @@ event-emitter@~0.3.4:
d "~0.1.1"
es5-ext "~0.10.7"
+event-stream@~3.3.0:
+ version "3.3.4"
+ resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571"
+ dependencies:
+ duplexer "~0.1.1"
+ from "~0"
+ map-stream "~0.1.0"
+ pause-stream "0.0.11"
+ split "0.3"
+ stream-combiner "~0.0.4"
+ through "~2.3.1"
+
eventemitter3@1.x.x:
version "1.2.0"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.2.0.tgz#1c86991d816ad1e504750e73874224ecf3bec508"
@@ -1809,7 +2100,7 @@ events@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924"
-eventsource@0.1.6:
+eventsource@0.1.6, eventsource@^0.1.3:
version "0.1.6"
resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-0.1.6.tgz#0acede849ed7dd1ccc32c811bb11b944d4f29232"
dependencies:
@@ -1910,6 +2201,10 @@ fast-levenshtein@~2.0.4:
version "2.0.6"
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
+fastparse@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.1.tgz#d1e2643b38a94d7583b479060e6c4affc94071f8"
+
faye-websocket@^0.10.0:
version "0.10.0"
resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4"
@@ -1922,6 +2217,12 @@ faye-websocket@~0.11.0:
dependencies:
websocket-driver ">=0.5.1"
+faye-websocket@~0.7.3:
+ version "0.7.3"
+ resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.7.3.tgz#cc4074c7f4a4dfd03af54dd65c354b135132ce11"
+ dependencies:
+ websocket-driver ">=0.3.6"
+
fd-slicer@~1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.0.1.tgz#8b5bcbd9ec327c5041bf9ab023fd6750f1177e65"
@@ -1959,6 +2260,10 @@ fileset@^2.0.2:
glob "^7.0.3"
minimatch "^3.0.3"
+filesize@3.3.0:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.3.0.tgz#53149ea3460e3b2e024962a51648aa572cf98122"
+
filesize@^3.5.4:
version "3.5.4"
resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.5.4.tgz#742fc7fb6aef4ee3878682600c22f840731e1fda"
@@ -2027,6 +2332,10 @@ flat-cache@^1.2.1:
graceful-fs "^4.1.2"
write "^0.2.1"
+flatten@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782"
+
for-in@^0.1.5:
version "0.1.6"
resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.6.tgz#c9f96e89bfad18a545af5ec3ed352a1d9e5b4dc8"
@@ -2057,6 +2366,10 @@ fresh@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.3.0.tgz#651f838e22424e7566de161d8358caa199f83d4f"
+from@~0:
+ version "0.1.7"
+ resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe"
+
fs-extra@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-1.0.0.tgz#cd3ce5f7e7cb6145883fcae3191e9877f8587950"
@@ -2180,7 +2493,22 @@ globby@^5.0.0:
pify "^2.0.0"
pinkie-promise "^2.0.0"
-graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9:
+got@^3.2.0:
+ version "3.3.1"
+ resolved "https://registry.yarnpkg.com/got/-/got-3.3.1.tgz#e5d0ed4af55fc3eef4d56007769d98192bcb2eca"
+ dependencies:
+ duplexify "^3.2.0"
+ infinity-agent "^2.0.0"
+ is-redirect "^1.0.0"
+ is-stream "^1.0.0"
+ lowercase-keys "^1.0.0"
+ nested-error-stacks "^1.0.0"
+ object-assign "^3.0.0"
+ prepend-http "^1.0.0"
+ read-all-stream "^3.0.0"
+ timed-out "^2.0.0"
+
+graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9:
version "4.1.11"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
@@ -2188,7 +2516,7 @@ graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9:
version "1.0.1"
resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
-gzip-size@^3.0.0:
+gzip-size@3.0.0, gzip-size@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-3.0.0.tgz#546188e9bdc337f673772f81660464b389dce520"
dependencies:
@@ -2247,6 +2575,10 @@ has@^1.0.1:
dependencies:
function-bind "^1.0.2"
+hash-sum@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/hash-sum/-/hash-sum-1.0.2.tgz#33b40777754c6432573c120cc3808bbd10d47f04"
+
hash.js@^1.0.0:
version "1.0.3"
resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.0.3.tgz#1332ff00156c0a0ffdd8236013d07b77a0451573"
@@ -2269,6 +2601,10 @@ hawk@~3.1.3:
hoek "2.x.x"
sntp "1.x.x"
+he@^1.1.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd"
+
hoek@2.x.x:
version "2.16.3"
resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed"
@@ -2293,10 +2629,25 @@ hpack.js@^2.1.6:
readable-stream "^2.0.1"
wbuf "^1.1.0"
-html-entities@^1.2.0:
+html-comment-regex@^1.1.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.1.tgz#668b93776eaae55ebde8f3ad464b307a4963625e"
+
+html-entities@1.2.0, html-entities@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.0.tgz#41948caf85ce82fed36e4e6a0ed371a6664379e2"
+htmlparser2@^3.8.2:
+ version "3.9.2"
+ resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb00b338"
+ dependencies:
+ domelementtype "^1.3.0"
+ domhandler "^2.3.0"
+ domutils "^1.5.1"
+ entities "^1.1.1"
+ inherits "^2.0.1"
+ readable-stream "^2.0.2"
+
http-deceiver@^1.2.4:
version "1.2.7"
resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87"
@@ -2341,10 +2692,18 @@ iconv-lite@0.4.15:
version "0.4.15"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb"
+icss-replace-symbols@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.0.2.tgz#cb0b6054eb3af6edc9ab1d62d01933e2d4c8bfa5"
+
ieee754@^1.1.4:
version "1.1.8"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4"
+ignore-by-default@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09"
+
ignore@^3.2.0:
version "3.2.2"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.2.2.tgz#1c51e1ef53bab6ddc15db4d9ac4ec139eceb3410"
@@ -2357,10 +2716,18 @@ imurmurhash@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
+indexes-of@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607"
+
indexof@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d"
+infinity-agent@^2.0.0:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/infinity-agent/-/infinity-agent-2.0.3.tgz#45e0e2ff7a9eb030b27d62b74b3744b7a7ac4216"
+
inflight@^1.0.4:
version "1.0.6"
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
@@ -2376,7 +2743,7 @@ inherits@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1"
-ini@~1.3.0:
+ini@^1.3.4, ini@~1.3.0:
version "1.3.4"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e"
@@ -2416,6 +2783,10 @@ ipaddr.js@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.2.0.tgz#8aba49c9192799585bdd643e0ccb50e8ae777ba4"
+is-absolute-url@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6"
+
is-absolute@^0.2.3:
version "0.2.6"
resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-0.2.6.tgz#20de69f3db942ef2d87b9c2da36f172235b1b5eb"
@@ -2502,6 +2873,10 @@ is-my-json-valid@^2.10.0, is-my-json-valid@^2.12.4:
jsonpointer "^4.0.0"
xtend "^4.0.0"
+is-npm@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-1.0.0.tgz#f2fb63a65e4905b406c86072765a1a4dc793b9f4"
+
is-number@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-0.1.1.tgz#69a7af116963d47206ec9bd9b48a14216f1e3806"
@@ -2528,6 +2903,10 @@ is-path-inside@^1.0.0:
dependencies:
path-is-inside "^1.0.1"
+is-plain-obj@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e"
+
is-posix-bracket@^0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4"
@@ -2540,6 +2919,10 @@ is-property@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84"
+is-redirect@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-redirect/-/is-redirect-1.0.0.tgz#1d03dded53bd8db0f30c26e4f95d36fc7c87dc24"
+
is-relative@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-0.2.1.tgz#d27f4c7d516d175fb610db84bbeef23c3bc97aa5"
@@ -2552,10 +2935,16 @@ is-resolvable@^1.0.0:
dependencies:
tryit "^1.0.1"
-is-stream@^1.0.1:
+is-stream@^1.0.0, is-stream@^1.0.1:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
+is-svg@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-2.1.0.tgz#cf61090da0d9efbcab8722deba6f032208dbb0e9"
+ dependencies:
+ html-comment-regex "^1.1.0"
+
is-typedarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
@@ -2707,6 +3096,19 @@ jquery@>=1.8.0, jquery@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/jquery/-/jquery-2.2.1.tgz#3c3e16854ad3d2ac44ac65021b17426d22ad803f"
+js-base64@^2.1.9:
+ version "2.1.9"
+ resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.1.9.tgz#f0e80ae039a4bd654b5f281fc93f04a914a7fcce"
+
+js-beautify@^1.6.3:
+ version "1.6.12"
+ resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.6.12.tgz#78b75933505d376da6e5a28e9b7887e0094db8b5"
+ dependencies:
+ config-chain "~1.1.5"
+ editorconfig "^0.13.2"
+ mkdirp "~0.5.0"
+ nopt "~3.0.1"
+
js-cookie@^2.1.3:
version "2.1.3"
resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.1.3.tgz#48071625217ac9ecfab8c343a13d42ec09ff0526"
@@ -2715,13 +3117,20 @@ js-tokens@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.1.tgz#08e9f132484a2c45a30907e9dc4d5567b7f114d7"
-js-yaml@3.x, js-yaml@^3.5.1, js-yaml@^3.7.0:
+js-yaml@3.x, js-yaml@^3.4.3, js-yaml@^3.5.1, js-yaml@^3.7.0:
version "3.8.1"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.8.1.tgz#782ba50200be7b9e5a8537001b7804db3ad02628"
dependencies:
argparse "^1.0.7"
esprima "^3.1.1"
+js-yaml@~3.7.0:
+ version "3.7.0"
+ resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.7.0.tgz#5c967ddd837a9bfdca5f2de84253abe8a1c03b80"
+ dependencies:
+ argparse "^1.0.7"
+ esprima "^2.6.0"
+
jsbn@~0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.0.tgz#650987da0dd74f4ebf5a11377a2aa2d273e97dfd"
@@ -2883,6 +3292,12 @@ klaw@^1.0.0:
optionalDependencies:
graceful-fs "^4.1.9"
+latest-version@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-1.0.1.tgz#72cfc46e3e8d1be651e1ebb54ea9f6ea96f374bb"
+ dependencies:
+ package-json "^1.0.0"
+
lazy-cache@^1.0.3:
version "1.0.4"
resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e"
@@ -2929,7 +3344,7 @@ loader-utils@^0.2.11, loader-utils@^0.2.16, loader-utils@^0.2.5:
json5 "^0.5.0"
object-assign "^4.0.1"
-loader-utils@^1.0.2:
+loader-utils@^1.0.2, loader-utils@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754429f5cd"
dependencies:
@@ -2944,16 +3359,55 @@ locate-path@^2.0.0:
p-locate "^2.0.0"
path-exists "^3.0.0"
+lodash._baseassign@^3.0.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e"
+ dependencies:
+ lodash._basecopy "^3.0.0"
+ lodash.keys "^3.0.0"
+
+lodash._basecopy@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36"
+
lodash._baseget@^3.0.0:
version "3.7.2"
resolved "https://registry.yarnpkg.com/lodash._baseget/-/lodash._baseget-3.7.2.tgz#1b6ae1d5facf3c25532350a13c1197cb8bb674f4"
+lodash._bindcallback@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e"
+
+lodash._createassigner@^3.0.0:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/lodash._createassigner/-/lodash._createassigner-3.1.1.tgz#838a5bae2fdaca63ac22dee8e19fa4e6d6970b11"
+ dependencies:
+ lodash._bindcallback "^3.0.0"
+ lodash._isiterateecall "^3.0.0"
+ lodash.restparam "^3.0.0"
+
+lodash._getnative@^3.0.0:
+ version "3.9.1"
+ resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5"
+
+lodash._isiterateecall@^3.0.0:
+ version "3.0.9"
+ resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c"
+
lodash._topath@^3.0.0:
version "3.8.1"
resolved "https://registry.yarnpkg.com/lodash._topath/-/lodash._topath-3.8.1.tgz#3ec5e2606014f4cb97f755fe6914edd8bfc00eac"
dependencies:
lodash.isarray "^3.0.0"
+lodash.assign@^3.0.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-3.2.0.tgz#3ce9f0234b4b2223e296b8fa0ac1fee8ebca64fa"
+ dependencies:
+ lodash._baseassign "^3.0.0"
+ lodash._createassigner "^3.0.0"
+ lodash.keys "^3.0.0"
+
lodash.camelcase@4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.1.1.tgz#065b3ff08f0b7662f389934c46a5504c90e0b2d8"
@@ -2962,6 +3416,10 @@ lodash.camelcase@4.1.1:
lodash.deburr "^4.0.0"
lodash.words "^4.0.0"
+lodash.camelcase@^4.3.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
+
lodash.capitalize@^4.0.0:
version "4.2.1"
resolved "https://registry.yarnpkg.com/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz#f826c9b4e2a8511d84e3aca29db05e1a4f3b72a9"
@@ -2974,6 +3432,13 @@ lodash.deburr@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/lodash.deburr/-/lodash.deburr-4.1.0.tgz#ddb1bbb3ef07458c0177ba07de14422cb033ff9b"
+lodash.defaults@^3.1.2:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-3.1.2.tgz#c7308b18dbf8bc9372d701a73493c61192bd2e2c"
+ dependencies:
+ lodash.assign "^3.0.0"
+ lodash.restparam "^3.0.0"
+
lodash.get@4.4.2:
version "4.4.2"
resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
@@ -2985,6 +3450,10 @@ lodash.get@^3.7.0:
lodash._baseget "^3.0.0"
lodash._topath "^3.0.0"
+lodash.isarguments@^3.0.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a"
+
lodash.isarray@^3.0.0:
version "3.0.4"
resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55"
@@ -2996,6 +3465,22 @@ lodash.kebabcase@4.0.1:
lodash.deburr "^4.0.0"
lodash.words "^4.0.0"
+lodash.keys@^3.0.0:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a"
+ dependencies:
+ lodash._getnative "^3.0.0"
+ lodash.isarguments "^3.0.0"
+ lodash.isarray "^3.0.0"
+
+lodash.memoize@^4.1.2:
+ version "4.1.2"
+ resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
+
+lodash.restparam@^3.0.0:
+ version "3.6.1"
+ resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805"
+
lodash.snakecase@4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/lodash.snakecase/-/lodash.snakecase-4.0.1.tgz#bd012e5d2f93f7b58b9303e9a7fbfd5db13d6281"
@@ -3003,6 +3488,10 @@ lodash.snakecase@4.0.1:
lodash.deburr "^4.0.0"
lodash.words "^4.0.0"
+lodash.uniq@^4.5.0:
+ version "4.5.0"
+ resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
+
lodash.words@^4.0.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/lodash.words/-/lodash.words-4.2.0.tgz#5ecfeaf8ecf8acaa8e0c8386295f1993c9cf4036"
@@ -3032,10 +3521,39 @@ loose-envify@^1.0.0:
dependencies:
js-tokens "^3.0.0"
+lowercase-keys@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306"
+
lru-cache@2.2.x:
version "2.2.4"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.2.4.tgz#6c658619becf14031d0d0b594b16042ce4dc063d"
+lru-cache@^3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-3.2.0.tgz#71789b3b7f5399bec8565dda38aa30d2a097efee"
+ dependencies:
+ pseudomap "^1.0.1"
+
+lru-cache@^4.0.1:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.0.2.tgz#1d17679c069cda5d040991a09dbc2c0db377e55e"
+ dependencies:
+ pseudomap "^1.0.1"
+ yallist "^2.0.0"
+
+macaddress@^0.2.8:
+ version "0.2.8"
+ resolved "https://registry.yarnpkg.com/macaddress/-/macaddress-0.2.8.tgz#5904dc537c39ec6dbefeae902327135fa8511f12"
+
+map-stream@~0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194"
+
+math-expression-evaluator@^1.2.14:
+ version "1.2.16"
+ resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.16.tgz#b357fa1ca9faefb8e48d10c14ef2bcb2d9f0a7c9"
+
media-typer@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
@@ -3102,7 +3620,7 @@ minimalistic-assert@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3"
-"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3:
+"minimatch@2 || 3", minimatch@3.0.3, minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774"
dependencies:
@@ -3160,6 +3678,12 @@ negotiator@0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9"
+nested-error-stacks@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-1.0.2.tgz#19f619591519f096769a5ba9a86e6eeec823c3cf"
+ dependencies:
+ inherits "~2.0.1"
+
node-libs-browser@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-1.1.1.tgz#2a38243abedd7dffcd07a97c9aca5668975a6fea"
@@ -3239,12 +3763,33 @@ node-zopfli@^2.0.0:
nan "^2.0.0"
node-pre-gyp "^0.6.4"
-nopt@3.x, nopt@~3.0.6:
+nodemon@^1.11.0:
+ version "1.11.0"
+ resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-1.11.0.tgz#226c562bd2a7b13d3d7518b49ad4828a3623d06c"
+ dependencies:
+ chokidar "^1.4.3"
+ debug "^2.2.0"
+ es6-promise "^3.0.2"
+ ignore-by-default "^1.0.0"
+ lodash.defaults "^3.1.2"
+ minimatch "^3.0.0"
+ ps-tree "^1.0.1"
+ touch "1.0.0"
+ undefsafe "0.0.3"
+ update-notifier "0.5.0"
+
+nopt@3.x, nopt@~3.0.1, nopt@~3.0.6:
version "3.0.6"
resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9"
dependencies:
abbrev "1"
+nopt@~1.0.10:
+ version "1.0.10"
+ resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee"
+ dependencies:
+ abbrev "1"
+
normalize-package-data@^2.3.2:
version "2.3.5"
resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.3.5.tgz#8d924f142960e1777e7ffe170543631cc7cb02df"
@@ -3258,6 +3803,19 @@ normalize-path@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.0.1.tgz#47886ac1662760d4261b7d979d241709d3ce3f7a"
+normalize-range@^0.1.2:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942"
+
+normalize-url@^1.4.0:
+ version "1.9.1"
+ resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c"
+ dependencies:
+ object-assign "^4.0.1"
+ prepend-http "^1.0.0"
+ query-string "^4.1.0"
+ sort-keys "^1.0.0"
+
npmlog@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.0.2.tgz#d03950e0e78ce1527ba26d2a7592e9348ac3e75f"
@@ -3267,6 +3825,10 @@ npmlog@^4.0.1:
gauge "~2.7.1"
set-blocking "~2.0.0"
+num2fraction@^1.2.2:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede"
+
number-is-nan@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
@@ -3279,6 +3841,10 @@ object-assign@4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.0.tgz#7a3b3d0e98063d43f4c03f2e8ae6cd51a86883a0"
+object-assign@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2"
+
object-assign@^4.0.1, object-assign@^4.1.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
@@ -3314,7 +3880,7 @@ once@1.x, once@^1.3.0, once@^1.4.0:
dependencies:
wrappy "1"
-once@~1.3.3:
+once@~1.3.0, once@~1.3.3:
version "1.3.3"
resolved "https://registry.yarnpkg.com/once/-/once-1.3.3.tgz#b2e261557ce4c314ec8304f3fa82663e4297ca20"
dependencies:
@@ -3367,7 +3933,7 @@ os-browserify@^0.2.0:
version "0.2.1"
resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.2.1.tgz#63fc4ccee5d2d7763d26bbf8601078e6c2e0044f"
-os-homedir@^1.0.0:
+os-homedir@^1.0.0, os-homedir@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
@@ -3377,10 +3943,17 @@ os-locale@^1.4.0:
dependencies:
lcid "^1.0.0"
-os-tmpdir@^1.0.1, os-tmpdir@~1.0.1:
+os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
+osenv@^0.1.0:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.4.tgz#42fe6d5953df06c8064be6f176c3d05aaaa34644"
+ dependencies:
+ os-homedir "^1.0.0"
+ os-tmpdir "^1.0.0"
+
p-limit@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.1.0.tgz#b07ff2d9a5d88bec806035895a2bab66a27988bc"
@@ -3391,6 +3964,13 @@ p-locate@^2.0.0:
dependencies:
p-limit "^1.1.0"
+package-json@^1.0.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/package-json/-/package-json-1.2.0.tgz#c8ecac094227cdf76a316874ed05e27cc939a0e0"
+ dependencies:
+ got "^3.2.0"
+ registry-url "^3.0.0"
+
pako@~0.2.0:
version "0.2.9"
resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75"
@@ -3484,6 +4064,12 @@ path-type@^1.0.0:
pify "^2.0.0"
pinkie-promise "^2.0.0"
+pause-stream@0.0.11:
+ version "0.0.11"
+ resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445"
+ dependencies:
+ through "~2.3"
+
pbkdf2@^3.0.3:
version "3.0.9"
resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.9.tgz#f2c4b25a600058b3c3773c086c37dbbee1ffe693"
@@ -3552,10 +4138,275 @@ portfinder@^1.0.9:
debug "^2.2.0"
mkdirp "0.5.x"
+postcss-calc@^5.2.0:
+ version "5.3.1"
+ resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-5.3.1.tgz#77bae7ca928ad85716e2fda42f261bf7c1d65b5e"
+ dependencies:
+ postcss "^5.0.2"
+ postcss-message-helpers "^2.0.0"
+ reduce-css-calc "^1.2.6"
+
+postcss-colormin@^2.1.8:
+ version "2.2.2"
+ resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-2.2.2.tgz#6631417d5f0e909a3d7ec26b24c8a8d1e4f96e4b"
+ dependencies:
+ colormin "^1.0.5"
+ postcss "^5.0.13"
+ postcss-value-parser "^3.2.3"
+
+postcss-convert-values@^2.3.4:
+ version "2.6.1"
+ resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz#bbd8593c5c1fd2e3d1c322bb925dcae8dae4d62d"
+ dependencies:
+ postcss "^5.0.11"
+ postcss-value-parser "^3.1.2"
+
+postcss-discard-comments@^2.0.4:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz#befe89fafd5b3dace5ccce51b76b81514be00e3d"
+ dependencies:
+ postcss "^5.0.14"
+
+postcss-discard-duplicates@^2.0.1:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz#b9abf27b88ac188158a5eb12abcae20263b91932"
+ dependencies:
+ postcss "^5.0.4"
+
+postcss-discard-empty@^2.0.1:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz#d2b4bd9d5ced5ebd8dcade7640c7d7cd7f4f92b5"
+ dependencies:
+ postcss "^5.0.14"
+
+postcss-discard-overridden@^0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz#8b1eaf554f686fb288cd874c55667b0aa3668d58"
+ dependencies:
+ postcss "^5.0.16"
+
+postcss-discard-unused@^2.2.1:
+ version "2.2.3"
+ resolved "https://registry.yarnpkg.com/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz#bce30b2cc591ffc634322b5fb3464b6d934f4433"
+ dependencies:
+ postcss "^5.0.14"
+ uniqs "^2.0.0"
+
+postcss-filter-plugins@^2.0.0:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/postcss-filter-plugins/-/postcss-filter-plugins-2.0.2.tgz#6d85862534d735ac420e4a85806e1f5d4286d84c"
+ dependencies:
+ postcss "^5.0.4"
+ uniqid "^4.0.0"
+
+postcss-load-config@^1.1.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-1.2.0.tgz#539e9afc9ddc8620121ebf9d8c3673e0ce50d28a"
+ dependencies:
+ cosmiconfig "^2.1.0"
+ object-assign "^4.1.0"
+ postcss-load-options "^1.2.0"
+ postcss-load-plugins "^2.3.0"
+
+postcss-load-options@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/postcss-load-options/-/postcss-load-options-1.2.0.tgz#b098b1559ddac2df04bc0bb375f99a5cfe2b6d8c"
+ dependencies:
+ cosmiconfig "^2.1.0"
+ object-assign "^4.1.0"
+
+postcss-load-plugins@^2.3.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/postcss-load-plugins/-/postcss-load-plugins-2.3.0.tgz#745768116599aca2f009fad426b00175049d8d92"
+ dependencies:
+ cosmiconfig "^2.1.1"
+ object-assign "^4.1.0"
+
+postcss-merge-idents@^2.1.5:
+ version "2.1.7"
+ resolved "https://registry.yarnpkg.com/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz#4c5530313c08e1d5b3bbf3d2bbc747e278eea270"
+ dependencies:
+ has "^1.0.1"
+ postcss "^5.0.10"
+ postcss-value-parser "^3.1.1"
+
+postcss-merge-longhand@^2.0.1:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz#23d90cd127b0a77994915332739034a1a4f3d658"
+ dependencies:
+ postcss "^5.0.4"
+
+postcss-merge-rules@^2.0.3:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz#d1df5dfaa7b1acc3be553f0e9e10e87c61b5f721"
+ dependencies:
+ browserslist "^1.5.2"
+ caniuse-api "^1.5.2"
+ postcss "^5.0.4"
+ postcss-selector-parser "^2.2.2"
+ vendors "^1.0.0"
+
+postcss-message-helpers@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz#a4f2f4fab6e4fe002f0aed000478cdf52f9ba60e"
+
+postcss-minify-font-values@^1.0.2:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz#4b58edb56641eba7c8474ab3526cafd7bbdecb69"
+ dependencies:
+ object-assign "^4.0.1"
+ postcss "^5.0.4"
+ postcss-value-parser "^3.0.2"
+
+postcss-minify-gradients@^1.0.1:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz#5dbda11373703f83cfb4a3ea3881d8d75ff5e6e1"
+ dependencies:
+ postcss "^5.0.12"
+ postcss-value-parser "^3.3.0"
+
+postcss-minify-params@^1.0.4:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz#ad2ce071373b943b3d930a3fa59a358c28d6f1f3"
+ dependencies:
+ alphanum-sort "^1.0.1"
+ postcss "^5.0.2"
+ postcss-value-parser "^3.0.2"
+ uniqs "^2.0.0"
+
+postcss-minify-selectors@^2.0.4:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz#b2c6a98c0072cf91b932d1a496508114311735bf"
+ dependencies:
+ alphanum-sort "^1.0.2"
+ has "^1.0.1"
+ postcss "^5.0.14"
+ postcss-selector-parser "^2.0.0"
+
+postcss-modules-extract-imports@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.0.1.tgz#8fb3fef9a6dd0420d3f6d4353cf1ff73f2b2a341"
+ dependencies:
+ postcss "^5.0.4"
+
+postcss-modules-local-by-default@^1.0.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.1.1.tgz#29a10673fa37d19251265ca2ba3150d9040eb4ce"
+ dependencies:
+ css-selector-tokenizer "^0.6.0"
+ postcss "^5.0.4"
+
+postcss-modules-scope@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-1.0.2.tgz#ff977395e5e06202d7362290b88b1e8cd049de29"
+ dependencies:
+ css-selector-tokenizer "^0.6.0"
+ postcss "^5.0.4"
+
+postcss-modules-values@^1.1.0:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-1.2.2.tgz#f0e7d476fe1ed88c5e4c7f97533a3e772ad94ca1"
+ dependencies:
+ icss-replace-symbols "^1.0.2"
+ postcss "^5.0.14"
+
+postcss-normalize-charset@^1.1.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz#ef9ee71212d7fe759c78ed162f61ed62b5cb93f1"
+ dependencies:
+ postcss "^5.0.5"
+
+postcss-normalize-url@^3.0.7:
+ version "3.0.8"
+ resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz#108f74b3f2fcdaf891a2ffa3ea4592279fc78222"
+ dependencies:
+ is-absolute-url "^2.0.0"
+ normalize-url "^1.4.0"
+ postcss "^5.0.14"
+ postcss-value-parser "^3.2.3"
+
+postcss-ordered-values@^2.1.0:
+ version "2.2.3"
+ resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz#eec6c2a67b6c412a8db2042e77fe8da43f95c11d"
+ dependencies:
+ postcss "^5.0.4"
+ postcss-value-parser "^3.0.1"
+
+postcss-reduce-idents@^2.2.2:
+ version "2.4.0"
+ resolved "https://registry.yarnpkg.com/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz#c2c6d20cc958284f6abfbe63f7609bf409059ad3"
+ dependencies:
+ postcss "^5.0.4"
+ postcss-value-parser "^3.0.2"
+
+postcss-reduce-initial@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz#68f80695f045d08263a879ad240df8dd64f644ea"
+ dependencies:
+ postcss "^5.0.4"
+
+postcss-reduce-transforms@^1.0.3:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz#ff76f4d8212437b31c298a42d2e1444025771ae1"
+ dependencies:
+ has "^1.0.1"
+ postcss "^5.0.8"
+ postcss-value-parser "^3.0.1"
+
+postcss-selector-parser@^2.0.0, postcss-selector-parser@^2.2.2:
+ version "2.2.3"
+ resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz#f9437788606c3c9acee16ffe8d8b16297f27bb90"
+ dependencies:
+ flatten "^1.0.2"
+ indexes-of "^1.0.1"
+ uniq "^1.0.1"
+
+postcss-svgo@^2.1.1:
+ version "2.1.6"
+ resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-2.1.6.tgz#b6df18aa613b666e133f08adb5219c2684ac108d"
+ dependencies:
+ is-svg "^2.0.0"
+ postcss "^5.0.14"
+ postcss-value-parser "^3.2.3"
+ svgo "^0.7.0"
+
+postcss-unique-selectors@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz#981d57d29ddcb33e7b1dfe1fd43b8649f933ca1d"
+ dependencies:
+ alphanum-sort "^1.0.1"
+ postcss "^5.0.4"
+ uniqs "^2.0.0"
+
+postcss-value-parser@^3.0.1, postcss-value-parser@^3.0.2, postcss-value-parser@^3.1.1, postcss-value-parser@^3.1.2, postcss-value-parser@^3.2.3, postcss-value-parser@^3.3.0:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz#87f38f9f18f774a4ab4c8a232f5c5ce8872a9d15"
+
+postcss-zindex@^2.0.1:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/postcss-zindex/-/postcss-zindex-2.2.0.tgz#d2109ddc055b91af67fc4cb3b025946639d2af22"
+ dependencies:
+ has "^1.0.1"
+ postcss "^5.0.4"
+ uniqs "^2.0.0"
+
+postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0.14, postcss@^5.0.16, postcss@^5.0.2, postcss@^5.0.21, postcss@^5.0.4, postcss@^5.0.5, postcss@^5.0.6, postcss@^5.0.8, postcss@^5.2.16:
+ version "5.2.16"
+ resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.16.tgz#732b3100000f9ff8379a48a53839ed097376ad57"
+ dependencies:
+ chalk "^1.1.3"
+ js-base64 "^2.1.9"
+ source-map "^0.5.6"
+ supports-color "^3.2.3"
+
prelude-ls@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
+prepend-http@^1.0.0:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
+
preserve@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
@@ -3576,6 +4427,10 @@ progress@^1.1.8, progress@~1.1.8:
version "1.1.8"
resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be"
+proto-list@~1.2.1:
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849"
+
proxy-addr@~1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-1.1.3.tgz#dc97502f5722e888467b3fa2297a7b1ff47df074"
@@ -3587,6 +4442,16 @@ prr@~0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/prr/-/prr-0.0.0.tgz#1a84b85908325501411853d0081ee3fa86e2926a"
+ps-tree@^1.0.1:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/ps-tree/-/ps-tree-1.1.0.tgz#b421b24140d6203f1ed3c76996b4427b08e8c014"
+ dependencies:
+ event-stream "~3.3.0"
+
+pseudomap@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
+
public-encrypt@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.0.tgz#39f699f3a46560dd5ebacbca693caf7c65c18cc6"
@@ -3605,6 +4470,10 @@ punycode@^1.2.4, punycode@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
+q@^1.1.2:
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/q/-/q-1.5.0.tgz#dd01bac9d06d30e6f219aecb8253ee9ebdc308f1"
+
qjobs@^1.1.4:
version "1.1.5"
resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.1.5.tgz#659de9f2cf8dcc27a1481276f205377272382e73"
@@ -3621,6 +4490,13 @@ qs@~6.3.0:
version "6.3.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.0.tgz#f403b264f23bc01228c74131b407f18d5ea5d442"
+query-string@^4.1.0:
+ version "4.3.2"
+ resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.2.tgz#ec0fd765f58a50031a3968c2431386f8947a5cdd"
+ dependencies:
+ object-assign "^4.1.0"
+ strict-uri-encode "^1.0.0"
+
querystring-es3@^0.2.0:
version "0.2.1"
resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
@@ -3666,7 +4542,7 @@ raw-loader@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-0.5.1.tgz#0c3d0beaed8a01c966d9787bf778281252a979aa"
-rc@~1.1.6:
+rc@^1.0.1, rc@~1.1.6:
version "1.1.6"
resolved "https://registry.yarnpkg.com/rc/-/rc-1.1.6.tgz#43651b76b6ae53b5c802f1151fa3fc3b059969c9"
dependencies:
@@ -3675,6 +4551,28 @@ rc@~1.1.6:
minimist "^1.2.0"
strip-json-comments "~1.0.4"
+react-dev-utils@^0.5.2:
+ version "0.5.2"
+ resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-0.5.2.tgz#50d0b962d3a94b6c2e8f2011ed6468e4124bc410"
+ dependencies:
+ ansi-html "0.0.5"
+ chalk "1.1.3"
+ escape-string-regexp "1.0.5"
+ filesize "3.3.0"
+ gzip-size "3.0.0"
+ html-entities "1.2.0"
+ opn "4.0.2"
+ recursive-readdir "2.1.1"
+ sockjs-client "1.0.1"
+ strip-ansi "3.0.1"
+
+read-all-stream@^3.0.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/read-all-stream/-/read-all-stream-3.1.0.tgz#35c3e177f2078ef789ee4bfafa4373074eaef4fa"
+ dependencies:
+ pinkie-promise "^2.0.0"
+ readable-stream "^2.0.0"
+
read-pkg-up@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02"
@@ -3690,7 +4588,7 @@ read-pkg@^1.0.0:
normalize-package-data "^2.3.2"
path-type "^1.0.0"
-"readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.0.2, readable-stream@^2.1.0, readable-stream@^2.2.2:
+readable-stream@^2.0.0, "readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.1.0, readable-stream@^2.2.2:
version "2.2.2"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.2.tgz#a9e6fec3c7dda85f8bb1b3ba7028604556fc825e"
dependencies:
@@ -3702,7 +4600,7 @@ read-pkg@^1.0.0:
string_decoder "~0.10.x"
util-deprecate "~1.0.1"
-readable-stream@^2.0.1, readable-stream@^2.0.5, readable-stream@~2.0.0, readable-stream@~2.0.6:
+readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@~2.0.0, readable-stream@~2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e"
dependencies:
@@ -3757,6 +4655,26 @@ rechoir@^0.6.2:
dependencies:
resolve "^1.1.6"
+recursive-readdir@2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.1.1.tgz#a01cfc7f7f38a53ec096a096f63a50489c3e297c"
+ dependencies:
+ minimatch "3.0.3"
+
+reduce-css-calc@^1.2.6:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716"
+ dependencies:
+ balanced-match "^0.4.2"
+ math-expression-evaluator "^1.2.14"
+ reduce-function-call "^1.0.1"
+
+reduce-function-call@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/reduce-function-call/-/reduce-function-call-1.0.2.tgz#5a200bf92e0e37751752fe45b0ab330fd4b6be99"
+ dependencies:
+ balanced-match "^0.4.2"
+
regenerate@^1.2.1:
version "1.3.2"
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.2.tgz#d1941c67bad437e1be76433add5b385f95b19260"
@@ -3780,6 +4698,14 @@ regex-cache@^0.4.2:
is-equal-shallow "^0.1.3"
is-primitive "^2.0.0"
+regexpu-core@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-1.0.0.tgz#86a763f58ee4d7c2f6b102e4764050de7ed90c6b"
+ dependencies:
+ regenerate "^1.2.1"
+ regjsgen "^0.2.0"
+ regjsparser "^0.1.4"
+
regexpu-core@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-2.0.0.tgz#49d038837b8dcf8bfa5b9a42139938e6ea2ae240"
@@ -3788,6 +4714,12 @@ regexpu-core@^2.0.0:
regjsgen "^0.2.0"
regjsparser "^0.1.4"
+registry-url@^3.0.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942"
+ dependencies:
+ rc "^1.0.1"
+
regjsgen@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7"
@@ -3810,6 +4742,12 @@ repeat-string@^1.5.2:
version "1.6.1"
resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
+repeating@^1.1.2:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/repeating/-/repeating-1.1.3.tgz#3d4114218877537494f97f77f9785fab810fa4ac"
+ dependencies:
+ is-finite "^1.0.0"
+
repeating@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda"
@@ -3851,6 +4789,10 @@ require-directory@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
+require-from-string@^1.1.0:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-1.2.1.tgz#529c9ccef27380adfec9a2f965b649bbee636418"
+
require-main-filename@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1"
@@ -3915,6 +4857,10 @@ safe-buffer@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7"
+sax@~1.2.1:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.2.tgz#fd8631a23bc7826bef5d871bdb87378c95647828"
+
select-hose@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
@@ -3923,7 +4869,13 @@ select2@3.5.2-browserify:
version "3.5.2-browserify"
resolved "https://registry.yarnpkg.com/select2/-/select2-3.5.2-browserify.tgz#dc4dafda38d67a734e8a97a46f0d3529ae05391d"
-"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@~5.3.0:
+semver-diff@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36"
+ dependencies:
+ semver "^5.0.3"
+
+"semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.3.0, semver@~5.3.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
@@ -4000,6 +4952,10 @@ shelljs@^0.7.5:
interpret "^1.0.0"
rechoir "^0.6.2"
+sigmund@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590"
+
signal-exit@^3.0.0:
version "3.0.2"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
@@ -4012,6 +4968,10 @@ slice-ansi@0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35"
+slide@^1.1.5:
+ version "1.1.6"
+ resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707"
+
sntp@1.x.x:
version "1.0.9"
resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198"
@@ -4062,6 +5022,17 @@ socket.io@1.7.2:
socket.io-client "1.7.2"
socket.io-parser "2.3.1"
+sockjs-client@1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.0.1.tgz#8943ae05b46547bc2054816c409002cf5e2fe026"
+ dependencies:
+ debug "^2.1.0"
+ eventsource "^0.1.3"
+ faye-websocket "~0.7.3"
+ inherits "^2.0.1"
+ json3 "^3.3.2"
+ url-parse "^1.0.1"
+
sockjs-client@1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.1.2.tgz#f0212a8550e4c9468c8cceaeefd2e3493c033ad5"
@@ -4080,14 +5051,20 @@ sockjs@0.3.18:
faye-websocket "^0.10.0"
uuid "^2.0.2"
-source-list-map@^1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-1.1.1.tgz#1a33ac210ca144d1e561f906ebccab5669ff4cb4"
+sort-keys@^1.0.0:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad"
+ dependencies:
+ is-plain-obj "^1.0.0"
-source-list-map@~0.1.7:
+source-list-map@^0.1.7, source-list-map@~0.1.7:
version "0.1.8"
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-0.1.8.tgz#c550b2ab5427f6b3f21f5afead88c4f5587b2106"
+source-list-map@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-1.1.1.tgz#1a33ac210ca144d1e561f906ebccab5669ff4cb4"
+
source-map-support@^0.4.2:
version "0.4.11"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.11.tgz#647f939978b38535909530885303daf23279f322"
@@ -4106,7 +5083,7 @@ source-map@^0.4.4:
dependencies:
amdefine ">=0.0.4"
-source-map@^0.5.0, source-map@^0.5.3, source-map@~0.5.1, source-map@~0.5.3:
+source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1, source-map@~0.5.3:
version "0.5.6"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412"
@@ -4150,6 +5127,12 @@ spdy@^3.4.1:
select-hose "^2.0.0"
spdy-transport "^2.0.15"
+split@0.3:
+ version "0.3.3"
+ resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f"
+ dependencies:
+ through "2"
+
sprintf-js@~1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
@@ -4184,6 +5167,12 @@ stream-browserify@^2.0.1:
inherits "~2.0.1"
readable-stream "^2.0.2"
+stream-combiner@~0.0.4:
+ version "0.0.4"
+ resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14"
+ dependencies:
+ duplexer "~0.1.1"
+
stream-http@^2.3.1:
version "2.6.3"
resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.6.3.tgz#4c3ddbf9635968ea2cfd4e48d43de5def2625ac3"
@@ -4194,6 +5183,20 @@ stream-http@^2.3.1:
to-arraybuffer "^1.0.0"
xtend "^4.0.0"
+stream-shift@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952"
+
+strict-uri-encode@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
+
+string-length@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/string-length/-/string-length-1.0.1.tgz#56970fb1c38558e9e70b728bf3de269ac45adfac"
+ dependencies:
+ strip-ansi "^3.0.0"
+
string-width@^1.0.1, string-width@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
@@ -4217,7 +5220,7 @@ stringstream@~0.0.4:
version "0.0.5"
resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878"
-strip-ansi@^3.0.0, strip-ansi@^3.0.1:
+strip-ansi@3.0.1, strip-ansi@^3.0.0, strip-ansi@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
dependencies:
@@ -4249,12 +5252,24 @@ supports-color@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
-supports-color@^3.1.0, supports-color@^3.1.1, supports-color@^3.1.2:
+supports-color@^3.1.0, supports-color@^3.1.1, supports-color@^3.1.2, supports-color@^3.2.3:
version "3.2.3"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6"
dependencies:
has-flag "^1.0.0"
+svgo@^0.7.0:
+ version "0.7.2"
+ resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.7.2.tgz#9f5772413952135c6fefbf40afe6a4faa88b4bb5"
+ dependencies:
+ coa "~1.0.1"
+ colors "~1.1.2"
+ csso "~2.3.1"
+ js-yaml "~3.7.0"
+ mkdirp "~0.5.1"
+ sax "~1.2.1"
+ whet.extend "~0.9.9"
+
table@^3.7.8:
version "3.8.3"
resolved "https://registry.yarnpkg.com/table/-/table-3.8.3.tgz#2bbc542f0fda9861a755d3947fefd8b3f513855f"
@@ -4325,7 +5340,7 @@ throttleit@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-1.0.0.tgz#9e785836daf46743145a5984b6268d828528ac6c"
-through@^2.3.6:
+through@2, through@^2.3.6, through@~2.3, through@~2.3.1:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
@@ -4333,6 +5348,10 @@ timeago.js@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/timeago.js/-/timeago.js-2.0.5.tgz#730c74fbdb0b0917a553675a4460e3a7f80db86c"
+timed-out@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-2.0.0.tgz#f38b0ae81d3747d628001f41dafc652ace671c0a"
+
timers-browserify@^1.4.2:
version "1.4.2"
resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-1.4.2.tgz#c9c58b575be8407375cb5e2462dacee74359f41d"
@@ -4363,6 +5382,12 @@ to-fast-properties@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.2.tgz#f3f5c0c3ba7299a7ef99427e44633257ade43320"
+touch@1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/touch/-/touch-1.0.0.tgz#449cbe2dbae5a8c8038e30d71fa0ff464947c4de"
+ dependencies:
+ nopt "~1.0.10"
+
tough-cookie@~2.3.0:
version "2.3.2"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.2.tgz#f081f76e4c85720e6c37a5faced737150d84072a"
@@ -4435,14 +5460,44 @@ unc-path-regex@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa"
+undefsafe@0.0.3:
+ version "0.0.3"
+ resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-0.0.3.tgz#ecca3a03e56b9af17385baac812ac83b994a962f"
+
underscore@^1.8.3:
version "1.8.3"
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022"
+uniq@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff"
+
+uniqid@^4.0.0:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/uniqid/-/uniqid-4.1.1.tgz#89220ddf6b751ae52b5f72484863528596bb84c1"
+ dependencies:
+ macaddress "^0.2.8"
+
+uniqs@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02"
+
unpipe@1.0.0, unpipe@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
+update-notifier@0.5.0:
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-0.5.0.tgz#07b5dc2066b3627ab3b4f530130f7eddda07a4cc"
+ dependencies:
+ chalk "^1.0.0"
+ configstore "^1.0.0"
+ is-npm "^1.0.0"
+ latest-version "^1.0.0"
+ repeating "^1.1.2"
+ semver-diff "^2.0.0"
+ string-length "^1.0.0"
+
url-parse@1.0.x:
version "1.0.5"
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.0.5.tgz#0854860422afdcfefeb6c965c662d4800169927b"
@@ -4450,7 +5505,7 @@ url-parse@1.0.x:
querystringify "0.0.x"
requires-port "1.0.x"
-url-parse@^1.1.1:
+url-parse@^1.0.1, url-parse@^1.1.1:
version "1.1.7"
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.1.7.tgz#025cff999653a459ab34232147d89514cc87d74a"
dependencies:
@@ -4491,7 +5546,7 @@ utils-merge@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.0.tgz#0294fb922bb9375153541c4f7096231f287c8af8"
-uuid@^2.0.2:
+uuid@^2.0.1, uuid@^2.0.2:
version "2.0.3"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a"
@@ -4510,6 +5565,10 @@ vary@~1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.0.tgz#e1e5affbbd16ae768dd2674394b9ad3022653140"
+vendors@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.1.tgz#37ad73c8ee417fb3d580e785312307d274847f22"
+
verror@1.3.6:
version "1.3.6"
resolved "https://registry.yarnpkg.com/verror/-/verror-1.3.6.tgz#cff5df12946d297d2baaefaa2689e25be01c005c"
@@ -4530,13 +5589,52 @@ void-elements@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec"
+vue-hot-reload-api@^2.0.11:
+ version "2.0.11"
+ resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.0.11.tgz#bf26374fb73366ce03f799e65ef5dfd0e28a1568"
+
+vue-loader@^11.3.4:
+ version "11.3.4"
+ resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-11.3.4.tgz#65e10a44ce092d906e14bbc72981dec99eb090d2"
+ dependencies:
+ consolidate "^0.14.0"
+ hash-sum "^1.0.2"
+ js-beautify "^1.6.3"
+ loader-utils "^1.1.0"
+ lru-cache "^4.0.1"
+ postcss "^5.0.21"
+ postcss-load-config "^1.1.0"
+ postcss-selector-parser "^2.0.0"
+ source-map "^0.5.6"
+ vue-hot-reload-api "^2.0.11"
+ vue-style-loader "^2.0.0"
+ vue-template-es2015-compiler "^1.2.2"
+
vue-resource@^0.9.3:
version "0.9.3"
resolved "https://registry.yarnpkg.com/vue-resource/-/vue-resource-0.9.3.tgz#ab46e1c44ea219142dcc28ae4043b3b04c80959d"
-vue@^2.2.4:
- version "2.2.4"
- resolved "https://registry.yarnpkg.com/vue/-/vue-2.2.4.tgz#d0a3a050a80a12356d7950ae5a7b3131048209cc"
+vue-style-loader@^2.0.0:
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-2.0.5.tgz#f0efac992febe3f12e493e334edb13cd235a3d22"
+ dependencies:
+ hash-sum "^1.0.2"
+ loader-utils "^1.0.2"
+
+vue-template-compiler@^2.2.6:
+ version "2.2.6"
+ resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.2.6.tgz#2e2928daf0cd0feca9dfc35a9729adeae173ec68"
+ dependencies:
+ de-indent "^1.0.2"
+ he "^1.1.0"
+
+vue-template-es2015-compiler@^1.2.2:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.5.1.tgz#0c36cc57aa3a9ec13e846342cb14a72fcac8bd93"
+
+vue@^2.2.6:
+ version "2.2.6"
+ resolved "https://registry.yarnpkg.com/vue/-/vue-2.2.6.tgz#451714b394dd6d4eae7b773c40c2034a59621aed"
watchpack@^1.3.1:
version "1.3.1"
@@ -4637,7 +5735,7 @@ webpack@^2.3.3:
webpack-sources "^0.2.3"
yargs "^6.0.0"
-websocket-driver@>=0.5.1:
+websocket-driver@>=0.3.6, websocket-driver@>=0.5.1:
version "0.6.5"
resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.6.5.tgz#5cb2556ceb85f4373c6d8238aa691c8454e13a36"
dependencies:
@@ -4647,6 +5745,10 @@ websocket-extensions@>=0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.1.tgz#76899499c184b6ef754377c2dbb0cd6cb55d29e7"
+whet.extend@~0.9.9:
+ version "0.9.9"
+ resolved "https://registry.yarnpkg.com/whet.extend/-/whet.extend-0.9.9.tgz#f877d5bf648c97e5aa542fadc16d6a259b9c11a1"
+
which-module@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f"
@@ -4690,6 +5792,14 @@ wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+write-file-atomic@^1.1.2:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-1.3.1.tgz#7d45ba32316328dd1ec7d90f60ebc0d845bb759a"
+ dependencies:
+ graceful-fs "^4.1.11"
+ imurmurhash "^0.1.4"
+ slide "^1.1.5"
+
write@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757"
@@ -4707,6 +5817,12 @@ wtf-8@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/wtf-8/-/wtf-8-1.0.0.tgz#392d8ba2d0f1c34d1ee2d630f15d0efb68e1048a"
+xdg-basedir@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-2.0.0.tgz#edbc903cc385fc04523d966a335504b5504d1bd2"
+ dependencies:
+ os-homedir "^1.0.0"
+
xmlhttprequest-ssl@1.5.3:
version "1.5.3"
resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.3.tgz#185a888c04eca46c3e4070d99f7b49de3528992d"
@@ -4719,6 +5835,10 @@ y18n@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41"
+yallist@^2.0.0:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
+
yargs-parser@^4.2.0:
version "4.2.1"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-4.2.1.tgz#29cceac0dc4f03c6c87b4a9f217dd18c9f74871c"