summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.eslintrc.yml2
-rw-r--r--.flayignore9
-rw-r--r--.gitignore1
-rw-r--r--.gitlab-ci.yml4
-rw-r--r--.rubocop_todo.yml2
-rw-r--r--CONTRIBUTING.md17
-rw-r--r--app/assets/javascripts/behaviors/markdown/copy_as_gfm.js36
-rw-r--r--app/assets/javascripts/blob/viewer/index.js2
-rw-r--r--app/assets/javascripts/boards/components/board_delete.js2
-rw-r--r--app/assets/javascripts/boards/stores/boards_store.js2
-rw-r--r--app/assets/javascripts/commits.js2
-rw-r--r--app/assets/javascripts/create_merge_request_dropdown.js51
-rw-r--r--app/assets/javascripts/deploy_keys/components/app.vue2
-rw-r--r--app/assets/javascripts/environments/components/environment_stop.vue2
-rw-r--r--app/assets/javascripts/ide/components/pipelines/list.vue2
-rw-r--r--app/assets/javascripts/ide/components/repo_editor.vue11
-rw-r--r--app/assets/javascripts/ide/lib/diff/diff_worker.js2
-rw-r--r--app/assets/javascripts/ide/services/index.js2
-rw-r--r--app/assets/javascripts/ide/stores/modules/commit/actions.js62
-rw-r--r--app/assets/javascripts/ide/stores/modules/merge_requests/actions.js3
-rw-r--r--app/assets/javascripts/ide/stores/modules/pipelines/actions.js4
-rw-r--r--app/assets/javascripts/ide/stores/mutations/file.js1
-rw-r--r--app/assets/javascripts/ide/stores/utils.js4
-rw-r--r--app/assets/javascripts/ide/stores/workers/files_decorator_worker.js2
-rw-r--r--app/assets/javascripts/issue_show/components/app.vue2
-rw-r--r--app/assets/javascripts/issue_show/components/edit_actions.vue2
-rw-r--r--app/assets/javascripts/issue_show/components/locked_warning.vue2
-rw-r--r--app/assets/javascripts/lazy_loader.js4
-rw-r--r--app/assets/javascripts/lib/utils/bootstrap_linked_tabs.js2
-rw-r--r--app/assets/javascripts/lib/utils/number_utils.js2
-rw-r--r--app/assets/javascripts/line_highlighter.js4
-rw-r--r--app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js35
-rw-r--r--app/assets/javascripts/merge_request_tabs.js2
-rw-r--r--app/assets/javascripts/milestone.js4
-rw-r--r--app/assets/javascripts/monitoring/components/graph/flag.vue2
-rw-r--r--app/assets/javascripts/monitoring/utils/multiple_time_series.js2
-rw-r--r--app/assets/javascripts/notes.js8
-rw-r--r--app/assets/javascripts/notes/components/noteable_discussion.vue2
-rw-r--r--app/assets/javascripts/notes/components/noteable_note.vue4
-rw-r--r--app/assets/javascripts/pages/users/user_tabs.js4
-rw-r--r--app/assets/javascripts/performance_bar/components/detailed_metric.vue4
-rw-r--r--app/assets/javascripts/project_find_file.js2
-rw-r--r--app/assets/javascripts/project_import.js2
-rw-r--r--app/assets/javascripts/projects/project_new.js4
-rw-r--r--app/assets/javascripts/search_autocomplete.js2
-rw-r--r--app/assets/javascripts/settings_panels.js4
-rw-r--r--app/assets/javascripts/shortcuts_find_file.js4
-rw-r--r--app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue2
-rw-r--r--app/assets/javascripts/sidebar/sidebar_mediator.js2
-rw-r--r--app/assets/javascripts/single_file_diff.js2
-rw-r--r--app/assets/javascripts/snippet/snippet_embed.js2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/gl_modal.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue2
-rw-r--r--app/assets/stylesheets/bootstrap_migration.scss6
-rw-r--r--app/assets/stylesheets/framework/blocks.scss1
-rw-r--r--app/assets/stylesheets/framework/buttons.scss8
-rw-r--r--app/assets/stylesheets/framework/common.scss4
-rw-r--r--app/assets/stylesheets/framework/gfm.scss1
-rw-r--r--app/assets/stylesheets/framework/markdown_area.scss4
-rw-r--r--app/assets/stylesheets/framework/modal.scss4
-rw-r--r--app/assets/stylesheets/framework/secondary_navigation_elements.scss2
-rw-r--r--app/assets/stylesheets/framework/variables.scss4
-rw-r--r--app/assets/stylesheets/pages/boards.scss11
-rw-r--r--app/assets/stylesheets/pages/groups.scss2
-rw-r--r--app/assets/stylesheets/pages/labels.scss4
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss6
-rw-r--r--app/assets/stylesheets/pages/note_form.scss2
-rw-r--r--app/assets/stylesheets/pages/pipelines.scss1
-rw-r--r--app/assets/stylesheets/pages/projects.scss35
-rw-r--r--app/assets/stylesheets/pages/settings.scss2
-rw-r--r--app/assets/stylesheets/performance_bar.scss8
-rw-r--r--app/controllers/application_controller.rb6
-rw-r--r--app/controllers/concerns/internal_redirect.rb4
-rw-r--r--app/controllers/projects/blob_controller.rb13
-rw-r--r--app/controllers/projects/milestones_controller.rb4
-rw-r--r--app/finders/snippets_finder.rb15
-rw-r--r--app/graphql/resolvers/merge_request_resolver.rb11
-rw-r--r--app/graphql/types/project_type.rb7
-rw-r--r--app/graphql/types/query_type.rb7
-rw-r--r--app/helpers/projects_helper.rb3
-rw-r--r--app/models/concerns/cache_markdown_field.rb2
-rw-r--r--app/models/project.rb2
-rw-r--r--app/models/project_services/gemnasium_service.rb7
-rw-r--r--app/uploaders/favicon_uploader.rb11
-rw-r--r--app/views/admin/appearances/_form.html.haml6
-rw-r--r--app/views/errors/access_denied.html.haml2
-rw-r--r--app/views/projects/_project_templates.html.haml24
-rw-r--r--app/views/projects/_stat_anchor_list.html.haml2
-rw-r--r--app/views/projects/diffs/_collapsed.html.haml2
-rw-r--r--app/views/projects/empty.html.haml8
-rw-r--r--app/views/projects/merge_requests/creations/_new_submit.html.haml2
-rw-r--r--app/views/shared/boards/components/sidebar/_labels.html.haml2
-rw-r--r--app/views/shared/builds/_build_output.html.haml2
-rw-r--r--app/views/shared/issuable/_sidebar_assignees.html.haml2
-rw-r--r--app/views/shared/issuable/form/_metadata.html.haml5
-rw-r--r--app/workers/git_garbage_collect_worker.rb58
-rw-r--r--changelogs/unreleased/47646-ui-glitch.yml5
-rw-r--r--changelogs/unreleased/47871-new-mr-tab-active-state.yml5
-rw-r--r--changelogs/unreleased/author-doc-fix.yml5
-rw-r--r--changelogs/unreleased/blackst0ne-rails5-fix-optimistic-lock-values.yml5
-rw-r--r--changelogs/unreleased/blackst0ne-rails5-fix-snippets-finder.yml5
-rw-r--r--changelogs/unreleased/bvl-graphql-nested-merge-request.yml5
-rw-r--r--changelogs/unreleased/bw-enable-commonmark.yml5
-rw-r--r--changelogs/unreleased/cache-doc-fix.yml5
-rw-r--r--changelogs/unreleased/mk-rake-task-verify-remote-files.yml5
-rw-r--r--changelogs/unreleased/rails5-fix-47804.yml5
-rw-r--r--changelogs/unreleased/rails5-fix-47805.yml6
-rw-r--r--changelogs/unreleased/rails5-fix-47835.yml6
-rw-r--r--changelogs/unreleased/rails5-fix-47836.yml6
-rw-r--r--changelogs/unreleased/rd-33733-showing-created-date-instead-of-updated-date-in-project-lists.yml5
-rw-r--r--changelogs/unreleased/remove-link-label-vertical-alignment-property.yml5
-rw-r--r--changelogs/unreleased/rosulk-patch-12.yml5
-rw-r--r--changelogs/unreleased/safari-scrollbar-bug.yml5
-rw-r--r--changelogs/unreleased/winh-new-branch-url-encode.yml5
-rw-r--r--config/initializers/active_record_locking.rb111
-rw-r--r--config/initializers/active_record_migration.rb10
-rw-r--r--db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb4
-rw-r--r--db/migrate/20161226122833_remove_dot_git_from_usernames.rb4
-rw-r--r--doc/administration/raketasks/check.md7
-rw-r--r--doc/api/README.md2
-rw-r--r--doc/api/graphql/index.md4
-rw-r--r--doc/api/merge_requests.md62
-rw-r--r--doc/ci/yaml/README.md10
-rw-r--r--doc/development/new_fe_guide/style/prettier.md14
-rw-r--r--doc/integration/google.md7
-rw-r--r--doc/university/README.md12
-rw-r--r--doc/user/markdown.md95
-rw-r--r--doc/user/permissions.md57
-rw-r--r--doc/user/project/issue_board.md12
-rw-r--r--doc/user/project/milestones/index.md1
-rw-r--r--lib/api/helpers.rb3
-rw-r--r--lib/api/markdown.rb4
-rw-r--r--lib/banzai/filter/markdown_filter.rb2
-rw-r--r--lib/banzai/filter/milestone_reference_filter.rb2
-rw-r--r--lib/gitlab/background_migration/prepare_untracked_uploads.rb3
-rw-r--r--lib/gitlab/database.rb7
-rw-r--r--lib/gitlab/favicon.rb2
-rw-r--r--lib/gitlab/git/blame.rb19
-rw-r--r--lib/gitlab/git/repository.rb147
-rw-r--r--lib/gitlab/gitaly_client.rb14
-rw-r--r--lib/gitlab/github_import/parallel_importer.rb9
-rw-r--r--lib/gitlab/verify/batch_verifier.rb59
-rw-r--r--lib/gitlab/verify/job_artifacts.rb10
-rw-r--r--lib/gitlab/verify/lfs_objects.rb12
-rw-r--r--lib/gitlab/verify/rake_task.rb2
-rw-r--r--lib/gitlab/verify/uploads.rb12
-rw-r--r--lib/peek/rblineprof/custom_controller_helpers.rb2
-rw-r--r--lib/system_check/orphans/repository_check.rb16
-rw-r--r--lib/system_check/simple_executor.rb30
-rw-r--r--lib/tasks/migrate/setup_postgresql.rake15
-rw-r--r--package.json2
-rw-r--r--qa/qa/runtime/browser.rb14
-rw-r--r--scripts/frontend/prettier.js53
-rwxr-xr-xscripts/trigger-build181
-rwxr-xr-xscripts/trigger-build-cloud-native61
-rwxr-xr-xscripts/trigger-build-omnibus108
-rw-r--r--spec/controllers/admin/application_settings_controller_spec.rb2
-rw-r--r--spec/controllers/application_controller_spec.rb18
-rw-r--r--spec/controllers/concerns/internal_redirect_spec.rb25
-rw-r--r--spec/controllers/projects/blob_controller_spec.rb19
-rw-r--r--spec/controllers/projects_controller_spec.rb12
-rw-r--r--spec/controllers/uploads_controller_spec.rb17
-rw-r--r--spec/features/issues/filtered_search/dropdown_assignee_spec.rb51
-rw-r--r--spec/features/issues/user_uses_slash_commands_spec.rb4
-rw-r--r--spec/features/labels_hierarchy_spec.rb4
-rw-r--r--spec/features/markdown/copy_as_gfm_spec.rb9
-rw-r--r--spec/features/markdown/markdown_spec.rb55
-rw-r--r--spec/features/projects/deploy_keys_spec.rb3
-rw-r--r--spec/features/projects/diffs/diff_show_spec.rb3
-rw-r--r--spec/features/projects/issues/user_comments_on_issue_spec.rb7
-rw-r--r--spec/features/task_lists_spec.rb45
-rw-r--r--spec/fixtures/markdown.md.erb10
-rw-r--r--spec/graphql/resolvers/merge_request_resolver_spec.rb23
-rw-r--r--spec/graphql/types/project_type_spec.rb9
-rw-r--r--spec/graphql/types/query_type_spec.rb16
-rw-r--r--spec/helpers/markup_helper_spec.rb2
-rw-r--r--spec/helpers/projects_helper_spec.rb10
-rw-r--r--spec/javascripts/blob/viewer/index_spec.js4
-rw-r--r--spec/javascripts/bootstrap_linked_tabs_spec.js2
-rw-r--r--spec/javascripts/commits_spec.js2
-rw-r--r--spec/javascripts/create_merge_request_dropdown_spec.js67
-rw-r--r--spec/javascripts/environments/environments_app_spec.js2
-rw-r--r--spec/javascripts/environments/folder/environments_folder_view_spec.js2
-rw-r--r--spec/javascripts/ide/components/repo_editor_spec.js11
-rw-r--r--spec/javascripts/ide/stores/actions/file_spec.js4
-rw-r--r--spec/javascripts/ide/stores/modules/commit/actions_spec.js99
-rw-r--r--spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js13
-rw-r--r--spec/javascripts/ide/stores/modules/pipelines/actions_spec.js17
-rw-r--r--spec/javascripts/ide/stores/utils_spec.js56
-rw-r--r--spec/javascripts/issue_show/components/app_spec.js4
-rw-r--r--spec/javascripts/lib/utils/common_utils_spec.js4
-rw-r--r--spec/javascripts/notes_spec.js4
-rw-r--r--spec/javascripts/pipelines/pipelines_spec.js2
-rw-r--r--spec/javascripts/settings_panels_spec.js4
-rw-r--r--spec/javascripts/shortcuts_issuable_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/gl_modal_spec.js8
-rw-r--r--spec/lib/banzai/filter/image_lazy_load_filter_spec.rb2
-rw-r--r--spec/lib/banzai/filter/markdown_filter_spec.rb4
-rw-r--r--spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb2
-rw-r--r--spec/lib/gitlab/database_spec.rb9
-rw-r--r--spec/lib/gitlab/favicon_spec.rb2
-rw-r--r--spec/lib/gitlab/git/blame_spec.rb10
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb79
-rw-r--r--spec/lib/gitlab/git_access_wiki_spec.rb4
-rw-r--r--spec/lib/gitlab/verify/job_artifacts_spec.rb29
-rw-r--r--spec/lib/gitlab/verify/lfs_objects_spec.rb25
-rw-r--r--spec/lib/gitlab/verify/uploads_spec.rb27
-rw-r--r--spec/migrations/migrate_process_commit_worker_jobs_spec.rb6
-rw-r--r--spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb8
-rw-r--r--spec/models/ci/build_trace_chunk_spec.rb8
-rw-r--r--spec/models/concerns/cache_markdown_field_spec.rb10
-rw-r--r--spec/models/project_services/jira_service_spec.rb2
-rw-r--r--spec/models/project_spec.rb14
-rw-r--r--spec/models/project_wiki_spec.rb6
-rw-r--r--spec/models/remote_mirror_spec.rb4
-rw-r--r--spec/requests/api/graphql/merge_request_query_spec.rb49
-rw-r--r--spec/requests/api/graphql/project_query_spec.rb63
-rw-r--r--spec/requests/api/internal_spec.rb1
-rw-r--r--spec/services/projects/create_service_spec.rb5
-rw-r--r--spec/spec_helper.rb1
-rw-r--r--spec/support/gitaly.rb2
-rw-r--r--spec/support/helpers/graphql_helpers.rb15
-rw-r--r--spec/support/helpers/migrations_helpers.rb4
-rw-r--r--spec/support/matchers/graphql_matchers.rb6
-rw-r--r--spec/support/shoulda/matchers/rails_shim.rb27
-rw-r--r--spec/uploaders/favicon_uploader_spec.rb29
-rw-r--r--spec/views/errors/access_denied.html.haml_spec.rb7
-rw-r--r--spec/workers/git_garbage_collect_worker_spec.rb117
-rw-r--r--spec/workers/repository_remove_remote_worker_spec.rb4
-rw-r--r--yarn.lock18
232 files changed, 1779 insertions, 1423 deletions
diff --git a/.eslintrc.yml b/.eslintrc.yml
index f851e3b67e6..5b416c9bf1b 100644
--- a/.eslintrc.yml
+++ b/.eslintrc.yml
@@ -71,7 +71,5 @@ rules:
body: 1
## Destructuring: https://eslint.org/docs/rules/prefer-destructuring
prefer-destructuring: off
- ## no-restricted-globals: https://eslint.org/docs/rules/no-restricted-globals
- no-restricted-globals: off
## no-multi-assign: https://eslint.org/docs/rules/no-multi-assign
no-multi-assign: off
diff --git a/.flayignore b/.flayignore
index 7faa6c7bb90..3e5063674ff 100644
--- a/.flayignore
+++ b/.flayignore
@@ -14,3 +14,12 @@ lib/gitlab/gitaly_client/ref_service.rb
lib/gitlab/gitaly_client/commit_service.rb
lib/gitlab/git/commit.rb
lib/gitlab/git/tag.rb
+
+ee/db/**/*
+ee/app/serializers/ee/merge_request_widget_entity.rb
+ee/lib/api/epics.rb
+ee/lib/api/geo_nodes.rb
+ee/lib/ee/gitlab/ldap/sync/admin_users.rb
+ee/app/workers/geo/file_download_dispatch_worker/job_artifact_job_finder.rb
+ee/app/workers/geo/file_download_dispatch_worker/lfs_object_job_finder.rb
+ee/spec/**/*
diff --git a/.gitignore b/.gitignore
index 51b77d5ac9e..21dc67384aa 100644
--- a/.gitignore
+++ b/.gitignore
@@ -76,3 +76,4 @@ eslint-report.html
/.rspec
/plugins/*
/.gitlab_pages_secret
+package-lock.json
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 9a0102c65fd..e7304b9c057 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -264,10 +264,10 @@ package-and-qa:
<<: *single-script-job
variables:
<<: *single-script-job-variables
- SCRIPT_NAME: trigger-build-omnibus
+ SCRIPT_NAME: trigger-build
retry: 0
script:
- - ./$SCRIPT_NAME
+ - ./$SCRIPT_NAME omnibus
when: manual
only:
- //@gitlab-org/gitlab-ce
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index 1fb352306d7..ccf301e6c78 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -487,7 +487,7 @@ Style/EmptyLiteral:
- 'lib/gitlab/fogbugz_import/importer.rb'
- 'lib/gitlab/git/diff_collection.rb'
- 'lib/gitlab/gitaly_client.rb'
- - 'scripts/trigger-build-omnibus'
+ - 'scripts/trigger-build'
- 'spec/features/merge_requests/versions_spec.rb'
- 'spec/helpers/merge_requests_helper_spec.rb'
- 'spec/lib/gitlab/request_context_spec.rb'
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index fb78973a727..dcdf520ee6b 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -303,17 +303,26 @@ The UX team uses labels to manage their workflow.
The ~"UX" label on an issue is a signal to the UX team that it will need UX attention.
To better understand the priority by which UX tackles issues, see the [UX section](https://about.gitlab.com/handbook/engineering/ux) of the handbook.
-Once an issue has been worked on and is ready for development, a UXer applies the ~"UX ready" label to that issue.
+Once an issue has been worked on and is ready for development, a UXer removes the ~"UX" label and applies the ~"UX ready" label to that issue.
The UX team has a special type label called ~"design artifact". This label indicates that the final output
-for an issue is a UX solution/design. The solution will be developed by frontend and/or backend in a subsequent milestone.
+for an issue is a UX solution/design. The solution will be developed by frontend and/or backend in a subsequent milestone.
Any issue labeled ~"design artifact" should not also be labeled ~"frontend" or ~"backend" since no development is
needed until the solution has been decided.
~"design artifact" issues are like any other issue and should contain a milestone label, ~"Deliverable" or ~"Stretch", when scheduled in the current milestone.
-Once the ~"design artifact" issue has been completed, the UXer removes the ~"design artifact" label and applies the ~"UX ready" label. The Product Manager can use the
-existing issue or decide to create a whole new issue for the purpose of development.
+To prevent the misunderstanding that a feature will be be delivered in the
+assigned milestone, when only UX design is planned for that milestone, the
+Product Manager should create a separate issue for the ~"design artifact",
+assign the ~UX, ~"design artifact" and ~"Deliverable" labels, add a milestone
+and use a title that makes it clear that the scheduled issue is design only
+(e.g. `Design exploration for XYZ`).
+
+When the ~"design artifact" issue has been completed, the UXer removes the ~UX
+label, adds the ~"UX ready" label and closes the issue. This indicates the
+design artifact is complete. The UXer will also copy the designs to related
+issues for implementation in an upcoming milestone.
## Issue tracker
diff --git a/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js b/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js
index 75cf90de0b5..1ea6dd909e9 100644
--- a/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js
+++ b/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js
@@ -119,7 +119,7 @@ const gfmRules = {
return el.outerHTML;
},
'dl'(el, text) {
- let lines = text.trim().split('\n');
+ let lines = text.replace(/\n\n/g, '\n').trim().split('\n');
// Add two spaces to the front of subsequent list items lines,
// or leave the line entirely blank.
lines = lines.map((l) => {
@@ -129,9 +129,13 @@ const gfmRules = {
return ` ${line}`;
});
- return `<dl>\n${lines.join('\n')}\n</dl>`;
+ return `<dl>\n${lines.join('\n')}\n</dl>\n`;
},
- 'sub, dt, dd, kbd, q, samp, var, ruby, rt, rp, abbr, summary, details'(el, text) {
+ 'dt, dd, summary, details'(el, text) {
+ const tag = el.nodeName.toLowerCase();
+ return `<${tag}>${text}</${tag}>\n`;
+ },
+ 'sup, sub, kbd, q, samp, var, ruby, rt, rp, abbr'(el, text) {
const tag = el.nodeName.toLowerCase();
return `<${tag}>${text}</${tag}>`;
},
@@ -215,22 +219,22 @@ const gfmRules = {
return text.replace(/^- /mg, '1. ');
},
'h1'(el, text) {
- return `# ${text.trim()}`;
+ return `# ${text.trim()}\n`;
},
'h2'(el, text) {
- return `## ${text.trim()}`;
+ return `## ${text.trim()}\n`;
},
'h3'(el, text) {
- return `### ${text.trim()}`;
+ return `### ${text.trim()}\n`;
},
'h4'(el, text) {
- return `#### ${text.trim()}`;
+ return `#### ${text.trim()}\n`;
},
'h5'(el, text) {
- return `##### ${text.trim()}`;
+ return `##### ${text.trim()}\n`;
},
'h6'(el, text) {
- return `###### ${text.trim()}`;
+ return `###### ${text.trim()}\n`;
},
'strong'(el, text) {
return `**${text}**`;
@@ -241,11 +245,13 @@ const gfmRules = {
'del'(el, text) {
return `~~${text}~~`;
},
- 'sup'(el, text) {
- return `^${text}`;
- },
'hr'(el) {
- return '-----';
+ // extra leading \n is to ensure that there is a blank line between
+ // a list followed by an hr, otherwise this breaks old redcarpet rendering
+ return '\n-----\n';
+ },
+ 'p'(el, text) {
+ return `${text.trim()}\n`;
},
'table'(el) {
const theadEl = el.querySelector('thead');
@@ -263,7 +269,9 @@ const gfmRules = {
let before = '';
let after = '';
- switch (cell.style.textAlign) {
+ const alignment = cell.align || cell.style.textAlign;
+
+ switch (alignment) {
case 'center':
before = ':';
after = ':';
diff --git a/app/assets/javascripts/blob/viewer/index.js b/app/assets/javascripts/blob/viewer/index.js
index f61c0be9230..5485248cfaf 100644
--- a/app/assets/javascripts/blob/viewer/index.js
+++ b/app/assets/javascripts/blob/viewer/index.js
@@ -70,7 +70,7 @@ export default class BlobViewer {
const initialViewer = this.$fileHolder[0].querySelector('.blob-viewer:not(.hidden)');
let initialViewerName = initialViewer.getAttribute('data-type');
- if (this.switcher && location.hash.indexOf('#L') === 0) {
+ if (this.switcher && window.location.hash.indexOf('#L') === 0) {
initialViewerName = 'simple';
}
diff --git a/app/assets/javascripts/boards/components/board_delete.js b/app/assets/javascripts/boards/components/board_delete.js
index 4482b3b3e70..4dd9aebeed9 100644
--- a/app/assets/javascripts/boards/components/board_delete.js
+++ b/app/assets/javascripts/boards/components/board_delete.js
@@ -17,7 +17,7 @@ gl.issueBoards.BoardDelete = Vue.extend({
deleteBoard () {
$(this.$el).tooltip('hide');
- if (confirm('Are you sure you want to delete this list?')) {
+ if (window.confirm('Are you sure you want to delete this list?')) {
this.list.destroy();
}
}
diff --git a/app/assets/javascripts/boards/stores/boards_store.js b/app/assets/javascripts/boards/stores/boards_store.js
index 7dc83843e9b..ffe86468b12 100644
--- a/app/assets/javascripts/boards/stores/boards_store.js
+++ b/app/assets/javascripts/boards/stores/boards_store.js
@@ -145,6 +145,6 @@ gl.issueBoards.BoardsStore = {
return filteredList[0];
},
updateFiltersUrl () {
- history.pushState(null, null, `?${this.filter.path}`);
+ window.history.pushState(null, null, `?${this.filter.path}`);
}
};
diff --git a/app/assets/javascripts/commits.js b/app/assets/javascripts/commits.js
index 7e2a3573f81..9a3ea7a55b6 100644
--- a/app/assets/javascripts/commits.js
+++ b/app/assets/javascripts/commits.js
@@ -45,7 +45,7 @@ export default class CommitsList {
this.content.fadeTo('fast', 1.0);
// Change url so if user reload a page - search results are saved
- history.replaceState({
+ window.history.replaceState({
page: commitsUrl,
}, document.title, commitsUrl);
})
diff --git a/app/assets/javascripts/create_merge_request_dropdown.js b/app/assets/javascripts/create_merge_request_dropdown.js
index 09d490106df..108082799ef 100644
--- a/app/assets/javascripts/create_merge_request_dropdown.js
+++ b/app/assets/javascripts/create_merge_request_dropdown.js
@@ -66,8 +66,14 @@ export default class CreateMergeRequestDropdown {
}
bindEvents() {
- this.createMergeRequestButton.addEventListener('click', this.onClickCreateMergeRequestButton.bind(this));
- this.createTargetButton.addEventListener('click', this.onClickCreateMergeRequestButton.bind(this));
+ this.createMergeRequestButton.addEventListener(
+ 'click',
+ this.onClickCreateMergeRequestButton.bind(this),
+ );
+ this.createTargetButton.addEventListener(
+ 'click',
+ this.onClickCreateMergeRequestButton.bind(this),
+ );
this.branchInput.addEventListener('keyup', this.onChangeInput.bind(this));
this.dropdownToggle.addEventListener('click', this.onClickSetFocusOnBranchNameInput.bind(this));
this.refInput.addEventListener('keyup', this.onChangeInput.bind(this));
@@ -77,7 +83,8 @@ export default class CreateMergeRequestDropdown {
checkAbilityToCreateBranch() {
this.setUnavailableButtonState();
- axios.get(this.canCreatePath)
+ axios
+ .get(this.canCreatePath)
.then(({ data }) => {
this.setUnavailableButtonState(false);
@@ -105,7 +112,8 @@ export default class CreateMergeRequestDropdown {
createBranch() {
this.isCreatingBranch = true;
- return axios.post(this.createBranchPath)
+ return axios
+ .post(this.createBranchPath)
.then(({ data }) => {
this.branchCreated = true;
window.location.href = data.url;
@@ -116,7 +124,8 @@ export default class CreateMergeRequestDropdown {
createMergeRequest() {
this.isCreatingMergeRequest = true;
- return axios.post(this.createMrPath)
+ return axios
+ .post(this.createMrPath)
.then(({ data }) => {
this.mergeRequestCreated = true;
window.location.href = data.url;
@@ -195,7 +204,8 @@ export default class CreateMergeRequestDropdown {
getRef(ref, target = 'all') {
if (!ref) return false;
- return axios.get(this.refsPath + ref)
+ return axios
+ .get(`${this.refsPath}${encodeURIComponent(ref)}`)
.then(({ data }) => {
const branches = data[Object.keys(data)[0]];
const tags = data[Object.keys(data)[1]];
@@ -204,7 +214,8 @@ export default class CreateMergeRequestDropdown {
if (target === 'branch') {
result = CreateMergeRequestDropdown.findByValue(branches, ref);
} else {
- result = CreateMergeRequestDropdown.findByValue(branches, ref, true) ||
+ result =
+ CreateMergeRequestDropdown.findByValue(branches, ref, true) ||
CreateMergeRequestDropdown.findByValue(tags, ref, true);
this.suggestedRef = result;
}
@@ -255,11 +266,13 @@ export default class CreateMergeRequestDropdown {
}
isBusy() {
- return this.isCreatingMergeRequest ||
+ return (
+ this.isCreatingMergeRequest ||
this.mergeRequestCreated ||
this.isCreatingBranch ||
this.branchCreated ||
- this.isGettingRef;
+ this.isGettingRef
+ );
}
onChangeInput(event) {
@@ -271,7 +284,8 @@ export default class CreateMergeRequestDropdown {
value = this.branchInput.value;
} else if (event.target === this.refInput) {
target = 'ref';
- value = event.target.value.slice(0, event.target.selectionStart) +
+ value =
+ event.target.value.slice(0, event.target.selectionStart) +
event.target.value.slice(event.target.selectionEnd);
} else {
return false;
@@ -396,7 +410,8 @@ export default class CreateMergeRequestDropdown {
showNotAvailableMessage(target) {
const { input, message } = this.getTargetData(target);
- const text = target === 'branch' ? __('Branch is already taken') : __('Source is not available');
+ const text =
+ target === 'branch' ? __('Branch is already taken') : __('Source is not available');
this.removeMessage(target);
input.classList.add('gl-field-error-outline');
@@ -459,11 +474,15 @@ export default class CreateMergeRequestDropdown {
// target - 'branch' or 'ref'
// ref - string - the new value to use as branch or ref
updateCreatePaths(target, ref) {
- const pathReplacement = `$1${ref}`;
+ const pathReplacement = `$1${encodeURIComponent(ref)}`;
- this.createBranchPath = this.createBranchPath.replace(this.regexps[target].createBranchPath,
- pathReplacement);
- this.createMrPath = this.createMrPath.replace(this.regexps[target].createMrPath,
- pathReplacement);
+ this.createBranchPath = this.createBranchPath.replace(
+ this.regexps[target].createBranchPath,
+ pathReplacement,
+ );
+ this.createMrPath = this.createMrPath.replace(
+ this.regexps[target].createMrPath,
+ pathReplacement,
+ );
}
}
diff --git a/app/assets/javascripts/deploy_keys/components/app.vue b/app/assets/javascripts/deploy_keys/components/app.vue
index 2cfa13fdc75..d91e4809126 100644
--- a/app/assets/javascripts/deploy_keys/components/app.vue
+++ b/app/assets/javascripts/deploy_keys/components/app.vue
@@ -98,7 +98,7 @@ export default {
},
disableKey(deployKey, callback) {
// eslint-disable-next-line no-alert
- if (confirm(s__('DeployKeys|You are going to remove this deploy key. Are you sure?'))) {
+ if (window.confirm(s__('DeployKeys|You are going to remove this deploy key. Are you sure?'))) {
this.service
.disableKey(deployKey.id)
.then(this.fetchKeys)
diff --git a/app/assets/javascripts/environments/components/environment_stop.vue b/app/assets/javascripts/environments/components/environment_stop.vue
index faaaf899a0d..eba58bedd6d 100644
--- a/app/assets/javascripts/environments/components/environment_stop.vue
+++ b/app/assets/javascripts/environments/components/environment_stop.vue
@@ -40,7 +40,7 @@
methods: {
onClick() {
// eslint-disable-next-line no-alert
- if (confirm('Are you sure you want to stop this environment?')) {
+ if (window.confirm('Are you sure you want to stop this environment?')) {
this.isLoading = true;
$(this.$el).tooltip('dispose');
diff --git a/app/assets/javascripts/ide/components/pipelines/list.vue b/app/assets/javascripts/ide/components/pipelines/list.vue
index 4d29e9f8060..5757dfdc925 100644
--- a/app/assets/javascripts/ide/components/pipelines/list.vue
+++ b/app/assets/javascripts/ide/components/pipelines/list.vue
@@ -94,7 +94,7 @@ export default {
<p class="append-bottom-0">
{{ __('Found errors in your .gitlab-ci.yml:') }}
</p>
- <p class="append-bottom-0">
+ <p class="append-bottom-0 break-word">
{{ latestPipeline.yamlError }}
</p>
<p
diff --git a/app/assets/javascripts/ide/components/repo_editor.vue b/app/assets/javascripts/ide/components/repo_editor.vue
index 24b6a4fdea1..08ee12fd98f 100644
--- a/app/assets/javascripts/ide/components/repo_editor.vue
+++ b/app/assets/javascripts/ide/components/repo_editor.vue
@@ -20,7 +20,13 @@ export default {
},
},
computed: {
- ...mapState(['rightPanelCollapsed', 'viewer', 'panelResizing', 'currentActivityView']),
+ ...mapState([
+ 'rightPanelCollapsed',
+ 'viewer',
+ 'panelResizing',
+ 'currentActivityView',
+ 'rightPane',
+ ]),
...mapGetters([
'currentMergeRequest',
'getStagedFile',
@@ -88,6 +94,9 @@ export default {
this.editor.updateDimensions();
}
},
+ rightPane() {
+ this.editor.updateDimensions();
+ },
},
beforeDestroy() {
this.editor.dispose();
diff --git a/app/assets/javascripts/ide/lib/diff/diff_worker.js b/app/assets/javascripts/ide/lib/diff/diff_worker.js
index e74c4046330..f09930e8158 100644
--- a/app/assets/javascripts/ide/lib/diff/diff_worker.js
+++ b/app/assets/javascripts/ide/lib/diff/diff_worker.js
@@ -1,8 +1,10 @@
import { computeDiff } from './diff';
+// eslint-disable-next-line no-restricted-globals
self.addEventListener('message', (e) => {
const data = e.data;
+ // eslint-disable-next-line no-restricted-globals
self.postMessage({
path: data.path,
changes: computeDiff(data.originalContent, data.newContent),
diff --git a/app/assets/javascripts/ide/services/index.js b/app/assets/javascripts/ide/services/index.js
index e8b51f2b516..da9de25302a 100644
--- a/app/assets/javascripts/ide/services/index.js
+++ b/app/assets/javascripts/ide/services/index.js
@@ -9,7 +9,7 @@ export default {
return Vue.http.get(endpoint, { params: { format: 'json' } });
},
getFileData(endpoint) {
- return Vue.http.get(endpoint, { params: { format: 'json' } });
+ return Vue.http.get(endpoint, { params: { format: 'json', viewer: 'none' } });
},
getRawFileData(file) {
if (file.tempFile) {
diff --git a/app/assets/javascripts/ide/stores/modules/commit/actions.js b/app/assets/javascripts/ide/stores/modules/commit/actions.js
index 0a0db4033c8..7219abc4185 100644
--- a/app/assets/javascripts/ide/stores/modules/commit/actions.js
+++ b/app/assets/javascripts/ide/stores/modules/commit/actions.js
@@ -49,31 +49,6 @@ export const setLastCommitMessage = ({ rootState, commit }, data) => {
commit(rootTypes.SET_LAST_COMMIT_MSG, commitMsg, { root: true });
};
-export const checkCommitStatus = ({ rootState }) =>
- service
- .getBranchData(rootState.currentProjectId, rootState.currentBranchId)
- .then(({ data }) => {
- const { id } = data.commit;
- const selectedBranch =
- rootState.projects[rootState.currentProjectId].branches[rootState.currentBranchId];
-
- if (selectedBranch.workingReference !== id) {
- return true;
- }
-
- return false;
- })
- .catch(() =>
- flash(
- __('Error checking branch data. Please try again.'),
- 'alert',
- document,
- null,
- false,
- true,
- ),
- );
-
export const updateFilesAfterCommit = ({ commit, dispatch, rootState }, { data }) => {
const selectedProject = rootState.projects[rootState.currentProjectId];
const lastCommit = {
@@ -128,24 +103,17 @@ export const updateFilesAfterCommit = ({ commit, dispatch, rootState }, { data }
export const commitChanges = ({ commit, state, getters, dispatch, rootState, rootGetters }) => {
const newBranch = state.commitAction !== consts.COMMIT_TO_CURRENT_BRANCH;
- const payload = createCommitPayload(getters.branchName, newBranch, state, rootState);
- const getCommitStatus = newBranch ? Promise.resolve(false) : dispatch('checkCommitStatus');
+ const payload = createCommitPayload({
+ branch: getters.branchName,
+ newBranch,
+ state,
+ rootState,
+ });
commit(types.UPDATE_LOADING, true);
- return getCommitStatus
- .then(
- branchChanged =>
- new Promise(resolve => {
- if (branchChanged) {
- // show the modal with a Bootstrap call
- $('#ide-create-branch-modal').modal('show');
- } else {
- resolve();
- }
- }),
- )
- .then(() => service.commit(rootState.currentProjectId, payload))
+ return service
+ .commit(rootState.currentProjectId, payload)
.then(({ data }) => {
commit(types.UPDATE_LOADING, false);
@@ -220,12 +188,16 @@ export const commitChanges = ({ commit, state, getters, dispatch, rootState, roo
);
})
.catch(err => {
- let errMsg = __('Error committing changes. Please try again.');
- if (err.response.data && err.response.data.message) {
- errMsg += ` (${stripHtml(err.response.data.message)})`;
+ if (err.response.status === 400) {
+ $('#ide-create-branch-modal').modal('show');
+ } else {
+ let errMsg = __('Error committing changes. Please try again.');
+ if (err.response.data && err.response.data.message) {
+ errMsg += ` (${stripHtml(err.response.data.message)})`;
+ }
+ flash(errMsg, 'alert', document, null, false, true);
+ window.dispatchEvent(new Event('resize'));
}
- flash(errMsg, 'alert', document, null, false, true);
- window.dispatchEvent(new Event('resize'));
commit(types.UPDATE_LOADING, false);
});
diff --git a/app/assets/javascripts/ide/stores/modules/merge_requests/actions.js b/app/assets/javascripts/ide/stores/modules/merge_requests/actions.js
index 4e1df80b3a2..551dd322c9b 100644
--- a/app/assets/javascripts/ide/stores/modules/merge_requests/actions.js
+++ b/app/assets/javascripts/ide/stores/modules/merge_requests/actions.js
@@ -31,15 +31,16 @@ export const openMergeRequest = ({ commit, dispatch }, { projectPath, id }) => {
commit(rootTypes.CLEAR_PROJECTS, null, { root: true });
commit(rootTypes.SET_CURRENT_MERGE_REQUEST, `${id}`, { root: true });
commit(rootTypes.RESET_OPEN_FILES, null, { root: true });
- dispatch('pipelines/resetLatestPipeline', null, { root: true });
dispatch('setCurrentBranchId', '', { root: true });
dispatch('pipelines/stopPipelinePolling', null, { root: true })
.then(() => {
+ dispatch('pipelines/resetLatestPipeline', null, { root: true });
dispatch('pipelines/clearEtagPoll', null, { root: true });
})
.catch(e => {
throw e;
});
+ dispatch('setRightPane', null, { root: true });
router.push(`/project/${projectPath}/merge_requests/${id}`);
};
diff --git a/app/assets/javascripts/ide/stores/modules/pipelines/actions.js b/app/assets/javascripts/ide/stores/modules/pipelines/actions.js
index 6718f7eae4e..fe1dc9ac8f8 100644
--- a/app/assets/javascripts/ide/stores/modules/pipelines/actions.js
+++ b/app/assets/javascripts/ide/stores/modules/pipelines/actions.js
@@ -106,7 +106,9 @@ export const fetchJobTrace = ({ dispatch, state }) => {
.catch(() => dispatch('receiveJobTraceError'));
};
-export const resetLatestPipeline = ({ commit }) =>
+export const resetLatestPipeline = ({ commit }) => {
commit(types.RECEIVE_LASTEST_PIPELINE_SUCCESS, null);
+ commit(types.SET_DETAIL_JOB, null);
+};
export default () => {};
diff --git a/app/assets/javascripts/ide/stores/mutations/file.js b/app/assets/javascripts/ide/stores/mutations/file.js
index 5826f6cb828..46547820425 100644
--- a/app/assets/javascripts/ide/stores/mutations/file.js
+++ b/app/assets/javascripts/ide/stores/mutations/file.js
@@ -47,6 +47,7 @@ export default {
baseRaw: null,
html: data.html,
size: data.size,
+ lastCommitSha: data.last_commit_sha,
});
},
[types.SET_FILE_RAW_DATA](state, { file, raw }) {
diff --git a/app/assets/javascripts/ide/stores/utils.js b/app/assets/javascripts/ide/stores/utils.js
index a04a33cd12d..10368a4d97c 100644
--- a/app/assets/javascripts/ide/stores/utils.js
+++ b/app/assets/javascripts/ide/stores/utils.js
@@ -17,6 +17,7 @@ export const dataStructure = () => ({
changed: false,
staged: false,
lastCommitPath: '',
+ lastCommitSha: '',
lastCommit: {
id: '',
url: '',
@@ -104,7 +105,7 @@ export const setPageTitle = title => {
document.title = title;
};
-export const createCommitPayload = (branch, newBranch, state, rootState) => ({
+export const createCommitPayload = ({ branch, newBranch, state, rootState }) => ({
branch,
commit_message: state.commitMessage,
actions: rootState.stagedFiles.map(f => ({
@@ -112,6 +113,7 @@ export const createCommitPayload = (branch, newBranch, state, rootState) => ({
file_path: f.path,
content: f.content,
encoding: f.base64 ? 'base64' : 'text',
+ last_commit_id: newBranch ? undefined : f.lastCommitSha,
})),
start_branch: newBranch ? rootState.currentBranchId : undefined,
});
diff --git a/app/assets/javascripts/ide/stores/workers/files_decorator_worker.js b/app/assets/javascripts/ide/stores/workers/files_decorator_worker.js
index 0a1c253c637..fa35c215880 100644
--- a/app/assets/javascripts/ide/stores/workers/files_decorator_worker.js
+++ b/app/assets/javascripts/ide/stores/workers/files_decorator_worker.js
@@ -1,6 +1,7 @@
import { viewerInformationForPath } from '~/vue_shared/components/content_viewer/lib/viewer_utils';
import { decorateData, sortTree } from '../utils';
+// eslint-disable-next-line no-restricted-globals
self.addEventListener('message', e => {
const { data, projectId, branchId, tempFile = false, content = '', base64 = false } = e.data;
@@ -89,6 +90,7 @@ self.addEventListener('message', e => {
return acc;
}, {});
+ // eslint-disable-next-line no-restricted-globals
self.postMessage({
entries,
treeList: sortTree(treeList),
diff --git a/app/assets/javascripts/issue_show/components/app.vue b/app/assets/javascripts/issue_show/components/app.vue
index e87a8ed7fea..b6364318537 100644
--- a/app/assets/javascripts/issue_show/components/app.vue
+++ b/app/assets/javascripts/issue_show/components/app.vue
@@ -226,7 +226,7 @@
.then(res => res.data)
.then(data => this.checkForSpam(data))
.then((data) => {
- if (location.pathname !== data.web_url) {
+ if (window.location.pathname !== data.web_url) {
visitUrl(data.web_url);
}
diff --git a/app/assets/javascripts/issue_show/components/edit_actions.vue b/app/assets/javascripts/issue_show/components/edit_actions.vue
index 8cb0ab22bfb..597c6d69a81 100644
--- a/app/assets/javascripts/issue_show/components/edit_actions.vue
+++ b/app/assets/javascripts/issue_show/components/edit_actions.vue
@@ -38,7 +38,7 @@
},
deleteIssuable() {
// eslint-disable-next-line no-alert
- if (confirm('Issue will be removed! Are you sure?')) {
+ if (window.confirm('Issue will be removed! Are you sure?')) {
this.deleteLoading = true;
eventHub.$emit('delete.issuable');
diff --git a/app/assets/javascripts/issue_show/components/locked_warning.vue b/app/assets/javascripts/issue_show/components/locked_warning.vue
index 1c2789f154a..ad0d40faf32 100644
--- a/app/assets/javascripts/issue_show/components/locked_warning.vue
+++ b/app/assets/javascripts/issue_show/components/locked_warning.vue
@@ -2,7 +2,7 @@
export default {
computed: {
currentPath() {
- return location.pathname;
+ return window.location.pathname;
},
},
};
diff --git a/app/assets/javascripts/lazy_loader.js b/app/assets/javascripts/lazy_loader.js
index dbbf1637a47..9482d131344 100644
--- a/app/assets/javascripts/lazy_loader.js
+++ b/app/assets/javascripts/lazy_loader.js
@@ -44,8 +44,8 @@ export default class LazyLoader {
requestAnimationFrame(() => this.checkElementsInView());
}
checkElementsInView() {
- const scrollTop = pageYOffset;
- const visHeight = scrollTop + innerHeight + SCROLL_THRESHOLD;
+ const scrollTop = window.pageYOffset;
+ const visHeight = scrollTop + window.innerHeight + SCROLL_THRESHOLD;
// Loading Images which are in the current viewport or close to them
this.lazyImages = this.lazyImages.filter((selectedImage) => {
diff --git a/app/assets/javascripts/lib/utils/bootstrap_linked_tabs.js b/app/assets/javascripts/lib/utils/bootstrap_linked_tabs.js
index 3873f4528ce..c28ed04f94f 100644
--- a/app/assets/javascripts/lib/utils/bootstrap_linked_tabs.js
+++ b/app/assets/javascripts/lib/utils/bootstrap_linked_tabs.js
@@ -93,7 +93,7 @@ export default class LinkedTabs {
const newState = `${copySource}${this.currentLocation.search}${this.currentLocation.hash}`;
- history.replaceState({
+ window.history.replaceState({
url: newState,
}, document.title, newState);
return newState;
diff --git a/app/assets/javascripts/lib/utils/number_utils.js b/app/assets/javascripts/lib/utils/number_utils.js
index a02c79b787e..f086d962221 100644
--- a/app/assets/javascripts/lib/utils/number_utils.js
+++ b/app/assets/javascripts/lib/utils/number_utils.js
@@ -12,7 +12,7 @@ export function formatRelevantDigits(number) {
let digitsLeft = '';
let relevantDigits = 0;
let formattedNumber = '';
- if (!isNaN(Number(number))) {
+ if (!Number.isNaN(Number(number))) {
digitsLeft = number.toString().split('.')[0];
switch (digitsLeft.length) {
case 1:
diff --git a/app/assets/javascripts/line_highlighter.js b/app/assets/javascripts/line_highlighter.js
index f2323f57455..232b59e199c 100644
--- a/app/assets/javascripts/line_highlighter.js
+++ b/app/assets/javascripts/line_highlighter.js
@@ -35,7 +35,7 @@ const LineHighlighter = function(options = {}) {
options.highlightLineClass = options.highlightLineClass || 'hll';
options.fileHolderSelector = options.fileHolderSelector || '.file-holder';
options.scrollFileHolder = options.scrollFileHolder || false;
- options.hash = options.hash || location.hash;
+ options.hash = options.hash || window.location.hash;
this.options = options;
this._hash = options.hash;
@@ -170,7 +170,7 @@ LineHighlighter.prototype.setHash = function(firstLineNumber, lastLineNumber) {
//
// This method is stubbed in tests.
LineHighlighter.prototype.__setLocationHash__ = function(value) {
- return history.pushState({
+ return window.history.pushState({
url: value
// We're using pushState instead of assigning location.hash directly to
// prevent the page from scrolling on the hashchange event
diff --git a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js
index 326d4523cce..491858c3602 100644
--- a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js
+++ b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js
@@ -1,13 +1,9 @@
-/* eslint-disable new-cap, comma-dangle, no-new */
-
import $ from 'jquery';
import Vue from 'vue';
-import Flash from '../flash';
+import createFlash from '../flash';
import initIssuableSidebar from '../init_issuable_sidebar';
import './merge_conflict_store';
import MergeConflictsService from './merge_conflict_service';
-import './mixins/line_conflict_utils';
-import './mixins/line_conflict_actions';
import './components/diff_file_editor';
import './components/inline_conflict_lines';
import './components/parallel_conflict_lines';
@@ -19,7 +15,7 @@ export default function initMergeConflicts() {
const mergeConflictsStore = gl.mergeConflicts.mergeConflictsStore;
const mergeConflictsService = new MergeConflictsService({
conflictsPath: conflictsEl.dataset.conflictsPath,
- resolveConflictsPath: conflictsEl.dataset.resolveConflictsPath
+ resolveConflictsPath: conflictsEl.dataset.resolveConflictsPath,
});
initIssuableSidebar();
@@ -29,17 +25,26 @@ export default function initMergeConflicts() {
components: {
'diff-file-editor': gl.mergeConflicts.diffFileEditor,
'inline-conflict-lines': gl.mergeConflicts.inlineConflictLines,
- 'parallel-conflict-lines': gl.mergeConflicts.parallelConflictLines
+ 'parallel-conflict-lines': gl.mergeConflicts.parallelConflictLines,
},
data: mergeConflictsStore.state,
computed: {
- conflictsCountText() { return mergeConflictsStore.getConflictsCountText(); },
- readyToCommit() { return mergeConflictsStore.isReadyToCommit(); },
- commitButtonText() { return mergeConflictsStore.getCommitButtonText(); },
- showDiffViewTypeSwitcher() { return mergeConflictsStore.fileTextTypePresent(); }
+ conflictsCountText() {
+ return mergeConflictsStore.getConflictsCountText();
+ },
+ readyToCommit() {
+ return mergeConflictsStore.isReadyToCommit();
+ },
+ commitButtonText() {
+ return mergeConflictsStore.getCommitButtonText();
+ },
+ showDiffViewTypeSwitcher() {
+ return mergeConflictsStore.fileTextTypePresent();
+ },
},
created() {
- mergeConflictsService.fetchConflictsData()
+ mergeConflictsService
+ .fetchConflictsData()
.then(({ data }) => {
if (data.type === 'error') {
mergeConflictsStore.setFailedRequest(data.message);
@@ -87,9 +92,9 @@ export default function initMergeConflicts() {
})
.catch(() => {
mergeConflictsStore.setSubmitState(false);
- new Flash('Failed to save merge conflicts resolutions. Please try again!');
+ createFlash('Failed to save merge conflicts resolutions. Please try again!');
});
- }
- }
+ },
+ },
});
}
diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js
index 493c119dc6f..65ab41559be 100644
--- a/app/assets/javascripts/merge_request_tabs.js
+++ b/app/assets/javascripts/merge_request_tabs.js
@@ -362,7 +362,7 @@ export default class MergeRequestTabs {
//
// status - Boolean, true to show, false to hide
toggleLoading(status) {
- $('.mr-loading-status .loading').toggleClass('hidden', !status);
+ $('.mr-loading-status .loading').toggleClass('hide', !status);
}
diffViewType() {
diff --git a/app/assets/javascripts/milestone.js b/app/assets/javascripts/milestone.js
index 325fa570f37..6da04020881 100644
--- a/app/assets/javascripts/milestone.js
+++ b/app/assets/javascripts/milestone.js
@@ -18,13 +18,13 @@ export default class Milestone {
return $('a[data-toggle="tab"]').on('show.bs.tab', (e) => {
const $target = $(e.target);
- location.hash = $target.attr('href');
+ window.location.hash = $target.attr('href');
this.loadTab($target);
});
}
// eslint-disable-next-line class-methods-use-this
loadInitialTab() {
- const $target = $(`.js-milestone-tabs a[href="${location.hash}"]`);
+ const $target = $(`.js-milestone-tabs a[href="${window.location.hash}"]`);
if ($target.length) {
$target.tab('show');
diff --git a/app/assets/javascripts/monitoring/components/graph/flag.vue b/app/assets/javascripts/monitoring/components/graph/flag.vue
index 282c5c24384..92fe98508ad 100644
--- a/app/assets/javascripts/monitoring/components/graph/flag.vue
+++ b/app/assets/javascripts/monitoring/components/graph/flag.vue
@@ -97,7 +97,7 @@ export default {
? this.deploymentFlagData.seriesIndex
: indexFromCoordinates;
const value = series.values[index] && series.values[index].value;
- if (isNaN(value)) {
+ if (Number.isNaN(value)) {
return '-';
}
return `${formatRelevantDigits(value)}${this.unitOfDisplay}`;
diff --git a/app/assets/javascripts/monitoring/utils/multiple_time_series.js b/app/assets/javascripts/monitoring/utils/multiple_time_series.js
index 4d3f1f1a7cc..ed3a27dd68b 100644
--- a/app/assets/javascripts/monitoring/utils/multiple_time_series.js
+++ b/app/assets/javascripts/monitoring/utils/multiple_time_series.js
@@ -73,7 +73,7 @@ function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom
timeSeriesScaleX.ticks(d3.timeMinute, 60);
timeSeriesScaleY.domain(yDom);
- const defined = d => !isNaN(d.value) && d.value != null;
+ const defined = d => !Number.isNaN(d.value) && d.value != null;
const lineFunction = d3
.line()
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index b2c1a26bbae..27c5dedcf0b 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -315,7 +315,7 @@ export default class Notes {
if (discussionNoteForm.length) {
if ($textarea.val() !== '') {
if (
- !confirm('Are you sure you want to cancel creating this comment?')
+ !window.confirm('Are you sure you want to cancel creating this comment?')
) {
return;
}
@@ -329,7 +329,7 @@ export default class Notes {
newText = $textarea.val();
if (originalText !== newText) {
if (
- !confirm('Are you sure you want to cancel editing this comment?')
+ !window.confirm('Are you sure you want to cancel editing this comment?')
) {
return;
}
@@ -1675,7 +1675,7 @@ export default class Notes {
<div class="note-header">
<div class="note-header-info">
<a href="/${_.escape(currentUsername)}">
- <span class="d-none d-sm-block">${_.escape(
+ <span class="d-none d-sm-inline-block">${_.escape(
currentUsername,
)}</span>
<span class="note-headline-light">${_.escape(
@@ -1694,7 +1694,7 @@ export default class Notes {
</li>`,
);
- $tempNote.find('.d-none.d-sm-block').text(_.escape(currentUserFullname));
+ $tempNote.find('.d-none.d-sm-inline-block').text(_.escape(currentUserFullname));
$tempNote
.find('.note-headline-light')
.text(`@${_.escape(currentUsername)}`);
diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue
index f9f5041a9f9..3f865431155 100644
--- a/app/assets/javascripts/notes/components/noteable_discussion.vue
+++ b/app/assets/javascripts/notes/components/noteable_discussion.vue
@@ -152,7 +152,7 @@ export default {
const msg = 'Are you sure you want to cancel creating this comment?';
// eslint-disable-next-line no-alert
- if (!confirm(msg)) {
+ if (!window.confirm(msg)) {
return;
}
}
diff --git a/app/assets/javascripts/notes/components/noteable_note.vue b/app/assets/javascripts/notes/components/noteable_note.vue
index ec3ee407f0a..713f93456b1 100644
--- a/app/assets/javascripts/notes/components/noteable_note.vue
+++ b/app/assets/javascripts/notes/components/noteable_note.vue
@@ -77,7 +77,7 @@ export default {
},
deleteHandler() {
// eslint-disable-next-line no-alert
- if (confirm('Are you sure you want to delete this comment?')) {
+ if (window.confirm('Are you sure you want to delete this comment?')) {
this.isDeleting = true;
this.deleteNote(this.note)
@@ -129,7 +129,7 @@ export default {
formCancelHandler(shouldConfirm, isDirty) {
if (shouldConfirm && isDirty) {
// eslint-disable-next-line no-alert
- if (!confirm('Are you sure you want to cancel editing this comment?'))
+ if (!window.confirm('Are you sure you want to cancel editing this comment?'))
return;
}
this.$refs.noteBody.resetAutoSave();
diff --git a/app/assets/javascripts/pages/users/user_tabs.js b/app/assets/javascripts/pages/users/user_tabs.js
index 9404b06615e..a2ca03536f2 100644
--- a/app/assets/javascripts/pages/users/user_tabs.js
+++ b/app/assets/javascripts/pages/users/user_tabs.js
@@ -180,14 +180,14 @@ export default class UserTabs {
}
toggleLoading(status) {
- return this.$parentEl.find('.loading-status .loading').toggleClass('hidden', !status);
+ return this.$parentEl.find('.loading-status .loading').toggleClass('hide', !status);
}
setCurrentAction(source) {
let newState = source;
newState = newState.replace(/\/+$/, '');
newState += this.windowLocation.search + this.windowLocation.hash;
- history.replaceState(
+ window.history.replaceState(
{
url: newState,
},
diff --git a/app/assets/javascripts/performance_bar/components/detailed_metric.vue b/app/assets/javascripts/performance_bar/components/detailed_metric.vue
index d765ff3b41c..dc7d6d29b8f 100644
--- a/app/assets/javascripts/performance_bar/components/detailed_metric.vue
+++ b/app/assets/javascripts/performance_bar/components/detailed_metric.vue
@@ -56,7 +56,7 @@ export default {
<gl-modal
:id="`modal-peek-${metric}-details`"
:header-title-text="header"
- modal-size="lg"
+ modal-size="xl"
class="performance-bar-modal"
>
<table
@@ -71,7 +71,7 @@ export default {
<td
v-for="key in keys"
:key="key"
- class="break-word all-words"
+ class="break-word"
>
{{ item[key] }}
</td>
diff --git a/app/assets/javascripts/project_find_file.js b/app/assets/javascripts/project_find_file.js
index 4c4acd487f8..1fed200db49 100644
--- a/app/assets/javascripts/project_find_file.js
+++ b/app/assets/javascripts/project_find_file.js
@@ -150,7 +150,7 @@ export default class ProjectFindFile {
}
goToTree() {
- return location.href = this.options.treeUrl;
+ return window.location.href = this.options.treeUrl;
}
goToBlob() {
diff --git a/app/assets/javascripts/project_import.js b/app/assets/javascripts/project_import.js
index d2d26d6f67e..5a0d2b642eb 100644
--- a/app/assets/javascripts/project_import.js
+++ b/app/assets/javascripts/project_import.js
@@ -2,7 +2,7 @@ import { visitUrl } from './lib/utils/url_utility';
export default function projectImport() {
setTimeout(() => {
- visitUrl(location.href);
+ visitUrl(window.location.href);
}, 5000);
}
diff --git a/app/assets/javascripts/projects/project_new.js b/app/assets/javascripts/projects/project_new.js
index 888b1d6ce33..002edb4663c 100644
--- a/app/assets/javascripts/projects/project_new.js
+++ b/app/assets/javascripts/projects/project_new.js
@@ -90,7 +90,7 @@ const bindEvents = () => {
function chooseTemplate() {
$('.template-option').hide();
$projectFieldsForm.addClass('selected');
- $selectedIcon.removeClass('active');
+ $selectedIcon.removeClass('d-block');
const value = $(this).val();
const templates = {
rails: {
@@ -109,7 +109,7 @@ const bindEvents = () => {
const selectedTemplate = templates[value];
$selectedTemplateText.text(selectedTemplate.text);
- $(selectedTemplate.icon).addClass('active');
+ $(selectedTemplate.icon).addClass('d-block');
$templateProjectNameInput.focus();
}
diff --git a/app/assets/javascripts/search_autocomplete.js b/app/assets/javascripts/search_autocomplete.js
index 2da022fde63..ef3c71eeafe 100644
--- a/app/assets/javascripts/search_autocomplete.js
+++ b/app/assets/javascripts/search_autocomplete.js
@@ -438,7 +438,7 @@ export default class SearchAutocomplete {
}
onClick(item, $el, e) {
- if (location.pathname.indexOf(item.url) !== -1) {
+ if (window.location.pathname.indexOf(item.url) !== -1) {
if (!e.metaKey) e.preventDefault();
if (!this.badgePresent) {
if (item.category === 'Projects') {
diff --git a/app/assets/javascripts/settings_panels.js b/app/assets/javascripts/settings_panels.js
index eecde4550f9..37b4a2a4c63 100644
--- a/app/assets/javascripts/settings_panels.js
+++ b/app/assets/javascripts/settings_panels.js
@@ -42,8 +42,8 @@ export default function initSettingsPanels() {
}
});
- if (location.hash) {
- const $target = $(location.hash);
+ if (window.location.hash) {
+ const $target = $(window.location.hash);
if ($target.length && $target.hasClass('settings')) {
expandSection($target);
}
diff --git a/app/assets/javascripts/shortcuts_find_file.js b/app/assets/javascripts/shortcuts_find_file.js
index 1e246a56b85..8658081c6c2 100644
--- a/app/assets/javascripts/shortcuts_find_file.js
+++ b/app/assets/javascripts/shortcuts_find_file.js
@@ -13,8 +13,8 @@ export default class ShortcutsFindFile extends ShortcutsNavigation {
element === this.projectFindFile.inputElement[0] &&
(combo === 'up' || combo === 'down' || combo === 'esc' || combo === 'enter')
) {
- // when press up/down key in textbox, cusor prevent to move to home/end
- event.preventDefault();
+ // when press up/down key in textbox, cursor prevent to move to home/end
+ e.preventDefault();
return false;
}
diff --git a/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue b/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue
index 1b866cca4b1..2b8d6207dea 100644
--- a/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue
+++ b/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue
@@ -54,7 +54,7 @@ export default {
updateConfidentialAttribute(confidential) {
this.service
.update('issue', { confidential })
- .then(() => location.reload())
+ .then(() => window.location.reload())
.catch(() => {
Flash(
__(
diff --git a/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue b/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue
index 10bedf8af1f..8bbc59f623a 100644
--- a/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue
+++ b/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue
@@ -76,7 +76,7 @@ export default {
.update(this.issuableType, {
discussion_locked: locked,
})
- .then(() => location.reload())
+ .then(() => window.location.reload())
.catch(() =>
Flash(
this.__(
diff --git a/app/assets/javascripts/sidebar/sidebar_mediator.js b/app/assets/javascripts/sidebar/sidebar_mediator.js
index d86557e870a..d9ca5e46770 100644
--- a/app/assets/javascripts/sidebar/sidebar_mediator.js
+++ b/app/assets/javascripts/sidebar/sidebar_mediator.js
@@ -80,7 +80,7 @@ export default class SidebarMediator {
return this.service.moveIssue(this.store.moveToProjectId)
.then(response => response.json())
.then((data) => {
- if (location.pathname !== data.web_url) {
+ if (window.location.pathname !== data.web_url) {
visitUrl(data.web_url);
}
});
diff --git a/app/assets/javascripts/single_file_diff.js b/app/assets/javascripts/single_file_diff.js
index 1afff0dba38..ae27c676fa0 100644
--- a/app/assets/javascripts/single_file_diff.js
+++ b/app/assets/javascripts/single_file_diff.js
@@ -11,7 +11,7 @@ import syntaxHighlight from './syntax_highlight';
const WRAPPER = '<div class="diff-content"></div>';
const LOADING_HTML = '<i class="fa fa-spinner fa-spin"></i>';
const ERROR_HTML = '<div class="nothing-here-block"><i class="fa fa-warning"></i> Could not load diff</div>';
-const COLLAPSED_HTML = '<div class="nothing-here-block diff-collapsed">This diff is collapsed. <a class="click-to-expand">Click to expand it.</a></div>';
+const COLLAPSED_HTML = '<div class="nothing-here-block diff-collapsed">This diff is collapsed. <button class="click-to-expand btn btn-link">Click to expand it.</button></div>';
export default class SingleFileDiff {
constructor(file) {
diff --git a/app/assets/javascripts/snippet/snippet_embed.js b/app/assets/javascripts/snippet/snippet_embed.js
index 81ec483f2d9..873a506a92f 100644
--- a/app/assets/javascripts/snippet/snippet_embed.js
+++ b/app/assets/javascripts/snippet/snippet_embed.js
@@ -1,5 +1,5 @@
export default () => {
- const { protocol, host, pathname } = location;
+ const { protocol, host, pathname } = window.location;
const shareBtn = document.querySelector('.js-share-btn');
const embedBtn = document.querySelector('.js-embed-btn');
const snippetUrlArea = document.querySelector('.js-snippet-url-area');
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue
index 7078e19d8bf..89c9a41f316 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue
@@ -62,7 +62,7 @@ export default {
v-if="mr.removeWIPPath"
:disabled="isMakingRequest"
type="button"
- class="btn btn-default btn-xs js-remove-wip"
+ class="btn btn-default btn-sm js-remove-wip"
@click="removeWIP">
<i
v-if="isMakingRequest"
diff --git a/app/assets/javascripts/vue_shared/components/gl_modal.vue b/app/assets/javascripts/vue_shared/components/gl_modal.vue
index c03a342e777..b298b989203 100644
--- a/app/assets/javascripts/vue_shared/components/gl_modal.vue
+++ b/app/assets/javascripts/vue_shared/components/gl_modal.vue
@@ -1,6 +1,6 @@
<script>
const buttonVariants = ['danger', 'primary', 'success', 'warning'];
-const sizeVariants = ['sm', 'md', 'lg'];
+const sizeVariants = ['sm', 'md', 'lg', 'xl'];
export default {
name: 'GlModal',
diff --git a/app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue b/app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue
index eccba61a8c0..38115f268bb 100644
--- a/app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue
+++ b/app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue
@@ -54,7 +54,7 @@
<div class="note-header">
<div class="note-header-info">
<a :href="getUserData.path">
- <span class="d-none d-sm-block">{{ getUserData.name }}</span>
+ <span class="d-none d-sm-inline-block">{{ getUserData.name }}</span>
<span class="note-headline-light">@{{ getUserData.username }}</span>
</a>
</div>
diff --git a/app/assets/stylesheets/bootstrap_migration.scss b/app/assets/stylesheets/bootstrap_migration.scss
index 810ed5bb0a6..5da0e672288 100644
--- a/app/assets/stylesheets/bootstrap_migration.scss
+++ b/app/assets/stylesheets/bootstrap_migration.scss
@@ -293,3 +293,9 @@ input[type=color].form-control {
color: $gl-text-color-secondary;
}
}
+
+.project-templates-buttons {
+ .btn {
+ vertical-align: unset;
+ }
+}
diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss
index c5be27f2d29..0de05548c68 100644
--- a/app/assets/stylesheets/framework/blocks.scss
+++ b/app/assets/stylesheets/framework/blocks.scss
@@ -16,6 +16,7 @@
.click-to-expand {
cursor: pointer;
+ vertical-align: initial;
}
}
}
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index 88b174491dd..523fcb05a87 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -121,10 +121,6 @@
&.btn-sm {
margin-left: $btn-sm-side-margin;
}
-
- &.btn-xs {
- margin-left: $btn-xs-side-margin;
- }
}
@mixin btn-svg {
@@ -150,10 +146,6 @@
line-height: 18px;
}
- &.btn-xs {
- padding: 2px 5px;
- }
-
&.btn-success,
&.btn-new,
&.btn-create,
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index e5197e27b82..326499125fc 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -440,10 +440,6 @@ img.emoji {
.break-word {
word-wrap: break-word;
-
- &.all-words {
- word-break: break-word;
- }
}
/** COMMON CLASSES **/
diff --git a/app/assets/stylesheets/framework/gfm.scss b/app/assets/stylesheets/framework/gfm.scss
index e378e84ca1b..1cf12b1a015 100644
--- a/app/assets/stylesheets/framework/gfm.scss
+++ b/app/assets/stylesheets/framework/gfm.scss
@@ -19,6 +19,7 @@
.gfm-color_chip {
display: inline-block;
+ line-height: 1;
margin: 0 0 2px 4px;
vertical-align: middle;
border-radius: 3px;
diff --git a/app/assets/stylesheets/framework/markdown_area.scss b/app/assets/stylesheets/framework/markdown_area.scss
index b893151e4fe..7290a174668 100644
--- a/app/assets/stylesheets/framework/markdown_area.scss
+++ b/app/assets/stylesheets/framework/markdown_area.scss
@@ -61,10 +61,6 @@
padding-top: 0;
line-height: 19px;
- &.btn.btn-xs {
- padding: 2px 5px;
- }
-
&:focus {
margin-top: -10px;
padding-top: 10px;
diff --git a/app/assets/stylesheets/framework/modal.scss b/app/assets/stylesheets/framework/modal.scss
index a7896cc3fc3..ffb40166c15 100644
--- a/app/assets/stylesheets/framework/modal.scss
+++ b/app/assets/stylesheets/framework/modal.scss
@@ -1,3 +1,7 @@
+.modal-xl {
+ max-width: 98%;
+}
+
.modal-header {
background-color: $modal-body-bg;
diff --git a/app/assets/stylesheets/framework/secondary_navigation_elements.scss b/app/assets/stylesheets/framework/secondary_navigation_elements.scss
index 4e2cc498883..2d9e9e6a67d 100644
--- a/app/assets/stylesheets/framework/secondary_navigation_elements.scss
+++ b/app/assets/stylesheets/framework/secondary_navigation_elements.scss
@@ -197,7 +197,7 @@
flex-flow: row wrap;
.nav-controls {
- $controls-margin: $btn-xs-side-margin - 2px;
+ $controls-margin: $btn-margin-5 - 2px;
flex: 0 0 100%;
&.controls-flex {
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index d1179df96a9..04d2a049f7d 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -278,7 +278,7 @@ $active-item-blue: $blue-500;
$layout-link-gray: #7e7c7c;
$btn-side-margin: 10px;
$btn-sm-side-margin: 7px;
-$btn-xs-side-margin: 5px;
+$btn-margin-5: 5px;
$issue-status-expired: $orange-500;
$issuable-sidebar-color: $gl-text-color-secondary;
$sidebar-block-hover-color: #ebebeb;
@@ -832,3 +832,5 @@ $input-border-color: $theme-gray-200;
$input-color: $gl-text-color;
$font-family-sans-serif: $regular_font;
$font-family-monospace: $monospace_font;
+$input-line-height: 20px;
+$btn-line-height: 20px;
diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss
index b2416a3d5bc..7c1d1626f1c 100644
--- a/app/assets/stylesheets/pages/boards.scss
+++ b/app/assets/stylesheets/pages/boards.scss
@@ -80,6 +80,7 @@
overflow-x: scroll;
white-space: nowrap;
min-height: 200px;
+ display: flex;
@include media-breakpoint-only(sm) {
height: calc(100vh - #{$issue-board-list-difference-sm});
@@ -110,17 +111,15 @@
.board {
display: inline-block;
- width: calc(85vw - 15px);
+ flex: 1;
+ min-width: 300px;
+ max-width: 400px;
height: 100%;
padding-right: ($gl-padding / 2);
padding-left: ($gl-padding / 2);
white-space: normal;
vertical-align: top;
- @include media-breakpoint-up(sm) {
- width: 400px;
- }
-
&.is-expandable {
.board-header {
cursor: pointer;
@@ -128,6 +127,8 @@
}
&.is-collapsed {
+ flex: none;
+ min-width: 0;
width: 50px;
.board-header {
diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss
index c2b42e02eee..05bf5596fb3 100644
--- a/app/assets/stylesheets/pages/groups.scss
+++ b/app/assets/stylesheets/pages/groups.scss
@@ -425,7 +425,7 @@
margin-left: 5px;
> .btn {
- margin-right: $btn-xs-side-margin;
+ margin-right: $btn-margin-5;
}
}
}
diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss
index 6882b4adb15..79cac7f4ff0 100644
--- a/app/assets/stylesheets/pages/labels.scss
+++ b/app/assets/stylesheets/pages/labels.scss
@@ -220,7 +220,7 @@
.label-link {
display: inline-flex;
- vertical-align: top;
+ vertical-align: text-bottom;
&:hover .color-label {
text-decoration: underline;
@@ -280,7 +280,7 @@
width: 150px;
flex-shrink: 0;
- .label {
+ .badge {
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index f50ca677800..99fe4a578be 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -51,12 +51,6 @@
opacity: 0.3;
}
- &.btn-xs {
- line-height: 1;
- padding: 5px 10px;
- margin-top: 1px;
- }
-
&.dropdown-toggle {
.fa {
color: inherit;
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index 3b037d066dc..3849a04db5d 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -129,7 +129,7 @@
.icon svg {
position: relative;
top: 2px;
- margin-right: $btn-xs-side-margin;
+ margin-right: $btn-margin-5;
width: $gl-font-size;
height: $gl-font-size;
fill: $orange-600;
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index 30428fd198d..4e1768f556a 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -36,6 +36,7 @@
}
.table-holder {
+ overflow: unset;
width: 100%;
}
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index caafda5fb05..7ac0eaec645 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -497,6 +497,12 @@
&:not(:first-child) {
border-top: 1px solid $border-color;
}
+
+ .btn-template-icon {
+ position: absolute;
+ left: $gl-padding;
+ top: $gl-padding;
+ }
}
.template-title {
@@ -514,12 +520,6 @@
}
}
- svg {
- position: absolute;
- left: $gl-padding;
- top: $gl-padding;
- }
-
.project-fields-form {
display: none;
@@ -530,34 +530,23 @@
}
.template-input-group {
- position: relative;
-
- @include media-breakpoint-up(sm) {
- display: flex;
- }
-
- .input-group-prepend,
- .input-group-append {
+ .input-group-prepend {
flex: 1;
- text-align: left;
- padding-left: ($gl-padding * 3);
- background-color: $white-light;
}
- .selected-template {
- line-height: 20px;
+ .input-group-text {
+ width: 100%;
+ background-color: $white-light;
}
.selected-icon {
+ padding-right: $gl-padding;
+
svg {
display: none;
top: 7px;
height: 20px;
width: 20px;
-
- &.active {
- display: block;
- }
}
}
}
diff --git a/app/assets/stylesheets/pages/settings.scss b/app/assets/stylesheets/pages/settings.scss
index 1f8e61257a9..4abb145067a 100644
--- a/app/assets/stylesheets/pages/settings.scss
+++ b/app/assets/stylesheets/pages/settings.scss
@@ -52,7 +52,7 @@
.settings-content {
max-height: 1px;
- overflow-y: scroll;
+ overflow-y: hidden;
padding-right: 110px;
animation: collapseMaxHeight 300ms ease-out;
// Keep the section from expanding when we scroll over it
diff --git a/app/assets/stylesheets/performance_bar.scss b/app/assets/stylesheets/performance_bar.scss
index 8cdf2275551..5127ddfde6e 100644
--- a/app/assets/stylesheets/performance_bar.scss
+++ b/app/assets/stylesheets/performance_bar.scss
@@ -107,12 +107,12 @@
}
.performance-bar-modal {
- .modal-footer {
- display: none;
+ .modal-body {
+ padding: 0;
}
- .modal-dialog {
- width: 860px;
+ .modal-footer {
+ display: none;
}
}
}
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 041837c5410..56312f801fb 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -284,8 +284,10 @@ class ApplicationController < ActionController::Base
return unless current_user
return if current_user.terms_accepted?
+ message = _("Please accept the Terms of Service before continuing.")
+
if sessionless_user?
- render_403
+ access_denied!(message)
else
# Redirect to the destination if the request is a get.
# Redirect to the source if it was a post, so the user can re-submit after
@@ -296,7 +298,7 @@ class ApplicationController < ActionController::Base
URI(request.referer).path if request.referer
end
- flash[:notice] = _("Please accept the Terms of Service before continuing.")
+ flash[:notice] = message
redirect_to terms_path(redirect: redirect_path), status: :found
end
end
diff --git a/app/controllers/concerns/internal_redirect.rb b/app/controllers/concerns/internal_redirect.rb
index 7409b2e89a5..10b9852e329 100644
--- a/app/controllers/concerns/internal_redirect.rb
+++ b/app/controllers/concerns/internal_redirect.rb
@@ -23,6 +23,10 @@ module InternalRedirect
nil
end
+ def sanitize_redirect(url_or_path)
+ safe_redirect_path(url_or_path) || safe_redirect_path_for_url(url_or_path)
+ end
+
def host_allowed?(uri)
uri.host == request.host &&
uri.port == request.port
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index 0c1c286a0a4..7c65f1f5dfe 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -197,15 +197,14 @@ class Projects::BlobController < Projects::ApplicationController
end
def show_json
- json = blob_json(@blob)
- return render_404 unless json
-
+ set_last_commit_sha
path_segments = @path.split('/')
path_segments.pop
tree_path = path_segments.join('/')
- render json: json.merge(
+ json = {
id: @blob.id,
+ last_commit_sha: @last_commit_sha,
path: blob.path,
name: blob.name,
extension: blob.extension,
@@ -221,6 +220,10 @@ class Projects::BlobController < Projects::ApplicationController
commits_path: project_commits_path(project, @id),
tree_path: project_tree_path(project, File.join(@ref, tree_path)),
permalink: project_blob_path(project, File.join(@commit.id, @path))
- )
+ }
+
+ json.merge!(blob_json(@blob) || {}) unless params[:viewer] == 'none'
+
+ render json: json
end
end
diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb
index 2494b56981d..f85dcfe6bfc 100644
--- a/app/controllers/projects/milestones_controller.rb
+++ b/app/controllers/projects/milestones_controller.rb
@@ -123,9 +123,9 @@ class Projects::MilestonesController < Projects::ApplicationController
def search_params
if request.format.json? && @project.group && can?(current_user, :read_group, @project.group)
- groups = @project.group.self_and_ancestors
+ groups = @project.group.self_and_ancestors_ids
end
- params.permit(:state).merge(project_ids: @project.id, group_ids: groups&.select(:id))
+ params.permit(:state).merge(project_ids: @project.id, group_ids: groups)
end
end
diff --git a/app/finders/snippets_finder.rb b/app/finders/snippets_finder.rb
index d498a2d6d11..9d3772d7541 100644
--- a/app/finders/snippets_finder.rb
+++ b/app/finders/snippets_finder.rb
@@ -54,7 +54,10 @@ class SnippetsFinder < UnionFinder
end
def authorized_snippets
- Snippet.where(feature_available_projects.or(not_project_related))
+ # This query was intentionally converted to a raw one to get it work in Rails 5.0.
+ # In Rails 5.0 and 5.1 there's a bug: https://github.com/rails/arel/issues/531
+ # Please convert it back when on rails 5.2 as it works again as expected since 5.2.
+ Snippet.where("#{feature_available_projects} OR #{not_project_related}")
.public_or_visible_to_user(current_user)
end
@@ -86,18 +89,20 @@ class SnippetsFinder < UnionFinder
def feature_available_projects
# Don't return any project related snippets if the user cannot read cross project
- return table[:id].eq(nil) unless Ability.allowed?(current_user, :read_cross_project)
+ return table[:id].eq(nil).to_sql unless Ability.allowed?(current_user, :read_cross_project)
projects = projects_for_user do |part|
part.with_feature_available_for_user(:snippets, current_user)
end.select(:id)
- arel_query = Arel::Nodes::SqlLiteral.new(projects.to_sql)
- table[:project_id].in(arel_query)
+ # This query was intentionally converted to a raw one to get it work in Rails 5.0.
+ # In Rails 5.0 and 5.1 there's a bug: https://github.com/rails/arel/issues/531
+ # Please convert it back when on rails 5.2 as it works again as expected since 5.2.
+ "snippets.project_id IN (#{projects.to_sql})"
end
def not_project_related
- table[:project_id].eq(nil)
+ table[:project_id].eq(nil).to_sql
end
def table
diff --git a/app/graphql/resolvers/merge_request_resolver.rb b/app/graphql/resolvers/merge_request_resolver.rb
index b1857ab09f7..9f2d348e95f 100644
--- a/app/graphql/resolvers/merge_request_resolver.rb
+++ b/app/graphql/resolvers/merge_request_resolver.rb
@@ -1,15 +1,14 @@
module Resolvers
class MergeRequestResolver < BaseResolver
- prepend FullPathResolver
-
- type Types::ProjectType, null: true
-
argument :iid, GraphQL::ID_TYPE,
required: true,
description: 'The IID of the merge request, e.g., "1"'
- def resolve(full_path:, iid:)
- project = model_by_full_path(Project, full_path)
+ type Types::MergeRequestType, null: true
+
+ alias_method :project, :object
+
+ def resolve(iid:)
return unless project.present?
BatchLoader.for(iid.to_s).batch(key: project.id) do |iids, loader|
diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb
index 9e885d5845a..d9058ae7431 100644
--- a/app/graphql/types/project_type.rb
+++ b/app/graphql/types/project_type.rb
@@ -61,5 +61,12 @@ module Types
field :request_access_enabled, GraphQL::BOOLEAN_TYPE, null: true
field :only_allow_merge_if_all_discussions_are_resolved, GraphQL::BOOLEAN_TYPE, null: true
field :printing_merge_request_link_enabled, GraphQL::BOOLEAN_TYPE, null: true
+
+ field :merge_request,
+ Types::MergeRequestType,
+ null: true,
+ resolver: Resolvers::MergeRequestResolver do
+ authorize :read_merge_request
+ end
end
end
diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb
index be79c78bf67..010ec2d7942 100644
--- a/app/graphql/types/query_type.rb
+++ b/app/graphql/types/query_type.rb
@@ -9,13 +9,6 @@ module Types
authorize :read_project
end
- field :merge_request, Types::MergeRequestType,
- null: true,
- resolver: Resolvers::MergeRequestResolver,
- description: "Find a merge request" do
- authorize :read_merge_request
- end
-
field :echo, GraphQL::STRING_TYPE, null: false, function: Functions::Echo.new
end
end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index cdbb572f80a..daad829faa2 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -171,11 +171,12 @@ module ProjectsHelper
key = [
project.route.cache_key,
project.cache_key,
+ project.last_activity_date,
controller.controller_name,
controller.action_name,
Gitlab::CurrentSettings.cache_key,
"cross-project:#{can?(current_user, :read_cross_project)}",
- 'v2.5'
+ 'v2.6'
]
key << pipeline_status_cache_key(project.pipeline_status) if project.pipeline_status.has_status?
diff --git a/app/models/concerns/cache_markdown_field.rb b/app/models/concerns/cache_markdown_field.rb
index db8cf322ef7..9f6358cecbe 100644
--- a/app/models/concerns/cache_markdown_field.rb
+++ b/app/models/concerns/cache_markdown_field.rb
@@ -114,7 +114,7 @@ module CacheMarkdownField
end
def latest_cached_markdown_version
- return CacheMarkdownField::CACHE_REDCARPET_VERSION unless cached_markdown_version
+ return CacheMarkdownField::CACHE_COMMONMARK_VERSION unless cached_markdown_version
if cached_markdown_version < CacheMarkdownField::CACHE_COMMONMARK_VERSION_START
CacheMarkdownField::CACHE_REDCARPET_VERSION
diff --git a/app/models/project.rb b/app/models/project.rb
index f0d8c40bfea..e5fa1c4db7b 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -68,7 +68,7 @@ class Project < ActiveRecord::Base
add_authentication_token_field :runners_token
- before_validation :mark_remote_mirrors_for_removal, if: -> { ActiveRecord::Base.connection.table_exists?(:remote_mirrors) }
+ before_validation :mark_remote_mirrors_for_removal, if: -> { RemoteMirror.table_exists? }
before_save :ensure_runners_token
diff --git a/app/models/project_services/gemnasium_service.rb b/app/models/project_services/gemnasium_service.rb
index 84248f9590b..8a6b0ed1a5f 100644
--- a/app/models/project_services/gemnasium_service.rb
+++ b/app/models/project_services/gemnasium_service.rb
@@ -43,13 +43,18 @@ class GemnasiumService < Service
def execute(data)
return unless supported_events.include?(data[:object_kind])
+ # Gitaly: this class will be removed https://gitlab.com/gitlab-org/gitlab-ee/issues/6010
+ repo_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ project.repository.path_to_repo
+ end
+
Gemnasium::GitlabService.execute(
ref: data[:ref],
before: data[:before],
after: data[:after],
token: token,
api_key: api_key,
- repo: project.repository.path_to_repo # Gitaly: fixed by https://gitlab.com/gitlab-org/security-products/gemnasium-migration/issues/9
+ repo: repo_path
)
end
end
diff --git a/app/uploaders/favicon_uploader.rb b/app/uploaders/favicon_uploader.rb
index 09afc63a5aa..3639375d474 100644
--- a/app/uploaders/favicon_uploader.rb
+++ b/app/uploaders/favicon_uploader.rb
@@ -1,17 +1,6 @@
class FaviconUploader < AttachmentUploader
EXTENSION_WHITELIST = %w[png ico].freeze
- include CarrierWave::MiniMagick
-
- version :favicon_main do
- process resize_to_fill: [32, 32]
- process convert: 'png'
-
- def full_filename(filename)
- filename_for_different_format(super(filename), 'png')
- end
- end
-
def extension_whitelist
EXTENSION_WHITELIST
end
diff --git a/app/views/admin/appearances/_form.html.haml b/app/views/admin/appearances/_form.html.haml
index 94db374040c..a0861870ba4 100644
--- a/app/views/admin/appearances/_form.html.haml
+++ b/app/views/admin/appearances/_form.html.haml
@@ -25,7 +25,7 @@
= f.label :favicon, 'Favicon', class: 'col-sm-2 col-form-label'
.col-sm-10
- if @appearance.favicon?
- = image_tag @appearance.favicon.favicon_main.url, class: 'appearance-light-logo-preview'
+ = image_tag @appearance.favicon_url, class: 'appearance-light-logo-preview'
- if @appearance.persisted?
%br
= link_to 'Remove favicon', favicon_admin_appearances_path, data: { confirm: "Favicon will be removed. Are you sure?"}, method: :delete, class: "btn btn-inverted btn-remove btn-sm remove-logo"
@@ -33,9 +33,9 @@
= f.hidden_field :favicon_cache
= f.file_field :favicon, class: ''
.hint
- Maximum file size is 1MB. Allowed image formats are #{favicon_extension_whitelist}.
+ Maximum file size is 1MB. Image size must be 32x32px. Allowed image formats are #{favicon_extension_whitelist}.
%br
- The resulting favicons will be cropped to be square and scaled down to a size of 32x32 px.
+ Images with incorrect dimensions are not resized automatically, and may result in unexpected behavior.
%fieldset.sign-in
%legend
diff --git a/app/views/errors/access_denied.html.haml b/app/views/errors/access_denied.html.haml
index 227c7884915..8ae29b9d337 100644
--- a/app/views/errors/access_denied.html.haml
+++ b/app/views/errors/access_denied.html.haml
@@ -1,4 +1,4 @@
-- message = local_assigns.fetch(:message)
+- message = local_assigns.fetch(:message, nil)
- content_for(:title, 'Access Denied')
= image_tag('illustrations/error-403.svg', alt: '403', lazy: false)
diff --git a/app/views/projects/_project_templates.html.haml b/app/views/projects/_project_templates.html.haml
index 9d27f51926e..d08807b5135 100644
--- a/app/views/projects/_project_templates.html.haml
+++ b/app/views/projects/_project_templates.html.haml
@@ -10,16 +10,18 @@
%a.btn.btn-default{ href: template.preview, rel: 'noopener noreferrer', target: '_blank' } Preview
.project-fields-form
- .form-group
- %label.label-light
- Template
- .input-group.template-input-group
- .input-group-prepend
- .input-group-text
- .selected-icon
- - Gitlab::ProjectTemplate.all.each do |template|
- = custom_icon(template.logo)
- .selected-template
- %button.btn.btn-default.change-template{ type: "button" } Change template
+ .row
+ .form-group.col-sm-12
+ %label.label-light
+ Template
+ .input-group.template-input-group
+ .input-group-prepend
+ .input-group-text
+ .selected-icon
+ - Gitlab::ProjectTemplate.all.each do |template|
+ = custom_icon(template.logo)
+ .selected-template
+ .input-group-append
+ %button.btn.btn-default.change-template{ type: "button" } Change template
= render 'new_project_fields', f: f, project_name_id: "template-project-name"
diff --git a/app/views/projects/_stat_anchor_list.html.haml b/app/views/projects/_stat_anchor_list.html.haml
index 8bffd1396ae..15ec58289e3 100644
--- a/app/views/projects/_stat_anchor_list.html.haml
+++ b/app/views/projects/_stat_anchor_list.html.haml
@@ -5,4 +5,4 @@
- anchors.each do |anchor|
%li.nav-item
= link_to_if anchor.link, anchor.label, anchor.link, class: anchor.enabled ? 'nav-link stat-link' : "nav-link btn btn-#{anchor.class_modifier || 'missing'}" do
- %span.stat-text= anchor.label
+ .stat-text= anchor.label
diff --git a/app/views/projects/diffs/_collapsed.html.haml b/app/views/projects/diffs/_collapsed.html.haml
index 5762f4d86d7..9bd1255fe00 100644
--- a/app/views/projects/diffs/_collapsed.html.haml
+++ b/app/views/projects/diffs/_collapsed.html.haml
@@ -2,4 +2,4 @@
- url = url_for(safe_params.merge(action: :diff_for_path, old_path: diff_file.old_path, new_path: diff_file.new_path, file_identifier: diff_file.file_identifier))
.nothing-here-block.diff-collapsed{ data: { diff_for_path: url } }
This diff is collapsed.
- %a.click-to-expand Click to expand it.
+ %button.click-to-expand.btn.btn-link Click to expand it.
diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml
index bf6fc8af12d..69753427d17 100644
--- a/app/views/projects/empty.html.haml
+++ b/app/views/projects/empty.html.haml
@@ -44,14 +44,14 @@
.git-empty
%fieldset
%h5 Git global setup
- %pre.card.bg-light
+ %pre.bg-light
:preserve
git config --global user.name "#{h git_user_name}"
git config --global user.email "#{h git_user_email}"
%fieldset
%h5 Create a new repository
- %pre.card.bg-light
+ %pre.bg-light
:preserve
git clone #{ content_tag(:span, default_url_to_repo, class: 'clone')}
cd #{h @project.path}
@@ -64,7 +64,7 @@
%fieldset
%h5 Existing folder
- %pre.card.bg-light
+ %pre.bg-light
:preserve
cd existing_folder
git init
@@ -77,7 +77,7 @@
%fieldset
%h5 Existing Git repository
- %pre.card.bg-light
+ %pre.bg-light
:preserve
cd existing_repo
git remote rename origin old-origin
diff --git a/app/views/projects/merge_requests/creations/_new_submit.html.haml b/app/views/projects/merge_requests/creations/_new_submit.html.haml
index ebcd99f2a9b..b2eacabc21a 100644
--- a/app/views/projects/merge_requests/creations/_new_submit.html.haml
+++ b/app/views/projects/merge_requests/creations/_new_submit.html.haml
@@ -25,7 +25,7 @@
= custom_icon ('illustration_no_commits')
- else
%ul.merge-request-tabs.nav.nav-tabs.nav-links.no-top.no-bottom
- %li.commits-tab.active
+ %li.commits-tab
= link_to url_for(safe_params), data: {target: 'div#commits', action: 'new', toggle: 'tab'} do
Commits
%span.badge.badge-pill= @commits.size
diff --git a/app/views/shared/boards/components/sidebar/_labels.html.haml b/app/views/shared/boards/components/sidebar/_labels.html.haml
index a112a9f1f7e..daee691e358 100644
--- a/app/views/shared/boards/components/sidebar/_labels.html.haml
+++ b/app/views/shared/boards/components/sidebar/_labels.html.haml
@@ -9,7 +9,7 @@
None
%a{ href: "#",
"v-for" => "label in issue.labels" }
- %span.badge.color-label.has-tooltip{ ":style" => "{ backgroundColor: label.color, color: label.textColor }" }
+ .badge.color-label.has-tooltip{ ":style" => "{ backgroundColor: label.color, color: label.textColor }" }
{{ label.title }}
- if can_admin_issue?
.selectbox
diff --git a/app/views/shared/builds/_build_output.html.haml b/app/views/shared/builds/_build_output.html.haml
index 07f1501fadd..0e18128a8f1 100644
--- a/app/views/shared/builds/_build_output.html.haml
+++ b/app/views/shared/builds/_build_output.html.haml
@@ -1,3 +1,3 @@
%pre.build-trace#build-trace
%code.bash.js-build-output
- .build-loader-animation.js-build-refresh
+ .build-loader-animation.js-build-refresh
diff --git a/app/views/shared/issuable/_sidebar_assignees.html.haml b/app/views/shared/issuable/_sidebar_assignees.html.haml
index ed3ef6155db..8a13c7a3b83 100644
--- a/app/views/shared/issuable/_sidebar_assignees.html.haml
+++ b/app/views/shared/issuable/_sidebar_assignees.html.haml
@@ -50,7 +50,7 @@
- data[:multi_select] = true
- data['dropdown-title'] = title
- data['dropdown-header'] = dropdown_options[:data][:'dropdown-header']
- - data['max-select'] = dropdown_options[:data][:'max-select']
+ - data['max-select'] = dropdown_options[:data][:'max-select'] if dropdown_options[:data][:'max-select']
- options[:data].merge!(data)
= dropdown_tag(title, options: options)
diff --git a/app/views/shared/issuable/form/_metadata.html.haml b/app/views/shared/issuable/form/_metadata.html.haml
index 01fbc163a14..bd87bb38e77 100644
--- a/app/views/shared/issuable/form/_metadata.html.haml
+++ b/app/views/shared/issuable/form/_metadata.html.haml
@@ -25,7 +25,10 @@
= form.hidden_field :label_ids, multiple: true, value: ''
.col-sm-10{ class: "#{"col-md-8" if has_due_date} #{'issuable-form-padding-top' if !has_labels}" }
.issuable-form-select-holder
- = render "shared/issuable/label_dropdown", classes: ["js-issuable-form-dropdown"], selected: issuable.labels, data_options: { field_name: "#{issuable.class.model_name.param_key}[label_ids][]", show_any: false}, dropdown_title: "Select label"
+ = render "shared/issuable/label_dropdown", classes: ["js-issuable-form-dropdown"], selected: issuable.labels, data_options: { field_name: "#{issuable.class.model_name.param_key}[label_ids][]", show_any: false }, dropdown_title: "Select label"
+
+ = render_if_exists "shared/issuable/form/weight", issuable: issuable, form: form
+
- if has_due_date
.col-lg-6
.form-group.row
diff --git a/app/workers/git_garbage_collect_worker.rb b/app/workers/git_garbage_collect_worker.rb
index f3c9e2b1582..ae5c5fac834 100644
--- a/app/workers/git_garbage_collect_worker.rb
+++ b/app/workers/git_garbage_collect_worker.rb
@@ -6,12 +6,6 @@ class GitGarbageCollectWorker
# Timeout set to 24h
LEASE_TIMEOUT = 86400
- GITALY_MIGRATED_TASKS = {
- gc: :garbage_collect,
- full_repack: :repack_full,
- incremental_repack: :repack_incremental
- }.freeze
-
def perform(project_id, task = :gc, lease_key = nil, lease_uuid = nil)
project = Project.find(project_id)
active_uuid = get_lease_uuid(lease_key)
@@ -27,21 +21,7 @@ class GitGarbageCollectWorker
end
task = task.to_sym
- cmd = command(task)
-
- gitaly_migrate(GITALY_MIGRATED_TASKS[task], status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- gitaly_call(task, project.repository.raw_repository)
- else
- repo_path = project.repository.path_to_repo
- description = "'#{cmd.join(' ')}' in #{repo_path}"
- Gitlab::GitLogger.info(description)
-
- output, status = Gitlab::Popen.popen(cmd, repo_path)
-
- Gitlab::GitLogger.error("#{description} failed:\n#{output}") unless status.zero?
- end
- end
+ gitaly_call(task, project.repository.raw_repository)
# Refresh the branch cache in case garbage collection caused a ref lookup to fail
flush_ref_caches(project) if task == :gc
@@ -82,21 +62,12 @@ class GitGarbageCollectWorker
when :incremental_repack
client.repack_incremental
end
- end
-
- def command(task)
- case task
- when :gc
- git(write_bitmaps: bitmaps_enabled?) + %w[gc]
- when :full_repack
- git(write_bitmaps: bitmaps_enabled?) + %w[repack -A -d --pack-kept-objects]
- when :incremental_repack
- # Normal git repack fails when bitmaps are enabled. It is impossible to
- # create a bitmap here anyway.
- git(write_bitmaps: false) + %w[repack -d]
- else
- raise "Invalid gc task: #{task.inspect}"
- end
+ rescue GRPC::NotFound => e
+ Gitlab::GitLogger.error("#{method} failed:\nRepository not found")
+ raise Gitlab::Git::Repository::NoRepository.new(e)
+ rescue GRPC::BadStatus => e
+ Gitlab::GitLogger.error("#{method} failed:\n#{e}")
+ raise Gitlab::Git::CommandError.new(e)
end
def flush_ref_caches(project)
@@ -108,19 +79,4 @@ class GitGarbageCollectWorker
def bitmaps_enabled?
Gitlab::CurrentSettings.housekeeping_bitmaps_enabled
end
-
- def git(write_bitmaps:)
- config_value = write_bitmaps ? 'true' : 'false'
- %W[git -c repack.writeBitmaps=#{config_value}]
- end
-
- def gitaly_migrate(method, status: Gitlab::GitalyClient::MigrationStatus::OPT_IN, &block)
- Gitlab::GitalyClient.migrate(method, status: status, &block)
- rescue GRPC::NotFound => e
- Gitlab::GitLogger.error("#{method} failed:\nRepository not found")
- raise Gitlab::Git::Repository::NoRepository.new(e)
- rescue GRPC::BadStatus => e
- Gitlab::GitLogger.error("#{method} failed:\n#{e}")
- raise Gitlab::Git::CommandError.new(e)
- end
end
diff --git a/changelogs/unreleased/47646-ui-glitch.yml b/changelogs/unreleased/47646-ui-glitch.yml
new file mode 100644
index 00000000000..384df4e2cc9
--- /dev/null
+++ b/changelogs/unreleased/47646-ui-glitch.yml
@@ -0,0 +1,5 @@
+---
+title: Line height fixed
+merge_request:
+author: Murat Dogan
+type: fixed
diff --git a/changelogs/unreleased/47871-new-mr-tab-active-state.yml b/changelogs/unreleased/47871-new-mr-tab-active-state.yml
new file mode 100644
index 00000000000..c3fc8672d82
--- /dev/null
+++ b/changelogs/unreleased/47871-new-mr-tab-active-state.yml
@@ -0,0 +1,5 @@
+---
+title: Fix active tab highlight when creating new merge request
+merge_request: 19781
+author: Jan Beckmann
+type: fixed
diff --git a/changelogs/unreleased/author-doc-fix.yml b/changelogs/unreleased/author-doc-fix.yml
new file mode 100644
index 00000000000..83521543239
--- /dev/null
+++ b/changelogs/unreleased/author-doc-fix.yml
@@ -0,0 +1,5 @@
+---
+title: Fix fields for author & assignee in MR API docs.
+merge_request: 19798
+author: gfyoung
+type: fixed
diff --git a/changelogs/unreleased/blackst0ne-rails5-fix-optimistic-lock-values.yml b/changelogs/unreleased/blackst0ne-rails5-fix-optimistic-lock-values.yml
new file mode 100644
index 00000000000..1915dff73ab
--- /dev/null
+++ b/changelogs/unreleased/blackst0ne-rails5-fix-optimistic-lock-values.yml
@@ -0,0 +1,5 @@
+---
+title: "[Rails5] Fix optimistic lock value"
+merge_request: 19878
+author: "@blackst0ne"
+type: fixed
diff --git a/changelogs/unreleased/blackst0ne-rails5-fix-snippets-finder.yml b/changelogs/unreleased/blackst0ne-rails5-fix-snippets-finder.yml
new file mode 100644
index 00000000000..597b85de26f
--- /dev/null
+++ b/changelogs/unreleased/blackst0ne-rails5-fix-snippets-finder.yml
@@ -0,0 +1,5 @@
+---
+title: "[Rails5] Fix snippets_finder arel queries"
+merge_request: 19796
+author: "@blackst0ne"
+type: fixed
diff --git a/changelogs/unreleased/bvl-graphql-nested-merge-request.yml b/changelogs/unreleased/bvl-graphql-nested-merge-request.yml
new file mode 100644
index 00000000000..f0f0488d31a
--- /dev/null
+++ b/changelogs/unreleased/bvl-graphql-nested-merge-request.yml
@@ -0,0 +1,5 @@
+---
+title: Allow querying a single merge request within a project
+merge_request: 19853
+author:
+type: changed
diff --git a/changelogs/unreleased/bw-enable-commonmark.yml b/changelogs/unreleased/bw-enable-commonmark.yml
new file mode 100644
index 00000000000..89252e5063d
--- /dev/null
+++ b/changelogs/unreleased/bw-enable-commonmark.yml
@@ -0,0 +1,5 @@
+---
+title: Use CommonMark syntax and rendering for new Markdown content
+merge_request: 19331
+author:
+type: added
diff --git a/changelogs/unreleased/cache-doc-fix.yml b/changelogs/unreleased/cache-doc-fix.yml
new file mode 100644
index 00000000000..db4726a92e9
--- /dev/null
+++ b/changelogs/unreleased/cache-doc-fix.yml
@@ -0,0 +1,5 @@
+---
+title: 'Remove incorrect CI doc re: PowerShell'
+merge_request: 19622
+author: gfyoung
+type: fixed
diff --git a/changelogs/unreleased/mk-rake-task-verify-remote-files.yml b/changelogs/unreleased/mk-rake-task-verify-remote-files.yml
new file mode 100644
index 00000000000..772aa11d89b
--- /dev/null
+++ b/changelogs/unreleased/mk-rake-task-verify-remote-files.yml
@@ -0,0 +1,5 @@
+---
+title: Add support for verifying remote uploads, artifacts, and LFS objects in check rake tasks
+merge_request: 19501
+author:
+type: added
diff --git a/changelogs/unreleased/rails5-fix-47804.yml b/changelogs/unreleased/rails5-fix-47804.yml
new file mode 100644
index 00000000000..3332ed3bbaa
--- /dev/null
+++ b/changelogs/unreleased/rails5-fix-47804.yml
@@ -0,0 +1,5 @@
+---
+title: Rails5 fix stack level too deep
+merge_request: 19762
+author: Jasper Maes
+type: fixed
diff --git a/changelogs/unreleased/rails5-fix-47805.yml b/changelogs/unreleased/rails5-fix-47805.yml
new file mode 100644
index 00000000000..8bd8ad5488c
--- /dev/null
+++ b/changelogs/unreleased/rails5-fix-47805.yml
@@ -0,0 +1,6 @@
+---
+title: 'Rails5 ActionController::ParameterMissing: param is missing or the value is
+ empty: application_setting'
+merge_request: 19763
+author: Jasper Maes
+type: fixed
diff --git a/changelogs/unreleased/rails5-fix-47835.yml b/changelogs/unreleased/rails5-fix-47835.yml
new file mode 100644
index 00000000000..fe9cbf1a03a
--- /dev/null
+++ b/changelogs/unreleased/rails5-fix-47835.yml
@@ -0,0 +1,6 @@
+---
+title: Rails5 fix no implicit conversion of Hash into String. ActionController::Parameters
+ no longer returns an hash in Rails 5
+merge_request: 19792
+author: Jasper Maes
+type: fixed
diff --git a/changelogs/unreleased/rails5-fix-47836.yml b/changelogs/unreleased/rails5-fix-47836.yml
new file mode 100644
index 00000000000..2aef2db607a
--- /dev/null
+++ b/changelogs/unreleased/rails5-fix-47836.yml
@@ -0,0 +1,6 @@
+---
+title: Rails5 fix passing Group objects array into for_projects_and_groups milestone
+ scope
+merge_request: 19863
+author: Jasper Maes
+type: fixed
diff --git a/changelogs/unreleased/rd-33733-showing-created-date-instead-of-updated-date-in-project-lists.yml b/changelogs/unreleased/rd-33733-showing-created-date-instead-of-updated-date-in-project-lists.yml
new file mode 100644
index 00000000000..3934381b0cf
--- /dev/null
+++ b/changelogs/unreleased/rd-33733-showing-created-date-instead-of-updated-date-in-project-lists.yml
@@ -0,0 +1,5 @@
+---
+title: Invalidate cache with project details when repository is updated
+merge_request: 19774
+author:
+type: fixed
diff --git a/changelogs/unreleased/remove-link-label-vertical-alignment-property.yml b/changelogs/unreleased/remove-link-label-vertical-alignment-property.yml
new file mode 100644
index 00000000000..40ec3998b05
--- /dev/null
+++ b/changelogs/unreleased/remove-link-label-vertical-alignment-property.yml
@@ -0,0 +1,5 @@
+---
+title: Change label link vertical alignment property
+merge_request: 18777
+author: George Tsiolis
+type: changed
diff --git a/changelogs/unreleased/rosulk-patch-12.yml b/changelogs/unreleased/rosulk-patch-12.yml
new file mode 100644
index 00000000000..9637c88d1a4
--- /dev/null
+++ b/changelogs/unreleased/rosulk-patch-12.yml
@@ -0,0 +1,5 @@
+---
+title: Flex issue board columns
+merge_request: 19250
+author: Roman Rosluk
+type: changed
diff --git a/changelogs/unreleased/safari-scrollbar-bug.yml b/changelogs/unreleased/safari-scrollbar-bug.yml
new file mode 100644
index 00000000000..792a66d1ada
--- /dev/null
+++ b/changelogs/unreleased/safari-scrollbar-bug.yml
@@ -0,0 +1,5 @@
+---
+title: Remove scrollbar in Safari in repo settings page
+merge_request: 19809
+author: gfyoung
+type: fixed
diff --git a/changelogs/unreleased/winh-new-branch-url-encode.yml b/changelogs/unreleased/winh-new-branch-url-encode.yml
new file mode 100644
index 00000000000..f3554d0d4a1
--- /dev/null
+++ b/changelogs/unreleased/winh-new-branch-url-encode.yml
@@ -0,0 +1,5 @@
+---
+title: Fix branch name encoding for dropdown on issue page
+merge_request: 19634
+author:
+type: fixed
diff --git a/config/initializers/active_record_locking.rb b/config/initializers/active_record_locking.rb
index 3e7111fd063..0861544c5a4 100644
--- a/config/initializers/active_record_locking.rb
+++ b/config/initializers/active_record_locking.rb
@@ -1,73 +1,80 @@
# rubocop:disable Lint/RescueException
-# Remove this entire initializer when we are at rails 5.0.
-# This file fixes the bug (see below) which has been fixed in the upstream.
-unless Gitlab.rails5?
- # This patch fixes https://github.com/rails/rails/issues/26024
- # TODO: Remove it when it's no longer necessary
-
- module ActiveRecord
- module Locking
- module Optimistic
- # We overwrite this method because we don't want to have default value
- # for newly created records
- def _create_record(attribute_names = self.attribute_names, *) # :nodoc:
- super
- end
+# Remove this monkey-patch when all lock_version values are converted from NULLs to zeros.
+# See https://gitlab.com/gitlab-org/gitlab-ce/issues/25228
+module ActiveRecord
+ module Locking
+ module Optimistic
+ # We overwrite this method because we don't want to have default value
+ # for newly created records
+ def _create_record(attribute_names = self.attribute_names, *) # :nodoc:
+ super
+ end
- def _update_record(attribute_names = self.attribute_names) #:nodoc:
- return super unless locking_enabled?
- return 0 if attribute_names.empty?
+ def _update_record(attribute_names = self.attribute_names) #:nodoc:
+ return super unless locking_enabled?
+ return 0 if attribute_names.empty?
- lock_col = self.class.locking_column
+ lock_col = self.class.locking_column
- previous_lock_value = send(lock_col).to_i # rubocop:disable GitlabSecurity/PublicSend
+ previous_lock_value = send(lock_col).to_i # rubocop:disable GitlabSecurity/PublicSend
- # This line is added as a patch
- previous_lock_value = nil if previous_lock_value == '0' || previous_lock_value == 0
+ # This line is added as a patch
+ previous_lock_value = nil if previous_lock_value == '0' || previous_lock_value == 0
- increment_lock
+ increment_lock
- attribute_names += [lock_col]
- attribute_names.uniq!
+ attribute_names += [lock_col]
+ attribute_names.uniq!
- begin
- relation = self.class.unscoped
+ begin
+ relation = self.class.unscoped
- affected_rows = relation.where(
- self.class.primary_key => id,
- lock_col => previous_lock_value
- ).update_all(
- attributes_for_update(attribute_names).map do |name|
- [name, _read_attribute(name)]
- end.to_h
- )
+ affected_rows = relation.where(
+ self.class.primary_key => id,
+ lock_col => previous_lock_value
+ ).update_all(
+ attributes_for_update(attribute_names).map do |name|
+ [name, _read_attribute(name)]
+ end.to_h
+ )
- unless affected_rows == 1
- raise ActiveRecord::StaleObjectError.new(self, "update")
- end
+ unless affected_rows == 1
+ raise ActiveRecord::StaleObjectError.new(self, "update")
+ end
- affected_rows
+ affected_rows
- # If something went wrong, revert the version.
- rescue Exception
- send(lock_col + '=', previous_lock_value) # rubocop:disable GitlabSecurity/PublicSend
- raise
- end
+ # If something went wrong, revert the version.
+ rescue Exception
+ send(lock_col + '=', previous_lock_value) # rubocop:disable GitlabSecurity/PublicSend
+ raise
end
+ end
- # This is patched because we need it to query `lock_version IS NULL`
- # rather than `lock_version = 0` whenever lock_version is NULL.
- def relation_for_destroy
- return super unless locking_enabled?
+ # This is patched because we need it to query `lock_version IS NULL`
+ # rather than `lock_version = 0` whenever lock_version is NULL.
+ def relation_for_destroy
+ return super unless locking_enabled?
- column_name = self.class.locking_column
- super.where(self.class.arel_table[column_name].eq(self[column_name]))
- end
+ column_name = self.class.locking_column
+ super.where(self.class.arel_table[column_name].eq(self[column_name]))
end
+ end
+
+ # This is patched because we want `lock_version` default to `NULL`
+ # rather than `0`
+ if Gitlab.rails5?
+ class LockingType
+ def deserialize(value)
+ super
+ end
- # This is patched because we want `lock_version` default to `NULL`
- # rather than `0`
+ def serialize(value)
+ super
+ end
+ end
+ else
class LockingType < SimpleDelegator
def type_cast_from_database(value)
super
diff --git a/config/initializers/active_record_migration.rb b/config/initializers/active_record_migration.rb
new file mode 100644
index 00000000000..04c06be7834
--- /dev/null
+++ b/config/initializers/active_record_migration.rb
@@ -0,0 +1,10 @@
+require 'active_record/migration'
+
+module ActiveRecord
+ class Migration
+ # data_source_exists? is not available in 4.2.10, table_exists deprecated in 5.0
+ def table_exists?(table_name)
+ ActiveRecord::Base.connection.data_source_exists?(table_name)
+ end
+ end
+end
diff --git a/db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb b/db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb
index a96ea7d9db4..dc16d5c5169 100644
--- a/db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb
+++ b/db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb
@@ -12,7 +12,9 @@ class MigrateProcessCommitWorkerJobs < ActiveRecord::Migration
end
def repository_storage_path
- Gitlab.config.repositories.storages[repository_storage].legacy_disk_path
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ Gitlab.config.repositories.storages[repository_storage].legacy_disk_path
+ end
end
def repository_path
diff --git a/db/migrate/20161226122833_remove_dot_git_from_usernames.rb b/db/migrate/20161226122833_remove_dot_git_from_usernames.rb
index 8986cd8cb4b..133435523e1 100644
--- a/db/migrate/20161226122833_remove_dot_git_from_usernames.rb
+++ b/db/migrate/20161226122833_remove_dot_git_from_usernames.rb
@@ -64,7 +64,9 @@ class RemoveDotGitFromUsernames < ActiveRecord::Migration
# we rename suffix instead of removing it
path = path.sub(/\.git\z/, '_git')
- check_routes(path.dup, 0, path)
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ check_routes(path.dup, 0, path)
+ end
end
def check_routes(base, counter, path)
diff --git a/doc/administration/raketasks/check.md b/doc/administration/raketasks/check.md
index 7d34d35e7d1..2649bf61d74 100644
--- a/doc/administration/raketasks/check.md
+++ b/doc/administration/raketasks/check.md
@@ -78,9 +78,10 @@ Example output:
## Uploaded Files Integrity
-Various types of file can be uploaded to a GitLab installation by users.
-Checksums are generated and stored in the database upon upload, and integrity
-checks using those checksums can be run. These checks also detect missing files.
+Various types of files can be uploaded to a GitLab installation by users.
+These integrity checks can detect missing files. Additionally, for locally
+stored files, checksums are generated and stored in the database upon upload,
+and these checks will verify them against current files.
Currently, integrity checks are supported for the following types of file:
diff --git a/doc/api/README.md b/doc/api/README.md
index 1c756dc855f..6267618d3bc 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -104,7 +104,7 @@ with a major point release of GitLab itself. All deprecations and changes
between two versions should be listed in the documentation. For the changes
between v3 and v4; please read the [v3 to v4 documentation](v3_to_v4.md)
-#### Current status
+### Current status
Currently only API version v4 is available. Version v3 was removed in
[GitLab 11.0](https://gitlab.com/gitlab-org/gitlab-ce/issues/36819).
diff --git a/doc/api/graphql/index.md b/doc/api/graphql/index.md
index dcd5377284c..59e27922f64 100644
--- a/doc/api/graphql/index.md
+++ b/doc/api/graphql/index.md
@@ -29,9 +29,7 @@ curl --data "value=100" --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://g
## Available queries
-A first iteration of a GraphQL API includes only 2 queries: `project` and
-`merge_request` and only returns scalar fields, or fields of the type `Project`
-or `MergeRequest`.
+A first iteration of a GraphQL API includes a query for: `project`. Within a project it is also possible to fetch a `mergeRequest` by IID.
## GraphiQL
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index 9f06e20f803..da74045b702 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -70,18 +70,18 @@ Parameters:
"author": {
"id": 1,
"username": "admin",
- "email": "admin@example.com",
"name": "Administrator",
"state": "active",
- "created_at": "2012-04-29T08:46:00Z"
+ "avatar_url": null,
+ "web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
"id": 1,
"username": "admin",
- "email": "admin@example.com",
"name": "Administrator",
"state": "active",
- "created_at": "2012-04-29T08:46:00Z"
+ "avatar_url": null,
+ "web_url" : "https://gitlab.example.com/admin"
},
"source_project_id": 2,
"target_project_id": 3,
@@ -190,18 +190,18 @@ Parameters:
"author": {
"id": 1,
"username": "admin",
- "email": "admin@example.com",
"name": "Administrator",
"state": "active",
- "created_at": "2012-04-29T08:46:00Z"
+ "avatar_url": null,
+ "web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
"id": 1,
"username": "admin",
- "email": "admin@example.com",
"name": "Administrator",
"state": "active",
- "created_at": "2012-04-29T08:46:00Z"
+ "avatar_url": null,
+ "web_url" : "https://gitlab.example.com/admin"
},
"source_project_id": 2,
"target_project_id": 3,
@@ -297,18 +297,18 @@ Parameters:
"author": {
"id": 1,
"username": "admin",
- "email": "admin@example.com",
"name": "Administrator",
"state": "active",
- "created_at": "2012-04-29T08:46:00Z"
+ "avatar_url": null,
+ "web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
"id": 1,
"username": "admin",
- "email": "admin@example.com",
"name": "Administrator",
"state": "active",
- "created_at": "2012-04-29T08:46:00Z"
+ "avatar_url": null,
+ "web_url" : "https://gitlab.example.com/admin"
},
"source_project_id": 2,
"target_project_id": 3,
@@ -548,14 +548,16 @@ Parameters:
"username": "jarrett",
"id": 5,
"state": "active",
- "avatar_url": "http://www.gravatar.com/avatar/b95567800f828948baf5f4160ebb2473?s=40&d=identicon"
+ "avatar_url": "http://www.gravatar.com/avatar/b95567800f828948baf5f4160ebb2473?s=40&d=identicon",
+ "web_url" : "https://gitlab.example.com/jarrett"
},
"assignee": {
"name": "Administrator",
"username": "root",
"id": 1,
"state": "active",
- "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40&d=identicon"
+ "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40&d=identicon",
+ "web_url" : "https://gitlab.example.com/root"
},
"source_project_id": 4,
"target_project_id": 4,
@@ -669,18 +671,18 @@ POST /projects/:id/merge_requests
"author": {
"id": 1,
"username": "admin",
- "email": "admin@example.com",
"name": "Administrator",
"state": "active",
- "created_at": "2012-04-29T08:46:00Z"
+ "avatar_url": null,
+ "web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
"id": 1,
"username": "admin",
- "email": "admin@example.com",
"name": "Administrator",
"state": "active",
- "created_at": "2012-04-29T08:46:00Z"
+ "avatar_url": null,
+ "web_url" : "https://gitlab.example.com/admin"
},
"source_project_id": 3,
"target_project_id": 4,
@@ -761,18 +763,18 @@ Must include at least one non-required attribute from above.
"author": {
"id": 1,
"username": "admin",
- "email": "admin@example.com",
"name": "Administrator",
"state": "active",
- "created_at": "2012-04-29T08:46:00Z"
+ "avatar_url": null,
+ "web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
"id": 1,
"username": "admin",
- "email": "admin@example.com",
"name": "Administrator",
"state": "active",
- "created_at": "2012-04-29T08:46:00Z"
+ "avatar_url": null,
+ "web_url" : "https://gitlab.example.com/admin"
},
"source_project_id": 3,
"target_project_id": 4,
@@ -870,18 +872,18 @@ Parameters:
"author": {
"id": 1,
"username": "admin",
- "email": "admin@example.com",
"name": "Administrator",
"state": "active",
- "created_at": "2012-04-29T08:46:00Z"
+ "avatar_url": null,
+ "web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
"id": 1,
"username": "admin",
- "email": "admin@example.com",
"name": "Administrator",
"state": "active",
- "created_at": "2012-04-29T08:46:00Z"
+ "avatar_url": null,
+ "web_url" : "https://gitlab.example.com/admin"
},
"source_project_id": 4,
"target_project_id": 4,
@@ -949,18 +951,18 @@ Parameters:
"author": {
"id": 1,
"username": "admin",
- "email": "admin@example.com",
"name": "Administrator",
"state": "active",
- "created_at": "2012-04-29T08:46:00Z"
+ "avatar_url": null,
+ "web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
"id": 1,
"username": "admin",
- "email": "admin@example.com",
"name": "Administrator",
"state": "active",
- "created_at": "2012-04-29T08:46:00Z"
+ "avatar_url": null,
+ "web_url" : "https://gitlab.example.com/admin"
},
"source_project_id": 4,
"target_project_id": 4,
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index f946536701e..8e13ceb9257 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -799,16 +799,6 @@ cache:
- binaries/
```
-If you use **Windows PowerShell** to run your shell scripts you need to replace
-`$` with `$env:`:
-
-```yaml
-cache:
- key: "$env:CI_COMMIT_REF_SLUG"
- paths:
- - binaries/
-```
-
### `cache:untracked`
Set `untracked: true` to cache all files that are untracked in your Git
diff --git a/doc/development/new_fe_guide/style/prettier.md b/doc/development/new_fe_guide/style/prettier.md
index eb18189282b..6395af6f815 100644
--- a/doc/development/new_fe_guide/style/prettier.md
+++ b/doc/development/new_fe_guide/style/prettier.md
@@ -43,3 +43,17 @@ yarn prettier-all-save
Formats all files in the repository with Prettier. (This should only be used to test global rule updates otherwise you would end up with huge MR's).
The source of these Yarn scripts can be found in `/scripts/frontend/prettier.js`.
+
+### Scripts during Conversion period
+
+```
+node ./scripts/frontend/prettier.js check ./vendor/
+```
+
+This will go over all files in a specific folder check it.
+
+```
+node ./scripts/frontend/prettier.js save ./vendor/
+```
+
+This will go over all files in a specific folder and save it.
diff --git a/doc/integration/google.md b/doc/integration/google.md
index ae1d848f439..8906f91b6b4 100644
--- a/doc/integration/google.md
+++ b/doc/integration/google.md
@@ -35,7 +35,12 @@ In Google's side:
1. You should now be able to see a Client ID and Client secret. Note them down
or keep this page open as you will need them later.
-1. From the **Dashboard** select **ENABLE APIS AND SERVICES > Compute > Google Kubernetes Engine API > Enable**
+1. From the **Dashboard** select **ENABLE APIS AND SERVICES > Compute > Google+ API > Enable**
+1. To enable projects to access [Google Kubernetes Engine](../user/project/clusters/index.md), you must also
+ enable these APIs:
+ - Google Kubernetes Engine API
+ - Cloud Resource Manager API
+ - Cloud Billing API
On your GitLab server:
diff --git a/doc/university/README.md b/doc/university/README.md
index aa68c841f92..595fc480887 100644
--- a/doc/university/README.md
+++ b/doc/university/README.md
@@ -15,11 +15,11 @@ Would you like to contribute to GitLab University? Then please take a look at ou
The curriculum is composed of GitLab videos, screencasts, presentations, projects and external GitLab content hosted on other services and has been organized into the following sections.
-1. [GitLab Beginner](#beginner)
-1. [GitLab Intermediate](#intermediate)
-1. [GitLab Advanced](#advanced)
-1. [External Articles](#external)
-1. [Resources for GitLab Team Members](#team)
+1. [GitLab Beginner](#1-gitlab-beginner)
+1. [GitLab Intermediate](#2-gitlab-intermediate)
+1. [GitLab Advanced](#3-gitlab-advanced)
+1. [External Articles](#4-external-articles)
+1. [Resources for GitLab Team Members](#5-resources-for-gitlab-team-members)
---
@@ -126,7 +126,7 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project
1. [IBM: Continuous Delivery vs Continuous Deployment - Video](https://www.youtube.com/watch?v=igwFj8PPSnw)
1. [Amazon: Transition to Continuous Delivery - Video](https://www.youtube.com/watch?v=esEFaY0FDKc)
2. [TechBeacon: Doing continuous delivery? Focus first on reducing release cycle times](https://techbeacon.com/doing-continuous-delivery-focus-first-reducing-release-cycle-times)
-1. See **[Integrations](#integrations)** for integrations with other CI services.
+1. See **[Integrations](#39-integrations)** for integrations with other CI services.
#### 2.4. Workflow
diff --git a/doc/user/markdown.md b/doc/user/markdown.md
index 5f976a8ad31..8e87c896a72 100644
--- a/doc/user/markdown.md
+++ b/doc/user/markdown.md
@@ -3,13 +3,15 @@
## GitLab Flavored Markdown (GFM)
> **Note:**
-Not all of the GitLab-specific extensions to Markdown that are described in
-this document currently work on our documentation website.
+> Not all of the GitLab-specific extensions to Markdown that are described in
+> this document currently work on our documentation website.
>
-For the best result, we encourage you to check this document out as rendered
+> For the best result, we encourage you to check this document out as rendered
by GitLab: [markdown.md]
-_GitLab uses the [Redcarpet Ruby library][redcarpet] for Markdown processing._
+_GitLab uses (as of 11.1) the [CommonMark Ruby Library][commonmarker] for Markdown processing of all new issues, merge requests, comments, and other Markdown content in the GitLab system. Previous content and Markdown files `.md` in the repositories are still processed using the [Redcarpet Ruby library][redcarpet]._
+
+_Where there are significant differences, we will try to call them out in this document._
GitLab uses "GitLab Flavored Markdown" (GFM). It extends the standard Markdown in a few significant ways to add some useful functionality. It was inspired by [GitHub Flavored Markdown](https://help.github.com/articles/basic-writing-and-formatting-syntax/).
@@ -21,7 +23,7 @@ You can use GFM in the following areas:
- milestones
- snippets (the snippet must be named with a `.md` extension)
- wiki pages
-- markdown documents inside the repository
+- markdown documents inside the repository (currently only rendered by Redcarpet)
You can also use other rich text files in GitLab. You might have to install a
dependency to do so. Please see the [github-markup gem readme](https://github.com/gitlabhq/markup#markups) for more information.
@@ -394,14 +396,14 @@ Color written inside backticks will be followed by a color "chip".
Examples:
- `#F00`
- `#F00A`
- `#FF0000`
- `#FF0000AA`
- `RGB(0,255,0)`
- `RGB(0%,100%,0%)`
- `RGBA(0,255,0,0.7)`
- `HSL(540,70%,50%)`
+ `#F00`
+ `#F00A`
+ `#FF0000`
+ `#FF0000AA`
+ `RGB(0,255,0)`
+ `RGB(0%,100%,0%)`
+ `RGBA(0,255,0,0.7)`
+ `HSL(540,70%,50%)`
`HSLA(540,70%,50%,0.7)`
Become:
@@ -414,7 +416,7 @@ Become:
`RGB(0%,100%,0%)`
`RGBA(0,255,0,0.7)`
`HSL(540,70%,50%)`
-`HSLA(540,70%,50%,0.7)`
+`HSLA(540,70%,50%,0.7)`
#### Supported formats:
@@ -500,6 +502,7 @@ For example:
# This header has Unicode in it: 한글
## This header has spaces in it
### This header has spaces in it
+## This header has 3.5 in it (and parentheses)
```
Would generate the following link IDs:
@@ -509,6 +512,7 @@ Would generate the following link IDs:
1. `this-header-has-unicode-in-it-한글`
1. `this-header-has-spaces-in-it`
1. `this-header-has-spaces-in-it-1`
+1. `this-header-has-3-5-in-it-and-parentheses`
Note that the Emoji processing happens before the header IDs are generated, so the Emoji is converted to an image which then gets removed from the ID.
@@ -543,9 +547,9 @@ Examples:
```no-highlight
1. First ordered list item
2. Another item
- * Unordered sub-list.
+ * Unordered sub-list.
1. Actual numbers don't matter, just that it's a number
- 1. Ordered sub-list
+ 1. Ordered sub-list
4. And another item.
* Unordered list can use asterisks
@@ -557,9 +561,9 @@ Become:
1. First ordered list item
2. Another item
- * Unordered sub-list.
+ * Unordered sub-list.
1. Actual numbers don't matter, just that it's a number
- 1. Ordered sub-list
+ 1. Ordered sub-list
4. And another item.
* Unordered list can use asterisks
@@ -567,33 +571,36 @@ Become:
+ Or pluses
If a list item contains multiple paragraphs,
-each subsequent paragraph should be indented with four spaces.
+each subsequent paragraph should be indented to the same level as the start of the list item text (_Redcarpet: paragraph should be indented with four spaces._)
Example:
```no-highlight
-1. First ordered list item
+1. First ordered list item
- Second paragraph of first item.
-2. Another item
+ Second paragraph of first item.
+
+2. Another item
```
Becomes:
1. First ordered list item
- Second paragraph of first item.
+ Paragraph of first item.
+
2. Another item
-If the second paragraph isn't indented with four spaces,
-the second list item will be incorrectly labeled as `1`.
+If the paragraph of the first item is not indented with the proper number of spaces,
+the paragraph will appear outside the list, instead of properly indented under the list item.
Example:
```no-highlight
1. First ordered list item
- Second paragraph of first item.
+ Paragraph of first item.
+
2. Another item
```
@@ -601,7 +608,8 @@ Becomes:
1. First ordered list item
- Second paragraph of first item.
+ Paragraph of first item.
+
2. Another item
### Links
@@ -719,20 +727,24 @@ Content can be collapsed using HTML's [`<details>`](https://developer.mozilla.or
<p>
<details>
<summary>Click me to collapse/fold.</summary>
-These details will remain hidden until expanded.
+
+These details <em>will</em> remain <strong>hidden</strong> until expanded.
<pre><code>PASTE LOGS HERE</code></pre>
+
</details>
</p>
-**Note:** Unfortunately Markdown is not supported inside these tags, as described by the [markdown specification](https://daringfireball.net/projects/markdown/syntax#html). You can work around this by using HTML, for example you can use `<pre><code>` tags instead of [code fences](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#code-and-syntax-highlighting).
+**Note:** Markdown inside these tags is supported, as long as you have a blank link after the `</summary>` tag and before the `</details>` tag, as shown in the example. _Redcarpet does not support Markdown inside these tags. You can work around this by using HTML, for example you can use `<pre><code>` tags instead of [code fences](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#code-and-syntax-highlighting)._
```html
<details>
<summary>Click me to collapse/fold.</summary>
-These details will remain hidden until expanded.
-<pre><code>PASTE LOGS HERE</code></pre>
+These details _will_ remain **hidden** until expanded.
+
+ PASTE LOGS HERE
+
</details>
```
@@ -774,7 +786,7 @@ Underscores
### Line Breaks
-My basic recommendation for learning how line breaks work is to experiment and discover -- hit &lt;Enter&gt; once (i.e., insert one newline), then hit it twice (i.e., insert two newlines), see what happens. You'll soon learn to get what you want. "Markdown Toggle" is your friend.
+A good way to learn how line breaks work is to experiment and discover -- hit <kbd>Enter</kbd> once (i.e., insert one newline), then hit it twice (i.e., insert two newlines), see what happens. You'll soon learn to get what you want. The "Preview" tab is your friend.
Here are some things to try out:
@@ -810,7 +822,7 @@ spaces.
### Tables
-Tables aren't part of the core Markdown spec, but they are part of GFM and Markdown Here supports them.
+Tables aren't part of the core Markdown spec, but they are part of GFM.
Example:
@@ -828,9 +840,7 @@ Becomes:
| cell 1 | cell 2 |
| cell 3 | cell 4 |
-**Note**
-
-The row of dashes between the table header and body must have at least three dashes in each column.
+**Note:** The row of dashes between the table header and body must have at least three dashes in each column.
By including colons in the header row, you can align the text within that column.
@@ -863,6 +873,18 @@ Becomes:
You can add footnotes to your text as follows.[^2]
+### Superscripts / Subscripts
+
+CommonMark and GFM currently do not support the superscript syntax ( `x^2` ) that Redcarpet does. You can use the standard HTML syntax for superscripts and subscripts.
+
+```
+The formula for water is H<sub>2</sub>O
+while the equation for the theory of relativity is E = mc<sup>2</sup>.
+```
+
+The formula for water is H<sub>2</sub>O while the equation for the theory of relativity is E = mc<sup>2</sup>.
+
+
## Wiki-specific Markdown
The following examples show how links inside wikis behave.
@@ -954,3 +976,4 @@ A link starting with a `/` is relative to the wiki root.
[katex]: https://github.com/Khan/KaTeX "KaTeX website"
[katex-subset]: https://github.com/Khan/KaTeX/wiki/Function-Support-in-KaTeX "Macros supported by KaTeX"
[asciidoctor-manual]: http://asciidoctor.org/docs/user-manual/#activating-stem-support "Asciidoctor user manual"
+[commonmarker]: https://github.com/gjtorikian/commonmarker
diff --git a/doc/user/permissions.md b/doc/user/permissions.md
index 16c19855136..b36b0b4f757 100644
--- a/doc/user/permissions.md
+++ b/doc/user/permissions.md
@@ -51,6 +51,9 @@ The following table depicts the various user permission levels in a project.
| See a container registry | | ✓ | ✓ | ✓ | ✓ |
| See environments | | ✓ | ✓ | ✓ | ✓ |
| See a list of merge requests | | ✓ | ✓ | ✓ | ✓ |
+| Manage related issues **[STARTER]** | | ✓ | ✓ | ✓ | ✓ |
+| Lock issue discussions | | ✓ | ✓ | ✓ | ✓ |
+| Lock merge request discussions | | | ✓ | ✓ | ✓ |
| Create new environments | | | ✓ | ✓ | ✓ |
| Stop environments | | | ✓ | ✓ | ✓ |
| Manage/Accept merge requests | | | ✓ | ✓ | ✓ |
@@ -76,11 +79,12 @@ The following table depicts the various user permission levels in a project.
| Edit project | | | | ✓ | ✓ |
| Add deploy keys to project | | | | ✓ | ✓ |
| Configure project hooks | | | | ✓ | ✓ |
-| Manage runners | | | | ✓ | ✓ |
+| Manage Runners | | | | ✓ | ✓ |
| Manage job triggers | | | | ✓ | ✓ |
| Manage variables | | | | ✓ | ✓ |
-| Manage pages | | | | ✓ | ✓ |
-| Manage pages domains and certificates | | | | ✓ | ✓ |
+| Manage GitLab Pages | | | | ✓ | ✓ |
+| Manage GitLab Pages domains and certificates | | | | ✓ | ✓ |
+| Remove GitLab Pages | | | | | ✓ |
| Manage clusters | | | | ✓ | ✓ |
| Edit comments (posted by any user) | | | | ✓ | ✓ |
| Switch visibility level | | | | | ✓ |
@@ -90,6 +94,7 @@ The following table depicts the various user permission levels in a project.
| Remove pages | | | | | ✓ |
| Force push to protected branches [^4] | | | | | |
| Remove protected branches [^4] | | | | | |
+| View project Audit Events | | | | ✓ | ✓ |
## Project features permissions
@@ -127,17 +132,12 @@ and drag issues around. Read though the
[documentation on Issue Boards permissions](project/issue_board.md#permissions)
to learn more.
-### File Locking permissions
-
-> Available in [GitLab Premium](https://about.gitlab.com/products/).
+### File Locking permissions **[PREMIUM]**
The user that locks a file or directory is the only one that can edit and push their changes back to the repository where the locked objects are located.
Read through the documentation on [permissions for File Locking](https://docs.gitlab.com/ee/user/project/file_lock.html#permissions-on-file-locking) to learn more.
-File Locking is available in
-[GitLab Premium](https://about.gitlab.com/products/) only.
-
### Confidential Issues permissions
Confidential issues can be accessed by reporters and higher permission levels,
@@ -160,6 +160,12 @@ group.
| Remove group | | | | | ✓ |
| Manage group labels | | ✓ | ✓ | ✓ | ✓ |
| Create/edit/delete group milestones | | | ✓ | ✓ | ✓ |
+| View private group epic **[ULTIMATE]** | | ✓ | ✓ | ✓ | ✓ |
+| View internal group epic **[ULTIMATE]** | ✓ | ✓ | ✓ | ✓ | ✓ |
+| View public group epic **[ULTIMATE]** | ✓ | ✓ | ✓ | ✓ | ✓ |
+| Create/edit group epic **[ULTIMATE]** | | ✓ | ✓ | ✓ | ✓ |
+| Delete group epic **[ULTIMATE]** | | | | | ✓ |
+| View group Audit Events | | | | | ✓ |
### Subgroup permissions
@@ -194,6 +200,27 @@ will find the option to flag the user as external.
By default new users are not set as external users. This behavior can be changed
by an administrator under **Admin > Application Settings**.
+## Auditor users **[PREMIUM ONLY]**
+
+>[Introduced][ee-998] in [GitLab Premium][eep] 8.17.
+
+Auditor users are given read-only access to all projects, groups, and other
+resources on the GitLab instance.
+
+An Auditor user should be able to access all projects and groups of a GitLab instance
+with the permissions described on the documentation on [auditor users permissions](https://docs.gitlab.com/ee/administration/auditor_users.html#permissions-and-restrictions-of-an-auditor-user).
+
+[Read more about Auditor users.](https://docs.gitlab.com/ee/administration/auditor_users.html)
+
+## Project features
+
+Project features like wiki and issues can be hidden from users depending on
+which visibility level you select on project settings.
+
+- Disabled: disabled for everyone
+- Only team members: only team members will see even if your project is public or internal
+- Everyone with access: everyone can see depending on your project visibility level
+
## GitLab CI/CD permissions
GitLab CI/CD permissions rely on the role the user has in GitLab. There are four
@@ -263,16 +290,6 @@ for details about the pipelines security model.
Since GitLab 8.15, LDAP user permissions can now be manually overridden by an admin user.
Read through the documentation on [LDAP users permissions](https://docs.gitlab.com/ee/articles/how_to_configure_ldap_gitlab_ee/index.html#updating-user-permissions-new-feature) to learn more.
-## Auditor users permissions
-
-> Available in [GitLab Premium](https://about.gitlab.com/products/).
-
-An Auditor user should be able to access all projects and groups of a GitLab instance
-with the permissions described on the documentation on [auditor users permissions](https://docs.gitlab.com/ee/administration/auditor_users.html#permissions-and-restrictions-of-an-auditor-user).
-
-Auditor users are available in [GitLab Premium](https://about.gitlab.com/products/)
-only.
-
[^1]: On public and internal projects, all users are able to perform this action
[^2]: Guest users can only view the confidential issues they created themselves
[^3]: If **Public pipelines** is enabled in **Project Settings > CI/CD**
@@ -283,3 +300,5 @@ only.
[ce-18994]: https://gitlab.com/gitlab-org/gitlab-ce/issues/18994
[new-mod]: project/new_ci_build_permissions_model.md
+[ee-998]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/998
+[eep]: https://about.gitlab.com/products/ \ No newline at end of file
diff --git a/doc/user/project/issue_board.md b/doc/user/project/issue_board.md
index aa2fcd82787..9ca1e6226c5 100644
--- a/doc/user/project/issue_board.md
+++ b/doc/user/project/issue_board.md
@@ -250,12 +250,12 @@ One group issue board per group was made available in GitLab 10.6 Core after mul
Different issue board features are available in different [GitLab tiers](https://about.gitlab.com/pricing/), as shown in the following table:
-| Tier | Number of project issue boards | Board with configuration in project issue boards | Number of group issue boards | Board with configuration in group issue boards |
-| --- | --- | --- | --- | --- |
-| Core | 1 | No | 1 | No |
-| Starter | Multiple | Yes | 1 | No |
-| Premium | Multiple | Yes | Multiple | Yes |
-| Ultimate | Multiple | Yes | Multiple | Yes |
+| Tier | Number of Project Issue Boards | Number of Group Issue Boards | Configurable Project Issue Boards | Configurable Group Issue Boards | Assignee Lists
+| --- | --- | --- | --- | --- | --- |
+| Core | 1 | 1 | No | No | No |
+| Starter | Multiple | 1 | Yes | No | No |
+| Premium | Multiple | Multiple | Yes | Yes | Yes |
+| Ultimate | Multiple | Multiple | Yes | Yes | Yes |
## Tips
diff --git a/doc/user/project/milestones/index.md b/doc/user/project/milestones/index.md
index 64bb33be547..632253db94c 100644
--- a/doc/user/project/milestones/index.md
+++ b/doc/user/project/milestones/index.md
@@ -10,7 +10,6 @@ Milestones allow you to organize issues and merge requests into a cohesive group
- **Project milestones** can be assigned to issues or merge requests in that project only.
- **Group milestones** can be assigned to any issue or merge request of any project in that group.
-- In the [future](https://gitlab.com/gitlab-org/gitlab-ce/issues/36862), you will be able to assign group milestones to issues and merge requests of projects in [subgroups](../../group/subgroups/index.md).
## Creating milestones
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 2ed331d4fd2..9c53b7c3fe7 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -272,7 +272,8 @@ module API
attrs[key] = params_hash[key]
end
end
- ActionController::Parameters.new(attrs).permit!
+ permitted_attrs = ActionController::Parameters.new(attrs).permit!
+ Gitlab.rails5? ? permitted_attrs.to_h : permitted_attrs
end
def filter_by_iid(items, iid)
diff --git a/lib/api/markdown.rb b/lib/api/markdown.rb
index b9ed68aa584..5d55224c1a7 100644
--- a/lib/api/markdown.rb
+++ b/lib/api/markdown.rb
@@ -10,9 +10,7 @@ module API
detail "This feature was introduced in GitLab 11.0."
end
post do
- # Explicitly set CommonMark as markdown engine to use.
- # Remove this set when https://gitlab.com/gitlab-org/gitlab-ce/issues/43011 is done.
- context = { markdown_engine: :common_mark, only_path: false }
+ context = { only_path: false }
if params[:project]
project = Project.find_by_full_path(params[:project])
diff --git a/lib/banzai/filter/markdown_filter.rb b/lib/banzai/filter/markdown_filter.rb
index c1e2b680240..944363f17d3 100644
--- a/lib/banzai/filter/markdown_filter.rb
+++ b/lib/banzai/filter/markdown_filter.rb
@@ -14,7 +14,7 @@ module Banzai
private
- DEFAULT_ENGINE = :redcarpet
+ DEFAULT_ENGINE = :common_mark
def engine(engine_from_context)
engine_from_context ||= DEFAULT_ENGINE
diff --git a/lib/banzai/filter/milestone_reference_filter.rb b/lib/banzai/filter/milestone_reference_filter.rb
index 858e790005c..af8448937b3 100644
--- a/lib/banzai/filter/milestone_reference_filter.rb
+++ b/lib/banzai/filter/milestone_reference_filter.rb
@@ -65,7 +65,7 @@ module Banzai
# We don't support IID lookups for group milestones, because IIDs can
# clash between group and project milestones.
if project.group && !params[:iid]
- finder_params[:group_ids] = project.group.self_and_ancestors.select(:id)
+ finder_params[:group_ids] = project.group.self_and_ancestors_ids
end
MilestonesFinder.new(finder_params).find_by(params)
diff --git a/lib/gitlab/background_migration/prepare_untracked_uploads.rb b/lib/gitlab/background_migration/prepare_untracked_uploads.rb
index 914a9e48a2f..522c69a0bb1 100644
--- a/lib/gitlab/background_migration/prepare_untracked_uploads.rb
+++ b/lib/gitlab/background_migration/prepare_untracked_uploads.rb
@@ -54,7 +54,8 @@ module Gitlab
def ensure_temporary_tracking_table_exists
table_name = :untracked_files_for_uploads
- unless UntrackedFile.connection.table_exists?(table_name)
+
+ unless ActiveRecord::Base.connection.data_source_exists?(table_name)
UntrackedFile.connection.create_table table_name do |t|
t.string :path, limit: 600, null: false
t.index :path, unique: true
diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb
index d49d055c3f2..4ad106e7b0a 100644
--- a/lib/gitlab/database.rb
+++ b/lib/gitlab/database.rb
@@ -188,8 +188,11 @@ module Gitlab
end
def self.cached_table_exists?(table_name)
- # Rails 5 uses data_source_exists? instead of table_exists?
- connection.schema_cache.table_exists?(table_name)
+ if Gitlab.rails5?
+ connection.schema_cache.data_source_exists?(table_name)
+ else
+ connection.schema_cache.table_exists?(table_name)
+ end
end
private_class_method :connection
diff --git a/lib/gitlab/favicon.rb b/lib/gitlab/favicon.rb
index 451c9daf761..faf7016d73a 100644
--- a/lib/gitlab/favicon.rb
+++ b/lib/gitlab/favicon.rb
@@ -2,7 +2,7 @@ module Gitlab
class Favicon
class << self
def main
- return appearance_favicon.favicon_main.url if appearance_favicon.exists?
+ return appearance_favicon.url if appearance_favicon.exists?
image_name =
if Gitlab::Utils.to_boolean(ENV['CANARY'])
diff --git a/lib/gitlab/git/blame.rb b/lib/gitlab/git/blame.rb
index 40b65f6c0da..e25e15f5c80 100644
--- a/lib/gitlab/git/blame.rb
+++ b/lib/gitlab/git/blame.rb
@@ -22,24 +22,9 @@ module Gitlab
private
def load_blame
- raw_output = @repo.gitaly_migrate(:blame, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- load_blame_by_gitaly
- else
- load_blame_by_shelling_out
- end
- end
-
- output = encode_utf8(raw_output)
- process_raw_blame output
- end
-
- def load_blame_by_gitaly
- @repo.gitaly_commit_client.raw_blame(@sha, @path)
- end
+ output = encode_utf8(@repo.gitaly_commit_client.raw_blame(@sha, @path))
- def load_blame_by_shelling_out
- @repo.shell_blame(@sha, @path)
+ process_raw_blame(output)
end
def process_raw_blame(output)
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index 61ae42a116b..eb5d6318dcb 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -120,13 +120,11 @@ module Gitlab
# Default branch in the repository
def root_ref
- @root_ref ||= gitaly_migrate(:root_ref, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- gitaly_ref_client.default_branch_name
- else
- discover_default_branch
- end
- end
+ gitaly_ref_client.default_branch_name
+ rescue GRPC::NotFound => e
+ raise NoRepository.new(e.message)
+ rescue GRPC::Unknown => e
+ raise Gitlab::Git::CommandError.new(e.message)
end
def rugged
@@ -152,23 +150,15 @@ module Gitlab
# Returns an Array of branch names
# sorted by name ASC
def branch_names
- gitaly_migrate(:branch_names, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- gitaly_ref_client.branch_names
- else
- branches.map(&:name)
- end
+ wrapped_gitaly_errors do
+ gitaly_ref_client.branch_names
end
end
# Returns an Array of Branches
def branches
- gitaly_migrate(:branches, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- gitaly_ref_client.branches
- else
- branches_filter
- end
+ wrapped_gitaly_errors do
+ gitaly_ref_client.branches
end
end
@@ -200,12 +190,8 @@ module Gitlab
end
def local_branches(sort_by: nil)
- gitaly_migrate(:local_branches, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- gitaly_ref_client.local_branches(sort_by: sort_by)
- else
- branches_filter(filter: :local, sort_by: sort_by)
- end
+ wrapped_gitaly_errors do
+ gitaly_ref_client.local_branches(sort_by: sort_by)
end
end
@@ -245,18 +231,6 @@ module Gitlab
# This refs by default not visible in project page and not cloned to client side.
alias_method :has_visible_content?, :has_local_branches?
- def has_local_branches_rugged?
- rugged.branches.each(:local).any? do |ref|
- begin
- ref.name && ref.target # ensures the branch is valid
-
- true
- rescue Rugged::ReferenceError
- false
- end
- end
- end
-
# Returns the number of valid tags
def tag_count
gitaly_migrate(:tag_names) do |is_enabled|
@@ -270,12 +244,8 @@ module Gitlab
# Returns an Array of tag names
def tag_names
- gitaly_migrate(:tag_names, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- gitaly_ref_client.tag_names
- else
- rugged.tags.map { |t| t.name }
- end
+ wrapped_gitaly_errors do
+ gitaly_ref_client.tag_names
end
end
@@ -283,12 +253,8 @@ module Gitlab
#
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/390
def tags
- gitaly_migrate(:tags, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- tags_from_gitaly
- else
- tags_from_rugged
- end
+ wrapped_gitaly_errors do
+ gitaly_ref_client.tags
end
end
@@ -364,31 +330,6 @@ module Gitlab
end.map(&:name)
end
- # Discovers the default branch based on the repository's available branches
- #
- # - If no branches are present, returns nil
- # - If one branch is present, returns its name
- # - If two or more branches are present, returns current HEAD or master or first branch
- def discover_default_branch
- names = branch_names
-
- return if names.empty?
-
- return names[0] if names.length == 1
-
- if rugged_head
- extracted_name = Ref.extract_branch_name(rugged_head.name)
-
- return extracted_name if names.include?(extracted_name)
- end
-
- if names.include?('master')
- 'master'
- else
- names[0]
- end
- end
-
def rugged_head
rugged.head
rescue Rugged::ReferenceError
@@ -1453,6 +1394,16 @@ module Gitlab
raise CommandError.new(e)
end
+ def wrapped_gitaly_errors(&block)
+ yield block
+ rescue GRPC::NotFound => e
+ raise NoRepository.new(e)
+ rescue GRPC::InvalidArgument => e
+ raise ArgumentError.new(e)
+ rescue GRPC::BadStatus => e
+ raise CommandError.new(e)
+ end
+
def clean_stale_repository_files
gitaly_migrate(:repository_cleanup, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
gitaly_repository_client.cleanup if is_enabled && exists?
@@ -1606,12 +1557,8 @@ module Gitlab
private
def uncached_has_local_branches?
- gitaly_migrate(:has_local_branches, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- gitaly_repository_client.has_local_branches?
- else
- has_local_branches_rugged?
- end
+ wrapped_gitaly_errors do
+ gitaly_repository_client.has_local_branches?
end
end
@@ -1731,20 +1678,6 @@ module Gitlab
}
end
- # Gitaly note: JV: Trying to get rid of the 'filter' option so we can implement this with 'git'.
- def branches_filter(filter: nil, sort_by: nil)
- branches = rugged.branches.each(filter).map do |rugged_ref|
- begin
- target_commit = Gitlab::Git::Commit.find(self, rugged_ref.target)
- Gitlab::Git::Branch.new(self, rugged_ref.name, rugged_ref.target, target_commit)
- rescue Rugged::ReferenceError
- # Omit invalid branch
- end
- end.compact
-
- sort_branches(branches, sort_by)
- end
-
def git_merged_branch_names(branch_names, root_sha)
git_arguments =
%W[branch --merged #{root_sha}
@@ -1956,37 +1889,11 @@ module Gitlab
end
end
- def tags_from_rugged
- rugged.references.each("refs/tags/*").map do |ref|
- message = nil
-
- if ref.target.is_a?(Rugged::Tag::Annotation)
- tag_message = ref.target.message
-
- if tag_message.respond_to?(:chomp)
- message = tag_message.chomp
- end
- end
-
- target_commit = Gitlab::Git::Commit.find(self, ref.target)
- Gitlab::Git::Tag.new(self, {
- name: ref.name,
- target: ref.target,
- target_commit: target_commit,
- message: message
- })
- end.sort_by(&:name)
- end
-
def last_commit_for_path_by_rugged(sha, path)
sha = last_commit_id_for_path_by_shelling_out(sha, path)
commit(sha)
end
- def tags_from_gitaly
- gitaly_ref_client.tags
- end
-
def size_by_shelling_out
popen(%w(du -sk), path).first.strip.to_i
end
diff --git a/lib/gitlab/gitaly_client.rb b/lib/gitlab/gitaly_client.rb
index 36e9adf27da..620362b52a9 100644
--- a/lib/gitlab/gitaly_client.rb
+++ b/lib/gitlab/gitaly_client.rb
@@ -33,11 +33,6 @@ module Gitlab
MAXIMUM_GITALY_CALLS = 35
CLIENT_NAME = (Sidekiq.server? ? 'gitlab-sidekiq' : 'gitlab-web').freeze
- # We have a mechanism to let GitLab automatically opt in to all Gitaly
- # features. We want to be able to exclude some features from automatic
- # opt-in. That is what EXPLICIT_OPT_IN_REQUIRED is for.
- EXPLICIT_OPT_IN_REQUIRED = [Gitlab::GitalyClient::StorageSettings::DISK_ACCESS_DENIED_FLAG].freeze
-
MUTEX = Mutex.new
class << self
@@ -249,7 +244,7 @@ module Gitlab
when MigrationStatus::OPT_OUT
true
when MigrationStatus::OPT_IN
- opt_into_all_features? && !EXPLICIT_OPT_IN_REQUIRED.include?(feature_name)
+ opt_into_all_features? && !explicit_opt_in_required.include?(feature_name)
else
false
end
@@ -259,6 +254,13 @@ module Gitlab
false
end
+ # We have a mechanism to let GitLab automatically opt in to all Gitaly
+ # features. We want to be able to exclude some features from automatic
+ # opt-in. This function has an override in EE.
+ def self.explicit_opt_in_required
+ []
+ end
+
# opt_into_all_features? returns true when the current environment
# is one in which we opt into features automatically
def self.opt_into_all_features?
diff --git a/lib/gitlab/github_import/parallel_importer.rb b/lib/gitlab/github_import/parallel_importer.rb
index b02b123c98e..a77ac1e4fa6 100644
--- a/lib/gitlab/github_import/parallel_importer.rb
+++ b/lib/gitlab/github_import/parallel_importer.rb
@@ -15,6 +15,15 @@ module Gitlab
true
end
+ # This is a workaround for a Ruby 2.3.7 bug. rspec-mocks cannot restore
+ # the visibility of prepended modules. See
+ # https://github.com/rspec/rspec-mocks/issues/1231 for more details.
+ if Rails.env.test?
+ def self.requires_ci_cd_setup?
+ raise NotImplementedError
+ end
+ end
+
def initialize(project)
@project = project
end
diff --git a/lib/gitlab/verify/batch_verifier.rb b/lib/gitlab/verify/batch_verifier.rb
index 1ef369a4b67..167ba1b3149 100644
--- a/lib/gitlab/verify/batch_verifier.rb
+++ b/lib/gitlab/verify/batch_verifier.rb
@@ -7,13 +7,15 @@ module Gitlab
@batch_size = batch_size
@start = start
@finish = finish
+
+ fix_google_api_logger
end
# Yields a Range of IDs and a Hash of failed verifications (object => error)
def run_batches(&blk)
- relation.in_batches(of: batch_size, start: start, finish: finish) do |relation| # rubocop: disable Cop/InBatches
- range = relation.first.id..relation.last.id
- failures = run_batch(relation)
+ all_relation.in_batches(of: batch_size, start: start, finish: finish) do |batch| # rubocop: disable Cop/InBatches
+ range = batch.first.id..batch.last.id
+ failures = run_batch_for(batch)
yield(range, failures)
end
@@ -29,24 +31,56 @@ module Gitlab
private
- def run_batch(relation)
- relation.map { |upload| verify(upload) }.compact.to_h
+ def run_batch_for(batch)
+ batch.map { |upload| verify(upload) }.compact.to_h
end
def verify(object)
+ local?(object) ? verify_local(object) : verify_remote(object)
+ rescue => err
+ failure(object, err.inspect)
+ end
+
+ def verify_local(object)
expected = expected_checksum(object)
actual = actual_checksum(object)
- raise 'Checksum missing' unless expected.present?
- raise 'Checksum mismatch' unless expected == actual
+ return failure(object, 'Checksum missing') unless expected.present?
+ return failure(object, 'Checksum mismatch') unless expected == actual
+
+ success
+ end
+ # We don't calculate checksum for remote objects, so just check existence
+ def verify_remote(object)
+ return failure(object, 'Remote object does not exist') unless remote_object_exists?(object)
+
+ success
+ end
+
+ def success
nil
- rescue => err
- [object, err]
+ end
+
+ def failure(object, message)
+ [object, message]
+ end
+
+ # It's already set to Logger::INFO, but acts as if it is set to
+ # Logger::DEBUG, and this fixes it...
+ def fix_google_api_logger
+ if Object.const_defined?('Google::Apis')
+ Google::Apis.logger.level = Logger::INFO
+ end
end
# This should return an ActiveRecord::Relation suitable for calling #in_batches on
- def relation
+ def all_relation
+ raise NotImplementedError.new
+ end
+
+ # Should return true if the object is stored locally
+ def local?(_object)
raise NotImplementedError.new
end
@@ -59,6 +93,11 @@ module Gitlab
def actual_checksum(_object)
raise NotImplementedError.new
end
+
+ # Be sure to perform a hard check of the remote object (don't just check DB value)
+ def remote_object_exists?(object)
+ raise NotImplementedError.new
+ end
end
end
end
diff --git a/lib/gitlab/verify/job_artifacts.rb b/lib/gitlab/verify/job_artifacts.rb
index 03500a61074..dbadfbde9e3 100644
--- a/lib/gitlab/verify/job_artifacts.rb
+++ b/lib/gitlab/verify/job_artifacts.rb
@@ -11,10 +11,14 @@ module Gitlab
private
- def relation
+ def all_relation
::Ci::JobArtifact.all
end
+ def local?(artifact)
+ artifact.local_store?
+ end
+
def expected_checksum(artifact)
artifact.file_sha256
end
@@ -22,6 +26,10 @@ module Gitlab
def actual_checksum(artifact)
Digest::SHA256.file(artifact.file.path).hexdigest
end
+
+ def remote_object_exists?(artifact)
+ artifact.file.file.exists?
+ end
end
end
end
diff --git a/lib/gitlab/verify/lfs_objects.rb b/lib/gitlab/verify/lfs_objects.rb
index 970e2a7b718..d3f58a73ac7 100644
--- a/lib/gitlab/verify/lfs_objects.rb
+++ b/lib/gitlab/verify/lfs_objects.rb
@@ -11,8 +11,12 @@ module Gitlab
private
- def relation
- LfsObject.with_files_stored_locally
+ def all_relation
+ LfsObject.all
+ end
+
+ def local?(lfs_object)
+ lfs_object.local_store?
end
def expected_checksum(lfs_object)
@@ -22,6 +26,10 @@ module Gitlab
def actual_checksum(lfs_object)
LfsObject.calculate_oid(lfs_object.file.path)
end
+
+ def remote_object_exists?(lfs_object)
+ lfs_object.file.file.exists?
+ end
end
end
end
diff --git a/lib/gitlab/verify/rake_task.rb b/lib/gitlab/verify/rake_task.rb
index dd138e6b92b..e190eaddc79 100644
--- a/lib/gitlab/verify/rake_task.rb
+++ b/lib/gitlab/verify/rake_task.rb
@@ -45,7 +45,7 @@ module Gitlab
return unless verbose?
failures.each do |object, error|
- say " - #{verifier.describe(object)}: #{error.inspect}".color(:red)
+ say " - #{verifier.describe(object)}: #{error}".color(:red)
end
end
end
diff --git a/lib/gitlab/verify/uploads.rb b/lib/gitlab/verify/uploads.rb
index 0ffa71a6d72..01f09ab8df7 100644
--- a/lib/gitlab/verify/uploads.rb
+++ b/lib/gitlab/verify/uploads.rb
@@ -11,8 +11,12 @@ module Gitlab
private
- def relation
- Upload.with_files_stored_locally
+ def all_relation
+ Upload.all
+ end
+
+ def local?(upload)
+ upload.local?
end
def expected_checksum(upload)
@@ -22,6 +26,10 @@ module Gitlab
def actual_checksum(upload)
Upload.hexdigest(upload.absolute_path)
end
+
+ def remote_object_exists?(upload)
+ upload.build_uploader.file.exists?
+ end
end
end
end
diff --git a/lib/peek/rblineprof/custom_controller_helpers.rb b/lib/peek/rblineprof/custom_controller_helpers.rb
index da24a36603e..9beb442bfa3 100644
--- a/lib/peek/rblineprof/custom_controller_helpers.rb
+++ b/lib/peek/rblineprof/custom_controller_helpers.rb
@@ -41,7 +41,7 @@ module Peek
]
end.sort_by{ |a,b,c,d,e,f| -f }
- output = "<div class='modal-dialog modal-lg'><div class='modal-content'>"
+ output = "<div class='modal-dialog modal-xl'><div class='modal-content'>"
output << "<div class='modal-header'>"
output << "<h4>Line profiling: #{human_description(params[:lineprofiler])}</h4>"
output << "<button class='close' type='button' data-dismiss='modal' aria-label='close'><span aria-hidden='true'>&times;</span></button>"
diff --git a/lib/system_check/orphans/repository_check.rb b/lib/system_check/orphans/repository_check.rb
index 5ef0b93ad08..2695c658874 100644
--- a/lib/system_check/orphans/repository_check.rb
+++ b/lib/system_check/orphans/repository_check.rb
@@ -5,16 +5,18 @@ module SystemCheck
attr_accessor :orphans
def multi_check
- Gitlab.config.repositories.storages.each do |storage_name, repository_storage|
- storage_path = repository_storage.legacy_disk_path
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ Gitlab.config.repositories.storages.each do |storage_name, repository_storage|
+ storage_path = repository_storage.legacy_disk_path
- $stdout.puts
- $stdout.puts "* Storage: #{storage_name} (#{storage_path})".color(:yellow)
+ $stdout.puts
+ $stdout.puts "* Storage: #{storage_name} (#{storage_path})".color(:yellow)
- repositories = disk_repositories(storage_path)
- orphans = (repositories - fetch_repositories(storage_name))
+ repositories = disk_repositories(storage_path)
+ orphans = (repositories - fetch_repositories(storage_name))
- print_orphans(orphans, storage_name)
+ print_orphans(orphans, storage_name)
+ end
end
end
diff --git a/lib/system_check/simple_executor.rb b/lib/system_check/simple_executor.rb
index d268f501b4a..99c9e984107 100644
--- a/lib/system_check/simple_executor.rb
+++ b/lib/system_check/simple_executor.rb
@@ -43,7 +43,7 @@ module SystemCheck
#
# @param [SystemCheck::BaseCheck] check_klass
def run_check(check_klass)
- $stdout.print "#{check_klass.display_name} ... "
+ print_display_name(check_klass)
check = check_klass.new
@@ -60,18 +60,18 @@ module SystemCheck
end
if check.check?
- $stdout.puts check_klass.check_pass.color(:green)
+ print_check_pass(check_klass)
else
- $stdout.puts check_klass.check_fail.color(:red)
+ print_check_failure(check_klass)
if check.can_repair?
$stdout.print 'Trying to fix error automatically. ...'
if check.repair!
- $stdout.puts 'Success'.color(:green)
+ print_success
return
else
- $stdout.puts 'Failed'.color(:red)
+ print_failure
end
end
@@ -83,6 +83,26 @@ module SystemCheck
private
+ def print_display_name(check_klass)
+ $stdout.print "#{check_klass.display_name} ... "
+ end
+
+ def print_check_pass(check_klass)
+ $stdout.puts check_klass.check_pass.color(:green)
+ end
+
+ def print_check_failure(check_klass)
+ $stdout.puts check_klass.check_fail.color(:red)
+ end
+
+ def print_success
+ $stdout.puts 'Success'.color(:green)
+ end
+
+ def print_failure
+ $stdout.puts 'Failed'.color(:red)
+ end
+
# Prints header content for the series of checks to be executed for this component
#
# @param [String] component name of the component relative to the checks being executed
diff --git a/lib/tasks/migrate/setup_postgresql.rake b/lib/tasks/migrate/setup_postgresql.rake
index e7aab50e42a..f69d204c579 100644
--- a/lib/tasks/migrate/setup_postgresql.rake
+++ b/lib/tasks/migrate/setup_postgresql.rake
@@ -22,3 +22,18 @@ task setup_postgresql: :environment do
ProjectNameLowerIndex.new.up
AddPathIndexToRedirectRoutes.new.up
end
+
+desc 'GitLab | Generate PostgreSQL Password Hash'
+task :postgresql_md5_hash do
+ require 'digest'
+ username = ENV.fetch('USERNAME') do |missing|
+ puts "You must provide an username with '#{missing}' ENV variable"
+ exit(1)
+ end
+ password = ENV.fetch('PASSWORD') do |missing|
+ puts "You must provide a password with '#{missing}' ENV variable"
+ exit(1)
+ end
+ hash = Digest::MD5.hexdigest("#{password}#{username}")
+ puts "The MD5 hash of your database password for user: #{username} -> #{hash}"
+end
diff --git a/package.json b/package.json
index 4e1ca905ee8..6a52af10dc3 100644
--- a/package.json
+++ b/package.json
@@ -130,7 +130,7 @@
"karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "3.0.0",
"nodemon": "^1.17.3",
- "prettier": "1.11.1",
+ "prettier": "1.12.1",
"webpack-dev-server": "^3.1.4"
}
}
diff --git a/qa/qa/runtime/browser.rb b/qa/qa/runtime/browser.rb
index a12d95683af..ecd273c6db8 100644
--- a/qa/qa/runtime/browser.rb
+++ b/qa/qa/runtime/browser.rb
@@ -102,19 +102,7 @@ module QA
def perform(&block)
visit(url)
- yield if block_given?
- rescue
- raise if block.nil?
-
- # RSpec examples will take care of screenshots on their own
- #
- unless block.binding.receiver.is_a?(RSpec::Core::ExampleGroup)
- screenshot_and_save_page
- end
-
- raise
- ensure
- clear! if block_given?
+ yield.tap { clear! } if block_given?
end
##
diff --git a/scripts/frontend/prettier.js b/scripts/frontend/prettier.js
index 39de77bc333..6e4e36b9b2d 100644
--- a/scripts/frontend/prettier.js
+++ b/scripts/frontend/prettier.js
@@ -9,6 +9,8 @@ const getStagedFiles = require('./frontend_script_utils').getStagedFiles;
const mode = process.argv[2] || 'check';
const shouldSave = mode === 'save' || mode === 'save-all';
const allFiles = mode === 'check-all' || mode === 'save-all';
+let dirPath = process.argv[3] || '';
+if (dirPath && dirPath.charAt(dirPath.length - 1) !== '/') dirPath += '/';
const config = {
patterns: ['**/*.js', '**/*.vue', '**/*.scss'],
@@ -39,9 +41,10 @@ prettierIgnore.add(
const availableExtensions = Object.keys(config.parsers);
-console.log(`Loading ${allFiles ? 'All' : 'Staged'} Files ...`);
+console.log(`Loading ${allFiles ? 'All' : 'Selected'} Files ...`);
-const stagedFiles = allFiles ? null : getStagedFiles(availableExtensions.map(ext => `*.${ext}`));
+const stagedFiles =
+ allFiles || dirPath ? null : getStagedFiles(availableExtensions.map(ext => `*.${ext}`));
if (stagedFiles) {
if (!stagedFiles.length || (stagedFiles.length === 1 && !stagedFiles[0])) {
@@ -60,6 +63,13 @@ if (allFiles) {
const patterns = config.patterns;
const globPattern = patterns.length > 1 ? `{${patterns.join(',')}}` : `${patterns.join(',')}`;
files = glob.sync(globPattern, { ignore }).filter(f => allFiles || stagedFiles.includes(f));
+} else if (dirPath) {
+ const ignore = config.ignore;
+ const patterns = config.patterns.map(item => {
+ return dirPath + item;
+ });
+ const globPattern = patterns.length > 1 ? `{${patterns.join(',')}}` : `${patterns.join(',')}`;
+ files = glob.sync(globPattern, { ignore });
} else {
files = stagedFiles.filter(f => availableExtensions.includes(f.split('.').pop()));
}
@@ -73,12 +83,11 @@ if (!files.length) {
console.log(`${shouldSave ? 'Updating' : 'Checking'} ${files.length} file(s)`);
-prettier
- .resolveConfig('.')
- .then(options => {
- console.log('Found options : ', options);
- files.forEach(file => {
- try {
+files.forEach(file => {
+ try {
+ prettier
+ .resolveConfig(file)
+ .then(options => {
const fileExtension = file.split('.').pop();
Object.assign(options, {
parser: config.parsers[fileExtension],
@@ -101,17 +110,17 @@ prettier
}
console.log(`Prettify Manually : ${file}`);
}
- } catch (error) {
- didError = true;
- console.log(`\n\nError with ${file}: ${error.message}`);
- }
- });
-
- if (didWarn || didError) {
- process.exit(1);
- }
- })
- .catch(e => {
- console.log(`Error on loading the Config File: ${e.message}`);
- process.exit(1);
- });
+ })
+ .catch(e => {
+ console.log(`Error on loading the Config File: ${e.message}`);
+ process.exit(1);
+ });
+ } catch (error) {
+ didError = true;
+ console.log(`\n\nError with ${file}: ${error.message}`);
+ }
+});
+
+if (didWarn || didError) {
+ process.exit(1);
+}
diff --git a/scripts/trigger-build b/scripts/trigger-build
new file mode 100755
index 00000000000..526f5164ede
--- /dev/null
+++ b/scripts/trigger-build
@@ -0,0 +1,181 @@
+#!/usr/bin/env ruby
+
+require 'net/http'
+require 'json'
+require 'cgi'
+
+module Trigger
+ OMNIBUS_PROJECT_PATH = 'gitlab-org/omnibus-gitlab'.freeze
+ CNG_PROJECT_PATH = 'gitlab-org/build/CNG'.freeze
+ TOKEN = ENV['BUILD_TRIGGER_TOKEN']
+
+ def self.ee?
+ ENV['CI_PROJECT_NAME'] == 'gitlab-ee' || File.exist?('CHANGELOG-EE.md')
+ end
+
+ class Omnibus
+ def initialize
+ @uri = URI("https://gitlab.com/api/v4/projects/#{CGI.escape(Trigger::OMNIBUS_PROJECT_PATH)}/trigger/pipeline")
+ @params = env_params.merge(file_params).merge(token: Trigger::TOKEN)
+ end
+
+ def invoke!
+ res = Net::HTTP.post_form(@uri, @params)
+ id = JSON.parse(res.body)['id']
+ project = Trigger::OMNIBUS_PROJECT_PATH
+
+ if id
+ puts "Triggered https://gitlab.com/#{project}/pipelines/#{id}"
+ puts "Waiting for downstream pipeline status"
+ else
+ raise "Trigger failed! The response from the trigger is: #{res.body}"
+ end
+
+ Trigger::Pipeline.new(project, id)
+ end
+
+ private
+
+ def env_params
+ {
+ "ref" => ENV["OMNIBUS_BRANCH"] || "master",
+ "variables[GITLAB_VERSION]" => ENV["CI_COMMIT_SHA"],
+ "variables[ALTERNATIVE_SOURCES]" => true,
+ "variables[ee]" => Trigger.ee? ? 'true' : 'false',
+ "variables[TRIGGERED_USER]" => ENV["GITLAB_USER_NAME"],
+ "variables[TRIGGER_SOURCE]" => "https://gitlab.com/gitlab-org/#{ENV['CI_PROJECT_NAME']}/-/jobs/#{ENV['CI_JOB_ID']}"
+ }
+ end
+
+ def file_params
+ Hash.new.tap do |params|
+ Dir.glob("*_VERSION").each do |version_file|
+ params["variables[#{version_file}]"] = File.read(version_file).strip
+ end
+ end
+ end
+ end
+
+ class CNG
+ def initialize
+ @uri = URI("https://gitlab.com/api/v4/projects/#{CGI.escape(Trigger::CNG_PROJECT_PATH)}/trigger/pipeline")
+ @ref_name = ENV['CI_COMMIT_REF_NAME']
+ @username = ENV['GITLAB_USER_NAME']
+ @project_name = ENV['CI_PROJECT_NAME']
+ @job_id = ENV['CI_JOB_ID']
+ @params = env_params.merge(file_params).merge(token: Trigger::TOKEN)
+ end
+
+ #
+ # Trigger a pipeline
+ #
+ def invoke!
+ res = Net::HTTP.post_form(@uri, @params)
+ id = JSON.parse(res.body)['id']
+ project = Trigger::CNG_PROJECT_PATH
+
+ if id
+ puts "Triggered https://gitlab.com/#{project}/pipelines/#{id}"
+ puts "Waiting for downstream pipeline status"
+ else
+ raise "Trigger failed! The response from the trigger is: #{res.body}"
+ end
+
+ Trigger::Pipeline.new(project, id)
+ end
+
+ private
+
+ def env_params
+ params = {
+ "ref" => ENV["CNG_BRANCH"] || "master",
+ "variables[TRIGGERED_USER]" => @username,
+ "variables[TRIGGER_SOURCE]" => "https://gitlab.com/gitlab-org/#{@project_name}/-/jobs/#{@job_id}"
+ }
+
+ if Trigger.ee?
+ params["variables[GITLAB_EE_VERSION]"] = @ref_name
+ params["variables[EE_PIPELINE]"] = 'true'
+ else
+ params["variables[GITLAB_CE_VERSION]"] = @ref_name
+ params["variables[CE_PIPELINE]"] = 'true'
+ end
+
+ params
+ end
+
+ # Read version files from all components
+ def file_params
+ Dir.glob("*_VERSION").each_with_object({}) do |version_file, params|
+ raw_version = File.read(version_file).strip
+ # if the version matches semver format, treat it as a tag and prepend `v`
+ version = if raw_version =~ Regexp.compile(/^\d+\.\d+\.\d+(-rc\d+)?(-ee)?$/)
+ "v#{raw_version}"
+ else
+ raw_version
+ end
+
+ params["variables[#{version_file}]"] = version
+ end
+ end
+ end
+
+ class Pipeline
+ INTERVAL = 60 # seconds
+ MAX_DURATION = 3600 * 3 # 3 hours
+
+ def initialize(project, id)
+ @start = Time.now.to_i
+ @uri = URI("https://gitlab.com/api/v4/projects/#{CGI.escape(project)}/pipelines/#{id}")
+ end
+
+ def wait!
+ loop do
+ raise "Pipeline timed out after waiting for #{duration} minutes!" if timeout?
+
+ case status
+ when :created, :pending, :running
+ print "."
+ sleep INTERVAL
+ when :success
+ puts "Pipeline succeeded in #{duration} minutes!"
+ break
+ else
+ raise "Pipeline did not succeed!"
+ end
+
+ STDOUT.flush
+ end
+ end
+
+ def timeout?
+ Time.now.to_i > (@start + MAX_DURATION)
+ end
+
+ def duration
+ (Time.now.to_i - @start) / 60
+ end
+
+ def status
+ req = Net::HTTP::Get.new(@uri)
+ req['PRIVATE-TOKEN'] = ENV['GITLAB_QA_ACCESS_TOKEN']
+
+ res = Net::HTTP.start(@uri.hostname, @uri.port, use_ssl: true) do |http|
+ http.request(req)
+ end
+
+ JSON.parse(res.body)['status'].to_s.to_sym
+ end
+ end
+end
+
+case ARGV[0]
+when 'omnibus'
+ Trigger::Omnibus.new.invoke!.wait!
+when 'cng'
+ Trigger::CNG.new.invoke!.wait!
+else
+ puts "Please provide a valid option:
+ omnibus - Triggers a pipeline that builds the omnibus-gitlab package
+ cng - Triggers a pipeline that builds images used by the GitLab helm chart"
+end
diff --git a/scripts/trigger-build-cloud-native b/scripts/trigger-build-cloud-native
deleted file mode 100755
index b6ca75a588d..00000000000
--- a/scripts/trigger-build-cloud-native
+++ /dev/null
@@ -1,61 +0,0 @@
-#!/usr/bin/env ruby
-
-require 'gitlab'
-
-#
-# Configure credentials to be used with gitlab gem
-#
-Gitlab.configure do |config|
- config.endpoint = 'https://gitlab.com/api/v4'
-end
-
-#
-# The remote project
-#
-GITLAB_CNG_REPO = 'gitlab-org/build/CNG'.freeze
-
-def ee?
- ENV['CI_PROJECT_NAME'] == 'gitlab-ee' || File.exist?('CHANGELOG-EE.md')
-end
-
-def read_file_version(filename)
- raw_version = File.read(filename).strip
-
- # if the version matches semver format, treat it as a tag and prepend `v`
- if raw_version =~ Regexp.compile(/^\d+\.\d+\.\d+(-rc\d+)?(-ee)?$/)
- "v#{raw_version}"
- else
- raw_version
- end
-end
-
-def params
- params = {
- 'GITLAB_SHELL_VERSION' => read_file_version('GITLAB_SHELL_VERSION'),
- 'GITALY_VERSION' => read_file_version('GITALY_SERVER_VERSION'),
- 'TRIGGERED_USER' => ENV['GITLAB_USER_NAME'],
- 'TRIGGER_SOURCE' => "https://gitlab.com/gitlab-org/#{ENV['CI_PROJECT_NAME']}/-/jobs/#{ENV['CI_JOB_ID']}"
- }
-
- if ee?
- params['EE_PIPELINE'] = 'true'
- params['GITLAB_EE_VERSION'] = ENV['CI_COMMIT_REF_NAME']
- else
- params['CE_PIPELINE'] = 'true'
- params['GITLAB_CE_VERSION'] = ENV['CI_COMMIT_REF_NAME']
- end
-
- params
-end
-
-#
-# Trigger a pipeline
-#
-def trigger_pipeline
- # Create the cross project pipeline using CI_JOB_TOKEN
- pipeline = Gitlab.run_trigger(GITLAB_CNG_REPO, ENV['CI_JOB_TOKEN'], 'master', params)
-
- puts "Triggered https://gitlab.com/#{GITLAB_CNG_REPO}/pipelines/#{pipeline.id}"
-end
-
-trigger_pipeline
diff --git a/scripts/trigger-build-omnibus b/scripts/trigger-build-omnibus
deleted file mode 100755
index 95f35b44f5a..00000000000
--- a/scripts/trigger-build-omnibus
+++ /dev/null
@@ -1,108 +0,0 @@
-#!/usr/bin/env ruby
-
-require 'net/http'
-require 'json'
-require 'cgi'
-
-module Omnibus
- PROJECT_PATH = 'gitlab-org/omnibus-gitlab'.freeze
-
- class Trigger
- TOKEN = ENV['BUILD_TRIGGER_TOKEN']
- TRIGGERER = ENV['CI_PROJECT_NAME']
-
- def initialize
- @uri = URI("https://gitlab.com/api/v4/projects/#{CGI.escape(Omnibus::PROJECT_PATH)}/trigger/pipeline")
- @params = env_params.merge(file_params).merge(token: TOKEN)
- end
-
- def invoke!
- res = Net::HTTP.post_form(@uri, @params)
- id = JSON.parse(res.body)['id']
-
- if id
- puts "Triggered https://gitlab.com/#{Omnibus::PROJECT_PATH}/pipelines/#{id}"
- puts "Waiting for downstream pipeline status"
- else
- raise "Trigger failed! The response from the trigger is: #{res.body}"
- end
-
- Omnibus::Pipeline.new(id)
- end
-
- private
-
- def ee?
- TRIGGERER == 'gitlab-ee' || File.exist?('CHANGELOG-EE.md')
- end
-
- def env_params
- {
- "ref" => ENV["OMNIBUS_BRANCH"] || "master",
- "variables[GITLAB_VERSION]" => ENV["CI_COMMIT_SHA"],
- "variables[ALTERNATIVE_SOURCES]" => true,
- "variables[ee]" => ee? ? 'true' : 'false',
- "variables[TRIGGERED_USER]" => ENV["GITLAB_USER_NAME"],
- "variables[TRIGGER_SOURCE]" => "https://gitlab.com/gitlab-org/#{ENV['CI_PROJECT_NAME']}/-/jobs/#{ENV['CI_JOB_ID']}"
- }
- end
-
- def file_params
- Hash.new.tap do |params|
- Dir.glob("*_VERSION").each do |version_file|
- params["variables[#{version_file}]"] = File.read(version_file).strip
- end
- end
- end
- end
-
- class Pipeline
- INTERVAL = 60 # seconds
- MAX_DURATION = 3600 * 3 # 3 hours
-
- def initialize(id)
- @start = Time.now.to_i
- @uri = URI("https://gitlab.com/api/v4/projects/#{CGI.escape(Omnibus::PROJECT_PATH)}/pipelines/#{id}")
- end
-
- def wait!
- loop do
- raise "Pipeline timed out after waiting for #{duration} minutes!" if timeout?
-
- case status
- when :created, :pending, :running
- print "."
- sleep INTERVAL
- when :success
- puts "Omnibus pipeline succeeded in #{duration} minutes!"
- break
- else
- raise "Omnibus pipeline did not succeed!"
- end
-
- STDOUT.flush
- end
- end
-
- def timeout?
- Time.now.to_i > (@start + MAX_DURATION)
- end
-
- def duration
- (Time.now.to_i - @start) / 60
- end
-
- def status
- req = Net::HTTP::Get.new(@uri)
- req['PRIVATE-TOKEN'] = ENV['GITLAB_QA_ACCESS_TOKEN']
-
- res = Net::HTTP.start(@uri.hostname, @uri.port, use_ssl: true) do |http|
- http.request(req)
- end
-
- JSON.parse(res.body)['status'].to_s.to_sym
- end
- end
-end
-
-Omnibus::Trigger.new.invoke!.wait!
diff --git a/spec/controllers/admin/application_settings_controller_spec.rb b/spec/controllers/admin/application_settings_controller_spec.rb
index b4fc2aa326f..9d10d725ff3 100644
--- a/spec/controllers/admin/application_settings_controller_spec.rb
+++ b/spec/controllers/admin/application_settings_controller_spec.rb
@@ -73,7 +73,7 @@ describe Admin::ApplicationSettingsController do
end
it 'updates the restricted_visibility_levels when empty array is passed' do
- put :update, application_setting: { restricted_visibility_levels: [] }
+ put :update, application_setting: { restricted_visibility_levels: [""] }
expect(response).to redirect_to(admin_application_settings_path)
expect(ApplicationSetting.current.restricted_visibility_levels).to be_empty
diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb
index 773bf25ed44..74f362fd7fc 100644
--- a/spec/controllers/application_controller_spec.rb
+++ b/spec/controllers/application_controller_spec.rb
@@ -458,6 +458,8 @@ describe ApplicationController do
end
context 'for sessionless users' do
+ render_views
+
before do
sign_out user
end
@@ -468,6 +470,14 @@ describe ApplicationController do
expect(response).to have_gitlab_http_status(403)
end
+ it 'renders the error message when the format was html' do
+ get :index,
+ private_token: create(:personal_access_token, user: user).token,
+ format: :html
+
+ expect(response.body).to have_content /accept the terms of service/i
+ end
+
it 'renders a 200 when the sessionless user accepted the terms' do
accept_terms(user)
@@ -502,7 +512,7 @@ describe ApplicationController do
context '422 errors' do
it 'logs a response with a string' do
- response = spy(ActionDispatch::Response, status: 422, body: 'Hello world', content_type: 'application/json')
+ response = spy(ActionDispatch::Response, status: 422, body: 'Hello world', content_type: 'application/json', cookies: {})
allow(controller).to receive(:response).and_return(response)
get :index
@@ -511,7 +521,7 @@ describe ApplicationController do
it 'logs a response with an array' do
body = ['I want', 'my hat back']
- response = spy(ActionDispatch::Response, status: 422, body: body, content_type: 'application/json')
+ response = spy(ActionDispatch::Response, status: 422, body: body, content_type: 'application/json', cookies: {})
allow(controller).to receive(:response).and_return(response)
get :index
@@ -519,7 +529,7 @@ describe ApplicationController do
end
it 'does not log a string with an empty body' do
- response = spy(ActionDispatch::Response, status: 422, body: nil, content_type: 'application/json')
+ response = spy(ActionDispatch::Response, status: 422, body: nil, content_type: 'application/json', cookies: {})
allow(controller).to receive(:response).and_return(response)
get :index
@@ -527,7 +537,7 @@ describe ApplicationController do
end
it 'does not log an HTML body' do
- response = spy(ActionDispatch::Response, status: 422, body: 'This is a test', content_type: 'application/html')
+ response = spy(ActionDispatch::Response, status: 422, body: 'This is a test', content_type: 'application/html', cookies: {})
allow(controller).to receive(:response).and_return(response)
get :index
diff --git a/spec/controllers/concerns/internal_redirect_spec.rb b/spec/controllers/concerns/internal_redirect_spec.rb
index a0ee13b2352..7e23b56356e 100644
--- a/spec/controllers/concerns/internal_redirect_spec.rb
+++ b/spec/controllers/concerns/internal_redirect_spec.rb
@@ -54,6 +54,31 @@ describe InternalRedirect do
end
end
+ describe '#sanitize_redirect' do
+ let(:valid_path) { '/hello/world?hello=world' }
+ let(:valid_url) { "http://test.host#{valid_path}" }
+
+ it 'returns `nil` for invalid paths' do
+ invalid_path = '//not/valid'
+
+ expect(controller.sanitize_redirect(invalid_path)).to eq nil
+ end
+
+ it 'returns `nil` for invalid urls' do
+ input = 'http://test.host:3000/invalid'
+
+ expect(controller.sanitize_redirect(input)).to eq nil
+ end
+
+ it 'returns input for valid paths' do
+ expect(controller.sanitize_redirect(valid_path)).to eq valid_path
+ end
+
+ it 'returns path for valid urls' do
+ expect(controller.sanitize_redirect(valid_url)).to eq valid_path
+ end
+ end
+
describe '#host_allowed?' do
it 'allows uris with the same host and port' do
expect(controller.host_allowed?(URI('http://test.host/test'))).to be(true)
diff --git a/spec/controllers/projects/blob_controller_spec.rb b/spec/controllers/projects/blob_controller_spec.rb
index 00a7df6ccc8..9e696e9cb29 100644
--- a/spec/controllers/projects/blob_controller_spec.rb
+++ b/spec/controllers/projects/blob_controller_spec.rb
@@ -55,6 +55,25 @@ describe Projects::BlobController do
expect(json_response).to have_key 'raw_path'
end
end
+
+ context "with viewer=none" do
+ let(:id) { 'master/README.md' }
+
+ before do
+ get(:show,
+ namespace_id: project.namespace,
+ project_id: project,
+ id: id,
+ format: :json,
+ viewer: 'none')
+ end
+
+ it do
+ expect(response).to be_ok
+ expect(json_response).not_to have_key 'html'
+ expect(json_response).to have_key 'raw_path'
+ end
+ end
end
context 'with tree path' do
diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb
index 5bd22ea803c..705b30f0130 100644
--- a/spec/controllers/projects_controller_spec.rb
+++ b/spec/controllers/projects_controller_spec.rb
@@ -296,16 +296,22 @@ describe ProjectsController do
shared_examples_for 'updating a project' do
context 'when only renaming a project path' do
it "sets the repository to the right path after a rename" do
- original_repository_path = project.repository.path
+ original_repository_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ project.repository.path
+ end
expect { update_project path: 'renamed_path' }
.to change { project.reload.path }
expect(project.path).to include 'renamed_path'
+ assign_repository_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ assigns(:repository).path
+ end
+
if project.hashed_storage?(:repository)
- expect(assigns(:repository).path).to eq(original_repository_path)
+ expect(assign_repository_path).to eq(original_repository_path)
else
- expect(assigns(:repository).path).to include(project.path)
+ expect(assign_repository_path).to include(project.path)
end
expect(response).to have_gitlab_http_status(302)
diff --git a/spec/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb
index 1df2c954893..eb94d395a9e 100644
--- a/spec/controllers/uploads_controller_spec.rb
+++ b/spec/controllers/uploads_controller_spec.rb
@@ -580,23 +580,6 @@ describe UploadsController do
expect(response).to have_gitlab_http_status(404)
end
end
-
- context 'has a valid filename on the version file' do
- it 'successfully returns the file' do
- get :show, model: 'appearance', mounted_as: 'favicon', id: appearance.id, filename: 'favicon_main_dk.png'
-
- expect(response).to have_gitlab_http_status(200)
- expect(response.header['Content-Disposition']).to end_with 'filename="favicon_main_dk.png"'
- end
- end
-
- context 'has an invalid filename on the version file' do
- it 'returns a 404' do
- get :show, model: 'appearance', mounted_as: 'favicon', id: appearance.id, filename: 'favicon_bogusversion_dk.png'
-
- expect(response).to have_gitlab_http_status(404)
- end
- end
end
end
end
diff --git a/spec/features/issues/filtered_search/dropdown_assignee_spec.rb b/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
index cbd0949c192..c8115db9212 100644
--- a/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
@@ -31,7 +31,7 @@ describe 'Dropdown assignee', :js do
describe 'behavior' do
it 'opens when the search bar has assignee:' do
- filtered_search.set('assignee:')
+ input_filtered_search('assignee:', submit: false, extra_space: false)
expect(page).to have_css(js_dropdown_assignee, visible: true)
end
@@ -44,6 +44,7 @@ describe 'Dropdown assignee', :js do
it 'should show loading indicator when opened' do
slow_requests do
+ # We aren't using `input_filtered_search` because we want to see the loading indicator
filtered_search.set('assignee:')
expect(page).to have_css('#js-dropdown-assignee .filter-dropdown-loading', visible: true)
@@ -51,19 +52,19 @@ describe 'Dropdown assignee', :js do
end
it 'should hide loading indicator when loaded' do
- filtered_search.set('assignee:')
+ input_filtered_search('assignee:', submit: false, extra_space: false)
expect(find(js_dropdown_assignee)).not_to have_css('.filter-dropdown-loading')
end
it 'should load all the assignees when opened' do
- filtered_search.set('assignee:')
+ input_filtered_search('assignee:', submit: false, extra_space: false)
expect(dropdown_assignee_size).to eq(4)
end
it 'shows current user at top of dropdown' do
- filtered_search.set('assignee:')
+ input_filtered_search('assignee:', submit: false, extra_space: false)
expect(filter_dropdown.first('.filter-dropdown-item')).to have_content(user.name)
end
@@ -71,7 +72,7 @@ describe 'Dropdown assignee', :js do
describe 'filtering' do
before do
- filtered_search.set('assignee:')
+ input_filtered_search('assignee:', submit: false, extra_space: false)
expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user_john.name)
expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user_jacob.name)
@@ -79,23 +80,21 @@ describe 'Dropdown assignee', :js do
end
it 'filters by name' do
- filtered_search.send_keys('j')
+ input_filtered_search('jac', submit: false, extra_space: false)
- expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user_john.name)
expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user_jacob.name)
expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_no_content(user.name)
end
it 'filters by case insensitive name' do
- filtered_search.send_keys('J')
+ input_filtered_search('JAC', submit: false, extra_space: false)
- expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user_john.name)
expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user_jacob.name)
expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_no_content(user.name)
end
it 'filters by username with symbol' do
- filtered_search.send_keys('@ot')
+ input_filtered_search('@ott', submit: false, extra_space: false)
expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user_jacob.name)
expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user.name)
@@ -103,7 +102,7 @@ describe 'Dropdown assignee', :js do
end
it 'filters by case insensitive username with symbol' do
- filtered_search.send_keys('@OT')
+ input_filtered_search('@OTT', submit: false, extra_space: false)
expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user_jacob.name)
expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user.name)
@@ -111,7 +110,9 @@ describe 'Dropdown assignee', :js do
end
it 'filters by username without symbol' do
- filtered_search.send_keys('ot')
+ input_filtered_search('ott', submit: false, extra_space: false)
+
+ wait_for_requests
expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user_jacob.name)
expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user.name)
@@ -119,7 +120,9 @@ describe 'Dropdown assignee', :js do
end
it 'filters by case insensitive username without symbol' do
- filtered_search.send_keys('OT')
+ input_filtered_search('OTT', submit: false, extra_space: false)
+
+ wait_for_requests
expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user_jacob.name)
expect(find("#{js_dropdown_assignee} .filter-dropdown")).to have_content(user.name)
@@ -129,7 +132,7 @@ describe 'Dropdown assignee', :js do
describe 'selecting from dropdown' do
before do
- filtered_search.set('assignee:')
+ input_filtered_search('assignee:', submit: false, extra_space: false)
end
it 'fills in the assignee username when the assignee has not been filtered' do
@@ -143,7 +146,7 @@ describe 'Dropdown assignee', :js do
end
it 'fills in the assignee username when the assignee has been filtered' do
- filtered_search.send_keys('roo')
+ input_filtered_search('roo', submit: false, extra_space: false)
click_assignee(user.name)
wait_for_requests
@@ -165,7 +168,7 @@ describe 'Dropdown assignee', :js do
describe 'selecting from dropdown without Ajax call' do
before do
Gitlab::Testing::RequestBlockerMiddleware.block_requests!
- filtered_search.set('assignee:')
+ input_filtered_search('assignee:', submit: false, extra_space: false)
end
after do
@@ -183,31 +186,31 @@ describe 'Dropdown assignee', :js do
describe 'input has existing content' do
it 'opens assignee dropdown with existing search term' do
- filtered_search.set('searchTerm assignee:')
+ input_filtered_search('searchTerm assignee:', submit: false, extra_space: false)
expect(page).to have_css(js_dropdown_assignee, visible: true)
end
it 'opens assignee dropdown with existing author' do
- filtered_search.set('author:@user assignee:')
+ input_filtered_search('author:@user assignee:', submit: false, extra_space: false)
expect(page).to have_css(js_dropdown_assignee, visible: true)
end
it 'opens assignee dropdown with existing label' do
- filtered_search.set('label:~bug assignee:')
+ input_filtered_search('label:~bug assignee:', submit: false, extra_space: false)
expect(page).to have_css(js_dropdown_assignee, visible: true)
end
it 'opens assignee dropdown with existing milestone' do
- filtered_search.set('milestone:%v1.0 assignee:')
+ input_filtered_search('milestone:%v1.0 assignee:', submit: false, extra_space: false)
expect(page).to have_css(js_dropdown_assignee, visible: true)
end
it 'opens assignee dropdown with existing my-reaction' do
- filtered_search.set('my-reaction:star assignee:')
+ input_filtered_search('my-reaction:star assignee:', submit: false, extra_space: false)
expect(page).to have_css(js_dropdown_assignee, visible: true)
end
@@ -215,8 +218,7 @@ describe 'Dropdown assignee', :js do
describe 'caching requests' do
it 'caches requests after the first load' do
- filtered_search.set('assignee')
- filtered_search.send_keys(':')
+ input_filtered_search('assignee:', submit: false, extra_space: false)
initial_size = dropdown_assignee_size
expect(initial_size).to be > 0
@@ -224,8 +226,7 @@ describe 'Dropdown assignee', :js do
new_user = create(:user)
project.add_master(new_user)
find('.filtered-search-box .clear-search').click
- filtered_search.set('assignee')
- filtered_search.send_keys(':')
+ input_filtered_search('assignee:', submit: false, extra_space: false)
expect(dropdown_assignee_size).to eq(initial_size)
end
diff --git a/spec/features/issues/user_uses_slash_commands_spec.rb b/spec/features/issues/user_uses_slash_commands_spec.rb
index dacca494755..17818beb947 100644
--- a/spec/features/issues/user_uses_slash_commands_spec.rb
+++ b/spec/features/issues/user_uses_slash_commands_spec.rb
@@ -226,7 +226,9 @@ feature 'Issues > User uses quick actions', :js do
it 'does not move the issue' do
add_note("/move #{project_unauthorized.full_path}")
- expect(page).not_to have_content 'Commands applied'
+ wait_for_requests
+
+ expect(page).to have_content 'Commands applied'
expect(issue.reload).to be_open
end
end
diff --git a/spec/features/labels_hierarchy_spec.rb b/spec/features/labels_hierarchy_spec.rb
index 4700ada1aae..5573148f8bc 100644
--- a/spec/features/labels_hierarchy_spec.rb
+++ b/spec/features/labels_hierarchy_spec.rb
@@ -34,7 +34,7 @@ feature 'Labels Hierarchy', :js, :nested_groups do
wait_for_requests
- expect(page).to have_selector('span.badge', text: label.title)
+ expect(page).to have_selector('.badge', text: label.title)
end
end
@@ -45,7 +45,7 @@ feature 'Labels Hierarchy', :js, :nested_groups do
wait_for_requests
- expect(page).not_to have_selector('span.badge', text: child_group_label.title)
+ expect(page).not_to have_selector('.badge', text: child_group_label.title)
end
end
diff --git a/spec/features/markdown/copy_as_gfm_spec.rb b/spec/features/markdown/copy_as_gfm_spec.rb
index 4d897f09b57..05228e27963 100644
--- a/spec/features/markdown/copy_as_gfm_spec.rb
+++ b/spec/features/markdown/copy_as_gfm_spec.rb
@@ -502,6 +502,13 @@ describe 'Copy as GFM', :js do
1. Numbered lists
GFM
+ # list item followed by an HR
+ <<-GFM.strip_heredoc,
+ - list item
+
+ -----
+ GFM
+
'# Heading',
'## Heading',
'### Heading',
@@ -515,8 +522,6 @@ describe 'Copy as GFM', :js do
'~~Strikethrough~~',
- '2^2',
-
'-----',
# table
diff --git a/spec/features/markdown/markdown_spec.rb b/spec/features/markdown/markdown_spec.rb
index c86ba8c50a5..cac8a5068ec 100644
--- a/spec/features/markdown/markdown_spec.rb
+++ b/spec/features/markdown/markdown_spec.rb
@@ -44,7 +44,7 @@ describe 'GitLab Markdown', :aggregate_failures do
# Shared behavior that all pipelines should exhibit
shared_examples 'all pipelines' do
- it 'includes Redcarpet extensions' do
+ it 'includes extensions' do
aggregate_failures 'does not parse emphasis inside of words' do
expect(doc.to_html).not_to match('foo<em>bar</em>baz')
end
@@ -72,10 +72,6 @@ describe 'GitLab Markdown', :aggregate_failures do
aggregate_failures 'parses strikethroughs' do
expect(doc).to have_selector(%{del:contains("and this text doesn't")})
end
-
- aggregate_failures 'parses superscript' do
- expect(doc).to have_selector('sup', count: 2)
- end
end
it 'includes SanitizationFilter' do
@@ -123,16 +119,24 @@ describe 'GitLab Markdown', :aggregate_failures do
expect(doc).to have_selector('details summary:contains("collapsible")')
end
- aggregate_failures 'permits style attribute in th elements' do
- expect(doc.at_css('th:contains("Header")')['style']).to eq 'text-align: center'
- expect(doc.at_css('th:contains("Row")')['style']).to eq 'text-align: right'
- expect(doc.at_css('th:contains("Example")')['style']).to eq 'text-align: left'
+ aggregate_failures 'permits align attribute in th elements' do
+ expect(doc.at_css('th:contains("Header")')['align']).to eq 'center'
+ expect(doc.at_css('th:contains("Row")')['align']).to eq 'right'
+ expect(doc.at_css('th:contains("Example")')['align']).to eq 'left'
end
- aggregate_failures 'permits style attribute in td elements' do
- expect(doc.at_css('td:contains("Foo")')['style']).to eq 'text-align: center'
- expect(doc.at_css('td:contains("Bar")')['style']).to eq 'text-align: right'
- expect(doc.at_css('td:contains("Baz")')['style']).to eq 'text-align: left'
+ aggregate_failures 'permits align attribute in td elements' do
+ expect(doc.at_css('td:contains("Foo")')['align']).to eq 'center'
+ expect(doc.at_css('td:contains("Bar")')['align']).to eq 'right'
+ expect(doc.at_css('td:contains("Baz")')['align']).to eq 'left'
+ end
+
+ aggregate_failures 'permits superscript elements' do
+ expect(doc).to have_selector('sup', count: 2)
+ end
+
+ aggregate_failures 'permits subscript elements' do
+ expect(doc).to have_selector('sub', count: 3)
end
aggregate_failures 'removes `rel` attribute from links' do
@@ -320,6 +324,31 @@ describe 'GitLab Markdown', :aggregate_failures do
end
end
+ context 'Redcarpet documents' do
+ before do
+ allow_any_instance_of(Banzai::Filter::MarkdownFilter).to receive(:engine).and_return('Redcarpet')
+ @html = markdown(@feat.raw_markdown)
+ end
+
+ it 'processes certain elements differently' do
+ aggregate_failures 'parses superscript' do
+ expect(doc).to have_selector('sup', count: 3)
+ end
+
+ aggregate_failures 'permits style attribute in th elements' do
+ expect(doc.at_css('th:contains("Header")')['style']).to eq 'text-align: center'
+ expect(doc.at_css('th:contains("Row")')['style']).to eq 'text-align: right'
+ expect(doc.at_css('th:contains("Example")')['style']).to eq 'text-align: left'
+ end
+
+ aggregate_failures 'permits style attribute in td elements' do
+ expect(doc.at_css('td:contains("Foo")')['style']).to eq 'text-align: center'
+ expect(doc.at_css('td:contains("Bar")')['style']).to eq 'text-align: right'
+ expect(doc.at_css('td:contains("Baz")')['style']).to eq 'text-align: left'
+ end
+ end
+ end
+
# Fake a `current_user` helper
def current_user
@feat.user
diff --git a/spec/features/projects/deploy_keys_spec.rb b/spec/features/projects/deploy_keys_spec.rb
index 43a23c42f83..1552a3512dd 100644
--- a/spec/features/projects/deploy_keys_spec.rb
+++ b/spec/features/projects/deploy_keys_spec.rb
@@ -22,7 +22,8 @@ describe 'Project deploy keys', :js do
accept_confirm { find('.ic-remove').click() }
- expect(page).not_to have_selector('.fa-spinner', count: 0)
+ wait_for_requests
+
expect(page).to have_selector('.deploy-key', count: 0)
end
end
diff --git a/spec/features/projects/diffs/diff_show_spec.rb b/spec/features/projects/diffs/diff_show_spec.rb
index c1307ab640f..9bfcb1e816a 100644
--- a/spec/features/projects/diffs/diff_show_spec.rb
+++ b/spec/features/projects/diffs/diff_show_spec.rb
@@ -166,8 +166,7 @@ feature 'Diff file viewer', :js do
context 'expanding the diff' do
before do
- # We can't use `click_link` because the "link" doesn't have an `href`.
- find('a.click-to-expand').click
+ click_button 'Click to expand it.'
wait_for_requests
end
diff --git a/spec/features/projects/issues/user_comments_on_issue_spec.rb b/spec/features/projects/issues/user_comments_on_issue_spec.rb
index c45fdc7642f..353f487485d 100644
--- a/spec/features/projects/issues/user_comments_on_issue_spec.rb
+++ b/spec/features/projects/issues/user_comments_on_issue_spec.rb
@@ -31,11 +31,14 @@ describe "User comments on issue", :js do
end
it "adds comment with code block" do
- comment = "```\nCommand [1]: /usr/local/bin/git , see [text](doc/text)\n```"
+ code_block_content = "Command [1]: /usr/local/bin/git , see [text](doc/text)"
+ comment = "```\n#{code_block_content}\n```"
add_note(comment)
- expect(page).to have_content(comment)
+ wait_for_requests
+
+ expect(page.find('pre code').text).to eq code_block_content
end
end
diff --git a/spec/features/task_lists_spec.rb b/spec/features/task_lists_spec.rb
index 2dc3c5e3927..f37d8998045 100644
--- a/spec/features/task_lists_spec.rb
+++ b/spec/features/task_lists_spec.rb
@@ -36,7 +36,7 @@ feature 'Task Lists' do
MARKDOWN
end
- let(:nested_tasks_markdown) do
+ let(:nested_tasks_markdown_redcarpet) do
<<-EOT.strip_heredoc
- [ ] Task a
- [x] Task a.1
@@ -49,6 +49,19 @@ feature 'Task Lists' do
EOT
end
+ let(:nested_tasks_markdown) do
+ <<-EOT.strip_heredoc
+ - [ ] Task a
+ - [x] Task a.1
+ - [ ] Task a.2
+ - [ ] Task b
+
+ 1. [ ] Task 1
+ 1. [ ] Task 1.1
+ 1. [x] Task 1.2
+ EOT
+ end
+
before do
Warden.test_mode!
@@ -141,13 +154,11 @@ feature 'Task Lists' do
end
end
- describe 'nested tasks', :js do
- let(:issue) { create(:issue, description: nested_tasks_markdown, author: user, project: project) }
-
+ shared_examples 'shared nested tasks' do
before do
+ allow(Banzai::Filter::MarkdownFilter).to receive(:engine).and_return('Redcarpet')
visit_issue(project, issue)
end
-
it 'renders' do
expect(page).to have_selector('ul.task-list', count: 2)
expect(page).to have_selector('li.task-list-item', count: 7)
@@ -171,6 +182,30 @@ feature 'Task Lists' do
expect(page).to have_content('marked the task Task 1.1 as complete')
end
end
+
+ describe 'nested tasks', :js do
+ context 'with Redcarpet' do
+ let(:issue) { create(:issue, description: nested_tasks_markdown_redcarpet, author: user, project: project) }
+
+ before do
+ allow_any_instance_of(Banzai::Filter::MarkdownFilter).to receive(:engine).and_return('Redcarpet')
+ visit_issue(project, issue)
+ end
+
+ it_behaves_like 'shared nested tasks'
+ end
+
+ context 'with CommonMark' do
+ let(:issue) { create(:issue, description: nested_tasks_markdown, author: user, project: project) }
+
+ before do
+ allow_any_instance_of(Banzai::Filter::MarkdownFilter).to receive(:engine).and_return('CommonMark')
+ visit_issue(project, issue)
+ end
+
+ it_behaves_like 'shared nested tasks'
+ end
+ end
end
describe 'for Notes' do
diff --git a/spec/fixtures/markdown.md.erb b/spec/fixtures/markdown.md.erb
index da32a46675f..e5d01c3bd03 100644
--- a/spec/fixtures/markdown.md.erb
+++ b/spec/fixtures/markdown.md.erb
@@ -43,8 +43,14 @@ This text says this, ~~and this text doesn't~~.
### Superscript
-This is my 1^(st) time using superscript in Markdown. Now this is my
-2^(nd).
+This is my 1<sup>(st)</sup> time using superscript in Markdown. Now this is my
+2<sup>(nd)</sup>.
+
+Redcarpet supports this superscript syntax ( x^2 ).
+
+### Subscript
+
+This (C<sub>6</sub>H<sub>12</sub>O<sub>6</sub>) is an example of subscripts in Markdown.
### Next step
diff --git a/spec/graphql/resolvers/merge_request_resolver_spec.rb b/spec/graphql/resolvers/merge_request_resolver_spec.rb
index af015533209..73993b3a039 100644
--- a/spec/graphql/resolvers/merge_request_resolver_spec.rb
+++ b/spec/graphql/resolvers/merge_request_resolver_spec.rb
@@ -10,49 +10,36 @@ describe Resolvers::MergeRequestResolver do
set(:other_project) { create(:project, :repository) }
set(:other_merge_request) { create(:merge_request, source_project: other_project, target_project: other_project) }
- let(:full_path) { project.full_path }
let(:iid_1) { merge_request_1.iid }
let(:iid_2) { merge_request_2.iid }
- let(:other_full_path) { other_project.full_path }
let(:other_iid) { other_merge_request.iid }
describe '#resolve' do
it 'batch-resolves merge requests by target project full path and IID' do
- path = full_path # avoid database query
-
result = batch(max_queries: 2) do
- [resolve_mr(path, iid_1), resolve_mr(path, iid_2)]
+ [resolve_mr(project, iid_1), resolve_mr(project, iid_2)]
end
expect(result).to contain_exactly(merge_request_1, merge_request_2)
end
it 'can batch-resolve merge requests from different projects' do
- path = project.full_path # avoid database queries
- other_path = other_full_path
-
result = batch(max_queries: 3) do
- [resolve_mr(path, iid_1), resolve_mr(path, iid_2), resolve_mr(other_path, other_iid)]
+ [resolve_mr(project, iid_1), resolve_mr(project, iid_2), resolve_mr(other_project, other_iid)]
end
expect(result).to contain_exactly(merge_request_1, merge_request_2, other_merge_request)
end
it 'resolves an unknown iid to nil' do
- result = batch { resolve_mr(full_path, -1) }
-
- expect(result).to be_nil
- end
-
- it 'resolves a known iid for an unknown full_path to nil' do
- result = batch { resolve_mr('unknown/project', iid_1) }
+ result = batch { resolve_mr(project, -1) }
expect(result).to be_nil
end
end
- def resolve_mr(full_path, iid)
- resolve(described_class, args: { full_path: full_path, iid: iid })
+ def resolve_mr(project, iid)
+ resolve(described_class, obj: project, args: { iid: iid })
end
end
diff --git a/spec/graphql/types/project_type_spec.rb b/spec/graphql/types/project_type_spec.rb
index e0f89105b86..b4eeca2e3f1 100644
--- a/spec/graphql/types/project_type_spec.rb
+++ b/spec/graphql/types/project_type_spec.rb
@@ -2,4 +2,13 @@ require 'spec_helper'
describe GitlabSchema.types['Project'] do
it { expect(described_class.graphql_name).to eq('Project') }
+
+ describe 'nested merge request' do
+ it { expect(described_class).to have_graphql_field(:merge_request) }
+
+ it 'authorizes the merge request' do
+ expect(described_class.fields['mergeRequest'])
+ .to require_graphql_authorizations(:read_merge_request)
+ end
+ end
end
diff --git a/spec/graphql/types/query_type_spec.rb b/spec/graphql/types/query_type_spec.rb
index 8488252fd59..e1df6f9811d 100644
--- a/spec/graphql/types/query_type_spec.rb
+++ b/spec/graphql/types/query_type_spec.rb
@@ -5,7 +5,7 @@ describe GitlabSchema.types['Query'] do
expect(described_class.graphql_name).to eq('Query')
end
- it { is_expected.to have_graphql_fields(:project, :merge_request, :echo) }
+ it { is_expected.to have_graphql_fields(:project, :echo) }
describe 'project field' do
subject { described_class.fields['project'] }
@@ -20,18 +20,4 @@ describe GitlabSchema.types['Query'] do
is_expected.to require_graphql_authorizations(:read_project)
end
end
-
- describe 'merge_request field' do
- subject { described_class.fields['mergeRequest'] }
-
- it 'finds MRs by project and IID' do
- is_expected.to have_graphql_arguments(:full_path, :iid)
- is_expected.to have_graphql_type(Types::MergeRequestType)
- is_expected.to have_graphql_resolver(Resolvers::MergeRequestResolver)
- end
-
- it 'authorizes with read_merge_request' do
- is_expected.to require_graphql_authorizations(:read_merge_request)
- end
- end
end
diff --git a/spec/helpers/markup_helper_spec.rb b/spec/helpers/markup_helper_spec.rb
index c0dc9293397..1a720aae55c 100644
--- a/spec/helpers/markup_helper_spec.rb
+++ b/spec/helpers/markup_helper_spec.rb
@@ -298,7 +298,7 @@ describe MarkupHelper do
it 'preserves code color scheme' do
object = create_object("```ruby\ndef test\n 'hello world'\nend\n```")
- expected = "\n<pre class=\"code highlight js-syntax-highlight ruby\">" \
+ expected = "<pre class=\"code highlight js-syntax-highlight ruby\">" \
"<code><span class=\"line\"><span class=\"k\">def</span> <span class=\"nf\">test</span>...</span>\n" \
"</code></pre>"
diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
index d372e58f63d..5cf9e9e8f12 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -90,6 +90,10 @@ describe ProjectsHelper do
expect(helper.project_list_cache_key(project)).to include(project.cache_key)
end
+ it "includes the last activity date" do
+ expect(helper.project_list_cache_key(project)).to include(project.last_activity_date)
+ end
+
it "includes the controller name" do
expect(helper.controller).to receive(:controller_name).and_return("testcontroller")
@@ -276,7 +280,11 @@ describe ProjectsHelper do
describe '#sanitizerepo_repo_path' do
let(:project) { create(:project, :repository) }
- let(:storage_path) { Gitlab.config.repositories.storages.default.legacy_disk_path }
+ let(:storage_path) do
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ Gitlab.config.repositories.storages.default.legacy_disk_path
+ end
+ end
before do
allow(Settings.shared).to receive(:[]).with('path').and_return('/base/repo/export/path')
diff --git a/spec/javascripts/blob/viewer/index_spec.js b/spec/javascripts/blob/viewer/index_spec.js
index f920c4ca945..8b79624d9f4 100644
--- a/spec/javascripts/blob/viewer/index_spec.js
+++ b/spec/javascripts/blob/viewer/index_spec.js
@@ -32,7 +32,7 @@ describe('Blob viewer', () => {
afterEach(() => {
mock.restore();
- location.hash = '';
+ window.location.hash = '';
});
it('loads source file after switching views', (done) => {
@@ -49,7 +49,7 @@ describe('Blob viewer', () => {
});
it('loads source file when line number is in hash', (done) => {
- location.hash = '#L1';
+ window.location.hash = '#L1';
new BlobViewer();
diff --git a/spec/javascripts/bootstrap_linked_tabs_spec.js b/spec/javascripts/bootstrap_linked_tabs_spec.js
index 93dc60d59fe..6f679369289 100644
--- a/spec/javascripts/bootstrap_linked_tabs_spec.js
+++ b/spec/javascripts/bootstrap_linked_tabs_spec.js
@@ -36,7 +36,7 @@ import LinkedTabs from '~/lib/utils/bootstrap_linked_tabs';
describe('on click', () => {
it('should change the url according to the clicked tab', () => {
- const historySpy = spyOn(history, 'replaceState').and.callFake(() => {});
+ const historySpy = spyOn(window.history, 'replaceState').and.callFake(() => {});
const linkedTabs = new LinkedTabs({
action: 'show',
diff --git a/spec/javascripts/commits_spec.js b/spec/javascripts/commits_spec.js
index 60d100e8544..28b89157bd3 100644
--- a/spec/javascripts/commits_spec.js
+++ b/spec/javascripts/commits_spec.js
@@ -56,7 +56,7 @@ describe('Commits List', () => {
beforeEach(() => {
commitsList.searchField.val('');
- spyOn(history, 'replaceState').and.stub();
+ spyOn(window.history, 'replaceState').and.stub();
mock = new MockAdapter(axios);
mock.onGet('/h5bp/html5-boilerplate/commits/master').reply(200, {
diff --git a/spec/javascripts/create_merge_request_dropdown_spec.js b/spec/javascripts/create_merge_request_dropdown_spec.js
new file mode 100644
index 00000000000..b229765a8c5
--- /dev/null
+++ b/spec/javascripts/create_merge_request_dropdown_spec.js
@@ -0,0 +1,67 @@
+import axios from '~/lib/utils/axios_utils';
+import MockAdapter from 'axios-mock-adapter';
+import CreateMergeRequestDropdown from '~/create_merge_request_dropdown';
+import { TEST_HOST } from 'spec/test_constants';
+
+describe('CreateMergeRequestDropdown', () => {
+ let axiosMock;
+ let dropdown;
+
+ beforeEach(() => {
+ axiosMock = new MockAdapter(axios);
+
+ setFixtures(`
+ <div id="dummy-wrapper-element">
+ <div class="available"></div>
+ <div class="unavailable">
+ <div class="fa"></div>
+ <div class="text"></div>
+ </div>
+ <div class="js-ref"></div>
+ <div class="js-create-merge-request"></div>
+ <div class="js-create-target"></div>
+ <div class="js-dropdown-toggle"></div>
+ </div>
+ `);
+
+ const dummyElement = document.getElementById('dummy-wrapper-element');
+ dropdown = new CreateMergeRequestDropdown(dummyElement);
+ dropdown.refsPath = `${TEST_HOST}/dummy/refs?search=`;
+ });
+
+ afterEach(() => {
+ axiosMock.restore();
+ });
+
+ describe('getRef', () => {
+ it('escapes branch names correctly', done => {
+ const endpoint = `${dropdown.refsPath}contains%23hash`;
+ spyOn(axios, 'get').and.callThrough();
+ axiosMock.onGet(endpoint).replyOnce({});
+
+ dropdown
+ .getRef('contains#hash')
+ .then(() => {
+ expect(axios.get).toHaveBeenCalledWith(endpoint);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+
+ describe('updateCreatePaths', () => {
+ it('escapes branch names correctly', () => {
+ dropdown.createBranchPath = `${TEST_HOST}/branches?branch_name=some-branch&issue=42`;
+ dropdown.createMrPath = `${TEST_HOST}/create_merge_request?branch_name=some-branch&ref=master`;
+
+ dropdown.updateCreatePaths('branch', 'contains#hash');
+
+ expect(dropdown.createBranchPath).toBe(
+ `${TEST_HOST}/branches?branch_name=contains%23hash&issue=42`,
+ );
+ expect(dropdown.createMrPath).toBe(
+ `${TEST_HOST}/create_merge_request?branch_name=contains%23hash&ref=master`,
+ );
+ });
+ });
+});
diff --git a/spec/javascripts/environments/environments_app_spec.js b/spec/javascripts/environments/environments_app_spec.js
index 615168645b4..6968fbc7ce7 100644
--- a/spec/javascripts/environments/environments_app_spec.js
+++ b/spec/javascripts/environments/environments_app_spec.js
@@ -220,7 +220,7 @@ describe('Environment', () => {
);
component = mountComponent(EnvironmentsComponent, mockData);
- spyOn(history, 'pushState').and.stub();
+ spyOn(window.history, 'pushState').and.stub();
});
describe('updateContent', () => {
diff --git a/spec/javascripts/environments/folder/environments_folder_view_spec.js b/spec/javascripts/environments/folder/environments_folder_view_spec.js
index f5ce4df0bfe..51d4213c38f 100644
--- a/spec/javascripts/environments/folder/environments_folder_view_spec.js
+++ b/spec/javascripts/environments/folder/environments_folder_view_spec.js
@@ -177,7 +177,7 @@ describe('Environments Folder View', () => {
});
component = mountComponent(Component, mockData);
- spyOn(history, 'pushState').and.stub();
+ spyOn(window.history, 'pushState').and.stub();
});
describe('updateContent', () => {
diff --git a/spec/javascripts/ide/components/repo_editor_spec.js b/spec/javascripts/ide/components/repo_editor_spec.js
index d318521d0a0..2256deb7dac 100644
--- a/spec/javascripts/ide/components/repo_editor_spec.js
+++ b/spec/javascripts/ide/components/repo_editor_spec.js
@@ -315,6 +315,17 @@ describe('RepoEditor', () => {
done();
});
});
+
+ it('calls updateDimensions when rightPane is updated', done => {
+ vm.$store.state.rightPane = 'testing';
+
+ vm.$nextTick(() => {
+ expect(vm.editor.updateDimensions).toHaveBeenCalled();
+ expect(vm.editor.updateDiffView).toHaveBeenCalled();
+
+ done();
+ });
+ });
});
describe('show tabs', () => {
diff --git a/spec/javascripts/ide/stores/actions/file_spec.js b/spec/javascripts/ide/stores/actions/file_spec.js
index 7bebc2288e3..5746683917e 100644
--- a/spec/javascripts/ide/stores/actions/file_spec.js
+++ b/spec/javascripts/ide/stores/actions/file_spec.js
@@ -166,12 +166,12 @@ describe('IDE store file actions', () => {
});
it('resets location.hash for line highlighting', done => {
- location.hash = 'test';
+ window.location.hash = 'test';
store
.dispatch('setFileActive', localFile.path)
.then(() => {
- expect(location.hash).not.toBe('test');
+ expect(window.location.hash).not.toBe('test');
done();
})
diff --git a/spec/javascripts/ide/stores/modules/commit/actions_spec.js b/spec/javascripts/ide/stores/modules/commit/actions_spec.js
index a2869ff378b..133ad627f34 100644
--- a/spec/javascripts/ide/stores/modules/commit/actions_spec.js
+++ b/spec/javascripts/ide/stores/modules/commit/actions_spec.js
@@ -108,77 +108,6 @@ describe('IDE commit module actions', () => {
});
});
- describe('checkCommitStatus', () => {
- beforeEach(() => {
- store.state.currentProjectId = 'abcproject';
- store.state.currentBranchId = 'master';
- store.state.projects.abcproject = {
- branches: {
- master: {
- workingReference: '1',
- },
- },
- };
- });
-
- it('calls service', done => {
- spyOn(service, 'getBranchData').and.returnValue(
- Promise.resolve({
- data: {
- commit: { id: '123' },
- },
- }),
- );
-
- store
- .dispatch('commit/checkCommitStatus')
- .then(() => {
- expect(service.getBranchData).toHaveBeenCalledWith('abcproject', 'master');
-
- done();
- })
- .catch(done.fail);
- });
-
- it('returns true if current ref does not equal returned ID', done => {
- spyOn(service, 'getBranchData').and.returnValue(
- Promise.resolve({
- data: {
- commit: { id: '123' },
- },
- }),
- );
-
- store
- .dispatch('commit/checkCommitStatus')
- .then(val => {
- expect(val).toBeTruthy();
-
- done();
- })
- .catch(done.fail);
- });
-
- it('returns false if current ref equals returned ID', done => {
- spyOn(service, 'getBranchData').and.returnValue(
- Promise.resolve({
- data: {
- commit: { id: '1' },
- },
- }),
- );
-
- store
- .dispatch('commit/checkCommitStatus')
- .then(val => {
- expect(val).toBeFalsy();
-
- done();
- })
- .catch(done.fail);
- });
- });
-
describe('updateFilesAfterCommit', () => {
const data = {
id: '123',
@@ -314,6 +243,7 @@ describe('IDE commit module actions', () => {
...file('changed'),
type: 'blob',
active: true,
+ lastCommitSha: '123456789',
};
store.state.stagedFiles.push(f);
store.state.changedFiles = [
@@ -366,6 +296,7 @@ describe('IDE commit module actions', () => {
file_path: jasmine.anything(),
content: jasmine.anything(),
encoding: jasmine.anything(),
+ last_commit_id: undefined,
},
],
start_branch: 'master',
@@ -376,6 +307,32 @@ describe('IDE commit module actions', () => {
.catch(done.fail);
});
+ it('sends lastCommit ID when not creating new branch', done => {
+ store.state.commit.commitAction = '1';
+
+ store
+ .dispatch('commit/commitChanges')
+ .then(() => {
+ expect(service.commit).toHaveBeenCalledWith('abcproject', {
+ branch: jasmine.anything(),
+ commit_message: 'testing 123',
+ actions: [
+ {
+ action: 'update',
+ file_path: jasmine.anything(),
+ content: jasmine.anything(),
+ encoding: jasmine.anything(),
+ last_commit_id: '123456789',
+ },
+ ],
+ start_branch: undefined,
+ });
+
+ done();
+ })
+ .catch(done.fail);
+ });
+
it('sets last Commit Msg', done => {
store
.dispatch('commit/commitChanges')
diff --git a/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js b/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js
index 03ec08d05c3..fa4c18931e5 100644
--- a/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js
+++ b/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js
@@ -208,18 +208,19 @@ describe('IDE merge requests actions', () => {
expect(commit.calls.argsFor(1)).toEqual(['SET_CURRENT_MERGE_REQUEST', '1', { root: true }]);
expect(commit.calls.argsFor(2)).toEqual(['RESET_OPEN_FILES', null, { root: true }]);
- expect(dispatch.calls.argsFor(0)).toEqual([
- 'pipelines/resetLatestPipeline',
+ expect(dispatch.calls.argsFor(0)).toEqual(['setCurrentBranchId', '', { root: true }]);
+ expect(dispatch.calls.argsFor(1)).toEqual([
+ 'pipelines/stopPipelinePolling',
null,
{ root: true },
]);
- expect(dispatch.calls.argsFor(1)).toEqual(['setCurrentBranchId', '', { root: true }]);
- expect(dispatch.calls.argsFor(2)).toEqual([
- 'pipelines/stopPipelinePolling',
+ expect(dispatch.calls.argsFor(2)).toEqual(['setRightPane', null, { root: true }]);
+ expect(dispatch.calls.argsFor(3)).toEqual([
+ 'pipelines/resetLatestPipeline',
null,
{ root: true },
]);
- expect(dispatch.calls.argsFor(3)).toEqual([
+ expect(dispatch.calls.argsFor(4)).toEqual([
'pipelines/clearEtagPoll',
null,
{ root: true },
diff --git a/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js b/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js
index f2f8e780cd1..f47e69d6e5b 100644
--- a/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js
+++ b/spec/javascripts/ide/stores/modules/pipelines/actions_spec.js
@@ -18,6 +18,7 @@ import actions, {
receiveJobTraceError,
receiveJobTraceSuccess,
fetchJobTrace,
+ resetLatestPipeline,
} from '~/ide/stores/modules/pipelines/actions';
import state from '~/ide/stores/modules/pipelines/state';
import * as types from '~/ide/stores/modules/pipelines/mutation_types';
@@ -416,4 +417,20 @@ describe('IDE pipelines actions', () => {
});
});
});
+
+ describe('resetLatestPipeline', () => {
+ it('commits reset mutations', done => {
+ testAction(
+ resetLatestPipeline,
+ null,
+ mockedState,
+ [
+ { type: types.RECEIVE_LASTEST_PIPELINE_SUCCESS, payload: null },
+ { type: types.SET_DETAIL_JOB, payload: null },
+ ],
+ [],
+ done,
+ );
+ });
+ });
});
diff --git a/spec/javascripts/ide/stores/utils_spec.js b/spec/javascripts/ide/stores/utils_spec.js
index f38ac6dd82f..a7bd443af51 100644
--- a/spec/javascripts/ide/stores/utils_spec.js
+++ b/spec/javascripts/ide/stores/utils_spec.js
@@ -1,4 +1,5 @@
import * as utils from '~/ide/stores/utils';
+import { file } from '../helpers';
describe('Multi-file store utils', () => {
describe('setPageTitle', () => {
@@ -63,4 +64,59 @@ describe('Multi-file store utils', () => {
expect(foundEntry).toBeUndefined();
});
});
+
+ describe('createCommitPayload', () => {
+ it('returns API payload', () => {
+ const state = {
+ commitMessage: 'commit message',
+ };
+ const rootState = {
+ stagedFiles: [
+ {
+ ...file('staged'),
+ path: 'staged',
+ content: 'updated file content',
+ lastCommitSha: '123456789',
+ },
+ {
+ ...file('newFile'),
+ path: 'added',
+ tempFile: true,
+ content: 'new file content',
+ base64: true,
+ lastCommitSha: '123456789',
+ },
+ ],
+ currentBranchId: 'master',
+ };
+ const payload = utils.createCommitPayload({
+ branch: 'master',
+ newBranch: false,
+ state,
+ rootState,
+ });
+
+ expect(payload).toEqual({
+ branch: 'master',
+ commit_message: 'commit message',
+ actions: [
+ {
+ action: 'update',
+ file_path: 'staged',
+ content: 'updated file content',
+ encoding: 'text',
+ last_commit_id: '123456789',
+ },
+ {
+ action: 'create',
+ file_path: 'added',
+ content: 'new file content',
+ encoding: 'base64',
+ last_commit_id: '123456789',
+ },
+ ],
+ start_branch: undefined,
+ });
+ });
+ });
});
diff --git a/spec/javascripts/issue_show/components/app_spec.js b/spec/javascripts/issue_show/components/app_spec.js
index bf1f0c822fe..eb5e0bddb74 100644
--- a/spec/javascripts/issue_show/components/app_spec.js
+++ b/spec/javascripts/issue_show/components/app_spec.js
@@ -145,7 +145,7 @@ describe('Issuable output', () => {
resolve({
data: {
confidential: false,
- web_url: location.pathname,
+ web_url: window.location.pathname,
},
});
}));
@@ -177,7 +177,7 @@ describe('Issuable output', () => {
spyOn(vm.service, 'updateIssuable').and.callFake(() => new Promise((resolve) => {
resolve({
data: {
- web_url: location.pathname,
+ web_url: window.location.pathname,
confidential: vm.isConfidential,
},
});
diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js
index 2d7cc3443cf..a9ec7f42a9d 100644
--- a/spec/javascripts/lib/utils/common_utils_spec.js
+++ b/spec/javascripts/lib/utils/common_utils_spec.js
@@ -40,13 +40,13 @@ describe('common_utils', () => {
});
it('should decode params', () => {
- history.pushState('', '', '?label_name%5B%5D=test');
+ window.history.pushState('', '', '?label_name%5B%5D=test');
expect(
commonUtils.getUrlParamsArray()[0],
).toBe('label_name[]=test');
- history.pushState('', '', '?');
+ window.history.pushState('', '', '?');
});
});
diff --git a/spec/javascripts/notes_spec.js b/spec/javascripts/notes_spec.js
index 648fb3e9bd3..acbf23e2007 100644
--- a/spec/javascripts/notes_spec.js
+++ b/spec/javascripts/notes_spec.js
@@ -974,7 +974,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
).toBeFalsy();
expect(
$tempNoteHeader
- .find('.d-none.d-sm-block')
+ .find('.d-none.d-sm-inline-block')
.text()
.trim(),
).toEqual(currentUserFullname);
@@ -1020,7 +1020,7 @@ import timeoutPromise from './helpers/set_timeout_promise_helper';
const $tempNoteHeader = $tempNote.find('.note-header');
expect(
$tempNoteHeader
- .find('.d-none.d-sm-block')
+ .find('.d-none.d-sm-inline-block')
.text()
.trim(),
).toEqual('Foo &lt;script&gt;alert(&quot;XSS&quot;)&lt;/script&gt;');
diff --git a/spec/javascripts/pipelines/pipelines_spec.js b/spec/javascripts/pipelines/pipelines_spec.js
index ff17602da2b..50141bd99b4 100644
--- a/spec/javascripts/pipelines/pipelines_spec.js
+++ b/spec/javascripts/pipelines/pipelines_spec.js
@@ -427,7 +427,7 @@ describe('Pipelines', () => {
describe('methods', () => {
beforeEach(() => {
- spyOn(history, 'pushState').and.stub();
+ spyOn(window.history, 'pushState').and.stub();
});
describe('updateContent', () => {
diff --git a/spec/javascripts/settings_panels_spec.js b/spec/javascripts/settings_panels_spec.js
index 4fba36bd4de..c1a69bd7018 100644
--- a/spec/javascripts/settings_panels_spec.js
+++ b/spec/javascripts/settings_panels_spec.js
@@ -9,11 +9,11 @@ describe('Settings Panels', () => {
describe('initSettingsPane', () => {
afterEach(() => {
- location.hash = '';
+ window.location.hash = '';
});
it('should expand linked hash fragment panel', () => {
- location.hash = '#autodevops-settings';
+ window.location.hash = '#autodevops-settings';
const pipelineSettingsPanel = document.querySelector('#autodevops-settings');
// Our test environment automatically expands everything so we need to clear that out first
diff --git a/spec/javascripts/shortcuts_issuable_spec.js b/spec/javascripts/shortcuts_issuable_spec.js
index d73608ed0ed..b10d8be6781 100644
--- a/spec/javascripts/shortcuts_issuable_spec.js
+++ b/spec/javascripts/shortcuts_issuable_spec.js
@@ -66,7 +66,7 @@ describe('ShortcutsIssuable', function () {
});
describe('with a multi-line selection', () => {
it('quotes the selected lines as a group', () => {
- stubSelection('<p>Selected line one.</p>\n\n<p>Selected line two.</p>\n\n<p>Selected line three.</p>');
+ stubSelection('<p>Selected line one.</p>\n<p>Selected line two.</p>\n<p>Selected line three.</p>');
this.shortcut.replyWithSelectedText(true);
expect($(this.selector).val()).toBe('> Selected line one.\n>\n> Selected line two.\n>\n> Selected line three.\n\n');
});
diff --git a/spec/javascripts/vue_shared/components/gl_modal_spec.js b/spec/javascripts/vue_shared/components/gl_modal_spec.js
index 23be8d93b81..e4737714312 100644
--- a/spec/javascripts/vue_shared/components/gl_modal_spec.js
+++ b/spec/javascripts/vue_shared/components/gl_modal_spec.js
@@ -208,6 +208,14 @@ describe('GlModal', () => {
expect(vm.$el.querySelector('.modal-dialog').classList.contains('modal-lg')).toEqual(true);
});
+ it('should render modal-xl', () => {
+ vm = mountComponent(modalComponent, {
+ modalSize: 'xl',
+ });
+
+ expect(vm.$el.querySelector('.modal-dialog').classList.contains('modal-xl')).toEqual(true);
+ });
+
it('should not add modal size classes when md size is passed', () => {
vm = mountComponent(modalComponent, {
modalSize: 'md',
diff --git a/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb b/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb
index c19de7b784a..41f957c4e00 100644
--- a/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb
+++ b/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Banzai::Filter::ImageLazyLoadFilter, lib: true do
+describe Banzai::Filter::ImageLazyLoadFilter do
include FilterSpecHelper
def image(path)
diff --git a/spec/lib/banzai/filter/markdown_filter_spec.rb b/spec/lib/banzai/filter/markdown_filter_spec.rb
index 00c407d1b69..ab14d77d552 100644
--- a/spec/lib/banzai/filter/markdown_filter_spec.rb
+++ b/spec/lib/banzai/filter/markdown_filter_spec.rb
@@ -7,13 +7,13 @@ describe Banzai::Filter::MarkdownFilter do
it 'adds language to lang attribute when specified' do
result = filter("```html\nsome code\n```")
- expect(result).to start_with("\n<pre><code lang=\"html\">")
+ expect(result).to start_with("<pre><code lang=\"html\">")
end
it 'does not add language to lang attribute when not specified' do
result = filter("```\nsome code\n```")
- expect(result).to start_with("\n<pre><code>")
+ expect(result).to start_with("<pre><code>")
end
end
end
diff --git a/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb b/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb
index 0d2074eed22..0dee683350f 100644
--- a/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb
+++ b/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb
@@ -114,7 +114,7 @@ describe Gitlab::BackgroundMigration::PopulateUntrackedUploads, :sidekiq, :migra
it 'does not drop the temporary tracking table after processing the batch, if there are still untracked rows' do
subject.perform(1, untracked_files_for_uploads.last.id - 1)
- expect(ActiveRecord::Base.connection.table_exists?(:untracked_files_for_uploads)).to be_truthy
+ expect(ActiveRecord::Base.connection.data_source_exists?(:untracked_files_for_uploads)).to be_truthy
end
it 'drops the temporary tracking table after processing the batch, if there are no untracked rows left' do
diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb
index 8ac36ae8bab..8bb246aa4bd 100644
--- a/spec/lib/gitlab/database_spec.rb
+++ b/spec/lib/gitlab/database_spec.rb
@@ -314,8 +314,13 @@ describe Gitlab::Database do
describe '.cached_table_exists?' do
it 'only retrieves data once per table' do
- expect(ActiveRecord::Base.connection).to receive(:table_exists?).with(:projects).once.and_call_original
- expect(ActiveRecord::Base.connection).to receive(:table_exists?).with(:bogus_table_name).once.and_call_original
+ if Gitlab.rails5?
+ expect(ActiveRecord::Base.connection).to receive(:data_source_exists?).with(:projects).once.and_call_original
+ expect(ActiveRecord::Base.connection).to receive(:data_source_exists?).with(:bogus_table_name).once.and_call_original
+ else
+ expect(ActiveRecord::Base.connection).to receive(:table_exists?).with(:projects).once.and_call_original
+ expect(ActiveRecord::Base.connection).to receive(:table_exists?).with(:bogus_table_name).once.and_call_original
+ end
2.times do
expect(described_class.cached_table_exists?(:projects)).to be_truthy
diff --git a/spec/lib/gitlab/favicon_spec.rb b/spec/lib/gitlab/favicon_spec.rb
index 08c4a474217..f36111a4946 100644
--- a/spec/lib/gitlab/favicon_spec.rb
+++ b/spec/lib/gitlab/favicon_spec.rb
@@ -19,7 +19,7 @@ RSpec.describe Gitlab::Favicon, :request_store do
it 'uses the custom favicon if a favicon appearance is present' do
create :appearance, favicon: fixture_file_upload('spec/fixtures/dk.png')
- expect(described_class.main).to match %r{/uploads/-/system/appearance/favicon/\d+/favicon_main_dk.png}
+ expect(described_class.main).to match %r{/uploads/-/system/appearance/favicon/\d+/dk.png}
end
end
diff --git a/spec/lib/gitlab/git/blame_spec.rb b/spec/lib/gitlab/git/blame_spec.rb
index 793228701cf..ba790b717ae 100644
--- a/spec/lib/gitlab/git/blame_spec.rb
+++ b/spec/lib/gitlab/git/blame_spec.rb
@@ -7,7 +7,7 @@ describe Gitlab::Git::Blame, seed_helper: true do
Gitlab::Git::Blame.new(repository, SeedRepo::Commit::ID, "CONTRIBUTING.md")
end
- shared_examples 'blaming a file' do
+ describe 'blaming a file' do
context "each count" do
it do
data = []
@@ -68,12 +68,4 @@ describe Gitlab::Git::Blame, seed_helper: true do
end
end
end
-
- context 'when Gitaly blame feature is enabled' do
- it_behaves_like 'blaming a file'
- end
-
- context 'when Gitaly blame feature is disabled', :skip_gitaly_mock do
- it_behaves_like 'blaming a file'
- end
end
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index 1744db1b17e..5bae99101e6 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -77,17 +77,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
describe '#root_ref' do
- context 'with gitaly disabled' do
- before do
- allow(Gitlab::GitalyClient).to receive(:feature_enabled?).and_return(false)
- end
-
- it 'calls #discover_default_branch' do
- expect(repository).to receive(:discover_default_branch)
- repository.root_ref
- end
- end
-
it 'returns UTF-8' do
expect(repository.root_ref).to be_utf8
end
@@ -153,46 +142,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
- describe "#discover_default_branch" do
- let(:master) { 'master' }
- let(:feature) { 'feature' }
- let(:feature2) { 'feature2' }
-
- around do |example|
- # discover_default_branch will be moved to gitaly-ruby
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- example.run
- end
- end
-
- it "returns 'master' when master exists" do
- expect(repository).to receive(:branch_names).at_least(:once).and_return([feature, master])
- expect(repository.discover_default_branch).to eq('master')
- end
-
- it "returns non-master when master exists but default branch is set to something else" do
- File.write(File.join(repository_path, 'HEAD'), 'ref: refs/heads/feature')
- expect(repository).to receive(:branch_names).at_least(:once).and_return([feature, master])
- expect(repository.discover_default_branch).to eq('feature')
- File.write(File.join(repository_path, 'HEAD'), 'ref: refs/heads/master')
- end
-
- it "returns a non-master branch when only one exists" do
- expect(repository).to receive(:branch_names).at_least(:once).and_return([feature])
- expect(repository.discover_default_branch).to eq('feature')
- end
-
- it "returns a non-master branch when more than one exists and master does not" do
- expect(repository).to receive(:branch_names).at_least(:once).and_return([feature, feature2])
- expect(repository.discover_default_branch).to eq('feature')
- end
-
- it "returns nil when no branch exists" do
- expect(repository).to receive(:branch_names).at_least(:once).and_return([])
- expect(repository.discover_default_branch).to be_nil
- end
- end
-
describe '#branch_names' do
subject { repository.branch_names }
@@ -476,7 +425,7 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
describe '#has_local_branches?' do
- shared_examples 'check for local branches' do
+ context 'check for local branches' do
it { expect(repository.has_local_branches?).to eq(true) }
context 'mutable' do
@@ -510,14 +459,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
end
-
- context 'with gitaly' do
- it_behaves_like 'check for local branches'
- end
-
- context 'without gitaly', :skip_gitaly_mock do
- it_behaves_like 'check for local branches'
- end
end
describe "#delete_branch" do
@@ -1395,24 +1336,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
- # With Gitaly enabled, Gitaly just doesn't return deleted branches.
- context 'with deleted branch with Gitaly disabled' do
- before do
- allow(Gitlab::GitalyClient).to receive(:feature_enabled?).and_return(false)
- end
-
- it 'returns no results' do
- ref = double()
- allow(ref).to receive(:name) { 'bad-branch' }
- allow(ref).to receive(:target) { raise Rugged::ReferenceError }
- branches = double()
- allow(branches).to receive(:each) { [ref].each }
- allow(repository_rugged).to receive(:branches) { branches }
-
- expect(subject).to be_empty
- end
- end
-
it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::RefService, :branches
end
diff --git a/spec/lib/gitlab/git_access_wiki_spec.rb b/spec/lib/gitlab/git_access_wiki_spec.rb
index 730ede99fc9..9c6c9fe13bf 100644
--- a/spec/lib/gitlab/git_access_wiki_spec.rb
+++ b/spec/lib/gitlab/git_access_wiki_spec.rb
@@ -52,7 +52,9 @@ describe Gitlab::GitAccessWiki do
context 'when the wiki repository does not exist' do
it 'returns not found' do
wiki_repo = project.wiki.repository
- FileUtils.rm_rf(wiki_repo.path)
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ FileUtils.rm_rf(wiki_repo.path)
+ end
# Sanity check for rm_rf
expect(wiki_repo.exists?).to eq(false)
diff --git a/spec/lib/gitlab/verify/job_artifacts_spec.rb b/spec/lib/gitlab/verify/job_artifacts_spec.rb
index ec490bdfde2..6e916a56564 100644
--- a/spec/lib/gitlab/verify/job_artifacts_spec.rb
+++ b/spec/lib/gitlab/verify/job_artifacts_spec.rb
@@ -21,15 +21,38 @@ describe Gitlab::Verify::JobArtifacts do
FileUtils.rm_f(artifact.file.path)
expect(failures.keys).to contain_exactly(artifact)
- expect(failure).to be_a(Errno::ENOENT)
- expect(failure.to_s).to include(artifact.file.path)
+ expect(failure).to include('No such file or directory')
+ expect(failure).to include(artifact.file.path)
end
it 'fails artifacts with a mismatched checksum' do
File.truncate(artifact.file.path, 0)
expect(failures.keys).to contain_exactly(artifact)
- expect(failure.to_s).to include('Checksum mismatch')
+ expect(failure).to include('Checksum mismatch')
+ end
+
+ context 'with remote files' do
+ let(:file) { double(:file) }
+
+ before do
+ stub_artifacts_object_storage
+ artifact.update!(file_store: ObjectStorage::Store::REMOTE)
+ expect(CarrierWave::Storage::Fog::File).to receive(:new).and_return(file)
+ end
+
+ it 'passes artifacts in object storage that exist' do
+ expect(file).to receive(:exists?).and_return(true)
+
+ expect(failures).to eq({})
+ end
+
+ it 'fails artifacts in object storage that do not exist' do
+ expect(file).to receive(:exists?).and_return(false)
+
+ expect(failures.keys).to contain_exactly(artifact)
+ expect(failure).to include('Remote object does not exist')
+ end
end
end
end
diff --git a/spec/lib/gitlab/verify/lfs_objects_spec.rb b/spec/lib/gitlab/verify/lfs_objects_spec.rb
index 0f890e2c7ce..2feaedd6f14 100644
--- a/spec/lib/gitlab/verify/lfs_objects_spec.rb
+++ b/spec/lib/gitlab/verify/lfs_objects_spec.rb
@@ -21,30 +21,37 @@ describe Gitlab::Verify::LfsObjects do
FileUtils.rm_f(lfs_object.file.path)
expect(failures.keys).to contain_exactly(lfs_object)
- expect(failure).to be_a(Errno::ENOENT)
- expect(failure.to_s).to include(lfs_object.file.path)
+ expect(failure).to include('No such file or directory')
+ expect(failure).to include(lfs_object.file.path)
end
it 'fails LFS objects with a mismatched oid' do
File.truncate(lfs_object.file.path, 0)
expect(failures.keys).to contain_exactly(lfs_object)
- expect(failure.to_s).to include('Checksum mismatch')
+ expect(failure).to include('Checksum mismatch')
end
context 'with remote files' do
+ let(:file) { double(:file) }
+
before do
stub_lfs_object_storage
+ lfs_object.update!(file_store: ObjectStorage::Store::REMOTE)
+ expect(CarrierWave::Storage::Fog::File).to receive(:new).and_return(file)
end
- it 'skips LFS objects in object storage' do
- local_failure = create(:lfs_object)
- create(:lfs_object, :object_storage)
+ it 'passes LFS objects in object storage that exist' do
+ expect(file).to receive(:exists?).and_return(true)
+
+ expect(failures).to eq({})
+ end
- failures = {}
- described_class.new(batch_size: 10).run_batches { |_, failed| failures.merge!(failed) }
+ it 'fails LFS objects in object storage that do not exist' do
+ expect(file).to receive(:exists?).and_return(false)
- expect(failures.keys).to contain_exactly(local_failure)
+ expect(failures.keys).to contain_exactly(lfs_object)
+ expect(failure).to include('Remote object does not exist')
end
end
end
diff --git a/spec/lib/gitlab/verify/uploads_spec.rb b/spec/lib/gitlab/verify/uploads_spec.rb
index 85768308edc..296866d3319 100644
--- a/spec/lib/gitlab/verify/uploads_spec.rb
+++ b/spec/lib/gitlab/verify/uploads_spec.rb
@@ -23,37 +23,44 @@ describe Gitlab::Verify::Uploads do
FileUtils.rm_f(upload.absolute_path)
expect(failures.keys).to contain_exactly(upload)
- expect(failure).to be_a(Errno::ENOENT)
- expect(failure.to_s).to include(upload.absolute_path)
+ expect(failure).to include('No such file or directory')
+ expect(failure).to include(upload.absolute_path)
end
it 'fails uploads with a mismatched checksum' do
upload.update!(checksum: 'something incorrect')
expect(failures.keys).to contain_exactly(upload)
- expect(failure.to_s).to include('Checksum mismatch')
+ expect(failure).to include('Checksum mismatch')
end
it 'fails uploads with a missing precalculated checksum' do
upload.update!(checksum: '')
expect(failures.keys).to contain_exactly(upload)
- expect(failure.to_s).to include('Checksum missing')
+ expect(failure).to include('Checksum missing')
end
context 'with remote files' do
+ let(:file) { double(:file) }
+
before do
stub_uploads_object_storage(AvatarUploader)
+ upload.update!(store: ObjectStorage::Store::REMOTE)
+ expect(CarrierWave::Storage::Fog::File).to receive(:new).and_return(file)
end
- it 'skips uploads in object storage' do
- local_failure = create(:upload)
- create(:upload, :object_storage)
+ it 'passes uploads in object storage that exist' do
+ expect(file).to receive(:exists?).and_return(true)
+
+ expect(failures).to eq({})
+ end
- failures = {}
- described_class.new(batch_size: 10).run_batches { |_, failed| failures.merge!(failed) }
+ it 'fails uploads in object storage that do not exist' do
+ expect(file).to receive(:exists?).and_return(false)
- expect(failures.keys).to contain_exactly(local_failure)
+ expect(failures.keys).to contain_exactly(upload)
+ expect(failure).to include('Remote object does not exist')
end
end
end
diff --git a/spec/migrations/migrate_process_commit_worker_jobs_spec.rb b/spec/migrations/migrate_process_commit_worker_jobs_spec.rb
index 4ee1d255fbd..ac34efa4f9d 100644
--- a/spec/migrations/migrate_process_commit_worker_jobs_spec.rb
+++ b/spec/migrations/migrate_process_commit_worker_jobs_spec.rb
@@ -6,7 +6,11 @@ require Rails.root.join('db', 'migrate', '20161124141322_migrate_process_commit_
describe MigrateProcessCommitWorkerJobs do
let(:project) { create(:project, :legacy_storage, :repository) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
let(:user) { create(:user) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
- let(:commit) { project.commit.raw.rugged_commit }
+ let(:commit) do
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ project.commit.raw.rugged_commit
+ end
+ end
describe 'Project' do
describe 'find_including_path' do
diff --git a/spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb b/spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb
index 560409f08de..5f5ba426d69 100644
--- a/spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb
+++ b/spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb
@@ -49,10 +49,14 @@ describe TurnNestedGroupsIntoRegularGroupsForMysql do
end
it 'renames the repository of any projects' do
- expect(updated_project.repository.path)
+ repo_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ updated_project.repository.path
+ end
+
+ expect(repo_path)
.to end_with("#{parent_group.name}-#{child_group.name}/#{updated_project.path}.git")
- expect(File.directory?(updated_project.repository.path)).to eq(true)
+ expect(File.directory?(repo_path)).to eq(true)
end
it 'creates a redirect route for renamed projects' do
diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb
index cbcf1e55979..b5a6d959ccb 100644
--- a/spec/models/ci/build_trace_chunk_spec.rb
+++ b/spec/models/ci/build_trace_chunk_spec.rb
@@ -54,14 +54,6 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
it { is_expected.to eq('Sample data in db') }
end
-
- context 'when data_store is others' do
- before do
- build_trace_chunk.send(:write_attribute, :data_store, -1)
- end
-
- it { expect { subject }.to raise_error('Unsupported data store') }
- end
end
describe '#set_data' do
diff --git a/spec/models/concerns/cache_markdown_field_spec.rb b/spec/models/concerns/cache_markdown_field_spec.rb
index b3797c1fb46..2d75422ee68 100644
--- a/spec/models/concerns/cache_markdown_field_spec.rb
+++ b/spec/models/concerns/cache_markdown_field_spec.rb
@@ -156,7 +156,7 @@ describe CacheMarkdownField do
end
it { expect(thing.foo_html).to eq(updated_html) }
- it { expect(thing.cached_markdown_version).to eq(CacheMarkdownField::CACHE_REDCARPET_VERSION) }
+ it { expect(thing.cached_markdown_version).to eq(CacheMarkdownField::CACHE_COMMONMARK_VERSION) }
end
describe '#cached_html_up_to_date?' do
@@ -234,7 +234,7 @@ describe CacheMarkdownField do
it 'returns default version when version is nil' do
thing.cached_markdown_version = nil
- is_expected.to eq(CacheMarkdownField::CACHE_REDCARPET_VERSION)
+ is_expected.to eq(CacheMarkdownField::CACHE_COMMONMARK_VERSION)
end
end
@@ -261,7 +261,7 @@ describe CacheMarkdownField do
thing.cached_markdown_version = nil
thing.refresh_markdown_cache
- expect(thing.cached_markdown_version).to eq(CacheMarkdownField::CACHE_REDCARPET_VERSION)
+ expect(thing.cached_markdown_version).to eq(CacheMarkdownField::CACHE_COMMONMARK_VERSION)
end
end
@@ -346,7 +346,7 @@ describe CacheMarkdownField do
expect(thing.foo_html).to eq(updated_html)
expect(thing.baz_html).to eq(updated_html)
- expect(thing.cached_markdown_version).to eq(CacheMarkdownField::CACHE_REDCARPET_VERSION)
+ expect(thing.cached_markdown_version).to eq(CacheMarkdownField::CACHE_COMMONMARK_VERSION)
end
end
@@ -366,7 +366,7 @@ describe CacheMarkdownField do
expect(thing.foo_html).to eq(updated_html)
expect(thing.baz_html).to eq(updated_html)
- expect(thing.cached_markdown_version).to eq(CacheMarkdownField::CACHE_REDCARPET_VERSION)
+ expect(thing.cached_markdown_version).to eq(CacheMarkdownField::CACHE_COMMONMARK_VERSION)
end
end
end
diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb
index b9f1c7dd5df..6c637533c6b 100644
--- a/spec/models/project_services/jira_service_spec.rb
+++ b/spec/models/project_services/jira_service_spec.rb
@@ -478,7 +478,7 @@ describe JiraService do
create :appearance, favicon: fixture_file_upload('spec/fixtures/dk.png')
props = described_class.new.send(:build_remote_link_props, url: 'http://example.com', title: 'title')
- expect(props[:object][:icon][:url16x16]).to match %r{^http://localhost/uploads/-/system/appearance/favicon/\d+/favicon_main_dk.png$}
+ expect(props[:object][:icon][:url16x16]).to match %r{^http://localhost/uploads/-/system/appearance/favicon/\d+/dk.png$}
end
end
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 585cf7aab44..bc9cce6b0c3 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -2943,7 +2943,7 @@ describe Project do
project.rename_repo
- expect(project.repository.rugged.config['gitlab.fullpath']).to eq(project.full_path)
+ expect(rugged_config['gitlab.fullpath']).to eq(project.full_path)
end
end
@@ -3104,7 +3104,7 @@ describe Project do
it 'updates project full path in .git/config' do
project.rename_repo
- expect(project.repository.rugged.config['gitlab.fullpath']).to eq(project.full_path)
+ expect(rugged_config['gitlab.fullpath']).to eq(project.full_path)
end
end
@@ -3525,13 +3525,13 @@ describe Project do
it 'writes full path in .git/config when key is missing' do
project.write_repository_config
- expect(project.repository.rugged.config['gitlab.fullpath']).to eq project.full_path
+ expect(rugged_config['gitlab.fullpath']).to eq project.full_path
end
it 'updates full path in .git/config when key is present' do
project.write_repository_config(gl_full_path: 'old/path')
- expect { project.write_repository_config }.to change { project.repository.rugged.config['gitlab.fullpath'] }.from('old/path').to(project.full_path)
+ expect { project.write_repository_config }.to change { rugged_config['gitlab.fullpath'] }.from('old/path').to(project.full_path)
end
it 'does not raise an error with an empty repository' do
@@ -3817,4 +3817,10 @@ describe Project do
let(:uploader_class) { AttachmentUploader }
end
end
+
+ def rugged_config
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ project.repository.rugged.config
+ end
+ end
end
diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb
index f1142832f1a..a3c20b3b3c1 100644
--- a/spec/models/project_wiki_spec.rb
+++ b/spec/models/project_wiki_spec.rb
@@ -188,7 +188,11 @@ describe ProjectWiki do
before do
subject.wiki # Make sure the wiki repo exists
- BareRepoOperations.new(subject.repository.path_to_repo).commit_file(image, 'image.png')
+ repo_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ subject.repository.path_to_repo
+ end
+
+ BareRepoOperations.new(repo_path).commit_file(image, 'image.png')
end
it 'returns the latest version of the file if it exists' do
diff --git a/spec/models/remote_mirror_spec.rb b/spec/models/remote_mirror_spec.rb
index 4c086eeadfc..3597b080021 100644
--- a/spec/models/remote_mirror_spec.rb
+++ b/spec/models/remote_mirror_spec.rb
@@ -74,7 +74,9 @@ describe RemoteMirror do
mirror.update_attribute(:url, 'http://foo:baz@test.com')
- config = repo.raw_repository.rugged.config
+ config = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ repo.raw_repository.rugged.config
+ end
expect(config["remote.#{mirror.remote_name}.url"]).to eq('http://foo:baz@test.com')
end
diff --git a/spec/requests/api/graphql/merge_request_query_spec.rb b/spec/requests/api/graphql/merge_request_query_spec.rb
deleted file mode 100644
index 12b1d5d18a2..00000000000
--- a/spec/requests/api/graphql/merge_request_query_spec.rb
+++ /dev/null
@@ -1,49 +0,0 @@
-require 'spec_helper'
-
-describe 'getting merge request information' do
- include GraphqlHelpers
-
- let(:project) { create(:project, :repository) }
- let(:merge_request) { create(:merge_request, source_project: project) }
- let(:current_user) { create(:user) }
-
- let(:query) do
- attributes = {
- 'fullPath' => merge_request.project.full_path,
- 'iid' => merge_request.iid
- }
- graphql_query_for('mergeRequest', attributes)
- end
-
- context 'when the user has access to the merge request' do
- before do
- project.add_developer(current_user)
- post_graphql(query, current_user: current_user)
- end
-
- it 'returns the merge request' do
- expect(graphql_data['mergeRequest']).not_to be_nil
- end
-
- # This is a field coming from the `MergeRequestPresenter`
- it 'includes a web_url' do
- expect(graphql_data['mergeRequest']['webUrl']).to be_present
- end
-
- it_behaves_like 'a working graphql query'
- end
-
- context 'when the user does not have access to the merge request' do
- before do
- post_graphql(query, current_user: current_user)
- end
-
- it 'returns an empty field' do
- post_graphql(query, current_user: current_user)
-
- expect(graphql_data['mergeRequest']).to be_nil
- end
-
- it_behaves_like 'a working graphql query'
- end
-end
diff --git a/spec/requests/api/graphql/project_query_spec.rb b/spec/requests/api/graphql/project_query_spec.rb
index 8196bcfa87c..796ffc9d569 100644
--- a/spec/requests/api/graphql/project_query_spec.rb
+++ b/spec/requests/api/graphql/project_query_spec.rb
@@ -13,27 +13,76 @@ describe 'getting project information' do
context 'when the user has access to the project' do
before do
project.add_developer(current_user)
- post_graphql(query, current_user: current_user)
end
it 'includes the project' do
+ post_graphql(query, current_user: current_user)
+
expect(graphql_data['project']).not_to be_nil
end
- it_behaves_like 'a working graphql query'
- end
+ it_behaves_like 'a working graphql query' do
+ before do
+ post_graphql(query, current_user: current_user)
+ end
+ end
- context 'when the user does not have access to the project' do
- before do
- post_graphql(query, current_user: current_user)
+ context 'when requesting a nested merge request' do
+ let(:merge_request) { create(:merge_request, source_project: project) }
+ let(:merge_request_graphql_data) { graphql_data['project']['mergeRequest'] }
+
+ let(:query) do
+ graphql_query_for(
+ 'project',
+ { 'fullPath' => project.full_path },
+ query_graphql_field('mergeRequest', iid: merge_request.iid)
+ )
+ end
+
+ it_behaves_like 'a working graphql query' do
+ before do
+ post_graphql(query, current_user: current_user)
+ end
+ end
+
+ it 'contains merge request information' do
+ post_graphql(query, current_user: current_user)
+
+ expect(merge_request_graphql_data).not_to be_nil
+ end
+
+ # This is a field coming from the `MergeRequestPresenter`
+ it 'includes a web_url' do
+ post_graphql(query, current_user: current_user)
+
+ expect(merge_request_graphql_data['webUrl']).to be_present
+ end
+
+ context 'when the user does not have access to the merge request' do
+ let(:project) { create(:project, :public, :repository) }
+
+ it 'returns nil' do
+ project.project_feature.update!(merge_requests_access_level: ProjectFeature::PRIVATE)
+
+ post_graphql(query)
+
+ expect(merge_request_graphql_data).to be_nil
+ end
+ end
end
+ end
+ context 'when the user does not have access to the project' do
it 'returns an empty field' do
post_graphql(query, current_user: current_user)
expect(graphql_data['project']).to be_nil
end
- it_behaves_like 'a working graphql query'
+ it_behaves_like 'a working graphql query' do
+ before do
+ post_graphql(query, current_user: current_user)
+ end
+ end
end
end
diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb
index bc32372d3a9..a56b913198c 100644
--- a/spec/requests/api/internal_spec.rb
+++ b/spec/requests/api/internal_spec.rb
@@ -522,7 +522,6 @@ describe API::Internal do
context 'the project path was changed' do
let(:project) { create(:project, :repository, :legacy_storage) }
- let!(:old_path_to_repo) { project.repository.path_to_repo }
let!(:repository) { project.repository }
before do
diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb
index a8f003b1073..e8cbf84e3be 100644
--- a/spec/services/projects/create_service_spec.rb
+++ b/spec/services/projects/create_service_spec.rb
@@ -272,8 +272,11 @@ describe Projects::CreateService, '#execute' do
it 'writes project full path to .git/config' do
project = create_project(user, opts)
+ rugged = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ project.repository.rugged
+ end
- expect(project.repository.rugged.config['gitlab.fullpath']).to eq project.full_path
+ expect(rugged.config['gitlab.fullpath']).to eq project.full_path
end
def create_project(user, opts)
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 8417b340de5..dac609e2545 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -87,6 +87,7 @@ RSpec.configure do |config|
config.include LiveDebugger, :js
config.include MigrationsHelpers, :migration
config.include RedisHelpers
+ config.include Rails.application.routes.url_helpers, type: :routing
if ENV['CI']
# This includes the first try, i.e. tests will be run 4 times before failing.
diff --git a/spec/support/gitaly.rb b/spec/support/gitaly.rb
index 5a1dd44bc9d..614aaa73693 100644
--- a/spec/support/gitaly.rb
+++ b/spec/support/gitaly.rb
@@ -9,7 +9,7 @@ RSpec.configure do |config|
# Use 'and_wrap_original' to make sure the arguments are valid
allow(Gitlab::GitalyClient).to receive(:feature_enabled?).and_wrap_original do |m, *args|
m.call(*args)
- !Gitlab::GitalyClient::EXPLICIT_OPT_IN_REQUIRED.include?(args.first)
+ !Gitlab::GitalyClient.explicit_opt_in_required.include?(args.first)
end
end
end
diff --git a/spec/support/helpers/graphql_helpers.rb b/spec/support/helpers/graphql_helpers.rb
index 30ff9a1196a..0930b9da368 100644
--- a/spec/support/helpers/graphql_helpers.rb
+++ b/spec/support/helpers/graphql_helpers.rb
@@ -34,14 +34,20 @@ module GraphqlHelpers
end
def graphql_query_for(name, attributes = {}, fields = nil)
+ <<~QUERY
+ {
+ #{query_graphql_field(name, attributes, fields)}
+ }
+ QUERY
+ end
+
+ def query_graphql_field(name, attributes = {}, fields = nil)
fields ||= all_graphql_fields_for(name.classify)
attributes = attributes_to_graphql(attributes)
<<~QUERY
- {
#{name}(#{attributes}) {
#{fields}
}
- }
QUERY
end
@@ -50,12 +56,15 @@ module GraphqlHelpers
return "" unless type
type.fields.map do |name, field|
+ # We can't guess arguments, so skip fields that require them
+ next if field.arguments.any?
+
if scalar?(field)
name
else
"#{name} { #{all_graphql_fields_for(field_type(field))} }"
end
- end.join("\n")
+ end.compact.join("\n")
end
def attributes_to_graphql(attributes)
diff --git a/spec/support/helpers/migrations_helpers.rb b/spec/support/helpers/migrations_helpers.rb
index 84abec75c26..0bc235701eb 100644
--- a/spec/support/helpers/migrations_helpers.rb
+++ b/spec/support/helpers/migrations_helpers.rb
@@ -10,10 +10,6 @@ module MigrationsHelpers
ActiveRecord::Migrator.migrations_paths
end
- def table_exists?(name)
- ActiveRecord::Base.connection.table_exists?(name)
- end
-
def migrations
ActiveRecord::Migrator.migrations(migrations_paths)
end
diff --git a/spec/support/matchers/graphql_matchers.rb b/spec/support/matchers/graphql_matchers.rb
index ba7a1c8cde0..d23cbaf4beb 100644
--- a/spec/support/matchers/graphql_matchers.rb
+++ b/spec/support/matchers/graphql_matchers.rb
@@ -13,6 +13,12 @@ RSpec::Matchers.define :have_graphql_fields do |*expected|
end
end
+RSpec::Matchers.define :have_graphql_field do |field_name|
+ match do |kls|
+ expect(kls.fields.keys).to include(GraphqlHelpers.fieldnamerize(field_name))
+ end
+end
+
RSpec::Matchers.define :have_graphql_arguments do |*expected|
include GraphqlHelpers
diff --git a/spec/support/shoulda/matchers/rails_shim.rb b/spec/support/shoulda/matchers/rails_shim.rb
new file mode 100644
index 00000000000..8d70598beb5
--- /dev/null
+++ b/spec/support/shoulda/matchers/rails_shim.rb
@@ -0,0 +1,27 @@
+# monkey patch which fixes serialization matcher in Rails 5
+# https://github.com/thoughtbot/shoulda-matchers/issues/913
+# This can be removed when a new version of shoulda-matchers
+# is released
+module Shoulda
+ module Matchers
+ class RailsShim
+ def self.serialized_attributes_for(model)
+ if defined?(::ActiveRecord::Type::Serialized)
+ # Rails 5+
+ serialized_columns = model.columns.select do |column|
+ model.type_for_attribute(column.name).is_a?(
+ ::ActiveRecord::Type::Serialized
+ )
+ end
+
+ serialized_columns.inject({}) do |hash, column| # rubocop:disable Style/EachWithObject
+ hash[column.name.to_s] = model.type_for_attribute(column.name).coder
+ hash
+ end
+ else
+ model.serialized_attributes
+ end
+ end
+ end
+ end
+end
diff --git a/spec/uploaders/favicon_uploader_spec.rb b/spec/uploaders/favicon_uploader_spec.rb
deleted file mode 100644
index 37deea8ab90..00000000000
--- a/spec/uploaders/favicon_uploader_spec.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-require 'spec_helper'
-
-RSpec.describe FaviconUploader do
- include CarrierWave::Test::Matchers
-
- let(:uploader) { described_class.new(build_stubbed(:user)) }
-
- after do
- uploader.remove!
- end
-
- def upload_fixture(filename)
- fixture_file_upload("spec/fixtures/#{filename}")
- end
-
- context 'versions' do
- before do
- uploader.store!(upload_fixture('dk.png'))
- end
-
- it 'has the correct format' do
- expect(uploader.favicon_main).to be_format('png')
- end
-
- it 'has the correct dimensions' do
- expect(uploader.favicon_main).to have_dimensions(32, 32)
- end
- end
-end
diff --git a/spec/views/errors/access_denied.html.haml_spec.rb b/spec/views/errors/access_denied.html.haml_spec.rb
new file mode 100644
index 00000000000..bde2f6f0169
--- /dev/null
+++ b/spec/views/errors/access_denied.html.haml_spec.rb
@@ -0,0 +1,7 @@
+require 'spec_helper'
+
+describe 'errors/access_denied' do
+ it 'does not fail to render when there is no message provided' do
+ expect { render }.not_to raise_error
+ end
+end
diff --git a/spec/workers/git_garbage_collect_worker_spec.rb b/spec/workers/git_garbage_collect_worker_spec.rb
index 807d1b8c084..e39dec556fc 100644
--- a/spec/workers/git_garbage_collect_worker_spec.rb
+++ b/spec/workers/git_garbage_collect_worker_spec.rb
@@ -11,36 +11,63 @@ describe GitGarbageCollectWorker do
subject { described_class.new }
describe "#perform" do
- shared_examples 'flushing ref caches' do |gitaly|
- context 'with active lease_uuid' do
+ context 'with active lease_uuid' do
+ before do
+ allow(subject).to receive(:get_lease_uuid).and_return(lease_uuid)
+ end
+
+ it "flushes ref caches when the task if 'gc'" do
+ expect(subject).to receive(:renew_lease).with(lease_key, lease_uuid).and_call_original
+ expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:garbage_collect)
+ .and_return(nil)
+ expect_any_instance_of(Repository).to receive(:after_create_branch).and_call_original
+ expect_any_instance_of(Repository).to receive(:branch_names).and_call_original
+ expect_any_instance_of(Repository).to receive(:has_visible_content?).and_call_original
+ expect_any_instance_of(Gitlab::Git::Repository).to receive(:has_visible_content?).and_call_original
+
+ subject.perform(project.id, :gc, lease_key, lease_uuid)
+ end
+ end
+
+ context 'with different lease than the active one' do
+ before do
+ allow(subject).to receive(:get_lease_uuid).and_return(SecureRandom.uuid)
+ end
+
+ it 'returns silently' do
+ expect_any_instance_of(Repository).not_to receive(:after_create_branch).and_call_original
+ expect_any_instance_of(Repository).not_to receive(:branch_names).and_call_original
+ expect_any_instance_of(Repository).not_to receive(:has_visible_content?).and_call_original
+
+ subject.perform(project.id, :gc, lease_key, lease_uuid)
+ end
+ end
+
+ context 'with no active lease' do
+ before do
+ allow(subject).to receive(:get_lease_uuid).and_return(false)
+ end
+
+ context 'when is able to get the lease' do
before do
- allow(subject).to receive(:get_lease_uuid).and_return(lease_uuid)
+ allow(subject).to receive(:try_obtain_lease).and_return(SecureRandom.uuid)
end
it "flushes ref caches when the task if 'gc'" do
- expect(subject).to receive(:renew_lease).with(lease_key, lease_uuid).and_call_original
- expect(subject).to receive(:command).with(:gc).and_return([:the, :command])
-
- if gitaly
- expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:garbage_collect)
- .and_return(nil)
- else
- expect(Gitlab::Popen).to receive(:popen)
- .with([:the, :command], project.repository.path_to_repo).and_return(["", 0])
- end
-
+ expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:garbage_collect)
+ .and_return(nil)
expect_any_instance_of(Repository).to receive(:after_create_branch).and_call_original
expect_any_instance_of(Repository).to receive(:branch_names).and_call_original
expect_any_instance_of(Repository).to receive(:has_visible_content?).and_call_original
expect_any_instance_of(Gitlab::Git::Repository).to receive(:has_visible_content?).and_call_original
- subject.perform(project.id, :gc, lease_key, lease_uuid)
+ subject.perform(project.id)
end
end
- context 'with different lease than the active one' do
+ context 'when no lease can be obtained' do
before do
- allow(subject).to receive(:get_lease_uuid).and_return(SecureRandom.uuid)
+ expect(subject).to receive(:try_obtain_lease).and_return(false)
end
it 'returns silently' do
@@ -49,63 +76,9 @@ describe GitGarbageCollectWorker do
expect_any_instance_of(Repository).not_to receive(:branch_names).and_call_original
expect_any_instance_of(Repository).not_to receive(:has_visible_content?).and_call_original
- subject.perform(project.id, :gc, lease_key, lease_uuid)
+ subject.perform(project.id)
end
end
-
- context 'with no active lease' do
- before do
- allow(subject).to receive(:get_lease_uuid).and_return(false)
- end
-
- context 'when is able to get the lease' do
- before do
- allow(subject).to receive(:try_obtain_lease).and_return(SecureRandom.uuid)
- end
-
- it "flushes ref caches when the task if 'gc'" do
- expect(subject).to receive(:command).with(:gc).and_return([:the, :command])
-
- if gitaly
- expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:garbage_collect)
- .and_return(nil)
- else
- expect(Gitlab::Popen).to receive(:popen)
- .with([:the, :command], project.repository.path_to_repo).and_return(["", 0])
- end
-
- expect_any_instance_of(Repository).to receive(:after_create_branch).and_call_original
- expect_any_instance_of(Repository).to receive(:branch_names).and_call_original
- expect_any_instance_of(Repository).to receive(:has_visible_content?).and_call_original
- expect_any_instance_of(Gitlab::Git::Repository).to receive(:has_visible_content?).and_call_original
-
- subject.perform(project.id)
- end
- end
-
- context 'when no lease can be obtained' do
- before do
- expect(subject).to receive(:try_obtain_lease).and_return(false)
- end
-
- it 'returns silently' do
- expect(subject).not_to receive(:command)
- expect_any_instance_of(Repository).not_to receive(:after_create_branch).and_call_original
- expect_any_instance_of(Repository).not_to receive(:branch_names).and_call_original
- expect_any_instance_of(Repository).not_to receive(:has_visible_content?).and_call_original
-
- subject.perform(project.id)
- end
- end
- end
- end
-
- context "with Gitaly turned on" do
- it_should_behave_like 'flushing ref caches', true
- end
-
- context "with Gitaly turned off", :disable_gitaly do
- it_should_behave_like 'flushing ref caches', false
end
context "repack_full" do
diff --git a/spec/workers/repository_remove_remote_worker_spec.rb b/spec/workers/repository_remove_remote_worker_spec.rb
index f22d7c1d073..5968c5da3c9 100644
--- a/spec/workers/repository_remove_remote_worker_spec.rb
+++ b/spec/workers/repository_remove_remote_worker_spec.rb
@@ -44,7 +44,9 @@ describe RepositoryRemoveRemoteWorker do
end
def create_remote_branch(remote_name, branch_name, target)
- rugged = project.repository.rugged
+ rugged = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ project.repository.rugged
+ end
rugged.references.create("refs/remotes/#{remote_name}/#{branch_name}", target.id)
end
end
diff --git a/yarn.lock b/yarn.lock
index 65d78173a5b..cefd7c9a62e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -270,11 +270,7 @@ acorn@^3.0.4:
version "3.3.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a"
-acorn@^5.0.0, acorn@^5.3.0:
- version "5.5.3"
- resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.5.3.tgz#f473dd47e0277a08e28e9bec5aeeb04751f0b8c9"
-
-acorn@^5.5.0:
+acorn@^5.0.0, acorn@^5.3.0, acorn@^5.5.0:
version "5.6.2"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.6.2.tgz#b1da1d7be2ac1b4a327fb9eab851702c5045b4e7"
@@ -4002,14 +3998,10 @@ icss-utils@^2.1.0:
dependencies:
postcss "^6.0.1"
-ieee754@^1.1.11:
+ieee754@^1.1.11, ieee754@^1.1.4:
version "1.1.11"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.11.tgz#c16384ffe00f5b7835824e67b6f2bd44a5229455"
-ieee754@^1.1.4:
- version "1.1.8"
- resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4"
-
iferr@^0.1.5:
version "0.1.5"
resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501"
@@ -6247,11 +6239,7 @@ preserve@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
-prettier@1.11.1:
- version "1.11.1"
- resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.11.1.tgz#61e43fc4cd44e68f2b0dfc2c38cd4bb0fccdcc75"
-
-prettier@^1.11.1:
+prettier@1.12.1, prettier@^1.11.1:
version "1.12.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.12.1.tgz#c1ad20e803e7749faf905a409d2367e06bbe7325"